Add cmd-k to delete current line
All checks were successful
Build / build (push) Successful in 2m31s

This commit is contained in:
Leon Mika 2026-03-14 21:52:13 +11:00
parent 3e8d101eec
commit d64779f660
10 changed files with 166 additions and 0 deletions

22
app.go
View file

@ -62,6 +62,28 @@ func (a *App) ListProcessors() (resp []ListProcessorsResponse) {
return resp return resp
} }
func (a *App) TriggerTextProcess(action string) {
runtime.EventsEmit(a.ctx, "request-text-process", RequestTextProcess{
Action: action,
})
}
func (a *App) PromptUser(label string, resp func(string)) {
runtime.EventsOnce(a.ctx, "prompt-response", func(data ...interface{}) {
if len(data) == 0 {
return
}
ans, ok := data[0].(string)
if !ok {
return
}
resp(ans)
})
runtime.EventsEmit(a.ctx, "prompt-request", PromptRequest{
Label: label,
})
}
func (a *App) ProcessText(req ProcessTextRequest) { func (a *App) ProcessText(req ProcessTextRequest) {
filter, ok := TextFilters[req.Action] filter, ok := TextFilters[req.Action]
if !ok { if !ok {

View file

@ -10,6 +10,15 @@
<div class="editor-mountpoint"></div> <div class="editor-mountpoint"></div>
<div class="status-bar deemphasized" data-controller="status">Cmd+P: open command palette. Shift+Cmd+P: rerun last command.</div> <div class="status-bar deemphasized" data-controller="status">Cmd+P: open command palette. Shift+Cmd+P: rerun last command.</div>
</div> </div>
<dialog id="prompt-dialog" data-controller="prompt">
<div class="dialog-body">
<label class="prompt-label" data-prompt-target="label"></label>
<div class="prompt-input">
<input data-prompt-target="input" type="text"
data-action="keyup.enter->prompt#submit keydown.esc->prompt#dismiss">
</div>
</div>
</dialog>
<dialog id="command-dialog" data-controller="commands" <dialog id="command-dialog" data-controller="commands"
data-action="dq-showcommands@window->commands#showCommands dq-rerunlastcommand@window->commands#rerunLastCommand"> data-action="dq-showcommands@window->commands#showCommands dq-rerunlastcommand@window->commands#rerunLastCommand">
<div class="dialog-body"> <div class="dialog-body">

View file

@ -27,6 +27,52 @@
font-size: 1.1em; font-size: 1.1em;
} }
dialog#prompt-dialog {
width: 450px;
max-width: 50%;
position: absolute;
top: 0;
left: 50%;
transform: translate(-50%, -50%);
padding: 0;
/* Frosted glass effect */
background: rgba(225, 225, 225, 0.38);
backdrop-filter: blur(15px);
-webkit-backdrop-filter: blur(15px);
/* Subtle grey border and rounded corners */
border: 1px solid rgba(169, 169, 169, 0.3);
border-radius: 12px;
/* Add subtle shadow for depth */
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
}
dialog#prompt-dialog .dialog-body {
display: flex;
flex-direction: column;
padding: 10px;
}
dialog#prompt-dialog .prompt-label {
font-size: 0.9em;
color: rgba(0, 0, 0, 0.6);
margin-bottom: 6px;
}
dialog#prompt-dialog input {
width: 100%;
border: none;
text-decoration: none;
outline: none;
font-size: 1.3em;
background-color: transparent;
font-weight: normal;
}
dialog#command-dialog { dialog#command-dialog {
width: 450px; width: 450px;
height: 400px; height: 400px;

View file

@ -43,6 +43,28 @@ export const commandPalette = keymap.of([{
let event = new CustomEvent('dq-rerunlastcommand'); let event = new CustomEvent('dq-rerunlastcommand');
window.dispatchEvent(event); window.dispatchEvent(event);
return true;
}
}, {
key: "Cmd-k",
run: (view) => {
const {state} = view;
const changes = [];
for (let range of state.selection.ranges) {
const line = state.doc.lineAt(range.head);
changes.push({
from: line.from,
to: line.to < state.doc.length ? line.to + 1 : line.to,
insert: ""
});
}
view.dispatch({
changes: changes,
sequential: true
});
return true; return true;
} }
}]); }]);

