Have got text transformations working

This commit is contained in:
Leon Mika 2025-09-06 11:26:54 +10:00
parent 9f2fa96b92
commit 41daf7cfc9
14 changed files with 178 additions and 17 deletions

33
app.go
View file

@ -2,7 +2,10 @@ package main
import ( import (
"context" "context"
"fmt" "log"
"github.com/wailsapp/wails/v2/pkg/runtime"
"lmika.dev/pkg/modash/moslice"
) )
// App struct // App struct
@ -22,6 +25,30 @@ func (a *App) startup(ctx context.Context) {
} }
// Greet returns a greeting for the given name // Greet returns a greeting for the given name
func (a *App) Greet(name string) string { func (a *App) ProcessText(req ProcessTextRequest) {
return fmt.Sprintf("Hello %s, It's show time!", name) filter, ok := TextFilters[req.Action]
if !ok {
log.Printf("Unknown filter: [%s]", req.Action)
return
}
resp, err := moslice.MapWithError(req.Input, func(span TextSpan) (TextSpan, error) {
outStr, err := filter(span.Text)
if err != nil {
return TextSpan{}, err
}
return TextSpan{
Text: outStr,
Pos: span.Pos,
Len: span.Len,
}, nil
})
if err != nil {
log.Printf("Error running filter: %s", err)
return
}
runtime.EventsEmit(a.ctx, "process-text-response", ProcessTextResponse{
Output: resp,
})
} }

View file

@ -15,10 +15,8 @@
data-action="keyup.enter->commands#runCommand keydown.esc->commands#dismissDialog keyup->commands#handleKeyup"> data-action="keyup.enter->commands#runCommand keydown.esc->commands#dismissDialog keyup->commands#handleKeyup">
</div> </div>
<select multiple class="command-options" data-commands-target="commandSelect"> <select multiple class="command-options" data-commands-target="commandSelect">
<option value="double"><span class="option-label">Double quotes</span></option> <option value="unquote"><span class="option-label">Unquote</span></option>
<option value="single"><span class="option-label">Single</span></option> <option value="format-json"><span class="option-label">Format JSON</span></option>
<option value="backtick"><span class="option-label">Backtick quotes</span></option>
<option value="none"><span class="option-label">None</span></option>
</select> </select>
</div> </div>
</dialog> </dialog>

View file

@ -1,5 +1,6 @@
.cm-editor { .cm-editor {
height: 100%; height: 100%;
font-size: 1.1em;
} }
dialog#command-dialog { dialog#command-dialog {

View file

