diff --git a/.forgejo/workflows/release.yml b/.forgejo/workflows/release.yml index 392e612..edbbdd7 100644 --- a/.forgejo/workflows/release.yml +++ b/.forgejo/workflows/release.yml @@ -21,14 +21,12 @@ jobs: - name: Running Wails doctor run: | wails doctor - - name: Installing Dependencies - run: | - npm install - name: Build run: | + npm install wails build -clean -platform darwin/arm64 -ldflags "-X main.VersionNumber=`git describe --tags --abbrev=0`" - name: Release - uses: https://lmika.dev/actions/wails-release@v1.0.4 + uses: https://lmika.dev/actions/wails-release@v1.0.3 with: developer-id-cert-base64: ${{ secrets.MACOS_SIGN_P12 }} developer-id-cert-password: ${{ secrets.MACOS_SIGN_PASSWORD }} @@ -37,7 +35,7 @@ jobs: notarization-api-issuer-id: ${{ secrets.MACOS_NOTARY_ISSUER_ID }} extra-build-flags: -ldflags "-X main.VersionNumber=${{ github.ref_name }}" s3-bucket: lmika-public-files - s3-key: 'Apps/Dequoter/{version}/{filename},Apps/Dequoter/latest/{filename}' + s3-key: Apps/Dequoter/{version}/{filename} s3-region: ap-southeast-2 s3-acl: public-read env: diff --git a/frontend/index.html b/frontend/index.html index 43a2da8..708247a 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -20,7 +20,7 @@ + data-action="dq-showcommands@window->commands#showCommands dq-rerunlastcommand@window->commands#rerunLastCommand dq-rerunlastcommand-line@window->commands#rerunLastCommandOnLine">
{ + let event = new CustomEvent('dq-rerunlastcommand-line'); + window.dispatchEvent(event); + + return true; + } +}, { key: "Cmd-k", run: (view) => { const {state} = view; diff --git a/frontend/src/controllers/commands_controller.js b/frontend/src/controllers/commands_controller.js index 5d74380..ec69f24 100644 --- a/frontend/src/controllers/commands_controller.js +++ b/frontend/src/controllers/commands_controller.js @@ -10,6 +10,10 @@ export class CommandsController extends Controller { "commandSelect", ]; + initialize() { + this._lineModeOnce = false; + } + async connect() { this._lastCommand = null; @@ -47,6 +51,7 @@ export class CommandsController extends Controller { runCommand(ev) { ev.preventDefault(); + this._promptController()?.clearUseLastValue(); textProcessor.runTextCommand(this.commandSelectTarget.value); this._lastCommand = this.commandSelectTarget.value; @@ -60,7 +65,21 @@ export class CommandsController extends Controller { return; } - textProcessor.runTextCommand(this._lastCommand); + this._promptController()?.useLastValueForNextPrompt(); + const lineMode = this._lineModeOnce; + this._lineModeOnce = false; + textProcessor.runTextCommand(this._lastCommand, { lineMode }); + } + + rerunLastCommandOnLine(ev) { + this._lineModeOnce = true; + this.rerunLastCommand(ev); + } + + _promptController() { + const el = document.getElementById("prompt-dialog"); + if (!el) return null; + return this.application.getControllerForElementAndIdentifier(el, "prompt"); } _filterOptions(filterText) { diff --git a/frontend/src/controllers/prompt_controller.js b/frontend/src/controllers/prompt_controller.js index c8ff4e3..fe10ee0 100644 --- a/frontend/src/controllers/prompt_controller.js +++ b/frontend/src/controllers/prompt_controller.js @@ -8,8 +8,16 @@ export class PromptController extends Controller { connect() { this._callback = null; + this._lastValue = null; + this._useLastNext = false; window.runtime.EventsOn("prompt-request", (data) => { + if (this._useLastNext && this._lastValue !== null) { + this._useLastNext = false; + window.runtime.EventsEmit("prompt-response", this._lastValue); + return; + } + this._useLastNext = false; this.prompt(data.label, (res) => { window.runtime.EventsEmit("prompt-response", res); }); @@ -19,14 +27,16 @@ export class PromptController extends Controller { prompt(label, callback) { this._callback = callback; this.labelTarget.textContent = label; - this.inputTarget.value = ""; + this.inputTarget.value = this._lastValue || ""; this.element.showModal(); + this.inputTarget.select(); this.inputTarget.focus(); } submit(ev) { ev.preventDefault(); let value = this.inputTarget.value; + this._lastValue = value; this.element.close(); if (this._callback) { this._callback(value); @@ -39,4 +49,12 @@ export class PromptController extends Controller { this._callback = null; this.element.close(); } + + useLastValueForNextPrompt() { + this._useLastNext = true; + } + + clearUseLastValue() { + this._useLastNext = false; + } } diff --git a/frontend/src/services.js b/frontend/src/services.js index 88da2e0..2d65b6d 100644 --- a/frontend/src/services.js +++ b/frontend/src/services.js @@ -46,16 +46,31 @@ class TextProcessor { }); } - async runTextCommand(command) { + async runTextCommand(command, opts) { if (this._editor === undefined) { return; } + const lineMode = opts && opts.lineMode; let ranges = this._editor.state.selection.ranges; - let shouldBeAll = ranges.reduce((a, r) => a && r.from === r.to, true); + let hasSelection = ranges.some(r => r.from !== r.to); let inputs = []; - if (shouldBeAll) { + if (lineMode && !hasSelection) { + this._appendInsertPos = undefined; + const doc = this._editor.state.doc; + const seen = new Set(); + for (let r of ranges) { + const line = doc.lineAt(r.head); + if (seen.has(line.number)) continue; + seen.add(line.number); + inputs.push({ + text: line.text, + pos: line.from, + len: line.to - line.from, + }); + } + } else if (!hasSelection) { this._appendInsertPos = this._editor.state.selection.main.head; inputs.push({ text: this._editor.state.doc.toString(), diff --git a/textfilters.go b/textfilters.go index e3ef6d7..1bd4334 100644 --- a/textfilters.go +++ b/textfilters.go @@ -4,7 +4,6 @@ import ( "bufio" "bytes" "context" - "encoding/base64" "encoding/json" "errors" "fmt" @@ -124,32 +123,6 @@ var TextFilters = map[string]TextProcessor{ return TextFilterResponse{Output: result}, nil }, }, - "compact-json": { - Label: "JSON: Compact", - Filter: func(ctx context.Context, input string) (resp TextFilterResponse, err error) { - var ( - dst bytes.Buffer - inBfr bytes.Buffer - outBfr bytes.Buffer - ) - - scnr := bufio.NewScanner(strings.NewReader(input)) - for scnr.Scan() { - line := scnr.Text() - inBfr.WriteString(line) - inBfr.WriteString("\n") - - if err := json.Compact(&outBfr, inBfr.Bytes()); err == nil { - dst.WriteString(outBfr.String()) - dst.WriteString("\n") - inBfr.Reset() - outBfr.Reset() - } - } - - return TextFilterResponse{Output: dst.String() + inBfr.String()}, nil - }, - }, "format-json": { Label: "JSON: Format", Filter: func(ctx context.Context, input string) (resp TextFilterResponse, err error) { @@ -200,48 +173,6 @@ var TextFilters = map[string]TextProcessor{ return TextFilterResponse{Output: string(jsonBytes)}, nil }, }, - "jwt-decode": { - Label: "JWT: Decode", - Filter: func(ctx context.Context, input string) (resp TextFilterResponse, err error) { - tokens := strings.Split(input, ".") - if len(tokens) != 3 { - return TextFilterResponse{}, errors.New("invalid JWT format") - } - - header, err := base64.RawURLEncoding.DecodeString(tokens[0]) - if err != nil { - return TextFilterResponse{}, err - } - payload, err := base64.RawURLEncoding.DecodeString(tokens[1]) - if err != nil { - return TextFilterResponse{}, err - } - - var headerJSON, payloadJSON bytes.Buffer - json.Indent(&headerJSON, header, "", " ") - json.Indent(&payloadJSON, payload, "", " ") - - return TextFilterResponse{Output: headerJSON.String() + "\n" + payloadJSON.String()}, nil - }, - }, - "base64-decode": { - Label: "Base64: Decode", - Filter: func(ctx context.Context, input string) (resp TextFilterResponse, err error) { - dst, err := base64.StdEncoding.DecodeString(input) - if err != nil { - return TextFilterResponse{}, err - } - - return TextFilterResponse{Output: string(dst)}, nil - }, - }, - "base64-encode": { - Label: "Base64: Encode", - Filter: func(ctx context.Context, input string) (resp TextFilterResponse, err error) { - dst := base64.StdEncoding.EncodeToString([]byte(input)) - return TextFilterResponse{Output: dst}, nil - }, - }, "lorem-ipsum": { Label: "Generate: Lorem Ipsum", Filter: func(ctx context.Context, input string) (resp TextFilterResponse, err error) {