View file

@ -0,0 +1,42 @@
import { Controller } from "@hotwired/stimulus"
export class PromptController extends Controller {
static targets = [
"label",
"input",
];
connect() {
this._callback = null;
window.runtime.EventsOn("prompt-request", (data) => {
this.prompt(data.label, (res) => {
window.runtime.EventsEmit("prompt-response", res);
});
});
}
prompt(label, callback) {
this._callback = callback;
this.labelTarget.textContent = label;
this.inputTarget.value = "";
this.element.showModal();
this.inputTarget.focus();
}
submit(ev) {
ev.preventDefault();
let value = this.inputTarget.value;
this.element.close();
if (this._callback) {
this._callback(value);
this._callback = null;
}
}
dismiss(ev) {
ev.preventDefault();
this._callback = null;
this.element.close();
}
}

View file

@ -11,6 +11,7 @@ import {indentWithTab} from "@codemirror/commands";
import {StatusController} from "./controllers/status_controller.js"; import {StatusController} from "./controllers/status_controller.js";
import {CommandsController} from "./controllers/commands_controller.js"; import {CommandsController} from "./controllers/commands_controller.js";
import {PromptController} from "./controllers/prompt_controller.js";
@ -28,6 +29,7 @@ const view = new EditorView({
window.Stimulus = Application.start() window.Stimulus = Application.start()
Stimulus.register("commands", CommandsController); Stimulus.register("commands", CommandsController);
Stimulus.register("prompt", PromptController);
Stimulus.register("status", StatusController); Stimulus.register("status", StatusController);
textProcessor.setCodeMirrorEditor(view); textProcessor.setCodeMirrorEditor(view);

View file

@ -29,6 +29,9 @@ class TextProcessor {
}); });
this._editor.dispatch({ changes: changes }); this._editor.dispatch({ changes: changes });
}); });
window.runtime.EventsOn("request-text-process", (data) => {
this.runTextCommand(data.action);
});
} }
async runTextCommand(command) { async runTextCommand(command) {

View file

@ -8,4 +8,8 @@ export function LoadCurrentBuffer():Promise<string>;
export function ProcessText(arg1:main.ProcessTextRequest):Promise<void>; export function ProcessText(arg1:main.ProcessTextRequest):Promise<void>;
export function PromptUser(arg1:string,arg2:any):Promise<void>;
export function SaveCurrentBuffer(arg1:string):Promise<void>; export function SaveCurrentBuffer(arg1:string):Promise<void>;
export function TriggerTextProcess(arg1:string):Promise<void>;

View file

@ -14,6 +14,14 @@ export function ProcessText(arg1) {
return window['go']['main']['App']['ProcessText'](arg1); return window['go']['main']['App']['ProcessText'](arg1);
} }
export function PromptUser(arg1, arg2) {
return window['go']['main']['App']['PromptUser'](arg1, arg2);
}
export function SaveCurrentBuffer(arg1) { export function SaveCurrentBuffer(arg1) {
return window['go']['main']['App']['SaveCurrentBuffer'](arg1); return window['go']['main']['App']['SaveCurrentBuffer'](arg1);
} }
export function TriggerTextProcess(arg1) {
return window['go']['main']['App']['TriggerTextProcess'](arg1);
}

View file

@ -25,3 +25,11 @@ type SetStatusbarMessage struct {
type ProcessTextResponse struct { type ProcessTextResponse struct {
Output []TextSpan `json:"output"` Output []TextSpan `json:"output"`
} }
type RequestTextProcess struct {
Action string `json:"action"`
}
type PromptRequest struct {
Label string `json:"label"`
}