@ -1,6 +1,8 @@
import { Controller } from "@hotwired/stimulus" import { Controller } from "@hotwired/stimulus"
export default class extends Controller { import { textProcessor } from "../services.js";
export class CommandsController extends Controller {
static targets = [ static targets = [
"commandInput", "commandInput",
"commandSelect", "commandSelect",
@ -30,7 +32,7 @@ export default class extends Controller {
runCommand(ev) { runCommand(ev) {
ev.preventDefault(); ev.preventDefault();
console.log("Do this: " + this.commandSelectTarget.value); textProcessor.runTextCommand(this.commandSelectTarget.value);
this.element.close(); this.element.close();
} }

View file

@ -5,7 +5,8 @@ import {EditorView, basicSetup} from "codemirror";
import {keymap} from "@codemirror/view"; import {keymap} from "@codemirror/view";
import { Application } from "@hotwired/stimulus"; import { Application } from "@hotwired/stimulus";
import CommandsController from "./controllers/commands_controller.js"; import { textProcessor } from "./services.js";
import {CommandsController} from "./controllers/commands_controller.js";
const view = new EditorView({ const view = new EditorView({
parent: document.querySelector("#app"), parent: document.querySelector("#app"),
@ -27,4 +28,6 @@ const view = new EditorView({
window.Stimulus = Application.start() window.Stimulus = Application.start()
Stimulus.register("commands", CommandsController); Stimulus.register("commands", CommandsController);
textProcessor.setCodeMirrorEditor(view);
view.focus(); view.focus();

31
frontend/src/services.js Normal file
View file

@ -0,0 +1,31 @@
import {ProcessText} from "../wailsjs/go/main/App";
class TextProcessor {
setCodeMirrorEditor(editor) {
this._editor = editor;
window.runtime.EventsOn("process-text-response", (data) => {
const changes = data.output.map(span => ({
from: span.pos,
to: span.pos + span.len,
insert: span.text,
}));
this._editor.dispatch({ changes: changes });
});
}
runTextCommand(command) {
if (this._editor === undefined) {
return;
}
ProcessText({
action: command,
input: [
{text: this._editor.state.doc.toString(), pos: 0, len: this._editor.state.doc.length}
],
});
}
}
export const textProcessor = new TextProcessor();

View file

@ -1,4 +1,5 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT // This file is automatically generated. DO NOT EDIT
import {main} from '../models';
export function Greet(arg1:string):Promise<string>; export function ProcessText(arg1:main.ProcessTextRequest):Promise<void>;

View file

@ -2,6 +2,6 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT // This file is automatically generated. DO NOT EDIT
export function Greet(arg1) { export function ProcessText(arg1) {
return window['go']['main']['App']['Greet'](arg1); return window['go']['main']['App']['ProcessText'](arg1);
} }

53
frontend/wailsjs/go/models.ts Executable file
View file

@ -0,0 +1,53 @@
export namespace main {
export class TextSpan {
text: string;
pos: number;
len: number;
static createFrom(source: any = {}) {
return new TextSpan(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.text = source["text"];
this.pos = source["pos"];
this.len = source["len"];
}
}
export class ProcessTextRequest {
action: string;
input: TextSpan[];
static createFrom(source: any = {}) {
return new ProcessTextRequest(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.action = source["action"];
this.input = this.convertValues(source["input"], TextSpan);
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice && a.map) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
}

9
go.mod
View file

@ -1,8 +1,13 @@
module dequoter module dequoter
go 1.23 go 1.23.3
require github.com/wailsapp/wails/v2 v2.10.2 toolchain go1.24.3
require (
github.com/wailsapp/wails/v2 v2.10.2
lmika.dev/pkg/modash v0.0.0-20250729120720-cdaa1c8abbe3
)
require ( require (
github.com/bep/debounce v1.2.1 // indirect github.com/bep/debounce v1.2.1 // indirect

2
go.sum
View file

@ -79,3 +79,5 @@ golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
lmika.dev/pkg/modash v0.0.0-20250729120720-cdaa1c8abbe3 h1:EqYZdM6ui++F2NPdFCjKAboOr14eVTIIiRfngBgg/WA=
lmika.dev/pkg/modash v0.0.0-20250729120720-cdaa1c8abbe3/go.mod h1:8NDl/yR1eCCEhip9FJlVuMNXIeaztQ0Ks/tizExFcTI=

View file

@ -17,7 +17,7 @@ func main() {
// Create application with options // Create application with options
err := wails.Run(&options.App{ err := wails.Run(&options.App{
Title: "dequoter", Title: "Dequoter",
Width: 1024, Width: 1024,
Height: 768, Height: 768,
AssetServer: &assetserver.Options{ AssetServer: &assetserver.Options{

16
models.go Normal file
View file

@ -0,0 +1,16 @@
package main
type TextSpan struct {
Text string `json:"text"`
Pos int `json:"pos"`
Len int `json:"len"`
}
type ProcessTextRequest struct {
Action string `json:"action"`
Input []TextSpan `json:"input"`
}
type ProcessTextResponse struct {
Output []TextSpan `json:"output"`
}

22
textfilters.go Normal file
View file

@ -0,0 +1,22 @@
package main
import (
"bytes"
"encoding/json"
"strconv"
)
type TextFilter func(input string) (output string, err error)
var TextFilters = map[string]TextFilter{
"unquote": func(input string) (output string, err error) {
return strconv.Unquote(input)
},
"format-json": func(input string) (output string, err error) {
var dst bytes.Buffer
if err := json.Indent(&dst, []byte(input), "", " "); err != nil {
return "", err
}
return dst.String(), nil
},
}