From 251f303f608d53bff1692d780b9a13f707825057 Mon Sep 17 00:00:00 2001 From: James Ross Date: Wed, 14 Oct 2026 00:50:52 -0700 Subject: [PATCH 1/8] Design Leader key configuration and persistent settings --- .../0159-leader-key-settings-narrow.svg | 1 + docs/design/0159-leader-key-settings-wide.svg | 1 + docs/design/0159-leader-key-settings.md | 236 ++++++++++++++++++ 3 files changed, 238 insertions(+) create mode 100644 docs/design/0159-leader-key-settings-narrow.svg create mode 100644 docs/design/0159-leader-key-settings-wide.svg create mode 100644 docs/design/0159-leader-key-settings.md diff --git a/docs/design/0159-leader-key-settings-narrow.svg b/docs/design/0159-leader-key-settings-narrow.svg new file mode 100644 index 00000000..67b104fe --- /dev/null +++ b/docs/design/0159-leader-key-settings-narrow.svg @@ -0,0 +1 @@ +SettingsF2 / Esc closeKeybindings› Leader: Press a keyEsc cancelsSaved in ~/.jimrc \ No newline at end of file diff --git a/docs/design/0159-leader-key-settings-wide.svg b/docs/design/0159-leader-key-settings-wide.svg new file mode 100644 index 00000000..f2f42a40 --- /dev/null +++ b/docs/design/0159-leader-key-settings-wide.svg @@ -0,0 +1 @@ +SettingsF2 / Esc closeKeybindings› Leader: BackslashEnter to changeSaved in ~/.jimrc \ No newline at end of file diff --git a/docs/design/0159-leader-key-settings.md b/docs/design/0159-leader-key-settings.md new file mode 100644 index 00000000..4166f255 --- /dev/null +++ b/docs/design/0159-leader-key-settings.md @@ -0,0 +1,236 @@ +--- +title: "DX-0159 - Configure the Leader key" +legend: "DX" +lane: "design" +issue: "https://github.com/flyingrobots/jedit/issues/335" +status: "active" +owners: + - "@flyingrobots" +created: "2026-10-14" +updated: "2026-10-14" +--- + +# DX-0159 - Configure the Leader key + +## Linked Issue + +- https://github.com/flyingrobots/jedit/issues/335 + +## Decision Summary + +Add a Keybindings section with a Leader row to Settings. Activate the row, +press one printable key, and persist it in a data-only JSON `~/.jimrc`. +Backslash is the default. This cycle configures the prefix; it does not add +leader command mappings or an executable configuration language. + +## Sponsored Human + +A keyboard user wants to choose Space or another Leader key and retain that +choice when restarting Jim. + +## Sponsored Agent + +An agent needs an inspectable configuration schema and settings state rather +than inferring the prefix from a rendered label. + +## Hill + +A user can select Leader in Settings, capture a key, cancel or recover from +failure, and restart with the saved preference. Runtime and filesystem specs +exercise that journey. + +## Current Truth + +Settings currently expose session-only appearance, editor, and locale choices. +There is no configuration-file adapter or Leader preference. +See [settings-session.ts#225:ecefbbb695ddeedaed94e0046c580e2041cca40f](https://github.com/flyingrobots/jedit/blob/ecefbbb695ddeedaed94e0046c580e2041cca40f/src/app/settings-session.ts#L225) +and [workspace-app.ts#64:ecefbbb695ddeedaed94e0046c580e2041cca40f](https://github.com/flyingrobots/jedit/blob/ecefbbb695ddeedaed94e0046c580e2041cca40f/src/adapters/workspace-app.ts#L64). + +## Problem + +Users cannot configure a Leader prefix. A session-only choice would disappear +on restart. + +## Scope + +- Keybindings section and single-key Leader capture. +- Global `~/.jimrc` load and atomic save through an injected settings port. +- Validation, visible errors, cancellation, and restart tests. + +## Non-Goals + +- Leader sequences, explorer mappings, general key remapping, project-local + configuration, executable Vimscript, or persistence of other settings. +- Buffer mutations or changes to the Echo text authority corridor. + +## User Experience / Product Shape + +Reuse the existing Settings drawer and its focus/navigation/rendering. +Leader displays Backslash, Space, or the chosen character. Enter, Space, or +Left/Right activation starts capture. Capture displays a persistent prompt. +The next unmodified printable character commits. Canonical `space` normalizes +to a literal space. Esc cancels without closing Settings. F2 cancels and +closes Settings. Other special keys and modified keys leave capture active +with a readable validation message. Ctrl+C retains shell quit behavior. + +Saving succeeds before the active preference changes. The row remains focused +and the existing settings toast confirms the change. A save error leaves the +old value active, shows the error in the row, and permits another attempt. +Malformed startup configuration falls back to backslash with a durable row +error; Jim never silently overwrites malformed configuration. + +### User Journey + +```mermaid +flowchart TD + Settings[Open Settings] --> Leader[Activate Leader] + Leader --> Capture[Press a printable key] + Capture --> Save[Save .jimrc] + Capture --> Cancel[Esc cancels] + Save --> Success[Show saved key] + Save --> Error[Keep old key and show error] + Error --> Leader + Success --> Restart[Restart loads saved key] +``` + +### Wide UI Mockup + +Existing drawer at 100 by 32, built-in dark theme. The new section uses the +same heading, row, and description treatment as existing settings. + +![Leader row, wide](0159-leader-key-settings-wide.svg) + +### Narrow UI Mockup + +Existing drawer at 40 by 16. Selection scrolling keeps the Leader row visible; +labels and help use the existing clipping behavior. + +![Leader row, narrow](0159-leader-key-settings-narrow.svg) + +### Accessibility Considerations + +Keyboard-only operation; textual value, capture prompt, and errors. No color +alone encodes success or failure. Pointer/touch settings editing remains +outside the existing drawer contract. + +## Runtime / API Contract + +A `LeaderKey` validated value owns a single printable Unicode scalar. +`JimSettingsPort.load()` returns the preference and an optional issue; +`saveLeader()` returns an optional error. Filesystem and JSON handling stay +in the adapter. The workspace owns capture state and uses the injected port. +No filesystem dependency enters the application core. + +## Lower Modes + +Plain-text rendering remains usable without color. Missing `.jimrc` uses the +default without creating a file. Hosts without a persistence adapter show a +save error instead of claiming durability. Settings need no Echo text read or +mutation for this preference. + +## Data / State Model + +| Category | Description | +| --- | --- | +| Source of truth | Saved `keybindings.leader` in `~/.jimrc`; active session preference after successful load/save. | +| Derived state | Display name and capture prompt. | +| Invalid states | Empty, multiple characters, controls, lone surrogates, special-key names. | +| Reset behavior | Capture cancellation preserves the active key. Backslash can be recaptured to restore default. | +| Serialization | JSON object, e.g. `{"keybindings":{"leader":" "}}`. | +| Preservation | Unrelated root and keybindings fields survive saves; malformed existing files remain untouched. | + +Atomic replacement prevents partially written configuration. Saving rereads +the file to preserve unrelated edits made since startup. Live reload and +multi-process write arbitration are not part of this cycle. + +## Accessibility Posture + +| Concern | Posture | +| --- | --- | +| Labels | Leader row with stable `leader` id. | +| Focus | Remains on the row after capture, cancellation, and failure. | +| Keyboard | Capture owns printable input; Esc cancels; Ctrl+C quits. | +| Secret handling | Configuration contains preferences only. | + +## Localization / Directionality Posture + +New section, labels, values, prompts, and errors use catalog keys. Update every +authored locale and regenerate the mirror locale. Existing drawer placement +and directionality are preserved. Validate with `node scripts/gen-i18n.mjs` +and rendered LTR/RTL settings specs. + +## Agent Inspectability / Explainability Posture + +Inspect `leaderKey`, capture state, row id `leader`, and the JSON file. Tests +exercise the public runtime and settings renderer rather than private text. + +## Linked Invariants + +- Tests are executable spec; validation and decoding belong at the boundary. +- Settings preference writes never mutate buffers or manufacture Echo evidence. +- Preserve unrelated configuration and user-owned checkout state. + +## Design Alternatives Considered + +### Cycle through a fixed list + +Simpler interaction but cannot express the user's own printable key. + +### Execute `.jimrc` as a script + +Adds an execution language for one preference. Data-only JSON meets this task. + +## Decision + +Capture one printable key and persist only that preference in global JSON. +Use the existing Settings grammar with a capture state rather than adding a +new modal or layout. + +## Implementation Slices + +- [ ] Slice 1: Validated Leader preference and lossless `.jimrc` adapter. +- [ ] Slice 2: Settings capture, production wiring, and localized feedback. +- [ ] Slice 3: Runtime/render/restart witness, docs, and required validation. + +## Tests To Write First + +- [ ] Settings render exposes the current preference and capture state. +- [ ] Runtime captures Space and custom keys; Esc/F2 cancel; invalid input stays in capture. +- [ ] Save/restart retains the key and preserves unrelated JSON fields. +- [ ] Malformed config and write failure retain previous data and visible errors. + +## Acceptance Criteria + +- [ ] User can configure the Leader key through Settings and restart with it. +- [ ] Configuration is optional, data-only, validated, and safely replaced. +- [ ] Rendered output and failure/cancellation paths have executable evidence. +- [ ] Catalogs, docs, issue, and PR reflect the scope. +- [ ] Required local checks pass; CI state is disclosed independently. + +## Validation Plan + +```bash +npm run check +``` + +## Playback / Witness + +Open Settings with F2, navigate to Keybindings → Leader, press Enter, then +Space. Close and restart Jim. Reopen Settings and verify Space. Repeat with +Esc during capture and with a malformed `.jimrc` in an isolated home directory. +Runtime specs reproduce this without writing the real user's home directory. + +## Risks + +Filesystem errors and malformed manual edits must remain visible. Do not +overwrite bad files or report a value as saved before persistence succeeds. +Existing hard-global key precedence must not steal printable capture input. + +## Follow-On Debt + +Leader command mappings remain separate from this preference-only cycle. +No new mapping behavior is promised by this design. + +## Retrospective + +Pending implementation and validation. From 646d399a308b7a8ab2cfb73bb13844a73828a008 Mon Sep 17 00:00:00 2001 From: James Ross Date: Wed, 14 Oct 2026 01:03:25 -0700 Subject: [PATCH 2/8] Add Leader key capture and persistent .jimrc settings --- docs/design/0159-leader-key-settings.md | 4 +- spec/i18n-mock.mjs | 12 + spec/leader-settings.spec.mjs | 224 ++++++++++++ spec/settings-session.spec.mjs | 1 + src/adapters/jim-settings.ts | 124 +++++++ src/adapters/workspace-app.ts | 33 +- src/app/leader-key.ts | 20 ++ src/app/leader-settings-row.ts | 69 ++++ src/app/settings-session.ts | 8 +- src/app/workspace/init.ts | 3 + src/app/workspace/key-binding-context.ts | 2 + src/app/workspace/key-bindings.ts | 1 + src/app/workspace/leader-key-capture.ts | 64 ++++ src/app/workspace/model.ts | 5 + src/app/workspace/runtime.ts | 10 + src/app/workspace/settings-key-bindings.ts | 17 +- src/app/workspace/settings.ts | 7 + src/app/workspace/viewer-overlays.ts | 2 + src/app/workspace/viewer.ts | 1 + .../workspace-runtime-dependencies.ts | 2 + src/generated/i18n.ts | 57 +-- src/ports/jim-settings.ts | 24 ++ src/ui/settings-drawer.ts | 3 +- src/ui/workspace-chrome.ts | 6 + translations.json | 340 ++++++++++++++++-- 25 files changed, 982 insertions(+), 57 deletions(-) create mode 100644 spec/leader-settings.spec.mjs create mode 100644 src/adapters/jim-settings.ts create mode 100644 src/app/leader-key.ts create mode 100644 src/app/leader-settings-row.ts create mode 100644 src/app/workspace/leader-key-capture.ts create mode 100644 src/ports/jim-settings.ts diff --git a/docs/design/0159-leader-key-settings.md b/docs/design/0159-leader-key-settings.md index 4166f255..2ead18cd 100644 --- a/docs/design/0159-leader-key-settings.md +++ b/docs/design/0159-leader-key-settings.md @@ -188,8 +188,8 @@ new modal or layout. ## Implementation Slices -- [ ] Slice 1: Validated Leader preference and lossless `.jimrc` adapter. -- [ ] Slice 2: Settings capture, production wiring, and localized feedback. +- [x] Slice 1: Validated Leader preference and lossless `.jimrc` adapter. +- [x] Slice 2: Settings capture, production wiring, and localized feedback. - [ ] Slice 3: Runtime/render/restart witness, docs, and required validation. ## Tests To Write First diff --git a/spec/i18n-mock.mjs b/spec/i18n-mock.mjs index d1326f83..9bccd1c7 100644 --- a/spec/i18n-mock.mjs +++ b/spec/i18n-mock.mjs @@ -36,6 +36,18 @@ export function createI18nMock(overrides = {}) { 'footer.command.hints.esc_cancel': 'esc cancel', }; const settings = { + 'settings.sections.keybindings': 'Keybindings', + "settings.rows.leader.label": "Leader", + "settings.rows.leader.description": "Enter to change; saved in ~/.jimrc.", + "settings.rows.leader.capture": "Press one key; Esc cancels.", + "settings.rows.leader.capturing_value": "Press a key", + "settings.rows.leader.space": "Space", + "settings.rows.leader.backslash": "Backslash", + "settings.rows.leader.invalid_key": "Use one printable key; Esc cancels.", + "settings.rows.leader.invalid": ".jimrc is invalid; fix it and retry.", + "settings.rows.leader.read_error": "Could not read .jimrc.", + "settings.rows.leader.write_error": "Could not save .jimrc; retry.", + "settings.rows.leader.unavailable": "Settings cannot be saved in this host.", 'settings.sections.appearance': 'Appearance', 'settings.sections.editor': 'Editor', 'settings.sections.runtime': 'Runtime', diff --git a/spec/leader-settings.spec.mjs b/spec/leader-settings.spec.mjs new file mode 100644 index 00000000..fad94f12 --- /dev/null +++ b/spec/leader-settings.spec.mjs @@ -0,0 +1,224 @@ +import assert from 'node:assert/strict'; +import { mkdtempSync, readFileSync, writeFileSync, existsSync, readdirSync, mkdirSync, rmSync, symlinkSync, lstatSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import path from 'node:path'; +import test from 'node:test'; +import { importDist, mockRuntime, fakeProductionTextSession } from './workspace-helpers.mjs'; + +const key = (value, flags = {}) => ({ type: 'key', key: value, ...flags }); + +async function modules() { + return { + runtime: await importDist('app', 'workspace', 'runtime.js'), + settings: await importDist('app', 'workspace', 'settings.js'), + drawer: await importDist('ui', 'settings-drawer.js'), + i18n: await importDist('adapters', 'bijou-i18n-adapter.js'), + themes: await importDist('ui', 'jedit-themes.js'), + }; +} + +function text(surface) { + return Array.from({ length: surface.height }, (_, y) => ( + Array.from({ length: surface.width }, (_, x) => surface.get(x, y).char).join('') + )).join('\n'); +} + +function selectLeader(runtime, settings, model) { + let opened = runtime.update(key('f2'), model)[0]; + const target = settings.settingsRows(opened).findIndex((row) => row.id === 'leader'); + assert.ok(target >= 0, 'Settings exposes a Leader row'); + for (let i = 0; i < target; i += 1) opened = runtime.update(key('down'), opened)[0]; + return opened; +} + +test('Settings renders Keybindings and a default Leader with capture and cancellation', async () => { + const m = await modules(); + const deps = mockRuntime(); + deps.initialModel.i18n = new m.i18n.BijouI18nAdapter(); + deps.initialModel.jeditTheme = m.themes.availableJeditThemes()[0]; + const runtime = m.runtime.createWorkspaceRuntime(deps); + let model = runtime.init()[0]; + assert.equal(model.leaderKey?.value, '\\', 'startup defaults to the Vim Leader'); + model = selectLeader(runtime, m.settings, model); + const render = () => text(m.drawer.renderSettingsDrawer({ + rows: m.settings.settingsRows(model), selectedIndex: model.settingsFocusIndex, + theme: model.jeditTheme, width: 40, height: 16, + })); + assert.match(render(), /Keybindings/); + assert.match(render(), /Leader.*Backslash/); + model = runtime.update(key('enter'), model)[0]; + assert.equal(model.leaderCaptureActive, true); + assert.match(render(), /Press a key/); + model = runtime.update(key('escape'), model)[0]; + assert.equal(model.settingsOpen, true); + assert.equal(model.leaderCaptureActive, false); + assert.equal(model.leaderKey.value, '\\'); + model = runtime.update(key('enter'), model)[0]; + model = runtime.update(key('f2'), model)[0]; + assert.equal(model.settingsOpen, false); + assert.equal(model.leaderCaptureActive, false); +}); + +test('Settings captures Space and custom keys, saves them, and reloads at runtime startup', async (t) => { + const home = mkdtempSync(path.join(tmpdir(), 'jim-leader-')); + t.after(() => rmSync(home, { recursive: true, force: true })); + const m = await modules(); + const { createJimSettingsPort } = await importDist('adapters', 'jim-settings.js'); + const settingsPort = createJimSettingsPort(home); + const deps = mockRuntime({ jimSettings: settingsPort }); + deps.initialModel.i18n = new m.i18n.BijouI18nAdapter(); + deps.initialModel.jeditTheme = m.themes.availableJeditThemes()[0]; + const runtime = m.runtime.createWorkspaceRuntime(deps); + let model = selectLeader(runtime, m.settings, runtime.init()[0]); + assert.equal(existsSync(path.join(home, '.jimrc')), false); + for (const [input, saved] of [['space', ' '], ['j', 'j'], ['q', 'q'], ['`', '`'], ['\\', '\\']]) { + model = runtime.update(key('enter'), model)[0]; + model = runtime.update(key(input), model)[0]; + assert.equal(model.leaderKey.value, saved); + assert.equal(model.leaderCaptureActive, false); + assert.equal(model.settingsOpen, true); + assert.equal(JSON.parse(readFileSync(path.join(home, '.jimrc'), 'utf8')).keybindings.leader, saved); + assert.equal(m.runtime.createWorkspaceRuntime(deps).init()[0].leaderKey.value, saved); + } + assert.deepEqual(readdirSync(home), ['.jimrc']); +}); + +test('invalid or modified keys remain in capture, and persistence failure retains the old Leader', async () => { + const m = await modules(); + const deps = mockRuntime(); + deps.initialModel.i18n = new m.i18n.BijouI18nAdapter(); + deps.initialModel.jeditTheme = m.themes.availableJeditThemes()[0]; + const runtime = m.runtime.createWorkspaceRuntime(deps); + let model = selectLeader(runtime, m.settings, runtime.init()[0]); + model = runtime.update(key('enter'), model)[0]; + for (const event of [key('up'), key('enter'), key('a', { ctrl: true }), key('a', { alt: true }), key('\n'), key('ab')]) { + model = runtime.update(event, model)[0]; + assert.equal(model.leaderCaptureActive, true); + assert.equal(model.leaderKey.value, '\\'); + assert.ok(model.leaderSettingsIssue); + } + model = runtime.update(key(','), model)[0]; + assert.equal(model.leaderCaptureActive, false); + assert.equal(model.leaderKey.value, '\\'); + assert.ok(model.leaderSettingsIssue, 'an absent persistence adapter cannot claim a saved key'); +}); + +test('.jimrc preserves unrelated fields and never replaces malformed or unreadable configuration', async (t) => { + const home = mkdtempSync(path.join(tmpdir(), 'jimrc-')); + t.after(() => rmSync(home, { recursive: true, force: true })); + const { createJimSettingsPort } = await importDist('adapters', 'jim-settings.js'); + const { LeaderKey } = await importDist('app', 'leader-key.js'); + const port = createJimSettingsPort(home); + const rc = path.join(home, '.jimrc'); + writeFileSync(rc, '{"theme":"custom","keybindings":{"other":[1,true],"leader":" "}}\n'); + assert.equal(port.load().leaderKey.value, ' '); + assert.equal(port.saveLeader(LeaderKey.from(',')), undefined); + assert.deepEqual(JSON.parse(readFileSync(rc, 'utf8')), { + theme: 'custom', keybindings: { other: [1, true], leader: ',' }, + }); + for (const source of ['not json', '[]', '{"keybindings":{"leader":"enter"}}', '{"keybindings":false}']) { + writeFileSync(rc, source); + assert.equal(port.load().leaderKey.value, '\\'); + assert.ok(port.load().issue); + assert.ok(port.saveLeader(LeaderKey.from(' '))); + assert.equal(readFileSync(rc, 'utf8'), source); + } + rmSync(rc); + mkdirSync(rc); + assert.ok(port.load().issue); + assert.ok(port.saveLeader(LeaderKey.from(' '))); +}); + +test('startup configuration errors stay visible and a repaired file can be saved', async (t) => { + const home = mkdtempSync(path.join(tmpdir(), 'jimrc-retry-')); + t.after(() => rmSync(home, { recursive: true, force: true })); + const m = await modules(); + const { createJimSettingsPort } = await importDist('adapters', 'jim-settings.js'); + const rc = path.join(home, '.jimrc'); + writeFileSync(rc, '{bad'); + const deps = mockRuntime({ jimSettings: createJimSettingsPort(home) }); + deps.initialModel.i18n = new m.i18n.BijouI18nAdapter(); + deps.initialModel.jeditTheme = m.themes.availableJeditThemes()[0]; + const runtime = m.runtime.createWorkspaceRuntime(deps); + let model = selectLeader(runtime, m.settings, runtime.init()[0]); + assert.ok(model.leaderSettingsIssue); + model = runtime.update(key('enter'), model)[0]; + model = runtime.update(key('space'), model)[0]; + assert.equal(model.leaderKey.value, '\\'); + assert.equal(readFileSync(rc, 'utf8'), '{bad'); + writeFileSync(rc, '{}'); + model = runtime.update(key('enter'), model)[0]; + model = runtime.update(key('space'), model)[0]; + assert.equal(model.leaderKey.value, ' '); + assert.equal(model.leaderSettingsIssue, undefined); +}); + +test('Leader validation accepts printable single keys and rejects controls and named keys', async () => { + const { LeaderKey } = await importDist('app', 'leader-key.js'); + for (const value of [' ', '\\', ',', 'é', '🦀']) assert.equal(LeaderKey.from(value)?.value, value); + for (const value of ['', 'enter', '\n', '\t', '\u001b', '\u007f', '\u200b', '\ud800', 'ab']) { + assert.equal(LeaderKey.from(value), undefined); + } +}); + +test('saving a dotfile symlink updates its target and preserves the symlink', async (t) => { + const home = mkdtempSync(path.join(tmpdir(), 'jimrc-link-')); + t.after(() => rmSync(home, { recursive: true, force: true })); + const { createJimSettingsPort } = await importDist('adapters', 'jim-settings.js'); + const { LeaderKey } = await importDist('app', 'leader-key.js'); + const target = path.join(home, 'settings.json'); + writeFileSync(target, '{"keybindings":{"leader":","},"theme":"custom"}'); + symlinkSync(target, path.join(home, '.jimrc')); + const port = createJimSettingsPort(home); + assert.equal(port.saveLeader(LeaderKey.from(' ')), undefined); + assert.equal(lstatSync(path.join(home, '.jimrc')).isSymbolicLink(), true); + assert.equal(JSON.parse(readFileSync(target, 'utf8')).keybindings.leader, ' '); +}); + +test('an atomic-write failure leaves the active Leader and shows a retryable row error', async (t) => { + const home = mkdtempSync(path.join(tmpdir(), 'jimrc-write-')); + t.after(() => rmSync(home, { recursive: true, force: true })); + const m = await modules(); + const { createJimSettingsPort } = await importDist('adapters', 'jim-settings.js'); + const port = createJimSettingsPort(path.join(home, 'missing')); + const deps = mockRuntime({ jimSettings: port }); + deps.initialModel.i18n = new m.i18n.BijouI18nAdapter(); + deps.initialModel.jeditTheme = m.themes.availableJeditThemes()[0]; + const runtime = m.runtime.createWorkspaceRuntime(deps); + let model = selectLeader(runtime, m.settings, runtime.init()[0]); + model = runtime.update(key('enter'), model)[0]; + model = runtime.update(key('space'), model)[0]; + assert.equal(model.leaderKey.value, '\\'); + assert.match(m.settings.settingsRows(model).find((row) => row.id === 'leader').description, /Could not save/); + mkdirSync(path.join(home, 'missing')); + model = runtime.update(key('enter'), model)[0]; + model = runtime.update(key('space'), model)[0]; + assert.equal(model.leaderKey.value, ' '); + assert.equal(model.leaderSettingsIssue, undefined); +}); + +test('production app composition saves and restores Leader through its settings port', async (t) => { + const home = mkdtempSync(path.join(tmpdir(), 'jimrc-app-')); + t.after(() => rmSync(home, { recursive: true, force: true })); + const m = await modules(); + const { createWorkspaceApp } = await importDist('adapters', 'workspace-app.js'); + const { createJimSettingsPort } = await importDist('adapters', 'jim-settings.js'); + const seed = mockRuntime().initialModel; + seed.i18n = new m.i18n.BijouI18nAdapter(); + seed.jeditTheme = m.themes.availableJeditThemes()[0]; + const options = { + initialColumns: 80, initialRows: 24, initialWorkingDirectory: home, + perfEnabled: false, seed, jimSettings: createJimSettingsPort(home), + }; + const textHost = { productionTextSession: fakeProductionTextSession(), closeProductionText: async () => {} }; + const app = createWorkspaceApp(options, textHost); + let model = selectLeader(app, m.settings, app.init()[0]); + model = app.update(key('enter'), model)[0]; + assert.match(text(app.view(model)), /Press a key/); + assert.doesNotMatch(text(app.view(model)), /q close/); + assert.match(text(app.view(model)), /Esc cancels/); + model = app.update(key('space'), model)[0]; + assert.equal(model.leaderKey.value, ' '); + assert.match(text(app.view(model)), /Leader.*Space/); + assert.equal(createWorkspaceApp(options, textHost).init()[0].leaderKey.value, ' '); +}); diff --git a/spec/settings-session.spec.mjs b/spec/settings-session.spec.mjs index ec4a7679..71ac07b5 100644 --- a/spec/settings-session.spec.mjs +++ b/spec/settings-session.spec.mjs @@ -60,6 +60,7 @@ test('jedit settings rows expose theme, footer, and markdown preview preferences ['Causal markers', 'Last save', settings.JEDIT_SETTING_ROW_KIND.Choice, false], ['Markdown preview', 'Source', settings.JEDIT_SETTING_ROW_KIND.Choice, false], ['Diagnostics', 'Open', settings.JEDIT_SETTING_ROW_KIND.Choice, false], + ['Leader', 'Backslash', settings.JEDIT_SETTING_ROW_KIND.Choice, false], ], ); }); diff --git a/src/adapters/jim-settings.ts b/src/adapters/jim-settings.ts new file mode 100644 index 00000000..e958c9e2 --- /dev/null +++ b/src/adapters/jim-settings.ts @@ -0,0 +1,124 @@ +import { closeSync, lstatSync, openSync, readFileSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync } from 'node:fs'; +import { homedir } from 'node:os'; +import { join } from 'node:path'; +import { randomUUID } from 'node:crypto'; +import { LeaderKey } from '../app/leader-key.js'; +import { + JimSettingsIssueKinds, + type JimSettingsIssue, + type JimSettingsPort, +} from '../ports/jim-settings.js'; + +export const JIMRC_FILENAME = '.jimrc'; +const CONFIG_ENCODING = 'utf8'; +const CONFIG_INDENT = 2; +const CONFIG_CREATE_MODE = 0o600; +const MISSING_FILE_CODE = 'ENOENT'; +type JsonValue = null | boolean | number | string | JsonValue[] | JsonObject; +interface JsonObject { [key: string]: JsonValue } +interface ConfigFile { readonly document: JsonObject; readonly mode: number; readonly path: string } + +// JSON is decoded only at this filesystem boundary. Arbitrary unrelated fields +// remain JSON data; they never become application configuration or executable code. +export function createJimSettingsPort(homeDirectory = homedir()): JimSettingsPort { + const path = join(homeDirectory, JIMRC_FILENAME); + return { + load: () => { + const file = readConfig(path); + return isIssue(file) + ? { leaderKey: LeaderKey.Default, issue: file } + : { leaderKey: leaderFromDocument(file.document) ?? LeaderKey.Default }; + }, + saveLeader: (leader) => { + const file = readConfig(path); + if (isIssue(file)) return file; + const bindings = file.document['keybindings']; + const document = { + ...file.document, + keybindings: { ...(isObject(bindings) ? bindings : {}), leader: leader.value }, + }; + return replaceConfig(file.path, `${JSON.stringify(document, null, CONFIG_INDENT)}\n`, file.mode); + }, + }; +} + +function readConfig(path: string): ConfigFile | JimSettingsIssue { + let source: string; + let mode: number; + let resolvedPath: string; + try { + source = readFileSync(path, CONFIG_ENCODING); + mode = statSync(path).mode; + resolvedPath = realpathSync(path); + } catch (cause) { + return cause instanceof Error && 'code' in cause && cause.code === MISSING_FILE_CODE + ? missingConfig(path) + : { kind: JimSettingsIssueKinds.Read, detail: cause instanceof Error ? cause.message : undefined }; + } + try { + const value: JsonValue = JSON.parse(source); + if (!isObject(value) || !validBindings(value['keybindings'])) { + return { kind: JimSettingsIssueKinds.Invalid }; + } + return { document: value, mode, path: resolvedPath }; + } catch { + return { kind: JimSettingsIssueKinds.Invalid }; + } +} + +function missingConfig(path: string): ConfigFile | JimSettingsIssue { + try { + lstatSync(path); + return { kind: JimSettingsIssueKinds.Read }; + } catch (cause) { + return cause instanceof Error && 'code' in cause && cause.code === MISSING_FILE_CODE + ? { document: {}, mode: CONFIG_CREATE_MODE, path } + : { kind: JimSettingsIssueKinds.Read }; + } +} + +function validBindings(value: JsonValue | undefined): boolean { + if (value === undefined) return true; + return isObject(value) && ( + value['leader'] === undefined || + (typeof value['leader'] === 'string' && LeaderKey.from(value['leader']) != null) + ); +} + +function leaderFromDocument(document: JsonObject): LeaderKey | undefined { + const bindings = document['keybindings']; + return isObject(bindings) && typeof bindings['leader'] === 'string' + ? LeaderKey.from(bindings['leader']) + : undefined; +} + +function isObject(value: JsonValue | undefined): value is JsonObject { + return value != null && typeof value === 'object' && !Array.isArray(value); +} + +function isIssue(value: ConfigFile | JimSettingsIssue): value is JimSettingsIssue { + return 'kind' in value; +} + +function replaceConfig(path: string, source: string, mode: number): JimSettingsIssue | undefined { + const temporary = `${path}.${randomUUID()}.tmp`; + let created = false; + try { + const descriptor = openSync(temporary, 'wx', mode); + created = true; + try { + writeFileSync(descriptor, source, CONFIG_ENCODING); + } finally { + closeSync(descriptor); + } + renameSync(temporary, path); + return undefined; + } catch (cause) { + return { kind: JimSettingsIssueKinds.Write, detail: cause instanceof Error ? cause.message : undefined }; + } finally { + if (created) { + try { unlinkSync(temporary); } catch { /* Renamed or already removed. */ } + } + } +} + diff --git a/src/adapters/workspace-app.ts b/src/adapters/workspace-app.ts index c4cbec1a..0b8b6c8b 100644 --- a/src/adapters/workspace-app.ts +++ b/src/adapters/workspace-app.ts @@ -19,8 +19,11 @@ import { } from './workspace-animation-commands.js'; import { openWorkspaceFileEntry } from '../app/workspace/file-tree.js'; import type { WorkspaceModel } from '../app/workspace/model.js'; +import { createJimSettingsPort } from './jim-settings.js'; +import type { JimSettingsPort } from '../ports/jim-settings.js'; export interface WorkspaceAppOptions { + jimSettings?: JimSettingsPort; initialColumns: number; initialRows: number; initialWorkingDirectory: string; @@ -60,6 +63,7 @@ function workspaceRuntimeDependencies( const sourceHighlighter = createGraftSourceHighlighter(); const titleSceneLoader = createTitleSceneLoaderPort(); return { + jimSettings: options.jimSettings ?? createJimSettingsPort(), initialColumns: options.initialColumns, initialRows: options.initialRows, initialWorkingDirectory: options.initialWorkingDirectory, @@ -74,16 +78,7 @@ function workspaceRuntimeDependencies( profileOnStartup: options.profileEnabled ?? false, initialModel: options.seed ?? createInitialModelSnapshot(nowMs(), options.initialWorkingDirectory, random), nowMs, - openEntry: ( - model: WorkspaceModel, - entry: NonNullable, - ) => openWorkspaceFileEntry(model, entry, nowMs, { - fileSystem: FileSystemPortAdapter, - editorFile, - graftSession, - sourceHighlighter, - productionTextSession: productionText.productionTextSession, - }), + openEntry: createEntryOpener(nowMs, editorFile, graftSession, sourceHighlighter, productionText), createTimeTickCmd: createWorkspaceTimeTickCmd, createNotificationTickCmd: createWorkspaceNotificationTickCmd, createDrawerAnimationCmd: createWorkspaceDrawerAnimationCmd, @@ -99,3 +94,21 @@ function workspaceRuntimeApp(runtime: ReturnType) routeRuntimeIssue: runtime.routeRuntimeIssue, }; } + +function createEntryOpener( + nowMs: () => number, + editorFile: typeof editorFilePort, + graftSession: ReturnType, + sourceHighlighter: ReturnType, + productionText: WorkspaceProductionTextDependencies, +) { + return (model: WorkspaceModel, entry: NonNullable) => openWorkspaceFileEntry( + model, entry, nowMs, { + fileSystem: FileSystemPortAdapter, + editorFile, + graftSession, + sourceHighlighter, + productionTextSession: productionText.productionTextSession, + }, + ); +} diff --git a/src/app/leader-key.ts b/src/app/leader-key.ts new file mode 100644 index 00000000..886429a2 --- /dev/null +++ b/src/app/leader-key.ts @@ -0,0 +1,20 @@ +const PRINTABLE_KEY_LENGTH = 1; +const NON_PRINTING_KEY = /[\p{Cc}\p{Cf}\p{Cs}\p{Zl}\p{Zp}]/u; + +export const LEADER_INPUT_KEYS = Object.freeze({ + Space: 'space', + LiteralSpace: ' ', + Escape: 'escape', +} as const); + +export class LeaderKey { + private constructor(readonly value: string) {} + + static from(value: string): LeaderKey | undefined { + return Array.from(value).length === PRINTABLE_KEY_LENGTH && !NON_PRINTING_KEY.test(value) + ? new LeaderKey(value) + : undefined; + } + + static readonly Default = new LeaderKey('\\'); +} diff --git a/src/app/leader-settings-row.ts b/src/app/leader-settings-row.ts new file mode 100644 index 00000000..1f913710 --- /dev/null +++ b/src/app/leader-settings-row.ts @@ -0,0 +1,69 @@ +import { LeaderKey, LEADER_INPUT_KEYS } from './leader-key.js'; +import { JimSettingsIssueKinds, type JimSettingsIssue } from '../ports/jim-settings.js'; +import { + JEDIT_SETTING_ACTION, + JEDIT_SETTING_ROW_KIND, + type JeditSettingsI18nState, + type JeditSettingsRow, +} from './settings-session.js'; + +export const LEADER_SETTING_ROW_ID = 'leader'; +export interface LeaderSettingsState { + readonly leaderKey?: LeaderKey; + readonly leaderCaptureActive?: boolean; + readonly leaderSettingsIssue?: JimSettingsIssue; +} + +const COPY = Object.freeze({ + Section: 'settings.sections.keybindings', + Label: 'settings.rows.leader.label', + Description: 'settings.rows.leader.description', + Capture: 'settings.rows.leader.capture', + CapturingValue: 'settings.rows.leader.capturing_value', + Space: 'settings.rows.leader.space', + Backslash: 'settings.rows.leader.backslash', + Invalid: 'settings.rows.leader.invalid', + Input: 'settings.rows.leader.invalid_key', + Read: 'settings.rows.leader.read_error', + Write: 'settings.rows.leader.write_error', + Unavailable: 'settings.rows.leader.unavailable', +} as const); + +const ISSUE_COPY = new Map([ + [JimSettingsIssueKinds.Input, COPY.Input], + [JimSettingsIssueKinds.Invalid, COPY.Invalid], + [JimSettingsIssueKinds.Read, COPY.Read], + [JimSettingsIssueKinds.Write, COPY.Write], + [JimSettingsIssueKinds.Unavailable, COPY.Unavailable], +]); + +export function leaderSettingsRow( + state: LeaderSettingsState & { readonly i18n: JeditSettingsI18nState }, +): JeditSettingsRow { + const key = state.leaderKey ?? LeaderKey.Default; + return { + id: LEADER_SETTING_ROW_ID, + section: state.i18n.t(COPY.Section), + label: state.i18n.t(COPY.Label), + description: leaderDescription(state), + valueLabel: state.leaderCaptureActive + ? state.i18n.t(COPY.CapturingValue) + : leaderValueLabel(key, state.i18n), + kind: JEDIT_SETTING_ROW_KIND.Choice, + action: JEDIT_SETTING_ACTION.CaptureLeader, + }; +} + +function leaderValueLabel(key: LeaderKey, i18n: JeditSettingsI18nState): string { + if (key.value === LEADER_INPUT_KEYS.LiteralSpace) return i18n.t(COPY.Space); + return key.value === LeaderKey.Default.value ? i18n.t(COPY.Backslash) : key.value; +} + +function leaderDescription(state: LeaderSettingsState & { readonly i18n: JeditSettingsI18nState }): string { + const issue = state.leaderSettingsIssue; + if (issue != null) { + const message = state.i18n.t(ISSUE_COPY.get(issue.kind) ?? COPY.Invalid); + return issue.detail == null ? message : `${message} ${issue.detail}`; + } + return state.i18n.t(state.leaderCaptureActive ? COPY.Capture : COPY.Description); +} diff --git a/src/app/settings-session.ts b/src/app/settings-session.ts index 87e42129..a20175cc 100644 --- a/src/app/settings-session.ts +++ b/src/app/settings-session.ts @@ -11,6 +11,7 @@ import { type WorkspaceCausalGutterBasis, } from './workspace/workspace-causal-gutter-basis.js'; import { ViewModes, type ViewMode } from './workspace/view-mode.js'; +import { leaderSettingsRow, type LeaderSettingsState } from './leader-settings-row.js'; export const JEDIT_SETTING_ACTION = { CycleTheme: Symbol('jedit.settings.action.cycle-theme'), @@ -23,6 +24,7 @@ export const JEDIT_SETTING_ACTION = { OpenDiagnostics: Symbol('jedit.settings.action.open-diagnostics'), CycleLocale: Symbol('jedit.settings.action.cycle-locale'), SelectLocale: Symbol('jedit.settings.action.select-locale'), + CaptureLeader: Symbol('jedit.settings.action.capture-leader'), } as const; export type JeditSettingAction = typeof JEDIT_SETTING_ACTION[keyof typeof JEDIT_SETTING_ACTION]; @@ -42,7 +44,7 @@ export type JeditSettingsActivationDelta = | typeof SETTINGS_ACTIVATE_NEXT_DELTA | typeof SETTINGS_ACTIVATE_PREVIOUS_DELTA; -export interface JeditSettingsState { +export interface JeditSettingsState extends LeaderSettingsState { readonly jeditTheme: JeditTheme; readonly footerVisible: boolean; readonly lineNumberMode: SourceLineNumberMode; @@ -80,6 +82,7 @@ export interface JeditSettingsKeyMsg { } export interface JeditSettingsHandlers { + captureLeader(model: Model): [Model, Cmd[]]; cycleTheme(model: Model): [Model, Cmd[]]; toggleThemeMode(model: Model): [Model, Cmd[]]; toggleFooter(model: Model): [Model, Cmd[]]; @@ -188,6 +191,7 @@ const SETTINGS_KEY_ACTIONS = new Map([ ]); type NonLocaleSettingsHandlerName = + | 'captureLeader' | 'cycleTheme' | 'toggleThemeMode' | 'toggleFooter' @@ -200,6 +204,7 @@ const SETTINGS_ACTION_HANDLERS: ReadonlyMap< JeditSettingAction, NonLocaleSettingsHandlerName > = new Map([ + [JEDIT_SETTING_ACTION.CaptureLeader, 'captureLeader'], [JEDIT_SETTING_ACTION.CycleTheme, 'cycleTheme'], [JEDIT_SETTING_ACTION.ToggleThemeMode, 'toggleThemeMode'], [JEDIT_SETTING_ACTION.ToggleFooter, 'toggleFooter'], @@ -228,6 +233,7 @@ export function jeditSettingsRows(state: JeditSettingsContext): readonly JeditSe if (state.diagnosticsAvailable) { rows.push(diagnosticsSettingsRow(state.i18n)); } + rows.push(leaderSettingsRow(state)); return rows; } diff --git a/src/app/workspace/init.ts b/src/app/workspace/init.ts index 0ae7d7bd..1045a119 100644 --- a/src/app/workspace/init.ts +++ b/src/app/workspace/init.ts @@ -25,6 +25,7 @@ import { ViewModes } from "./view-mode.js"; import { TEXT_RUNTIME_PROFILE_ECHO_HOSTED } from "../text-runtime-profile.js"; import { createWorkspaceTextAuthority } from "./workspace-text-authority.js"; import { emptyWorkspaceBufferRegistry } from "./workspace-buffer-registry.js"; +import { LeaderKey } from '../leader-key.js'; import { initialStartupFileModalState, INITIAL_WORKSPACE_CAUSAL_GUTTER_BASIS, @@ -107,6 +108,8 @@ function initialDrawerState() { function initialSettingsState() { return { + leaderKey: LeaderKey.Default, + leaderCaptureActive: false, lineNumberMode: INITIAL_LINE_NUMBER_MODE, gutterDimmed: false, causalGutterBasis: INITIAL_WORKSPACE_CAUSAL_GUTTER_BASIS, diff --git a/src/app/workspace/key-binding-context.ts b/src/app/workspace/key-binding-context.ts index 62e3ca42..29e726a2 100644 --- a/src/app/workspace/key-binding-context.ts +++ b/src/app/workspace/key-binding-context.ts @@ -8,6 +8,7 @@ import type { TitleSceneLoaderPort } from '../../ports/title-scene-loader.js'; import type { CreateDrawerAnimationCmd } from './drawer.js'; import type { WorkspaceMsg } from './msg.js'; import type { ProductionTextSession } from './production-text-session.js'; +import type { JimSettingsPort } from '../../ports/jim-settings.js'; export type CreateStartupFileDrawerAnimationCmd = ( from: number, @@ -15,6 +16,7 @@ export type CreateStartupFileDrawerAnimationCmd = ( ) => Cmd[]; export interface UpdateFromKeyDeps { + readonly jimSettings?: JimSettingsPort; readonly fileSystem: FileSystemPort; readonly editorFile: EditorFilePort; readonly sourceHighlighter: SourceHighlighter; diff --git a/src/app/workspace/key-bindings.ts b/src/app/workspace/key-bindings.ts index 4556d0ad..4f453560 100644 --- a/src/app/workspace/key-bindings.ts +++ b/src/app/workspace/key-bindings.ts @@ -25,6 +25,7 @@ export function updateFromKey( context: WorkspaceKeyBindingContext, ): KeyBindingResult { const result = updateQuitConfirmationKey(msg, model) + ?? (model.settingsOpen && model.leaderCaptureActive ? updateSettingsKey(msg, model, context) : undefined) ?? updateHardGlobalWorkspaceKey(msg, model) ?? updateWorkspaceOverlayKey(msg, model, context) ?? updateGlobalWorkspaceKey(msg, model, context) diff --git a/src/app/workspace/leader-key-capture.ts b/src/app/workspace/leader-key-capture.ts new file mode 100644 index 00000000..d9f5a6fa --- /dev/null +++ b/src/app/workspace/leader-key-capture.ts @@ -0,0 +1,64 @@ +import type { Cmd, KeyMsg } from '@flyingrobots/bijou-tui'; +import { LeaderKey, LEADER_INPUT_KEYS } from '../leader-key.js'; +import { JEDIT_SETTINGS_TOGGLE_KEY } from '../keybindings.js'; +import { JimSettingsIssueKinds } from '../../ports/jim-settings.js'; +import type { WorkspaceKeyBindingContext } from './key-binding-context.js'; +import type { WorkspaceModel } from './model.js'; +import type { WorkspaceMsg } from './msg.js'; + +type KeyBindingResult = [WorkspaceModel, Cmd[]]; +const FORCE_QUIT_KEY = 'c'; + +export function updateLeaderKeyCapture( + msg: KeyMsg, + model: WorkspaceModel, + context: WorkspaceKeyBindingContext, +): KeyBindingResult | undefined { + if (!captureHasFocus(model) || isForceQuit(msg)) { + return undefined; + } + if (msg.key === LEADER_INPUT_KEYS.Escape || msg.key === JEDIT_SETTINGS_TOGGLE_KEY) { + return [cancelLeaderCapture(msg, model), []]; + } + const value = msg.key === LEADER_INPUT_KEYS.Space ? LEADER_INPUT_KEYS.LiteralSpace : msg.key; + const leader = captureKey(msg, value); + return leader == null + ? [{ ...model, leaderSettingsIssue: { kind: JimSettingsIssueKinds.Input } }, []] + : [saveLeader(model, leader, context), []]; +} + +function cancelLeaderCapture(msg: KeyMsg, model: WorkspaceModel): WorkspaceModel { + return { + ...model, + settingsOpen: msg.key !== JEDIT_SETTINGS_TOGGLE_KEY, + leaderCaptureActive: false, + leaderSettingsIssue: model.leaderSettingsIssue?.kind === JimSettingsIssueKinds.Input + ? undefined + : model.leaderSettingsIssue, + }; +} + +function saveLeader(model: WorkspaceModel, leader: LeaderKey, context: WorkspaceKeyBindingContext): WorkspaceModel { + const port = context.deps.jimSettings; + const issue = port == null + ? { kind: JimSettingsIssueKinds.Unavailable } + : port.saveLeader(leader); + return { + ...model, + leaderKey: issue == null ? leader : model.leaderKey, + leaderCaptureActive: false, + leaderSettingsIssue: issue, + }; +} + +function captureHasFocus(model: WorkspaceModel): boolean { + return model.settingsOpen && model.leaderCaptureActive; +} + +function isForceQuit(msg: KeyMsg): boolean { + return msg.ctrl === true && msg.key === FORCE_QUIT_KEY; +} + +function captureKey(msg: KeyMsg, value: string): LeaderKey | undefined { + return msg.ctrl || msg.alt ? undefined : LeaderKey.from(value); +} diff --git a/src/app/workspace/model.ts b/src/app/workspace/model.ts index 708e19a4..9c3b5772 100644 --- a/src/app/workspace/model.ts +++ b/src/app/workspace/model.ts @@ -33,6 +33,8 @@ import type { } from "./command-completion-preview.js"; import type { WorkspaceInlinePanel } from "./workspace-inline-panel.js"; import type { WorkspaceCausalGutterBasis } from "./workspace-causal-gutter-basis.js"; +import type { LeaderKey } from '../leader-key.js'; +import type { JimSettingsIssue } from '../../ports/jim-settings.js'; export interface WorkspaceModel extends StartupFileModalState, @@ -63,6 +65,9 @@ export interface WorkspaceModel readonly gutterDimmed: boolean; readonly causalGutterBasis: WorkspaceCausalGutterBasis; readonly settingsOpen: boolean; + readonly leaderKey: LeaderKey; + readonly leaderCaptureActive: boolean; + readonly leaderSettingsIssue?: JimSettingsIssue; readonly settingsFocusIndex: number; readonly settingsDiagnosticsOpen: boolean; readonly jeditTheme: JeditTheme; diff --git a/src/app/workspace/runtime.ts b/src/app/workspace/runtime.ts index 1f130e19..637ab016 100644 --- a/src/app/workspace/runtime.ts +++ b/src/app/workspace/runtime.ts @@ -72,6 +72,7 @@ export const createWorkspaceRuntime = ( }, ), profiler: createInitialProfilerState(), + ...initialLeaderSettings(deps), }; return [model, initialRuntimeCommands(deps, model)]; }, @@ -434,6 +435,7 @@ function workspaceKeyDeps(deps: WorkspaceRuntimeDependencies) { deps.createStartupFileDrawerAnimationCmd, createNotificationTickCmd: deps.createNotificationTickCmd, deps: { + jimSettings: deps.jimSettings, fileSystem: deps.fileSystem, editorFile: deps.editorFile, sourceHighlighter: deps.sourceHighlighter, @@ -445,6 +447,14 @@ function workspaceKeyDeps(deps: WorkspaceRuntimeDependencies) { }; } +function initialLeaderSettings(deps: WorkspaceRuntimeDependencies) { + const saved = deps.jimSettings?.load(); + return saved == null ? {} : { + leaderKey: saved.leaderKey, + leaderSettingsIssue: saved.issue, + }; +} + function isProfilerMsg(msg: WorkspaceRuntimeMsg): msg is ProfilerMsg { return ( msg.type === ProfilerMessageTypes.Started || diff --git a/src/app/workspace/settings-key-bindings.ts b/src/app/workspace/settings-key-bindings.ts index b6cdb59c..ed71ac02 100644 --- a/src/app/workspace/settings-key-bindings.ts +++ b/src/app/workspace/settings-key-bindings.ts @@ -10,6 +10,8 @@ import type { WorkspaceModel } from './model.js'; import type { WorkspaceMsg } from './msg.js'; import { WorkspaceKeys } from './workspace-key.js'; import { settingsRows, workspaceSettingsHandlers } from './settings.js'; +import { updateLeaderKeyCapture } from './leader-key-capture.js'; +import { LEADER_SETTING_ROW_ID } from '../leader-settings-row.js'; import { NotificationPlacements, NotificationTones, @@ -31,12 +33,17 @@ export function updateSettingsKey( model: WorkspaceModel, context: WorkspaceKeyBindingContext, ): KeyBindingResult | undefined { + const captured = updateLeaderKeyCapture(msg, model, context); + if (captured != null) { + return withSettingsChangeToast(model, settingsRows(model), captured, context); + } if (msg.key === JEDIT_SETTINGS_TOGGLE_KEY) { return [{ ...model, settingsOpen: !model.settingsOpen, settingsFocusIndex: 0, settingsDiagnosticsOpen: false, + leaderCaptureActive: false, }, []]; } @@ -100,15 +107,21 @@ function settingsChangeMessage( return undefined; } const afterRow = settingsRows(after).find((row) => row.id === beforeRow.id); - const beforeValue = settingRowValue(beforeRow, before.i18n); + const beforeValue = settingRowValue(uncapturedLeaderRow(before, beforeRow), before.i18n); const afterValue = afterRow == null ? beforeValue - : settingRowValue(afterRow, after.i18n); + : settingRowValue(uncapturedLeaderRow(after, afterRow), after.i18n); return beforeValue === afterValue ? undefined : `${beforeRow.label}: ${beforeValue}${SETTINGS_TOAST_SEPARATOR}${afterValue}`; } +function uncapturedLeaderRow(model: WorkspaceModel, row: JeditSettingsRow): JeditSettingsRow { + return row.id === LEADER_SETTING_ROW_ID + ? settingsRows({ ...model, leaderCaptureActive: false }).find((candidate) => candidate.id === row.id) ?? row + : row; +} + function settingRowValue(row: JeditSettingsRow, i18n: WorkspaceModel['i18n']): string { if (row.valueLabel.length > 0) { return row.valueLabel; diff --git a/src/app/workspace/settings.ts b/src/app/workspace/settings.ts index 35a5bafd..b8619e74 100644 --- a/src/app/workspace/settings.ts +++ b/src/app/workspace/settings.ts @@ -29,6 +29,9 @@ export const WorkspaceTextDirections = Object.freeze({ export function settingsRows(model: WorkspaceModel): ReturnType { return jeditSettingsRows({ + leaderKey: model.leaderKey, + leaderCaptureActive: model.leaderCaptureActive, + leaderSettingsIssue: model.leaderSettingsIssue, i18n: model.i18n, jeditTheme: model.jeditTheme, footerVisible: model.footerVisible, @@ -50,6 +53,10 @@ export function workspaceSettingsHandlers( context: WorkspaceSettingsHandlerContext = {}, ): JeditSettingsHandlers { return { + captureLeader: (model) => ([{ + ...model, + leaderCaptureActive: true, + }, []]), cycleTheme: (model) => ([{ ...model, jeditTheme: nextJeditTheme(model.jeditTheme), diff --git a/src/app/workspace/viewer-overlays.ts b/src/app/workspace/viewer-overlays.ts index 8ecb424f..fa2b0b16 100644 --- a/src/app/workspace/viewer-overlays.ts +++ b/src/app/workspace/viewer-overlays.ts @@ -22,6 +22,7 @@ const STARTUP_FILE_MODAL_I18N_KEYS = Object.freeze({ CurrentDirectory: 'startupFileModal.current_directory', Empty: 'startupFileModal.empty', } as const); +const LEADER_CAPTURE_HINT_KEY = 'settings.rows.leader.capture_hint'; export function paintWorkspaceOverlays( screen: Surface, @@ -62,6 +63,7 @@ function paintSettingsOverlay( const settingsWidth = resolveSettingsDrawerWidth(model.columns); screen.blit( renderSettingsDrawer({ + closeHint: model.leaderCaptureActive ? model.i18n.t(LEADER_CAPTURE_HINT_KEY) : undefined, rows: settingsRows(model), selectedIndex: model.settingsFocusIndex, theme: model.jeditTheme, diff --git a/src/app/workspace/viewer.ts b/src/app/workspace/viewer.ts index 81b8cc9e..de7cdab7 100644 --- a/src/app/workspace/viewer.ts +++ b/src/app/workspace/viewer.ts @@ -139,6 +139,7 @@ function paintWorkspaceFooter(screen: Surface, model: WorkspaceModel): void { editorCursorPosition: workspaceFooterCursorPosition(model), pendingNormal: model.editor?.pendingNormal, settingsOpen: model.settingsOpen, + leaderCaptureActive: model.leaderCaptureActive, cwd: model.cwd, selectedEntry: model.entries[model.selectedIndex], editorPath: model.editor?.path, diff --git a/src/app/workspace/workspace-runtime-dependencies.ts b/src/app/workspace/workspace-runtime-dependencies.ts index 7ce31018..2c463e4f 100644 --- a/src/app/workspace/workspace-runtime-dependencies.ts +++ b/src/app/workspace/workspace-runtime-dependencies.ts @@ -14,12 +14,14 @@ import type { ProductionTextSession } from './production-text-session.js'; import type { ProfilerTracePort } from '../raytracer-profiler.js'; import type { renderWorkspace } from './viewer.js'; import type { WorkspaceEntryOpener } from './mouse.js'; +import type { JimSettingsPort } from '../../ports/jim-settings.js'; export type WorkspaceRuntimeMsg = WorkspaceMsg | ResizeMsg | KeyMsg | MouseMsg; export type WorkspaceRuntimeResult = [WorkspaceModel, Cmd[]]; export type WorkspaceResizeMsg = ResizeMsg; export interface WorkspaceRuntimeDependencies { + readonly jimSettings?: JimSettingsPort; // Supplied by the composition root so pointer clicks can open an entry. // Optional: the pointer path still selects without it, and test harnesses // that never click do not have to construct the filesystem dependencies. diff --git a/src/generated/i18n.ts b/src/generated/i18n.ts index 6981417b..6f6dc1e7 100644 --- a/src/generated/i18n.ts +++ b/src/generated/i18n.ts @@ -106,6 +106,7 @@ export type TranslationSchema = { readonly "appearance": string; readonly "editor": string; readonly "runtime": string; + readonly "keybindings": string; }; readonly "rows": { readonly "theme": { @@ -140,6 +141,20 @@ export type TranslationSchema = { readonly "label": string; readonly "description": string; }; + readonly "leader": { + readonly "label": string; + readonly "description": string; + readonly "capture": string; + readonly "capturing_value": string; + readonly "space": string; + readonly "backslash": string; + readonly "invalid_key": string; + readonly "invalid": string; + readonly "read_error": string; + readonly "write_error": string; + readonly "unavailable": string; + readonly "capture_hint": string; + }; }; readonly "values": { readonly "on": string; @@ -178,47 +193,47 @@ export interface LocaleMetadata { readonly direction: LocaleDirection; } -export const en: TranslationSchema = {"footer":{"mode":{"browse":"browse","insert":"insert","normal":"normal","preview":"preview","settings":"settings","files":"files","graft":"graft","history":"history"},"context":{"settings":"settings","graft_empty":"open a file to inspect it","history_empty":"no Echo evidence yet","history_count":"Echo evidence: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k move","enter_change":"enter change","f2_close":"f2 close","esc_close":"esc close","j_k_scroll":"j/k scroll","f3_source":"f3 source","f3_preview":"f3 preview","text_input":"text input","esc_normal":"esc normal","ctrl_s_save":"ctrl+s save","ctrl_t_theme":"ctrl+t theme","tab_focus":"tab focus","tab_indent":"tab indent","ctrl_l_scene_picker":"ctrl+l scene picker","scene_picker":"scene picker","ctrl_b_files":"ctrl+b files","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h history","enter_open":"enter open","backspace_up":"backspace up","ctrl_b_close":"ctrl+b close","enter_jump":"enter jump","r_refresh":"r refresh","ctrl_g_close":"ctrl+g close","ctrl_h_close":"ctrl+h close","i_insert":"i insert","o_open_line":"o open line","cc_line":"cc line","cw_word":"cw word","ce_word_end":"ce word-end","c0_start":"c0 start","c_end":"c$ end","dd_line":"dd line","dw_word":"dw word","de_word_end":"de word-end","d0_start":"d0 start","d_end":"d$ end","yy_line":"yy line","yw_word":"yw word","ye_word_end":"ye word-end","y0_start":"y0 start","y_end":"y$ end","gg_top":"gg top","esc_cancel":"esc cancel"},"evidence":{"head":"head","tick":"tick","anchor":"anchor","coverage":"coverage","complete":"complete","partial":"partial","unavailable":"unavailable"}},"history":{"title":"Echo History","empty":"No Echo evidence yet","header":"# tick kind status evidence summary"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; +export const en: TranslationSchema = {"footer":{"mode":{"browse":"browse","insert":"insert","normal":"normal","preview":"preview","settings":"settings","files":"files","graft":"graft","history":"history"},"context":{"settings":"settings","graft_empty":"open a file to inspect it","history_empty":"no Echo evidence yet","history_count":"Echo evidence: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k move","enter_change":"enter change","f2_close":"f2 close","esc_close":"esc close","j_k_scroll":"j/k scroll","f3_source":"f3 source","f3_preview":"f3 preview","text_input":"text input","esc_normal":"esc normal","ctrl_s_save":"ctrl+s save","ctrl_t_theme":"ctrl+t theme","tab_focus":"tab focus","tab_indent":"tab indent","ctrl_l_scene_picker":"ctrl+l scene picker","scene_picker":"scene picker","ctrl_b_files":"ctrl+b files","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h history","enter_open":"enter open","backspace_up":"backspace up","ctrl_b_close":"ctrl+b close","enter_jump":"enter jump","r_refresh":"r refresh","ctrl_g_close":"ctrl+g close","ctrl_h_close":"ctrl+h close","i_insert":"i insert","o_open_line":"o open line","cc_line":"cc line","cw_word":"cw word","ce_word_end":"ce word-end","c0_start":"c0 start","c_end":"c$ end","dd_line":"dd line","dw_word":"dw word","de_word_end":"de word-end","d0_start":"d0 start","d_end":"d$ end","yy_line":"yy line","yw_word":"yw word","ye_word_end":"ye word-end","y0_start":"y0 start","y_end":"y$ end","gg_top":"gg top","esc_cancel":"esc cancel"},"evidence":{"head":"head","tick":"tick","anchor":"anchor","coverage":"coverage","complete":"complete","partial":"partial","unavailable":"unavailable"}},"history":{"title":"Echo History","empty":"No Echo evidence yet","header":"# tick kind status evidence summary"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime","keybindings":"Keybindings"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."},"leader":{"label":"Leader","description":"Enter to change; saved in ~/.jimrc.","capture":"Press one key; Esc cancels.","capturing_value":"Press a key","space":"Space","backslash":"Backslash","invalid_key":"Use one printable key; Esc cancels.","invalid":".jimrc is invalid; fix it and retry.","read_error":"Could not read .jimrc.","write_error":"Could not save .jimrc; retry.","unavailable":"Settings cannot be saved in this host.","capture_hint":"Esc cancels; F2 closes"}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; -export const fr: TranslationSchema = {"footer":{"mode":{"browse":"parcourir","insert":"insertion","normal":"normal","preview":"aperçu","settings":"paramètres","files":"fichiers","graft":"graft","history":"historique"},"context":{"settings":"paramètres","graft_empty":"ouvrez un fichier pour l'inspecter","history_empty":"pas encore de preuve Echo","history_count":"Preuves Echo : {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k déplacer","enter_change":"enter changer","f2_close":"f2 fermer","esc_close":"esc fermer","j_k_scroll":"j/k défiler","f3_source":"f3 source","f3_preview":"f3 aperçu","text_input":"saisie texte","esc_normal":"esc normal","ctrl_s_save":"ctrl+s enregistrer","ctrl_t_theme":"ctrl+t thème","tab_focus":"tab focus","tab_indent":"tab retrait","ctrl_l_scene_picker":"ctrl+l scène","scene_picker":"sélecteur de scène","ctrl_b_files":"ctrl+b fichiers","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h historique","enter_open":"enter ouvrir","backspace_up":"backspace haut","ctrl_b_close":"ctrl+b fermer","enter_jump":"enter aller","r_refresh":"r actualiser","ctrl_g_close":"ctrl+g fermer","ctrl_h_close":"ctrl+h fermer","i_insert":"i insérer","o_open_line":"o ouvrir ligne","cc_line":"cc ligne","cw_word":"cw mot","ce_word_end":"ce fin mot","c0_start":"c0 début","c_end":"c$ fin","dd_line":"dd ligne","dw_word":"dw mot","de_word_end":"de fin mot","d0_start":"d0 début","d_end":"d$ fin","yy_line":"yy ligne","yw_word":"yw mot","ye_word_end":"ye fin mot","y0_start":"y0 début","y_end":"y$ fin","gg_top":"gg haut","esc_cancel":"esc annuler"},"evidence":{"head":"tête","tick":"tick","anchor":"ancre","coverage":"couverture","complete":"complète","partial":"partielle","unavailable":"indisponible"}},"history":{"title":"Historique Echo","empty":"Pas encore de preuve Echo","header":"# tick type statut preuve résumé"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; +export const fr: TranslationSchema = {"footer":{"mode":{"browse":"parcourir","insert":"insertion","normal":"normal","preview":"aperçu","settings":"paramètres","files":"fichiers","graft":"graft","history":"historique"},"context":{"settings":"paramètres","graft_empty":"ouvrez un fichier pour l'inspecter","history_empty":"pas encore de preuve Echo","history_count":"Preuves Echo : {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k déplacer","enter_change":"enter changer","f2_close":"f2 fermer","esc_close":"esc fermer","j_k_scroll":"j/k défiler","f3_source":"f3 source","f3_preview":"f3 aperçu","text_input":"saisie texte","esc_normal":"esc normal","ctrl_s_save":"ctrl+s enregistrer","ctrl_t_theme":"ctrl+t thème","tab_focus":"tab focus","tab_indent":"tab retrait","ctrl_l_scene_picker":"ctrl+l scène","scene_picker":"sélecteur de scène","ctrl_b_files":"ctrl+b fichiers","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h historique","enter_open":"enter ouvrir","backspace_up":"backspace haut","ctrl_b_close":"ctrl+b fermer","enter_jump":"enter aller","r_refresh":"r actualiser","ctrl_g_close":"ctrl+g fermer","ctrl_h_close":"ctrl+h fermer","i_insert":"i insérer","o_open_line":"o ouvrir ligne","cc_line":"cc ligne","cw_word":"cw mot","ce_word_end":"ce fin mot","c0_start":"c0 début","c_end":"c$ fin","dd_line":"dd ligne","dw_word":"dw mot","de_word_end":"de fin mot","d0_start":"d0 début","d_end":"d$ fin","yy_line":"yy ligne","yw_word":"yw mot","ye_word_end":"ye fin mot","y0_start":"y0 début","y_end":"y$ fin","gg_top":"gg haut","esc_cancel":"esc annuler"},"evidence":{"head":"tête","tick":"tick","anchor":"ancre","coverage":"couverture","complete":"complète","partial":"partielle","unavailable":"indisponible"}},"history":{"title":"Historique Echo","empty":"Pas encore de preuve Echo","header":"# tick type statut preuve résumé"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime","keybindings":"Raccourcis clavier"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."},"leader":{"label":"Leader","description":"Entrée : modifier; fichier ~/.jimrc.","capture":"Appuyez sur une touche; Échap annule.","capturing_value":"Appuyez sur une touche","space":"Espace","backslash":"Barre oblique inverse","invalid_key":"Une touche imprimable; Échap annule.","invalid":".jimrc invalide; corrigez et réessayez.","read_error":"Lecture de .jimrc impossible.","write_error":"Enregistrement de .jimrc impossible.","unavailable":"Ce mode ne peut pas enregistrer.","capture_hint":"Échap annule; F2 ferme"}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; -export const it: TranslationSchema = {"footer":{"mode":{"browse":"sfoglia","insert":"inserisci","normal":"normale","preview":"anteprima","settings":"impostazioni","files":"file","graft":"graft","history":"cronologia"},"context":{"settings":"impostazioni","graft_empty":"apri un file per ispezionarlo","history_empty":"nessuna prova Echo ancora","history_count":"Prove Echo: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k muovi","enter_change":"enter cambia","f2_close":"f2 chiudi","esc_close":"esc chiudi","j_k_scroll":"j/k scorri","f3_source":"f3 sorgente","f3_preview":"f3 anteprima","text_input":"testo","esc_normal":"esc normale","ctrl_s_save":"ctrl+s salva","ctrl_t_theme":"ctrl+t tema","tab_focus":"tab focus","tab_indent":"tab rientra","ctrl_l_scene_picker":"ctrl+l scena","scene_picker":"selettore scena","ctrl_b_files":"ctrl+b file","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h cronologia","enter_open":"enter apri","backspace_up":"backspace su","ctrl_b_close":"ctrl+b chiudi","enter_jump":"enter vai","r_refresh":"r aggiorna","ctrl_g_close":"ctrl+g chiudi","ctrl_h_close":"ctrl+h chiudi","i_insert":"i inserisci","o_open_line":"o apri riga","cc_line":"cc riga","cw_word":"cw parola","ce_word_end":"ce fine parola","c0_start":"c0 inizio","c_end":"c$ fine","dd_line":"dd riga","dw_word":"dw parola","de_word_end":"de fine parola","d0_start":"d0 inizio","d_end":"d$ fine","yy_line":"yy riga","yw_word":"yw parola","ye_word_end":"ye fine parola","y0_start":"y0 inizio","y_end":"y$ fine","gg_top":"gg cima","esc_cancel":"esc annulla"},"evidence":{"head":"testa","tick":"tick","anchor":"ancora","coverage":"copertura","complete":"completa","partial":"parziale","unavailable":"non disponibile"}},"history":{"title":"Cronologia Echo","empty":"Nessuna prova Echo ancora","header":"# tick tipo stato prova riepilogo"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; +export const it: TranslationSchema = {"footer":{"mode":{"browse":"sfoglia","insert":"inserisci","normal":"normale","preview":"anteprima","settings":"impostazioni","files":"file","graft":"graft","history":"cronologia"},"context":{"settings":"impostazioni","graft_empty":"apri un file per ispezionarlo","history_empty":"nessuna prova Echo ancora","history_count":"Prove Echo: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k muovi","enter_change":"enter cambia","f2_close":"f2 chiudi","esc_close":"esc chiudi","j_k_scroll":"j/k scorri","f3_source":"f3 sorgente","f3_preview":"f3 anteprima","text_input":"testo","esc_normal":"esc normale","ctrl_s_save":"ctrl+s salva","ctrl_t_theme":"ctrl+t tema","tab_focus":"tab focus","tab_indent":"tab rientra","ctrl_l_scene_picker":"ctrl+l scena","scene_picker":"selettore scena","ctrl_b_files":"ctrl+b file","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h cronologia","enter_open":"enter apri","backspace_up":"backspace su","ctrl_b_close":"ctrl+b chiudi","enter_jump":"enter vai","r_refresh":"r aggiorna","ctrl_g_close":"ctrl+g chiudi","ctrl_h_close":"ctrl+h chiudi","i_insert":"i inserisci","o_open_line":"o apri riga","cc_line":"cc riga","cw_word":"cw parola","ce_word_end":"ce fine parola","c0_start":"c0 inizio","c_end":"c$ fine","dd_line":"dd riga","dw_word":"dw parola","de_word_end":"de fine parola","d0_start":"d0 inizio","d_end":"d$ fine","yy_line":"yy riga","yw_word":"yw parola","ye_word_end":"ye fine parola","y0_start":"y0 inizio","y_end":"y$ fine","gg_top":"gg cima","esc_cancel":"esc annulla"},"evidence":{"head":"testa","tick":"tick","anchor":"ancora","coverage":"copertura","complete":"completa","partial":"parziale","unavailable":"non disponibile"}},"history":{"title":"Cronologia Echo","empty":"Nessuna prova Echo ancora","header":"# tick tipo stato prova riepilogo"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime","keybindings":"Scorciatoie da tastiera"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."},"leader":{"label":"Leader","description":"Invio per cambiare; file ~/.jimrc.","capture":"Premi un tasto; Esc annulla.","capturing_value":"Premi un tasto","space":"Spazio","backslash":"Barra inversa","invalid_key":"Un tasto stampabile; Esc annulla.","invalid":".jimrc non valido; correggi e riprova.","read_error":"Impossibile leggere .jimrc.","write_error":"Impossibile salvare .jimrc; riprova.","unavailable":"Questo ambiente non salva le impostazioni.","capture_hint":"Esc annulla; F2 chiude"}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; -export const de: TranslationSchema = {"footer":{"mode":{"browse":"durchsuchen","insert":"einfügen","normal":"normal","preview":"vorschau","settings":"einstellungen","files":"dateien","graft":"graft","history":"verlauf"},"context":{"settings":"einstellungen","graft_empty":"datei öffnen zum prüfen","history_empty":"noch keine Echo-Nachweise","history_count":"Echo-Nachweise: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k bewegen","enter_change":"enter ändern","f2_close":"f2 schließen","esc_close":"esc schließen","j_k_scroll":"j/k scrollen","f3_source":"f3 quelltext","f3_preview":"f3 vorschau","text_input":"texteingabe","esc_normal":"esc normal","ctrl_s_save":"ctrl+s speichern","ctrl_t_theme":"ctrl+t theme","tab_focus":"tab fokus","tab_indent":"tab einrücken","ctrl_l_scene_picker":"ctrl+l szene","scene_picker":"szenenauswahl","ctrl_b_files":"ctrl+b dateien","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h verlauf","enter_open":"enter öffnen","backspace_up":"backspace hoch","ctrl_b_close":"ctrl+b schließen","enter_jump":"enter springen","r_refresh":"r aktualisieren","ctrl_g_close":"ctrl+g schließen","ctrl_h_close":"ctrl+h schließen","i_insert":"i einfügen","o_open_line":"o zeile öffnen","cc_line":"cc zeile","cw_word":"cw wort","ce_word_end":"ce wortende","c0_start":"c0 anfang","c_end":"c$ ende","dd_line":"dd zeile","dw_word":"dw wort","de_word_end":"de wortende","d0_start":"d0 anfang","d_end":"d$ ende","yy_line":"yy zeile","yw_word":"yw wort","ye_word_end":"ye wortende","y0_start":"y0 anfang","y_end":"y$ ende","gg_top":"gg oben","esc_cancel":"esc abbrechen"},"evidence":{"head":"Kopf","tick":"Tick","anchor":"Anker","coverage":"Abdeckung","complete":"vollständig","partial":"teilweise","unavailable":"nicht verfügbar"}},"history":{"title":"Echo-Verlauf","empty":"Noch keine Echo-Nachweise","header":"# tick art status nachweis zusammenfassung"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; +export const de: TranslationSchema = {"footer":{"mode":{"browse":"durchsuchen","insert":"einfügen","normal":"normal","preview":"vorschau","settings":"einstellungen","files":"dateien","graft":"graft","history":"verlauf"},"context":{"settings":"einstellungen","graft_empty":"datei öffnen zum prüfen","history_empty":"noch keine Echo-Nachweise","history_count":"Echo-Nachweise: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k bewegen","enter_change":"enter ändern","f2_close":"f2 schließen","esc_close":"esc schließen","j_k_scroll":"j/k scrollen","f3_source":"f3 quelltext","f3_preview":"f3 vorschau","text_input":"texteingabe","esc_normal":"esc normal","ctrl_s_save":"ctrl+s speichern","ctrl_t_theme":"ctrl+t theme","tab_focus":"tab fokus","tab_indent":"tab einrücken","ctrl_l_scene_picker":"ctrl+l szene","scene_picker":"szenenauswahl","ctrl_b_files":"ctrl+b dateien","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h verlauf","enter_open":"enter öffnen","backspace_up":"backspace hoch","ctrl_b_close":"ctrl+b schließen","enter_jump":"enter springen","r_refresh":"r aktualisieren","ctrl_g_close":"ctrl+g schließen","ctrl_h_close":"ctrl+h schließen","i_insert":"i einfügen","o_open_line":"o zeile öffnen","cc_line":"cc zeile","cw_word":"cw wort","ce_word_end":"ce wortende","c0_start":"c0 anfang","c_end":"c$ ende","dd_line":"dd zeile","dw_word":"dw wort","de_word_end":"de wortende","d0_start":"d0 anfang","d_end":"d$ ende","yy_line":"yy zeile","yw_word":"yw wort","ye_word_end":"ye wortende","y0_start":"y0 anfang","y_end":"y$ ende","gg_top":"gg oben","esc_cancel":"esc abbrechen"},"evidence":{"head":"Kopf","tick":"Tick","anchor":"Anker","coverage":"Abdeckung","complete":"vollständig","partial":"teilweise","unavailable":"nicht verfügbar"}},"history":{"title":"Echo-Verlauf","empty":"Noch keine Echo-Nachweise","header":"# tick art status nachweis zusammenfassung"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime","keybindings":"Tastenkürzel"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."},"leader":{"label":"Leader","description":"Enter zum Ändern; Datei ~/.jimrc.","capture":"Taste drücken; Esc bricht ab.","capturing_value":"Taste drücken","space":"Leertaste","backslash":"Rückstrich","invalid_key":"Ein druckbares Zeichen; Esc bricht ab.","invalid":".jimrc ungültig; korrigieren und erneut versuchen.","read_error":".jimrc konnte nicht gelesen werden.","write_error":".jimrc konnte nicht gespeichert werden.","unavailable":"Diese Umgebung speichert keine Einstellungen.","capture_hint":"Esc abbricht; F2 schließt"}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; -export const es: TranslationSchema = {"footer":{"mode":{"browse":"explorar","insert":"insertar","normal":"normal","preview":"vista previa","settings":"ajustes","files":"archivos","graft":"graft","history":"historial"},"context":{"settings":"ajustes","graft_empty":"abre un archivo para inspeccionarlo","history_empty":"sin evidencia Echo todavía","history_count":"Evidencia Echo: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k mover","enter_change":"enter cambiar","f2_close":"f2 cerrar","esc_close":"esc cerrar","j_k_scroll":"j/k desplazar","f3_source":"f3 fuente","f3_preview":"f3 vista previa","text_input":"texto","esc_normal":"esc normal","ctrl_s_save":"ctrl+s guardar","ctrl_t_theme":"ctrl+t tema","tab_focus":"tab foco","tab_indent":"tab sangría","ctrl_l_scene_picker":"ctrl+l escena","scene_picker":"selector de escena","ctrl_b_files":"ctrl+b archivos","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h historial","enter_open":"enter abrir","backspace_up":"backspace arriba","ctrl_b_close":"ctrl+b cerrar","enter_jump":"enter saltar","r_refresh":"r actualizar","ctrl_g_close":"ctrl+g cerrar","ctrl_h_close":"ctrl+h cerrar","i_insert":"i insertar","o_open_line":"o abrir línea","cc_line":"cc línea","cw_word":"cw palabra","ce_word_end":"ce fin palabra","c0_start":"c0 inicio","c_end":"c$ fin","dd_line":"dd línea","dw_word":"dw palabra","de_word_end":"de fin palabra","d0_start":"d0 inicio","d_end":"d$ fin","yy_line":"yy línea","yw_word":"yw palabra","ye_word_end":"ye fin palabra","y0_start":"y0 inicio","y_end":"y$ fin","gg_top":"gg arriba","esc_cancel":"esc cancelar"},"evidence":{"head":"cabeza","tick":"tick","anchor":"ancla","coverage":"cobertura","complete":"completa","partial":"parcial","unavailable":"no disponible"}},"history":{"title":"Historial Echo","empty":"Sin evidencia Echo todavía","header":"# tick tipo estado evidencia resumen"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; +export const es: TranslationSchema = {"footer":{"mode":{"browse":"explorar","insert":"insertar","normal":"normal","preview":"vista previa","settings":"ajustes","files":"archivos","graft":"graft","history":"historial"},"context":{"settings":"ajustes","graft_empty":"abre un archivo para inspeccionarlo","history_empty":"sin evidencia Echo todavía","history_count":"Evidencia Echo: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k mover","enter_change":"enter cambiar","f2_close":"f2 cerrar","esc_close":"esc cerrar","j_k_scroll":"j/k desplazar","f3_source":"f3 fuente","f3_preview":"f3 vista previa","text_input":"texto","esc_normal":"esc normal","ctrl_s_save":"ctrl+s guardar","ctrl_t_theme":"ctrl+t tema","tab_focus":"tab foco","tab_indent":"tab sangría","ctrl_l_scene_picker":"ctrl+l escena","scene_picker":"selector de escena","ctrl_b_files":"ctrl+b archivos","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h historial","enter_open":"enter abrir","backspace_up":"backspace arriba","ctrl_b_close":"ctrl+b cerrar","enter_jump":"enter saltar","r_refresh":"r actualizar","ctrl_g_close":"ctrl+g cerrar","ctrl_h_close":"ctrl+h cerrar","i_insert":"i insertar","o_open_line":"o abrir línea","cc_line":"cc línea","cw_word":"cw palabra","ce_word_end":"ce fin palabra","c0_start":"c0 inicio","c_end":"c$ fin","dd_line":"dd línea","dw_word":"dw palabra","de_word_end":"de fin palabra","d0_start":"d0 inicio","d_end":"d$ fin","yy_line":"yy línea","yw_word":"yw palabra","ye_word_end":"ye fin palabra","y0_start":"y0 inicio","y_end":"y$ fin","gg_top":"gg arriba","esc_cancel":"esc cancelar"},"evidence":{"head":"cabeza","tick":"tick","anchor":"ancla","coverage":"cobertura","complete":"completa","partial":"parcial","unavailable":"no disponible"}},"history":{"title":"Historial Echo","empty":"Sin evidencia Echo todavía","header":"# tick tipo estado evidencia resumen"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime","keybindings":"Atajos de teclado"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."},"leader":{"label":"Leader","description":"Enter para cambiar; archivo ~/.jimrc.","capture":"Pulsa una tecla; Esc cancela.","capturing_value":"Pulsa una tecla","space":"Espacio","backslash":"Barra inversa","invalid_key":"Una tecla imprimible; Esc cancela.","invalid":".jimrc no válido; corrige y reintenta.","read_error":"No se pudo leer .jimrc.","write_error":"No se pudo guardar .jimrc; reintenta.","unavailable":"Este entorno no guarda ajustes.","capture_hint":"Esc cancela; F2 cierra"}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; -export const ko: TranslationSchema = {"footer":{"mode":{"browse":"탐색","insert":"삽입","normal":"일반","preview":"미리보기","settings":"설정","files":"파일","graft":"Graft","history":"기록"},"context":{"settings":"설정","graft_empty":"검사할 파일을 여세요","history_empty":"아직 Echo 증거 없음","history_count":"Echo 증거: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k 이동","enter_change":"enter 변경","f2_close":"f2 닫기","esc_close":"esc 닫기","j_k_scroll":"j/k 스크롤","f3_source":"f3 소스","f3_preview":"f3 미리보기","text_input":"텍스트 입력","esc_normal":"esc 일반","ctrl_s_save":"ctrl+s 저장","ctrl_t_theme":"ctrl+t 테마","tab_focus":"tab 포커스","tab_indent":"tab 들여쓰기","ctrl_l_scene_picker":"ctrl+l 장면","scene_picker":"장면 선택","ctrl_b_files":"ctrl+b 파일","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h 기록","enter_open":"enter 열기","backspace_up":"backspace 위로","ctrl_b_close":"ctrl+b 닫기","enter_jump":"enter 이동","r_refresh":"r 새로고침","ctrl_g_close":"ctrl+g 닫기","ctrl_h_close":"ctrl+h 닫기","i_insert":"i 삽입","o_open_line":"o 줄 열기","cc_line":"cc 줄","cw_word":"cw 단어","ce_word_end":"ce 단어 끝","c0_start":"c0 시작","c_end":"c$ 끝","dd_line":"dd 줄","dw_word":"dw 단어","de_word_end":"de 단어 끝","d0_start":"d0 시작","d_end":"d$ 끝","yy_line":"yy 줄","yw_word":"yw 단어","ye_word_end":"ye 단어 끝","y0_start":"y0 시작","y_end":"y$ 끝","gg_top":"gg 맨 위","esc_cancel":"esc 취소"},"evidence":{"head":"헤드","tick":"틱","anchor":"앵커","coverage":"범위","complete":"완전","partial":"부분","unavailable":"사용 불가"}},"history":{"title":"Echo 기록","empty":"아직 Echo 증거 없음","header":"# tick 종류 상태 증거 요약"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; +export const ko: TranslationSchema = {"footer":{"mode":{"browse":"탐색","insert":"삽입","normal":"일반","preview":"미리보기","settings":"설정","files":"파일","graft":"Graft","history":"기록"},"context":{"settings":"설정","graft_empty":"검사할 파일을 여세요","history_empty":"아직 Echo 증거 없음","history_count":"Echo 증거: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k 이동","enter_change":"enter 변경","f2_close":"f2 닫기","esc_close":"esc 닫기","j_k_scroll":"j/k 스크롤","f3_source":"f3 소스","f3_preview":"f3 미리보기","text_input":"텍스트 입력","esc_normal":"esc 일반","ctrl_s_save":"ctrl+s 저장","ctrl_t_theme":"ctrl+t 테마","tab_focus":"tab 포커스","tab_indent":"tab 들여쓰기","ctrl_l_scene_picker":"ctrl+l 장면","scene_picker":"장면 선택","ctrl_b_files":"ctrl+b 파일","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h 기록","enter_open":"enter 열기","backspace_up":"backspace 위로","ctrl_b_close":"ctrl+b 닫기","enter_jump":"enter 이동","r_refresh":"r 새로고침","ctrl_g_close":"ctrl+g 닫기","ctrl_h_close":"ctrl+h 닫기","i_insert":"i 삽입","o_open_line":"o 줄 열기","cc_line":"cc 줄","cw_word":"cw 단어","ce_word_end":"ce 단어 끝","c0_start":"c0 시작","c_end":"c$ 끝","dd_line":"dd 줄","dw_word":"dw 단어","de_word_end":"de 단어 끝","d0_start":"d0 시작","d_end":"d$ 끝","yy_line":"yy 줄","yw_word":"yw 단어","ye_word_end":"ye 단어 끝","y0_start":"y0 시작","y_end":"y$ 끝","gg_top":"gg 맨 위","esc_cancel":"esc 취소"},"evidence":{"head":"헤드","tick":"틱","anchor":"앵커","coverage":"범위","complete":"완전","partial":"부분","unavailable":"사용 불가"}},"history":{"title":"Echo 기록","empty":"아직 Echo 증거 없음","header":"# tick 종류 상태 증거 요약"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime","keybindings":"키 바인딩"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."},"leader":{"label":"Leader","description":"Enter로 변경; ~/.jimrc에 저장.","capture":"키를 누르세요; Esc로 취소.","capturing_value":"키를 누르세요","space":"스페이스","backslash":"백슬래시","invalid_key":"출력 가능한 키 하나; Esc로 취소.","invalid":".jimrc가 잘못되었습니다. 수정 후 재시도하세요.","read_error":".jimrc를 읽을 수 없습니다.","write_error":".jimrc를 저장할 수 없습니다. 재시도하세요.","unavailable":"이 환경에서는 설정을 저장할 수 없습니다.","capture_hint":"Esc 취소; F2 닫기"}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; -export const ja: TranslationSchema = {"footer":{"mode":{"browse":"参照","insert":"挿入","normal":"通常","preview":"プレビュー","settings":"設定","files":"ファイル","graft":"Graft","history":"履歴"},"context":{"settings":"設定","graft_empty":"検査するファイルを開く","history_empty":"Echo 証拠はまだありません","history_count":"Echo 証拠: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k 移動","enter_change":"enter 変更","f2_close":"f2 閉じる","esc_close":"esc 閉じる","j_k_scroll":"j/k スクロール","f3_source":"f3 ソース","f3_preview":"f3 プレビュー","text_input":"テキスト入力","esc_normal":"esc 通常","ctrl_s_save":"ctrl+s 保存","ctrl_t_theme":"ctrl+t テーマ","tab_focus":"tab フォーカス","tab_indent":"tab インデント","ctrl_l_scene_picker":"ctrl+l シーン","scene_picker":"シーン選択","ctrl_b_files":"ctrl+b ファイル","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h 履歴","enter_open":"enter 開く","backspace_up":"backspace 上へ","ctrl_b_close":"ctrl+b 閉じる","enter_jump":"enter 移動","r_refresh":"r 更新","ctrl_g_close":"ctrl+g 閉じる","ctrl_h_close":"ctrl+h 閉じる","i_insert":"i 挿入","o_open_line":"o 行を開く","cc_line":"cc 行","cw_word":"cw 単語","ce_word_end":"ce 単語末","c0_start":"c0 先頭","c_end":"c$ 末尾","dd_line":"dd 行","dw_word":"dw 単語","de_word_end":"de 単語末","d0_start":"d0 先頭","d_end":"d$ 末尾","yy_line":"yy 行","yw_word":"yw 単語","ye_word_end":"ye 単語末","y0_start":"y0 先頭","y_end":"y$ 末尾","gg_top":"gg 先頭","esc_cancel":"esc 取消"},"evidence":{"head":"ヘッド","tick":"ティック","anchor":"アンカー","coverage":"範囲","complete":"完全","partial":"部分","unavailable":"利用不可"}},"history":{"title":"Echo 履歴","empty":"Echo 証拠はまだありません","header":"# tick 種類 状態 証拠 概要"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; +export const ja: TranslationSchema = {"footer":{"mode":{"browse":"参照","insert":"挿入","normal":"通常","preview":"プレビュー","settings":"設定","files":"ファイル","graft":"Graft","history":"履歴"},"context":{"settings":"設定","graft_empty":"検査するファイルを開く","history_empty":"Echo 証拠はまだありません","history_count":"Echo 証拠: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k 移動","enter_change":"enter 変更","f2_close":"f2 閉じる","esc_close":"esc 閉じる","j_k_scroll":"j/k スクロール","f3_source":"f3 ソース","f3_preview":"f3 プレビュー","text_input":"テキスト入力","esc_normal":"esc 通常","ctrl_s_save":"ctrl+s 保存","ctrl_t_theme":"ctrl+t テーマ","tab_focus":"tab フォーカス","tab_indent":"tab インデント","ctrl_l_scene_picker":"ctrl+l シーン","scene_picker":"シーン選択","ctrl_b_files":"ctrl+b ファイル","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h 履歴","enter_open":"enter 開く","backspace_up":"backspace 上へ","ctrl_b_close":"ctrl+b 閉じる","enter_jump":"enter 移動","r_refresh":"r 更新","ctrl_g_close":"ctrl+g 閉じる","ctrl_h_close":"ctrl+h 閉じる","i_insert":"i 挿入","o_open_line":"o 行を開く","cc_line":"cc 行","cw_word":"cw 単語","ce_word_end":"ce 単語末","c0_start":"c0 先頭","c_end":"c$ 末尾","dd_line":"dd 行","dw_word":"dw 単語","de_word_end":"de 単語末","d0_start":"d0 先頭","d_end":"d$ 末尾","yy_line":"yy 行","yw_word":"yw 単語","ye_word_end":"ye 単語末","y0_start":"y0 先頭","y_end":"y$ 末尾","gg_top":"gg 先頭","esc_cancel":"esc 取消"},"evidence":{"head":"ヘッド","tick":"ティック","anchor":"アンカー","coverage":"範囲","complete":"完全","partial":"部分","unavailable":"利用不可"}},"history":{"title":"Echo 履歴","empty":"Echo 証拠はまだありません","header":"# tick 種類 状態 証拠 概要"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime","keybindings":"キーバインド"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."},"leader":{"label":"Leader","description":"Enterで変更、~/.jimrcに保存。","capture":"キーを押してください。Escで取消。","capturing_value":"キーを押す","space":"スペース","backslash":"バックスラッシュ","invalid_key":"表示可能なキー1つ。Escで取消。","invalid":".jimrcが無効です。修正して再試行。","read_error":".jimrcを読み込めません。","write_error":".jimrcを保存できません。再試行。","unavailable":"この環境では設定を保存できません。","capture_hint":"Escで取消、F2で閉じる"}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; -export const zhHans: TranslationSchema = {"footer":{"mode":{"browse":"浏览","insert":"插入","normal":"普通","preview":"预览","settings":"设置","files":"文件","graft":"Graft","history":"历史"},"context":{"settings":"设置","graft_empty":"打开文件以检查","history_empty":"还没有 Echo 证据","history_count":"Echo 证据: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k 移动","enter_change":"enter 更改","f2_close":"f2 关闭","esc_close":"esc 关闭","j_k_scroll":"j/k 滚动","f3_source":"f3 源码","f3_preview":"f3 预览","text_input":"文本输入","esc_normal":"esc 普通","ctrl_s_save":"ctrl+s 保存","ctrl_t_theme":"ctrl+t 主题","tab_focus":"tab 焦点","tab_indent":"tab 缩进","ctrl_l_scene_picker":"ctrl+l 场景","scene_picker":"场景选择","ctrl_b_files":"ctrl+b 文件","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h 历史","enter_open":"enter 打开","backspace_up":"backspace 上一级","ctrl_b_close":"ctrl+b 关闭","enter_jump":"enter 跳转","r_refresh":"r 刷新","ctrl_g_close":"ctrl+g 关闭","ctrl_h_close":"ctrl+h 关闭","i_insert":"i 插入","o_open_line":"o 打开行","cc_line":"cc 行","cw_word":"cw 词","ce_word_end":"ce 词尾","c0_start":"c0 开头","c_end":"c$ 结尾","dd_line":"dd 行","dw_word":"dw 词","de_word_end":"de 词尾","d0_start":"d0 开头","d_end":"d$ 结尾","yy_line":"yy 行","yw_word":"yw 词","ye_word_end":"ye 词尾","y0_start":"y0 开头","y_end":"y$ 结尾","gg_top":"gg 顶部","esc_cancel":"esc 取消"},"evidence":{"head":"头","tick":"时刻","anchor":"锚点","coverage":"覆盖","complete":"完整","partial":"部分","unavailable":"不可用"}},"history":{"title":"Echo 历史","empty":"还没有 Echo 证据","header":"# tick 类型 状态 证据 摘要"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; +export const zhHans: TranslationSchema = {"footer":{"mode":{"browse":"浏览","insert":"插入","normal":"普通","preview":"预览","settings":"设置","files":"文件","graft":"Graft","history":"历史"},"context":{"settings":"设置","graft_empty":"打开文件以检查","history_empty":"还没有 Echo 证据","history_count":"Echo 证据: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k 移动","enter_change":"enter 更改","f2_close":"f2 关闭","esc_close":"esc 关闭","j_k_scroll":"j/k 滚动","f3_source":"f3 源码","f3_preview":"f3 预览","text_input":"文本输入","esc_normal":"esc 普通","ctrl_s_save":"ctrl+s 保存","ctrl_t_theme":"ctrl+t 主题","tab_focus":"tab 焦点","tab_indent":"tab 缩进","ctrl_l_scene_picker":"ctrl+l 场景","scene_picker":"场景选择","ctrl_b_files":"ctrl+b 文件","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h 历史","enter_open":"enter 打开","backspace_up":"backspace 上一级","ctrl_b_close":"ctrl+b 关闭","enter_jump":"enter 跳转","r_refresh":"r 刷新","ctrl_g_close":"ctrl+g 关闭","ctrl_h_close":"ctrl+h 关闭","i_insert":"i 插入","o_open_line":"o 打开行","cc_line":"cc 行","cw_word":"cw 词","ce_word_end":"ce 词尾","c0_start":"c0 开头","c_end":"c$ 结尾","dd_line":"dd 行","dw_word":"dw 词","de_word_end":"de 词尾","d0_start":"d0 开头","d_end":"d$ 结尾","yy_line":"yy 行","yw_word":"yw 词","ye_word_end":"ye 词尾","y0_start":"y0 开头","y_end":"y$ 结尾","gg_top":"gg 顶部","esc_cancel":"esc 取消"},"evidence":{"head":"头","tick":"时刻","anchor":"锚点","coverage":"覆盖","complete":"完整","partial":"部分","unavailable":"不可用"}},"history":{"title":"Echo 历史","empty":"还没有 Echo 证据","header":"# tick 类型 状态 证据 摘要"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime","keybindings":"按键绑定"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."},"leader":{"label":"Leader","description":"按 Enter 修改;保存到 ~/.jimrc。","capture":"按一个键;Esc 取消。","capturing_value":"按一个键","space":"空格","backslash":"反斜杠","invalid_key":"使用一个可打印字符;Esc 取消。","invalid":".jimrc 无效;修正后重试。","read_error":"无法读取 .jimrc。","write_error":"无法保存 .jimrc;请重试。","unavailable":"此环境无法保存设置。","capture_hint":"Esc 取消;F2 关闭"}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; -export const ptBR: TranslationSchema = {"footer":{"mode":{"browse":"navegar","insert":"inserir","normal":"normal","preview":"prévia","settings":"configurações","files":"arquivos","graft":"graft","history":"histórico"},"context":{"settings":"configurações","graft_empty":"abra um arquivo para inspecionar","history_empty":"nenhuma evidência Echo ainda","history_count":"Evidências Echo: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k mover","enter_change":"enter alterar","f2_close":"f2 fechar","esc_close":"esc fechar","j_k_scroll":"j/k rolar","f3_source":"f3 código","f3_preview":"f3 prévia","text_input":"entrada de texto","esc_normal":"esc normal","ctrl_s_save":"ctrl+s salvar","ctrl_t_theme":"ctrl+t tema","tab_focus":"tab foco","tab_indent":"tab recuo","ctrl_l_scene_picker":"ctrl+l cena","scene_picker":"seletor de cena","ctrl_b_files":"ctrl+b arquivos","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h histórico","enter_open":"enter abrir","backspace_up":"backspace subir","ctrl_b_close":"ctrl+b fechar","enter_jump":"enter ir","r_refresh":"r atualizar","ctrl_g_close":"ctrl+g fechar","ctrl_h_close":"ctrl+h fechar","i_insert":"i inserir","o_open_line":"o abrir linha","cc_line":"cc linha","cw_word":"cw palavra","ce_word_end":"ce fim palavra","c0_start":"c0 início","c_end":"c$ fim","dd_line":"dd linha","dw_word":"dw palavra","de_word_end":"de fim palavra","d0_start":"d0 início","d_end":"d$ fim","yy_line":"yy linha","yw_word":"yw palavra","ye_word_end":"ye fim palavra","y0_start":"y0 início","y_end":"y$ fim","gg_top":"gg topo","esc_cancel":"esc cancelar"},"evidence":{"head":"cabeça","tick":"tick","anchor":"âncora","coverage":"cobertura","complete":"completa","partial":"parcial","unavailable":"indisponível"}},"history":{"title":"Histórico Echo","empty":"Nenhuma evidência Echo ainda","header":"# tick tipo status evidência resumo"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; +export const ptBR: TranslationSchema = {"footer":{"mode":{"browse":"navegar","insert":"inserir","normal":"normal","preview":"prévia","settings":"configurações","files":"arquivos","graft":"graft","history":"histórico"},"context":{"settings":"configurações","graft_empty":"abra um arquivo para inspecionar","history_empty":"nenhuma evidência Echo ainda","history_count":"Evidências Echo: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k mover","enter_change":"enter alterar","f2_close":"f2 fechar","esc_close":"esc fechar","j_k_scroll":"j/k rolar","f3_source":"f3 código","f3_preview":"f3 prévia","text_input":"entrada de texto","esc_normal":"esc normal","ctrl_s_save":"ctrl+s salvar","ctrl_t_theme":"ctrl+t tema","tab_focus":"tab foco","tab_indent":"tab recuo","ctrl_l_scene_picker":"ctrl+l cena","scene_picker":"seletor de cena","ctrl_b_files":"ctrl+b arquivos","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h histórico","enter_open":"enter abrir","backspace_up":"backspace subir","ctrl_b_close":"ctrl+b fechar","enter_jump":"enter ir","r_refresh":"r atualizar","ctrl_g_close":"ctrl+g fechar","ctrl_h_close":"ctrl+h fechar","i_insert":"i inserir","o_open_line":"o abrir linha","cc_line":"cc linha","cw_word":"cw palavra","ce_word_end":"ce fim palavra","c0_start":"c0 início","c_end":"c$ fim","dd_line":"dd linha","dw_word":"dw palavra","de_word_end":"de fim palavra","d0_start":"d0 início","d_end":"d$ fim","yy_line":"yy linha","yw_word":"yw palavra","ye_word_end":"ye fim palavra","y0_start":"y0 início","y_end":"y$ fim","gg_top":"gg topo","esc_cancel":"esc cancelar"},"evidence":{"head":"cabeça","tick":"tick","anchor":"âncora","coverage":"cobertura","complete":"completa","partial":"parcial","unavailable":"indisponível"}},"history":{"title":"Histórico Echo","empty":"Nenhuma evidência Echo ainda","header":"# tick tipo status evidência resumo"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime","keybindings":"Atalhos de teclado"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."},"leader":{"label":"Leader","description":"Enter para alterar; arquivo ~/.jimrc.","capture":"Pressione uma tecla; Esc cancela.","capturing_value":"Pressione uma tecla","space":"Espaço","backslash":"Barra invertida","invalid_key":"Uma tecla imprimível; Esc cancela.","invalid":".jimrc inválido; corrija e tente novamente.","read_error":"Não foi possível ler .jimrc.","write_error":"Não foi possível salvar .jimrc.","unavailable":"Este ambiente não salva configurações.","capture_hint":"Esc cancela; F2 fecha"}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; -export const ru: TranslationSchema = {"footer":{"mode":{"browse":"обзор","insert":"вставка","normal":"обычный","preview":"просмотр","settings":"настройки","files":"файлы","graft":"graft","history":"история"},"context":{"settings":"настройки","graft_empty":"откройте файл для проверки","history_empty":"пока нет доказательств Echo","history_count":"Доказательства Echo: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k переместить","enter_change":"enter изменить","f2_close":"f2 закрыть","esc_close":"esc закрыть","j_k_scroll":"j/k прокрутка","f3_source":"f3 исходник","f3_preview":"f3 просмотр","text_input":"ввод текста","esc_normal":"esc обычный","ctrl_s_save":"ctrl+s сохранить","ctrl_t_theme":"ctrl+t тема","tab_focus":"tab фокус","tab_indent":"tab отступ","ctrl_l_scene_picker":"ctrl+l сцена","scene_picker":"выбор сцены","ctrl_b_files":"ctrl+b файлы","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h история","enter_open":"enter открыть","backspace_up":"backspace вверх","ctrl_b_close":"ctrl+b закрыть","enter_jump":"enter перейти","r_refresh":"r обновить","ctrl_g_close":"ctrl+g закрыть","ctrl_h_close":"ctrl+h закрыть","i_insert":"i вставка","o_open_line":"o открыть строку","cc_line":"cc строка","cw_word":"cw слово","ce_word_end":"ce конец слова","c0_start":"c0 начало","c_end":"c$ конец","dd_line":"dd строка","dw_word":"dw слово","de_word_end":"de конец слова","d0_start":"d0 начало","d_end":"d$ конец","yy_line":"yy строка","yw_word":"yw слово","ye_word_end":"ye конец слова","y0_start":"y0 начало","y_end":"y$ конец","gg_top":"gg верх","esc_cancel":"esc отмена"},"evidence":{"head":"вершина","tick":"тик","anchor":"якорь","coverage":"покрытие","complete":"полное","partial":"частичное","unavailable":"недоступно"}},"history":{"title":"История Echo","empty":"Пока нет доказательств Echo","header":"# tick тип статус доказательство сводка"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; +export const ru: TranslationSchema = {"footer":{"mode":{"browse":"обзор","insert":"вставка","normal":"обычный","preview":"просмотр","settings":"настройки","files":"файлы","graft":"graft","history":"история"},"context":{"settings":"настройки","graft_empty":"откройте файл для проверки","history_empty":"пока нет доказательств Echo","history_count":"Доказательства Echo: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k переместить","enter_change":"enter изменить","f2_close":"f2 закрыть","esc_close":"esc закрыть","j_k_scroll":"j/k прокрутка","f3_source":"f3 исходник","f3_preview":"f3 просмотр","text_input":"ввод текста","esc_normal":"esc обычный","ctrl_s_save":"ctrl+s сохранить","ctrl_t_theme":"ctrl+t тема","tab_focus":"tab фокус","tab_indent":"tab отступ","ctrl_l_scene_picker":"ctrl+l сцена","scene_picker":"выбор сцены","ctrl_b_files":"ctrl+b файлы","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h история","enter_open":"enter открыть","backspace_up":"backspace вверх","ctrl_b_close":"ctrl+b закрыть","enter_jump":"enter перейти","r_refresh":"r обновить","ctrl_g_close":"ctrl+g закрыть","ctrl_h_close":"ctrl+h закрыть","i_insert":"i вставка","o_open_line":"o открыть строку","cc_line":"cc строка","cw_word":"cw слово","ce_word_end":"ce конец слова","c0_start":"c0 начало","c_end":"c$ конец","dd_line":"dd строка","dw_word":"dw слово","de_word_end":"de конец слова","d0_start":"d0 начало","d_end":"d$ конец","yy_line":"yy строка","yw_word":"yw слово","ye_word_end":"ye конец слова","y0_start":"y0 начало","y_end":"y$ конец","gg_top":"gg верх","esc_cancel":"esc отмена"},"evidence":{"head":"вершина","tick":"тик","anchor":"якорь","coverage":"покрытие","complete":"полное","partial":"частичное","unavailable":"недоступно"}},"history":{"title":"История Echo","empty":"Пока нет доказательств Echo","header":"# tick тип статус доказательство сводка"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime","keybindings":"Сочетания клавиш"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."},"leader":{"label":"Leader","description":"Enter: изменить; файл ~/.jimrc.","capture":"Нажмите клавишу; Esc отменяет.","capturing_value":"Нажмите клавишу","space":"Пробел","backslash":"Обратная косая черта","invalid_key":"Один печатный символ; Esc отменяет.","invalid":".jimrc некорректен; исправьте и повторите.","read_error":"Не удалось прочитать .jimrc.","write_error":"Не удалось сохранить .jimrc; повторите.","unavailable":"В этой среде нельзя сохранять настройки.","capture_hint":"Esc отмена; F2 закрыть"}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; -export const ar: TranslationSchema = {"footer":{"mode":{"browse":"تصفح","insert":"إدراج","normal":"عادي","preview":"معاينة","settings":"إعدادات","files":"ملفات","graft":"Graft","history":"السجل"},"context":{"settings":"إعدادات","graft_empty":"افتح ملفا لفحصه","history_empty":"لا توجد أدلة Echo بعد","history_count":"أدلة Echo: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k تحريك","enter_change":"enter تغيير","f2_close":"f2 إغلاق","esc_close":"esc إغلاق","j_k_scroll":"j/k تمرير","f3_source":"f3 المصدر","f3_preview":"f3 معاينة","text_input":"إدخال نص","esc_normal":"esc عادي","ctrl_s_save":"ctrl+s حفظ","ctrl_t_theme":"ctrl+t سمة","tab_focus":"tab تركيز","tab_indent":"tab إزاحة","ctrl_l_scene_picker":"ctrl+l مشهد","scene_picker":"اختيار مشهد","ctrl_b_files":"ctrl+b ملفات","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h سجل","enter_open":"enter فتح","backspace_up":"backspace أعلى","ctrl_b_close":"ctrl+b إغلاق","enter_jump":"enter انتقال","r_refresh":"r تحديث","ctrl_g_close":"ctrl+g إغلاق","ctrl_h_close":"ctrl+h إغلاق","i_insert":"i إدراج","o_open_line":"o فتح سطر","cc_line":"cc سطر","cw_word":"cw كلمة","ce_word_end":"ce نهاية كلمة","c0_start":"c0 بداية","c_end":"c$ نهاية","dd_line":"dd سطر","dw_word":"dw كلمة","de_word_end":"de نهاية كلمة","d0_start":"d0 بداية","d_end":"d$ نهاية","yy_line":"yy سطر","yw_word":"yw كلمة","ye_word_end":"ye نهاية كلمة","y0_start":"y0 بداية","y_end":"y$ نهاية","gg_top":"gg أعلى","esc_cancel":"esc إلغاء"},"evidence":{"head":"الرأس","tick":"النبضة","anchor":"المرساة","coverage":"التغطية","complete":"كاملة","partial":"جزئية","unavailable":"غير متاحة"}},"history":{"title":"سجل Echo","empty":"لا توجد أدلة Echo بعد","header":"# tick النوع الحالة الدليل الملخص"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; +export const ar: TranslationSchema = {"footer":{"mode":{"browse":"تصفح","insert":"إدراج","normal":"عادي","preview":"معاينة","settings":"إعدادات","files":"ملفات","graft":"Graft","history":"السجل"},"context":{"settings":"إعدادات","graft_empty":"افتح ملفا لفحصه","history_empty":"لا توجد أدلة Echo بعد","history_count":"أدلة Echo: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k تحريك","enter_change":"enter تغيير","f2_close":"f2 إغلاق","esc_close":"esc إغلاق","j_k_scroll":"j/k تمرير","f3_source":"f3 المصدر","f3_preview":"f3 معاينة","text_input":"إدخال نص","esc_normal":"esc عادي","ctrl_s_save":"ctrl+s حفظ","ctrl_t_theme":"ctrl+t سمة","tab_focus":"tab تركيز","tab_indent":"tab إزاحة","ctrl_l_scene_picker":"ctrl+l مشهد","scene_picker":"اختيار مشهد","ctrl_b_files":"ctrl+b ملفات","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h سجل","enter_open":"enter فتح","backspace_up":"backspace أعلى","ctrl_b_close":"ctrl+b إغلاق","enter_jump":"enter انتقال","r_refresh":"r تحديث","ctrl_g_close":"ctrl+g إغلاق","ctrl_h_close":"ctrl+h إغلاق","i_insert":"i إدراج","o_open_line":"o فتح سطر","cc_line":"cc سطر","cw_word":"cw كلمة","ce_word_end":"ce نهاية كلمة","c0_start":"c0 بداية","c_end":"c$ نهاية","dd_line":"dd سطر","dw_word":"dw كلمة","de_word_end":"de نهاية كلمة","d0_start":"d0 بداية","d_end":"d$ نهاية","yy_line":"yy سطر","yw_word":"yw كلمة","ye_word_end":"ye نهاية كلمة","y0_start":"y0 بداية","y_end":"y$ نهاية","gg_top":"gg أعلى","esc_cancel":"esc إلغاء"},"evidence":{"head":"الرأس","tick":"النبضة","anchor":"المرساة","coverage":"التغطية","complete":"كاملة","partial":"جزئية","unavailable":"غير متاحة"}},"history":{"title":"سجل Echo","empty":"لا توجد أدلة Echo بعد","header":"# tick النوع الحالة الدليل الملخص"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime","keybindings":"اختصارات لوحة المفاتيح"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."},"leader":{"label":"Leader","description":"Enter للتغيير؛ الحفظ في ~/.jimrc.","capture":"اضغط مفتاحًا؛ Esc للإلغاء.","capturing_value":"اضغط مفتاحًا","space":"مسافة","backslash":"شرطة مائلة عكسية","invalid_key":"حرف واحد قابل للطباعة؛ Esc للإلغاء.","invalid":"ملف .jimrc غير صالح؛ أصلحه وأعد المحاولة.","read_error":"تعذرت قراءة .jimrc.","write_error":"تعذر حفظ .jimrc؛ أعد المحاولة.","unavailable":"لا يمكن حفظ الإعدادات في هذه البيئة.","capture_hint":"Esc للإلغاء؛ F2 للإغلاق"}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; -export const id: TranslationSchema = {"footer":{"mode":{"browse":"jelajah","insert":"sisip","normal":"normal","preview":"pratinjau","settings":"pengaturan","files":"berkas","graft":"graft","history":"riwayat"},"context":{"settings":"pengaturan","graft_empty":"buka berkas untuk diperiksa","history_empty":"belum ada bukti Echo","history_count":"Bukti Echo: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k pindah","enter_change":"enter ubah","f2_close":"f2 tutup","esc_close":"esc tutup","j_k_scroll":"j/k gulir","f3_source":"f3 sumber","f3_preview":"f3 pratinjau","text_input":"input teks","esc_normal":"esc normal","ctrl_s_save":"ctrl+s simpan","ctrl_t_theme":"ctrl+t tema","tab_focus":"tab fokus","tab_indent":"tab inden","ctrl_l_scene_picker":"ctrl+l adegan","scene_picker":"pilih adegan","ctrl_b_files":"ctrl+b berkas","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h riwayat","enter_open":"enter buka","backspace_up":"backspace naik","ctrl_b_close":"ctrl+b tutup","enter_jump":"enter lompat","r_refresh":"r segarkan","ctrl_g_close":"ctrl+g tutup","ctrl_h_close":"ctrl+h tutup","i_insert":"i sisip","o_open_line":"o buka baris","cc_line":"cc baris","cw_word":"cw kata","ce_word_end":"ce akhir kata","c0_start":"c0 awal","c_end":"c$ akhir","dd_line":"dd baris","dw_word":"dw kata","de_word_end":"de akhir kata","d0_start":"d0 awal","d_end":"d$ akhir","yy_line":"yy baris","yw_word":"yw kata","ye_word_end":"ye akhir kata","y0_start":"y0 awal","y_end":"y$ akhir","gg_top":"gg atas","esc_cancel":"esc batal"},"evidence":{"head":"kepala","tick":"tick","anchor":"jangkar","coverage":"cakupan","complete":"lengkap","partial":"sebagian","unavailable":"tidak tersedia"}},"history":{"title":"Riwayat Echo","empty":"Belum ada bukti Echo","header":"# tick jenis status bukti ringkasan"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; +export const id: TranslationSchema = {"footer":{"mode":{"browse":"jelajah","insert":"sisip","normal":"normal","preview":"pratinjau","settings":"pengaturan","files":"berkas","graft":"graft","history":"riwayat"},"context":{"settings":"pengaturan","graft_empty":"buka berkas untuk diperiksa","history_empty":"belum ada bukti Echo","history_count":"Bukti Echo: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k pindah","enter_change":"enter ubah","f2_close":"f2 tutup","esc_close":"esc tutup","j_k_scroll":"j/k gulir","f3_source":"f3 sumber","f3_preview":"f3 pratinjau","text_input":"input teks","esc_normal":"esc normal","ctrl_s_save":"ctrl+s simpan","ctrl_t_theme":"ctrl+t tema","tab_focus":"tab fokus","tab_indent":"tab inden","ctrl_l_scene_picker":"ctrl+l adegan","scene_picker":"pilih adegan","ctrl_b_files":"ctrl+b berkas","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h riwayat","enter_open":"enter buka","backspace_up":"backspace naik","ctrl_b_close":"ctrl+b tutup","enter_jump":"enter lompat","r_refresh":"r segarkan","ctrl_g_close":"ctrl+g tutup","ctrl_h_close":"ctrl+h tutup","i_insert":"i sisip","o_open_line":"o buka baris","cc_line":"cc baris","cw_word":"cw kata","ce_word_end":"ce akhir kata","c0_start":"c0 awal","c_end":"c$ akhir","dd_line":"dd baris","dw_word":"dw kata","de_word_end":"de akhir kata","d0_start":"d0 awal","d_end":"d$ akhir","yy_line":"yy baris","yw_word":"yw kata","ye_word_end":"ye akhir kata","y0_start":"y0 awal","y_end":"y$ akhir","gg_top":"gg atas","esc_cancel":"esc batal"},"evidence":{"head":"kepala","tick":"tick","anchor":"jangkar","coverage":"cakupan","complete":"lengkap","partial":"sebagian","unavailable":"tidak tersedia"}},"history":{"title":"Riwayat Echo","empty":"Belum ada bukti Echo","header":"# tick jenis status bukti ringkasan"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime","keybindings":"Pintasan papan ketik"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."},"leader":{"label":"Leader","description":"Enter untuk ubah; simpan ~/.jimrc.","capture":"Tekan satu tombol; Esc batal.","capturing_value":"Tekan tombol","space":"Spasi","backslash":"Garis miring terbalik","invalid_key":"Satu karakter tercetak; Esc batal.","invalid":".jimrc tidak valid; perbaiki dan coba lagi.","read_error":"Tidak dapat membaca .jimrc.","write_error":"Tidak dapat menyimpan .jimrc; coba lagi.","unavailable":"Lingkungan ini tidak menyimpan pengaturan.","capture_hint":"Esc batal; F2 tutup"}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; -export const tr: TranslationSchema = {"footer":{"mode":{"browse":"gözat","insert":"ekle","normal":"normal","preview":"önizleme","settings":"ayarlar","files":"dosyalar","graft":"graft","history":"geçmiş"},"context":{"settings":"ayarlar","graft_empty":"incelemek için dosya aç","history_empty":"henüz Echo kanıtı yok","history_count":"Echo kanıtı: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k taşı","enter_change":"enter değiştir","f2_close":"f2 kapat","esc_close":"esc kapat","j_k_scroll":"j/k kaydır","f3_source":"f3 kaynak","f3_preview":"f3 önizleme","text_input":"metin girişi","esc_normal":"esc normal","ctrl_s_save":"ctrl+s kaydet","ctrl_t_theme":"ctrl+t tema","tab_focus":"tab odak","tab_indent":"tab girinti","ctrl_l_scene_picker":"ctrl+l sahne","scene_picker":"sahne seçici","ctrl_b_files":"ctrl+b dosyalar","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h geçmiş","enter_open":"enter aç","backspace_up":"backspace yukarı","ctrl_b_close":"ctrl+b kapat","enter_jump":"enter git","r_refresh":"r yenile","ctrl_g_close":"ctrl+g kapat","ctrl_h_close":"ctrl+h kapat","i_insert":"i ekle","o_open_line":"o satır aç","cc_line":"cc satır","cw_word":"cw kelime","ce_word_end":"ce kelime sonu","c0_start":"c0 başlangıç","c_end":"c$ son","dd_line":"dd satır","dw_word":"dw kelime","de_word_end":"de kelime sonu","d0_start":"d0 başlangıç","d_end":"d$ son","yy_line":"yy satır","yw_word":"yw kelime","ye_word_end":"ye kelime sonu","y0_start":"y0 başlangıç","y_end":"y$ son","gg_top":"gg üst","esc_cancel":"esc iptal"},"evidence":{"head":"baş","tick":"tik","anchor":"çapa","coverage":"kapsam","complete":"tam","partial":"kısmi","unavailable":"kullanılamıyor"}},"history":{"title":"Echo Geçmişi","empty":"Henüz Echo kanıtı yok","header":"# tick tür durum kanıt özet"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; +export const tr: TranslationSchema = {"footer":{"mode":{"browse":"gözat","insert":"ekle","normal":"normal","preview":"önizleme","settings":"ayarlar","files":"dosyalar","graft":"graft","history":"geçmiş"},"context":{"settings":"ayarlar","graft_empty":"incelemek için dosya aç","history_empty":"henüz Echo kanıtı yok","history_count":"Echo kanıtı: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k taşı","enter_change":"enter değiştir","f2_close":"f2 kapat","esc_close":"esc kapat","j_k_scroll":"j/k kaydır","f3_source":"f3 kaynak","f3_preview":"f3 önizleme","text_input":"metin girişi","esc_normal":"esc normal","ctrl_s_save":"ctrl+s kaydet","ctrl_t_theme":"ctrl+t tema","tab_focus":"tab odak","tab_indent":"tab girinti","ctrl_l_scene_picker":"ctrl+l sahne","scene_picker":"sahne seçici","ctrl_b_files":"ctrl+b dosyalar","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h geçmiş","enter_open":"enter aç","backspace_up":"backspace yukarı","ctrl_b_close":"ctrl+b kapat","enter_jump":"enter git","r_refresh":"r yenile","ctrl_g_close":"ctrl+g kapat","ctrl_h_close":"ctrl+h kapat","i_insert":"i ekle","o_open_line":"o satır aç","cc_line":"cc satır","cw_word":"cw kelime","ce_word_end":"ce kelime sonu","c0_start":"c0 başlangıç","c_end":"c$ son","dd_line":"dd satır","dw_word":"dw kelime","de_word_end":"de kelime sonu","d0_start":"d0 başlangıç","d_end":"d$ son","yy_line":"yy satır","yw_word":"yw kelime","ye_word_end":"ye kelime sonu","y0_start":"y0 başlangıç","y_end":"y$ son","gg_top":"gg üst","esc_cancel":"esc iptal"},"evidence":{"head":"baş","tick":"tik","anchor":"çapa","coverage":"kapsam","complete":"tam","partial":"kısmi","unavailable":"kullanılamıyor"}},"history":{"title":"Echo Geçmişi","empty":"Henüz Echo kanıtı yok","header":"# tick tür durum kanıt özet"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime","keybindings":"Tuş atamaları"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."},"leader":{"label":"Leader","description":"Enter ile değiştir; dosya ~/.jimrc.","capture":"Bir tuşa basın; Esc iptal eder.","capturing_value":"Bir tuşa basın","space":"Boşluk","backslash":"Ters eğik çizgi","invalid_key":"Yazdırılabilir tek tuş; Esc iptal eder.","invalid":".jimrc geçersiz; düzeltip yeniden deneyin.","read_error":".jimrc okunamadı.","write_error":".jimrc kaydedilemedi; yeniden deneyin.","unavailable":"Bu ortamda ayarlar kaydedilemez.","capture_hint":"Esc iptal; F2 kapat"}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; -export const hi: TranslationSchema = {"footer":{"mode":{"browse":"ब्राउज़","insert":"सम्मिलित","normal":"सामान्य","preview":"पूर्वावलोकन","settings":"सेटिंग","files":"फाइलें","graft":"Graft","history":"इतिहास"},"context":{"settings":"सेटिंग","graft_empty":"जांचने के लिए फाइल खोलें","history_empty":"अभी कोई Echo प्रमाण नहीं","history_count":"Echo प्रमाण: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k चलें","enter_change":"enter बदलें","f2_close":"f2 बंद","esc_close":"esc बंद","j_k_scroll":"j/k स्क्रोल","f3_source":"f3 स्रोत","f3_preview":"f3 पूर्वावलोकन","text_input":"पाठ इनपुट","esc_normal":"esc सामान्य","ctrl_s_save":"ctrl+s सहेजें","ctrl_t_theme":"ctrl+t थीम","tab_focus":"tab फोकस","tab_indent":"tab इंडेंट","ctrl_l_scene_picker":"ctrl+l दृश्य","scene_picker":"दृश्य चयन","ctrl_b_files":"ctrl+b फाइलें","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h इतिहास","enter_open":"enter खोलें","backspace_up":"backspace ऊपर","ctrl_b_close":"ctrl+b बंद","enter_jump":"enter जाएं","r_refresh":"r ताज़ा","ctrl_g_close":"ctrl+g बंद","ctrl_h_close":"ctrl+h बंद","i_insert":"i सम्मिलित","o_open_line":"o पंक्ति खोलें","cc_line":"cc पंक्ति","cw_word":"cw शब्द","ce_word_end":"ce शब्द अंत","c0_start":"c0 शुरुआत","c_end":"c$ अंत","dd_line":"dd पंक्ति","dw_word":"dw शब्द","de_word_end":"de शब्द अंत","d0_start":"d0 शुरुआत","d_end":"d$ अंत","yy_line":"yy पंक्ति","yw_word":"yw शब्द","ye_word_end":"ye शब्द अंत","y0_start":"y0 शुरुआत","y_end":"y$ अंत","gg_top":"gg ऊपर","esc_cancel":"esc रद्द"},"evidence":{"head":"शीर्ष","tick":"टिक","anchor":"एंकर","coverage":"कवरेज","complete":"पूर्ण","partial":"आंशिक","unavailable":"अनुपलब्ध"}},"history":{"title":"Echo इतिहास","empty":"अभी कोई Echo प्रमाण नहीं","header":"# tick प्रकार स्थिति प्रमाण सारांश"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; +export const hi: TranslationSchema = {"footer":{"mode":{"browse":"ब्राउज़","insert":"सम्मिलित","normal":"सामान्य","preview":"पूर्वावलोकन","settings":"सेटिंग","files":"फाइलें","graft":"Graft","history":"इतिहास"},"context":{"settings":"सेटिंग","graft_empty":"जांचने के लिए फाइल खोलें","history_empty":"अभी कोई Echo प्रमाण नहीं","history_count":"Echo प्रमाण: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k चलें","enter_change":"enter बदलें","f2_close":"f2 बंद","esc_close":"esc बंद","j_k_scroll":"j/k स्क्रोल","f3_source":"f3 स्रोत","f3_preview":"f3 पूर्वावलोकन","text_input":"पाठ इनपुट","esc_normal":"esc सामान्य","ctrl_s_save":"ctrl+s सहेजें","ctrl_t_theme":"ctrl+t थीम","tab_focus":"tab फोकस","tab_indent":"tab इंडेंट","ctrl_l_scene_picker":"ctrl+l दृश्य","scene_picker":"दृश्य चयन","ctrl_b_files":"ctrl+b फाइलें","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h इतिहास","enter_open":"enter खोलें","backspace_up":"backspace ऊपर","ctrl_b_close":"ctrl+b बंद","enter_jump":"enter जाएं","r_refresh":"r ताज़ा","ctrl_g_close":"ctrl+g बंद","ctrl_h_close":"ctrl+h बंद","i_insert":"i सम्मिलित","o_open_line":"o पंक्ति खोलें","cc_line":"cc पंक्ति","cw_word":"cw शब्द","ce_word_end":"ce शब्द अंत","c0_start":"c0 शुरुआत","c_end":"c$ अंत","dd_line":"dd पंक्ति","dw_word":"dw शब्द","de_word_end":"de शब्द अंत","d0_start":"d0 शुरुआत","d_end":"d$ अंत","yy_line":"yy पंक्ति","yw_word":"yw शब्द","ye_word_end":"ye शब्द अंत","y0_start":"y0 शुरुआत","y_end":"y$ अंत","gg_top":"gg ऊपर","esc_cancel":"esc रद्द"},"evidence":{"head":"शीर्ष","tick":"टिक","anchor":"एंकर","coverage":"कवरेज","complete":"पूर्ण","partial":"आंशिक","unavailable":"अनुपलब्ध"}},"history":{"title":"Echo इतिहास","empty":"अभी कोई Echo प्रमाण नहीं","header":"# tick प्रकार स्थिति प्रमाण सारांश"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime","keybindings":"की बाइंडिंग"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."},"leader":{"label":"Leader","description":"Enter से बदलें; ~/.jimrc में सहेजें।","capture":"एक कुंजी दबाएँ; Esc से रद्द करें।","capturing_value":"कुंजी दबाएँ","space":"स्पेस","backslash":"बैकस्लैश","invalid_key":"एक मुद्रण योग्य कुंजी; Esc से रद्द करें।","invalid":".jimrc अमान्य है; सुधारकर फिर कोशिश करें।","read_error":".jimrc पढ़ा नहीं जा सका।","write_error":".jimrc सहेजा नहीं जा सका; फिर कोशिश करें।","unavailable":"इस परिवेश में सेटिंग सहेजी नहीं जा सकती।","capture_hint":"Esc रद्द; F2 बंद"}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; -export const nl: TranslationSchema = {"footer":{"mode":{"browse":"bladeren","insert":"invoegen","normal":"normaal","preview":"voorbeeld","settings":"instellingen","files":"bestanden","graft":"graft","history":"geschiedenis"},"context":{"settings":"instellingen","graft_empty":"open een bestand om te inspecteren","history_empty":"nog geen Echo-bewijs","history_count":"Echo-bewijs: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k bewegen","enter_change":"enter wijzigen","f2_close":"f2 sluiten","esc_close":"esc sluiten","j_k_scroll":"j/k scrollen","f3_source":"f3 bron","f3_preview":"f3 voorbeeld","text_input":"tekstinvoer","esc_normal":"esc normaal","ctrl_s_save":"ctrl+s opslaan","ctrl_t_theme":"ctrl+t thema","tab_focus":"tab focus","tab_indent":"tab inspringen","ctrl_l_scene_picker":"ctrl+l scène","scene_picker":"scènekiezer","ctrl_b_files":"ctrl+b bestanden","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h geschiedenis","enter_open":"enter openen","backspace_up":"backspace omhoog","ctrl_b_close":"ctrl+b sluiten","enter_jump":"enter springen","r_refresh":"r vernieuwen","ctrl_g_close":"ctrl+g sluiten","ctrl_h_close":"ctrl+h sluiten","i_insert":"i invoegen","o_open_line":"o regel openen","cc_line":"cc regel","cw_word":"cw woord","ce_word_end":"ce woord-einde","c0_start":"c0 begin","c_end":"c$ einde","dd_line":"dd regel","dw_word":"dw woord","de_word_end":"de woord-einde","d0_start":"d0 begin","d_end":"d$ einde","yy_line":"yy regel","yw_word":"yw woord","ye_word_end":"ye woord-einde","y0_start":"y0 begin","y_end":"y$ einde","gg_top":"gg boven","esc_cancel":"esc annuleren"},"evidence":{"head":"kop","tick":"tick","anchor":"anker","coverage":"dekking","complete":"volledig","partial":"gedeeltelijk","unavailable":"niet beschikbaar"}},"history":{"title":"Echo-geschiedenis","empty":"Nog geen Echo-bewijs","header":"# tick soort status bewijs samenvatting"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; +export const nl: TranslationSchema = {"footer":{"mode":{"browse":"bladeren","insert":"invoegen","normal":"normaal","preview":"voorbeeld","settings":"instellingen","files":"bestanden","graft":"graft","history":"geschiedenis"},"context":{"settings":"instellingen","graft_empty":"open een bestand om te inspecteren","history_empty":"nog geen Echo-bewijs","history_count":"Echo-bewijs: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k bewegen","enter_change":"enter wijzigen","f2_close":"f2 sluiten","esc_close":"esc sluiten","j_k_scroll":"j/k scrollen","f3_source":"f3 bron","f3_preview":"f3 voorbeeld","text_input":"tekstinvoer","esc_normal":"esc normaal","ctrl_s_save":"ctrl+s opslaan","ctrl_t_theme":"ctrl+t thema","tab_focus":"tab focus","tab_indent":"tab inspringen","ctrl_l_scene_picker":"ctrl+l scène","scene_picker":"scènekiezer","ctrl_b_files":"ctrl+b bestanden","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h geschiedenis","enter_open":"enter openen","backspace_up":"backspace omhoog","ctrl_b_close":"ctrl+b sluiten","enter_jump":"enter springen","r_refresh":"r vernieuwen","ctrl_g_close":"ctrl+g sluiten","ctrl_h_close":"ctrl+h sluiten","i_insert":"i invoegen","o_open_line":"o regel openen","cc_line":"cc regel","cw_word":"cw woord","ce_word_end":"ce woord-einde","c0_start":"c0 begin","c_end":"c$ einde","dd_line":"dd regel","dw_word":"dw woord","de_word_end":"de woord-einde","d0_start":"d0 begin","d_end":"d$ einde","yy_line":"yy regel","yw_word":"yw woord","ye_word_end":"ye woord-einde","y0_start":"y0 begin","y_end":"y$ einde","gg_top":"gg boven","esc_cancel":"esc annuleren"},"evidence":{"head":"kop","tick":"tick","anchor":"anker","coverage":"dekking","complete":"volledig","partial":"gedeeltelijk","unavailable":"niet beschikbaar"}},"history":{"title":"Echo-geschiedenis","empty":"Nog geen Echo-bewijs","header":"# tick soort status bewijs samenvatting"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime","keybindings":"Sneltoetsen"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."},"leader":{"label":"Leader","description":"Enter om te wijzigen; ~/.jimrc.","capture":"Druk op een toets; Esc annuleert.","capturing_value":"Druk op een toets","space":"Spatie","backslash":"Backslash","invalid_key":"Eén afdrukbare toets; Esc annuleert.","invalid":".jimrc ongeldig; herstel en probeer opnieuw.","read_error":".jimrc kon niet worden gelezen.","write_error":".jimrc kon niet worden opgeslagen.","unavailable":"Deze omgeving kan instellingen niet opslaan.","capture_hint":"Esc annuleert; F2 sluit"}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; -export const pl: TranslationSchema = {"footer":{"mode":{"browse":"przeglądaj","insert":"wstaw","normal":"normalny","preview":"podgląd","settings":"ustawienia","files":"pliki","graft":"graft","history":"historia"},"context":{"settings":"ustawienia","graft_empty":"otwórz plik do sprawdzenia","history_empty":"brak jeszcze dowodów Echo","history_count":"Dowody Echo: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k przesuń","enter_change":"enter zmień","f2_close":"f2 zamknij","esc_close":"esc zamknij","j_k_scroll":"j/k przewiń","f3_source":"f3 źródło","f3_preview":"f3 podgląd","text_input":"wpisywanie tekstu","esc_normal":"esc normalny","ctrl_s_save":"ctrl+s zapisz","ctrl_t_theme":"ctrl+t motyw","tab_focus":"tab fokus","tab_indent":"tab wcięcie","ctrl_l_scene_picker":"ctrl+l scena","scene_picker":"wybór sceny","ctrl_b_files":"ctrl+b pliki","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h historia","enter_open":"enter otwórz","backspace_up":"backspace w górę","ctrl_b_close":"ctrl+b zamknij","enter_jump":"enter skocz","r_refresh":"r odśwież","ctrl_g_close":"ctrl+g zamknij","ctrl_h_close":"ctrl+h zamknij","i_insert":"i wstaw","o_open_line":"o otwórz linię","cc_line":"cc linia","cw_word":"cw słowo","ce_word_end":"ce koniec słowa","c0_start":"c0 początek","c_end":"c$ koniec","dd_line":"dd linia","dw_word":"dw słowo","de_word_end":"de koniec słowa","d0_start":"d0 początek","d_end":"d$ koniec","yy_line":"yy linia","yw_word":"yw słowo","ye_word_end":"ye koniec słowa","y0_start":"y0 początek","y_end":"y$ koniec","gg_top":"gg góra","esc_cancel":"esc anuluj"},"evidence":{"head":"głowa","tick":"takt","anchor":"kotwica","coverage":"pokrycie","complete":"pełne","partial":"częściowe","unavailable":"niedostępne"}},"history":{"title":"Historia Echo","empty":"Brak jeszcze dowodów Echo","header":"# tick typ status dowód podsumowanie"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; +export const pl: TranslationSchema = {"footer":{"mode":{"browse":"przeglądaj","insert":"wstaw","normal":"normalny","preview":"podgląd","settings":"ustawienia","files":"pliki","graft":"graft","history":"historia"},"context":{"settings":"ustawienia","graft_empty":"otwórz plik do sprawdzenia","history_empty":"brak jeszcze dowodów Echo","history_count":"Dowody Echo: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k przesuń","enter_change":"enter zmień","f2_close":"f2 zamknij","esc_close":"esc zamknij","j_k_scroll":"j/k przewiń","f3_source":"f3 źródło","f3_preview":"f3 podgląd","text_input":"wpisywanie tekstu","esc_normal":"esc normalny","ctrl_s_save":"ctrl+s zapisz","ctrl_t_theme":"ctrl+t motyw","tab_focus":"tab fokus","tab_indent":"tab wcięcie","ctrl_l_scene_picker":"ctrl+l scena","scene_picker":"wybór sceny","ctrl_b_files":"ctrl+b pliki","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h historia","enter_open":"enter otwórz","backspace_up":"backspace w górę","ctrl_b_close":"ctrl+b zamknij","enter_jump":"enter skocz","r_refresh":"r odśwież","ctrl_g_close":"ctrl+g zamknij","ctrl_h_close":"ctrl+h zamknij","i_insert":"i wstaw","o_open_line":"o otwórz linię","cc_line":"cc linia","cw_word":"cw słowo","ce_word_end":"ce koniec słowa","c0_start":"c0 początek","c_end":"c$ koniec","dd_line":"dd linia","dw_word":"dw słowo","de_word_end":"de koniec słowa","d0_start":"d0 początek","d_end":"d$ koniec","yy_line":"yy linia","yw_word":"yw słowo","ye_word_end":"ye koniec słowa","y0_start":"y0 początek","y_end":"y$ koniec","gg_top":"gg góra","esc_cancel":"esc anuluj"},"evidence":{"head":"głowa","tick":"takt","anchor":"kotwica","coverage":"pokrycie","complete":"pełne","partial":"częściowe","unavailable":"niedostępne"}},"history":{"title":"Historia Echo","empty":"Brak jeszcze dowodów Echo","header":"# tick typ status dowód podsumowanie"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime","keybindings":"Skróty klawiszowe"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."},"leader":{"label":"Leader","description":"Enter zmienia; zapis w ~/.jimrc.","capture":"Naciśnij klawisz; Esc anuluje.","capturing_value":"Naciśnij klawisz","space":"Spacja","backslash":"Ukośnik odwrotny","invalid_key":"Jeden drukowalny znak; Esc anuluje.","invalid":".jimrc niepoprawny; popraw i spróbuj ponownie.","read_error":"Nie można odczytać .jimrc.","write_error":"Nie można zapisać .jimrc; spróbuj ponownie.","unavailable":"To środowisko nie zapisuje ustawień.","capture_hint":"Esc anuluje; F2 zamyka"}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; -export const vi: TranslationSchema = {"footer":{"mode":{"browse":"duyệt","insert":"chèn","normal":"thường","preview":"xem trước","settings":"cài đặt","files":"tệp","graft":"graft","history":"lịch sử"},"context":{"settings":"cài đặt","graft_empty":"mở tệp để kiểm tra","history_empty":"chưa có bằng chứng Echo","history_count":"Bằng chứng Echo: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k di chuyển","enter_change":"enter đổi","f2_close":"f2 đóng","esc_close":"esc đóng","j_k_scroll":"j/k cuộn","f3_source":"f3 nguồn","f3_preview":"f3 xem trước","text_input":"nhập văn bản","esc_normal":"esc thường","ctrl_s_save":"ctrl+s lưu","ctrl_t_theme":"ctrl+t giao diện","tab_focus":"tab tiêu điểm","tab_indent":"tab thụt lề","ctrl_l_scene_picker":"ctrl+l cảnh","scene_picker":"chọn cảnh","ctrl_b_files":"ctrl+b tệp","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h lịch sử","enter_open":"enter mở","backspace_up":"backspace lên","ctrl_b_close":"ctrl+b đóng","enter_jump":"enter nhảy","r_refresh":"r làm mới","ctrl_g_close":"ctrl+g đóng","ctrl_h_close":"ctrl+h đóng","i_insert":"i chèn","o_open_line":"o mở dòng","cc_line":"cc dòng","cw_word":"cw từ","ce_word_end":"ce cuối từ","c0_start":"c0 đầu","c_end":"c$ cuối","dd_line":"dd dòng","dw_word":"dw từ","de_word_end":"de cuối từ","d0_start":"d0 đầu","d_end":"d$ cuối","yy_line":"yy dòng","yw_word":"yw từ","ye_word_end":"ye cuối từ","y0_start":"y0 đầu","y_end":"y$ cuối","gg_top":"gg đầu","esc_cancel":"esc hủy"},"evidence":{"head":"đầu","tick":"nhịp","anchor":"neo","coverage":"độ phủ","complete":"đầy đủ","partial":"một phần","unavailable":"không khả dụng"}},"history":{"title":"Lịch sử Echo","empty":"Chưa có bằng chứng Echo","header":"# tick loại trạng thái bằng chứng tóm tắt"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; +export const vi: TranslationSchema = {"footer":{"mode":{"browse":"duyệt","insert":"chèn","normal":"thường","preview":"xem trước","settings":"cài đặt","files":"tệp","graft":"graft","history":"lịch sử"},"context":{"settings":"cài đặt","graft_empty":"mở tệp để kiểm tra","history_empty":"chưa có bằng chứng Echo","history_count":"Bằng chứng Echo: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k di chuyển","enter_change":"enter đổi","f2_close":"f2 đóng","esc_close":"esc đóng","j_k_scroll":"j/k cuộn","f3_source":"f3 nguồn","f3_preview":"f3 xem trước","text_input":"nhập văn bản","esc_normal":"esc thường","ctrl_s_save":"ctrl+s lưu","ctrl_t_theme":"ctrl+t giao diện","tab_focus":"tab tiêu điểm","tab_indent":"tab thụt lề","ctrl_l_scene_picker":"ctrl+l cảnh","scene_picker":"chọn cảnh","ctrl_b_files":"ctrl+b tệp","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h lịch sử","enter_open":"enter mở","backspace_up":"backspace lên","ctrl_b_close":"ctrl+b đóng","enter_jump":"enter nhảy","r_refresh":"r làm mới","ctrl_g_close":"ctrl+g đóng","ctrl_h_close":"ctrl+h đóng","i_insert":"i chèn","o_open_line":"o mở dòng","cc_line":"cc dòng","cw_word":"cw từ","ce_word_end":"ce cuối từ","c0_start":"c0 đầu","c_end":"c$ cuối","dd_line":"dd dòng","dw_word":"dw từ","de_word_end":"de cuối từ","d0_start":"d0 đầu","d_end":"d$ cuối","yy_line":"yy dòng","yw_word":"yw từ","ye_word_end":"ye cuối từ","y0_start":"y0 đầu","y_end":"y$ cuối","gg_top":"gg đầu","esc_cancel":"esc hủy"},"evidence":{"head":"đầu","tick":"nhịp","anchor":"neo","coverage":"độ phủ","complete":"đầy đủ","partial":"một phần","unavailable":"không khả dụng"}},"history":{"title":"Lịch sử Echo","empty":"Chưa có bằng chứng Echo","header":"# tick loại trạng thái bằng chứng tóm tắt"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime","keybindings":"Phím tắt"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."},"leader":{"label":"Leader","description":"Enter để đổi; lưu vào ~/.jimrc.","capture":"Nhấn một phím; Esc để hủy.","capturing_value":"Nhấn một phím","space":"Dấu cách","backslash":"Dấu gạch chéo ngược","invalid_key":"Một ký tự in được; Esc để hủy.","invalid":".jimrc không hợp lệ; sửa rồi thử lại.","read_error":"Không đọc được .jimrc.","write_error":"Không lưu được .jimrc; thử lại.","unavailable":"Môi trường này không lưu được cài đặt.","capture_hint":"Esc hủy; F2 đóng"}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; -export const th: TranslationSchema = {"footer":{"mode":{"browse":"เรียกดู","insert":"แทรก","normal":"ปกติ","preview":"ดูตัวอย่าง","settings":"ตั้งค่า","files":"ไฟล์","graft":"Graft","history":"ประวัติ"},"context":{"settings":"ตั้งค่า","graft_empty":"เปิดไฟล์เพื่อตรวจสอบ","history_empty":"ยังไม่มีหลักฐาน Echo","history_count":"หลักฐาน Echo: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k ย้าย","enter_change":"enter เปลี่ยน","f2_close":"f2 ปิด","esc_close":"esc ปิด","j_k_scroll":"j/k เลื่อน","f3_source":"f3 ต้นฉบับ","f3_preview":"f3 ดูตัวอย่าง","text_input":"ป้อนข้อความ","esc_normal":"esc ปกติ","ctrl_s_save":"ctrl+s บันทึก","ctrl_t_theme":"ctrl+t ธีม","tab_focus":"tab โฟกัส","tab_indent":"tab เยื้อง","ctrl_l_scene_picker":"ctrl+l ฉาก","scene_picker":"เลือกฉาก","ctrl_b_files":"ctrl+b ไฟล์","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h ประวัติ","enter_open":"enter เปิด","backspace_up":"backspace ขึ้น","ctrl_b_close":"ctrl+b ปิด","enter_jump":"enter ไป","r_refresh":"r รีเฟรช","ctrl_g_close":"ctrl+g ปิด","ctrl_h_close":"ctrl+h ปิด","i_insert":"i แทรก","o_open_line":"o เปิดบรรทัด","cc_line":"cc บรรทัด","cw_word":"cw คำ","ce_word_end":"ce จบคำ","c0_start":"c0 เริ่ม","c_end":"c$ จบ","dd_line":"dd บรรทัด","dw_word":"dw คำ","de_word_end":"de จบคำ","d0_start":"d0 เริ่ม","d_end":"d$ จบ","yy_line":"yy บรรทัด","yw_word":"yw คำ","ye_word_end":"ye จบคำ","y0_start":"y0 เริ่ม","y_end":"y$ จบ","gg_top":"gg บน","esc_cancel":"esc ยกเลิก"},"evidence":{"head":"เฮด","tick":"ทิก","anchor":"จุดยึด","coverage":"ความครอบคลุม","complete":"ครบถ้วน","partial":"บางส่วน","unavailable":"ไม่พร้อมใช้งาน"}},"history":{"title":"ประวัติ Echo","empty":"ยังไม่มีหลักฐาน Echo","header":"# tick ชนิด สถานะ หลักฐาน สรุป"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; +export const th: TranslationSchema = {"footer":{"mode":{"browse":"เรียกดู","insert":"แทรก","normal":"ปกติ","preview":"ดูตัวอย่าง","settings":"ตั้งค่า","files":"ไฟล์","graft":"Graft","history":"ประวัติ"},"context":{"settings":"ตั้งค่า","graft_empty":"เปิดไฟล์เพื่อตรวจสอบ","history_empty":"ยังไม่มีหลักฐาน Echo","history_count":"หลักฐาน Echo: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k ย้าย","enter_change":"enter เปลี่ยน","f2_close":"f2 ปิด","esc_close":"esc ปิด","j_k_scroll":"j/k เลื่อน","f3_source":"f3 ต้นฉบับ","f3_preview":"f3 ดูตัวอย่าง","text_input":"ป้อนข้อความ","esc_normal":"esc ปกติ","ctrl_s_save":"ctrl+s บันทึก","ctrl_t_theme":"ctrl+t ธีม","tab_focus":"tab โฟกัส","tab_indent":"tab เยื้อง","ctrl_l_scene_picker":"ctrl+l ฉาก","scene_picker":"เลือกฉาก","ctrl_b_files":"ctrl+b ไฟล์","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h ประวัติ","enter_open":"enter เปิด","backspace_up":"backspace ขึ้น","ctrl_b_close":"ctrl+b ปิด","enter_jump":"enter ไป","r_refresh":"r รีเฟรช","ctrl_g_close":"ctrl+g ปิด","ctrl_h_close":"ctrl+h ปิด","i_insert":"i แทรก","o_open_line":"o เปิดบรรทัด","cc_line":"cc บรรทัด","cw_word":"cw คำ","ce_word_end":"ce จบคำ","c0_start":"c0 เริ่ม","c_end":"c$ จบ","dd_line":"dd บรรทัด","dw_word":"dw คำ","de_word_end":"de จบคำ","d0_start":"d0 เริ่ม","d_end":"d$ จบ","yy_line":"yy บรรทัด","yw_word":"yw คำ","ye_word_end":"ye จบคำ","y0_start":"y0 เริ่ม","y_end":"y$ จบ","gg_top":"gg บน","esc_cancel":"esc ยกเลิก"},"evidence":{"head":"เฮด","tick":"ทิก","anchor":"จุดยึด","coverage":"ความครอบคลุม","complete":"ครบถ้วน","partial":"บางส่วน","unavailable":"ไม่พร้อมใช้งาน"}},"history":{"title":"ประวัติ Echo","empty":"ยังไม่มีหลักฐาน Echo","header":"# tick ชนิด สถานะ หลักฐาน สรุป"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime","keybindings":"การผูกปุ่ม"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."},"leader":{"label":"Leader","description":"Enter เพื่อเปลี่ยน; บันทึก ~/.jimrc.","capture":"กดหนึ่งปุ่ม; Esc เพื่อยกเลิก.","capturing_value":"กดหนึ่งปุ่ม","space":"เว้นวรรค","backslash":"แบ็กสแลช","invalid_key":"ใช้อักขระที่พิมพ์ได้หนึ่งตัว; Esc ยกเลิก.","invalid":".jimrc ไม่ถูกต้อง; แก้ไขแล้วลองใหม่.","read_error":"อ่าน .jimrc ไม่ได้.","write_error":"บันทึก .jimrc ไม่ได้; ลองใหม่.","unavailable":"สภาพแวดล้อมนี้บันทึกการตั้งค่าไม่ได้.","capture_hint":"Esc ยกเลิก; F2 ปิด"}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; -export const fa: TranslationSchema = {"footer":{"mode":{"browse":"مرور","insert":"درج","normal":"عادی","preview":"پیش‌نمایش","settings":"تنظیمات","files":"فایل‌ها","graft":"Graft","history":"تاریخچه"},"context":{"settings":"تنظیمات","graft_empty":"برای بررسی یک فایل باز کنید","history_empty":"هنوز مدرک Echo وجود ندارد","history_count":"مدارک Echo: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k حرکت","enter_change":"enter تغییر","f2_close":"f2 بستن","esc_close":"esc بستن","j_k_scroll":"j/k پیمایش","f3_source":"f3 منبع","f3_preview":"f3 پیش‌نمایش","text_input":"ورودی متن","esc_normal":"esc عادی","ctrl_s_save":"ctrl+s ذخیره","ctrl_t_theme":"ctrl+t پوسته","tab_focus":"tab تمرکز","tab_indent":"tab تورفتگی","ctrl_l_scene_picker":"ctrl+l صحنه","scene_picker":"انتخاب صحنه","ctrl_b_files":"ctrl+b فایل‌ها","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h تاریخچه","enter_open":"enter باز کردن","backspace_up":"backspace بالا","ctrl_b_close":"ctrl+b بستن","enter_jump":"enter پرش","r_refresh":"r تازه‌سازی","ctrl_g_close":"ctrl+g بستن","ctrl_h_close":"ctrl+h بستن","i_insert":"i درج","o_open_line":"o باز کردن خط","cc_line":"cc خط","cw_word":"cw واژه","ce_word_end":"ce پایان واژه","c0_start":"c0 آغاز","c_end":"c$ پایان","dd_line":"dd خط","dw_word":"dw واژه","de_word_end":"de پایان واژه","d0_start":"d0 آغاز","d_end":"d$ پایان","yy_line":"yy خط","yw_word":"yw واژه","ye_word_end":"ye پایان واژه","y0_start":"y0 آغاز","y_end":"y$ پایان","gg_top":"gg بالا","esc_cancel":"esc لغو"},"evidence":{"head":"سر","tick":"تیک","anchor":"لنگر","coverage":"پوشش","complete":"کامل","partial":"جزئی","unavailable":"در دسترس نیست"}},"history":{"title":"تاریخچه Echo","empty":"هنوز مدرک Echo وجود ندارد","header":"# tick نوع وضعیت مدرک خلاصه"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; +export const fa: TranslationSchema = {"footer":{"mode":{"browse":"مرور","insert":"درج","normal":"عادی","preview":"پیش‌نمایش","settings":"تنظیمات","files":"فایل‌ها","graft":"Graft","history":"تاریخچه"},"context":{"settings":"تنظیمات","graft_empty":"برای بررسی یک فایل باز کنید","history_empty":"هنوز مدرک Echo وجود ندارد","history_count":"مدارک Echo: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k حرکت","enter_change":"enter تغییر","f2_close":"f2 بستن","esc_close":"esc بستن","j_k_scroll":"j/k پیمایش","f3_source":"f3 منبع","f3_preview":"f3 پیش‌نمایش","text_input":"ورودی متن","esc_normal":"esc عادی","ctrl_s_save":"ctrl+s ذخیره","ctrl_t_theme":"ctrl+t پوسته","tab_focus":"tab تمرکز","tab_indent":"tab تورفتگی","ctrl_l_scene_picker":"ctrl+l صحنه","scene_picker":"انتخاب صحنه","ctrl_b_files":"ctrl+b فایل‌ها","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h تاریخچه","enter_open":"enter باز کردن","backspace_up":"backspace بالا","ctrl_b_close":"ctrl+b بستن","enter_jump":"enter پرش","r_refresh":"r تازه‌سازی","ctrl_g_close":"ctrl+g بستن","ctrl_h_close":"ctrl+h بستن","i_insert":"i درج","o_open_line":"o باز کردن خط","cc_line":"cc خط","cw_word":"cw واژه","ce_word_end":"ce پایان واژه","c0_start":"c0 آغاز","c_end":"c$ پایان","dd_line":"dd خط","dw_word":"dw واژه","de_word_end":"de پایان واژه","d0_start":"d0 آغاز","d_end":"d$ پایان","yy_line":"yy خط","yw_word":"yw واژه","ye_word_end":"ye پایان واژه","y0_start":"y0 آغاز","y_end":"y$ پایان","gg_top":"gg بالا","esc_cancel":"esc لغو"},"evidence":{"head":"سر","tick":"تیک","anchor":"لنگر","coverage":"پوشش","complete":"کامل","partial":"جزئی","unavailable":"در دسترس نیست"}},"history":{"title":"تاریخچه Echo","empty":"هنوز مدرک Echo وجود ندارد","header":"# tick نوع وضعیت مدرک خلاصه"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime","keybindings":"میانبرهای صفحه‌کلید"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."},"leader":{"label":"Leader","description":"Enter برای تغییر؛ ذخیره در ~/.jimrc.","capture":"یک کلید بزنید؛ Esc برای لغو.","capturing_value":"یک کلید بزنید","space":"فاصله","backslash":"بک‌اسلش","invalid_key":"یک نویسه قابل چاپ؛ Esc برای لغو.","invalid":".jimrc نامعتبر است؛ اصلاح و دوباره تلاش کنید.","read_error":"خواندن .jimrc ممکن نیست.","write_error":"ذخیره .jimrc ممکن نیست؛ دوباره تلاش کنید.","unavailable":"در این محیط تنظیمات ذخیره نمی‌شوند.","capture_hint":"Esc لغو؛ F2 بستن"}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; -export const he: TranslationSchema = {"footer":{"mode":{"browse":"עיון","insert":"הוספה","normal":"רגיל","preview":"תצוגה","settings":"הגדרות","files":"קבצים","graft":"Graft","history":"היסטוריה"},"context":{"settings":"הגדרות","graft_empty":"פתח קובץ לבדיקה","history_empty":"עדיין אין ראיות Echo","history_count":"ראיות Echo: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k הזז","enter_change":"enter שנה","f2_close":"f2 סגור","esc_close":"esc סגור","j_k_scroll":"j/k גלול","f3_source":"f3 מקור","f3_preview":"f3 תצוגה","text_input":"קלט טקסט","esc_normal":"esc רגיל","ctrl_s_save":"ctrl+s שמור","ctrl_t_theme":"ctrl+t ערכת נושא","tab_focus":"tab מיקוד","tab_indent":"tab הזחה","ctrl_l_scene_picker":"ctrl+l סצנה","scene_picker":"בחירת סצנה","ctrl_b_files":"ctrl+b קבצים","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h היסטוריה","enter_open":"enter פתח","backspace_up":"backspace למעלה","ctrl_b_close":"ctrl+b סגור","enter_jump":"enter קפוץ","r_refresh":"r רענן","ctrl_g_close":"ctrl+g סגור","ctrl_h_close":"ctrl+h סגור","i_insert":"i הוסף","o_open_line":"o פתח שורה","cc_line":"cc שורה","cw_word":"cw מילה","ce_word_end":"ce סוף מילה","c0_start":"c0 התחלה","c_end":"c$ סוף","dd_line":"dd שורה","dw_word":"dw מילה","de_word_end":"de סוף מילה","d0_start":"d0 התחלה","d_end":"d$ סוף","yy_line":"yy שורה","yw_word":"yw מילה","ye_word_end":"ye סוף מילה","y0_start":"y0 התחלה","y_end":"y$ סוף","gg_top":"gg למעלה","esc_cancel":"esc בטל"},"evidence":{"head":"ראש","tick":"טיק","anchor":"עוגן","coverage":"כיסוי","complete":"מלא","partial":"חלקי","unavailable":"לא זמין"}},"history":{"title":"היסטוריית Echo","empty":"עדיין אין ראיות Echo","header":"# tick סוג מצב ראיה סיכום"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; +export const he: TranslationSchema = {"footer":{"mode":{"browse":"עיון","insert":"הוספה","normal":"רגיל","preview":"תצוגה","settings":"הגדרות","files":"קבצים","graft":"Graft","history":"היסטוריה"},"context":{"settings":"הגדרות","graft_empty":"פתח קובץ לבדיקה","history_empty":"עדיין אין ראיות Echo","history_count":"ראיות Echo: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k הזז","enter_change":"enter שנה","f2_close":"f2 סגור","esc_close":"esc סגור","j_k_scroll":"j/k גלול","f3_source":"f3 מקור","f3_preview":"f3 תצוגה","text_input":"קלט טקסט","esc_normal":"esc רגיל","ctrl_s_save":"ctrl+s שמור","ctrl_t_theme":"ctrl+t ערכת נושא","tab_focus":"tab מיקוד","tab_indent":"tab הזחה","ctrl_l_scene_picker":"ctrl+l סצנה","scene_picker":"בחירת סצנה","ctrl_b_files":"ctrl+b קבצים","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h היסטוריה","enter_open":"enter פתח","backspace_up":"backspace למעלה","ctrl_b_close":"ctrl+b סגור","enter_jump":"enter קפוץ","r_refresh":"r רענן","ctrl_g_close":"ctrl+g סגור","ctrl_h_close":"ctrl+h סגור","i_insert":"i הוסף","o_open_line":"o פתח שורה","cc_line":"cc שורה","cw_word":"cw מילה","ce_word_end":"ce סוף מילה","c0_start":"c0 התחלה","c_end":"c$ סוף","dd_line":"dd שורה","dw_word":"dw מילה","de_word_end":"de סוף מילה","d0_start":"d0 התחלה","d_end":"d$ סוף","yy_line":"yy שורה","yw_word":"yw מילה","ye_word_end":"ye סוף מילה","y0_start":"y0 התחלה","y_end":"y$ סוף","gg_top":"gg למעלה","esc_cancel":"esc בטל"},"evidence":{"head":"ראש","tick":"טיק","anchor":"עוגן","coverage":"כיסוי","complete":"מלא","partial":"חלקי","unavailable":"לא זמין"}},"history":{"title":"היסטוריית Echo","empty":"עדיין אין ראיות Echo","header":"# tick סוג מצב ראיה סיכום"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime","keybindings":"קיצורי מקשים"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."},"leader":{"label":"Leader","description":"Enter לשינוי; שמירה ב־~/.jimrc.","capture":"לחצו על מקש; Esc לביטול.","capturing_value":"לחצו על מקש","space":"רווח","backslash":"לוכסן הפוך","invalid_key":"תו אחד הניתן להדפסה; Esc לביטול.","invalid":".jimrc אינו תקין; תקנו ונסו שוב.","read_error":"לא ניתן לקרוא את .jimrc.","write_error":"לא ניתן לשמור את .jimrc; נסו שוב.","unavailable":"לא ניתן לשמור הגדרות בסביבה זו.","capture_hint":"Esc לביטול; F2 לסגירה"}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"}}; -export const me: TranslationSchema = {"footer":{"mode":{"browse":"esworb","insert":"tresni","normal":"lamron","preview":"weiverp","settings":"sgnittes","files":"selif","graft":"tfarg","history":"yrotsih"},"context":{"settings":"sgnittes","graft_empty":"ti tcepsni ot elif a nepo","history_empty":"tey ecnedive ohcE on","history_count":"{count} :ecnedive ohcE"},"command":{"invalid":"pleh rof pleh: epyt ;dezingocer ton dnammoC","hints":{"tab_accept":"tpecca bat","enter_run":"nur retne","esc_cancel":"lecnac cse"},"details":{"edit":"elif a nepO","write":"elif tnerruc eht etirW","quit":"tidej tiuQ","wq":"tiuq dna etirW","ttd":"daeh lacinonac gnivom tuohtiw kcit lasuac a evresbO","strand":"sdnarts etirw-no-ypoc tsil ro ,hctiws ,etaerC","braid":"setadidnac diarb timda ro ,weiverp ,weiV","why":"dnammoc lufgninaem tsal eht nialpxE","help":"pleh dnammoc wohS"}},"hints":{"j_k_move":"evom k/j","enter_change":"egnahc retne","f2_close":"esolc 2f","esc_close":"esolc cse","j_k_scroll":"llorcs k/j","f3_source":"ecruos 3f","f3_preview":"weiverp 3f","text_input":"tupni txet","esc_normal":"lamron cse","ctrl_s_save":"evas s+lrtc","ctrl_t_theme":"emeht t+lrtc","tab_focus":"sucof bat","tab_indent":"tnedni bat","ctrl_l_scene_picker":"rekcip enecs l+lrtc","scene_picker":"rekcip enecs","ctrl_b_files":"selif b+lrtc","ctrl_g_graft":"tfarg g+lrtc","ctrl_h_history":"yrotsih h+lrtc","enter_open":"nepo retne","backspace_up":"pu ecapskcab","ctrl_b_close":"esolc b+lrtc","enter_jump":"pmuj retne","r_refresh":"hserfer r","ctrl_g_close":"esolc g+lrtc","ctrl_h_close":"esolc h+lrtc","i_insert":"tresni i","o_open_line":"enil nepo o","cc_line":"enil cc","cw_word":"drow wc","ce_word_end":"dne-drow ec","c0_start":"trats 0c","c_end":"dne $c","dd_line":"enil dd","dw_word":"drow wd","de_word_end":"dne-drow ed","d0_start":"trats 0d","d_end":"dne $d","yy_line":"enil yy","yw_word":"drow wy","ye_word_end":"dne-drow ey","y0_start":"trats 0y","y_end":"dne $y","gg_top":"pot gg","esc_cancel":"lecnac cse"},"evidence":{"head":"daeh","tick":"kcit","anchor":"rohcna","coverage":"egarevoc","complete":"etelpmoc","partial":"laitrap","unavailable":"elbaliavanu"}},"history":{"title":"yrotsiH ohcE","empty":"tey ecnedive ohcE oN","header":"yrammus ecnedive sutats dnik kcit #"},"startupFileModal":{"title":"elif nepO","current_directory":"yrotcerid tnerruC","empty":"yrotcerid siht ni selif oN"},"settings":{"sections":{"appearance":"ecnaraeppA","editor":"rotidE","runtime":"emitnuR"},"rows":{"theme":{"label":"emehT","description":".semeht nevird-atad dellatsni neewteb hctiwS"},"theme_mode":{"label":"krad/thgiL","description":".noinapmoc krad ro thgil sti ot emeht tnerruc eht hctiwS"},"footer":{"label":"retooF","description":".egde mottob eht ta stnih dnammoc dna ,sucof ,edom wohS"},"markdown_preview":{"label":"weiverp nwodkraM","description":".weiverp dna ecruos neewteb reffub nwodkraM evitca eht hctiwS"},"diagnostics":{"label":"scitsongaiD","description":".gniriw emitnur lufroloC dna ,resrap ,tfarG tcepsnI"},"line_numbers":{"label":"srebmun eniL","description":".srebmun enil rotide evitaler-rosruc dna etulosba neewteb hctiwS"},"gutter_dimmed":{"label":"rettug miD","description":".tes nekot rettug demmid emeht eht esU"},"causal_gutter_basis":{"label":"srekram lasuaC","description":".srekram egnahc rettug yb desu sisab lasuac eht esoohC"}},"values":{"on":"nO","off":"ffO","theme_mode_dark":"kraD","theme_mode_light":"thgiL","line_numbers_absolute":"etulosbA","line_numbers_relative":"evitaleR","source":"ecruoS","preview":"weiverP","current":"tnerruC","open":"nepO","causal_gutter_last_save":"evas tsaL","causal_gutter_import":"tropmI","causal_gutter_selected_checkpoint":"tniopkcehc detceleS","causal_gutter_selected_tick":"kcit detceleS"},"toast":{"changed_title":"degnahc sgnitteS"}},"worldline":{"title":"senildlroW","empty":"tey senildlrow oN","header":"tcilfnoc atled daeh sisab eman dnik"},"why":{"range_obstructed_title":"detcurtsbo egnar yhW"}}; +export const me: TranslationSchema = {"footer":{"mode":{"browse":"esworb","insert":"tresni","normal":"lamron","preview":"weiverp","settings":"sgnittes","files":"selif","graft":"tfarg","history":"yrotsih"},"context":{"settings":"sgnittes","graft_empty":"ti tcepsni ot elif a nepo","history_empty":"tey ecnedive ohcE on","history_count":"{count} :ecnedive ohcE"},"command":{"invalid":"pleh rof pleh: epyt ;dezingocer ton dnammoC","hints":{"tab_accept":"tpecca bat","enter_run":"nur retne","esc_cancel":"lecnac cse"},"details":{"edit":"elif a nepO","write":"elif tnerruc eht etirW","quit":"tidej tiuQ","wq":"tiuq dna etirW","ttd":"daeh lacinonac gnivom tuohtiw kcit lasuac a evresbO","strand":"sdnarts etirw-no-ypoc tsil ro ,hctiws ,etaerC","braid":"setadidnac diarb timda ro ,weiverp ,weiV","why":"dnammoc lufgninaem tsal eht nialpxE","help":"pleh dnammoc wohS"}},"hints":{"j_k_move":"evom k/j","enter_change":"egnahc retne","f2_close":"esolc 2f","esc_close":"esolc cse","j_k_scroll":"llorcs k/j","f3_source":"ecruos 3f","f3_preview":"weiverp 3f","text_input":"tupni txet","esc_normal":"lamron cse","ctrl_s_save":"evas s+lrtc","ctrl_t_theme":"emeht t+lrtc","tab_focus":"sucof bat","tab_indent":"tnedni bat","ctrl_l_scene_picker":"rekcip enecs l+lrtc","scene_picker":"rekcip enecs","ctrl_b_files":"selif b+lrtc","ctrl_g_graft":"tfarg g+lrtc","ctrl_h_history":"yrotsih h+lrtc","enter_open":"nepo retne","backspace_up":"pu ecapskcab","ctrl_b_close":"esolc b+lrtc","enter_jump":"pmuj retne","r_refresh":"hserfer r","ctrl_g_close":"esolc g+lrtc","ctrl_h_close":"esolc h+lrtc","i_insert":"tresni i","o_open_line":"enil nepo o","cc_line":"enil cc","cw_word":"drow wc","ce_word_end":"dne-drow ec","c0_start":"trats 0c","c_end":"dne $c","dd_line":"enil dd","dw_word":"drow wd","de_word_end":"dne-drow ed","d0_start":"trats 0d","d_end":"dne $d","yy_line":"enil yy","yw_word":"drow wy","ye_word_end":"dne-drow ey","y0_start":"trats 0y","y_end":"dne $y","gg_top":"pot gg","esc_cancel":"lecnac cse"},"evidence":{"head":"daeh","tick":"kcit","anchor":"rohcna","coverage":"egarevoc","complete":"etelpmoc","partial":"laitrap","unavailable":"elbaliavanu"}},"history":{"title":"yrotsiH ohcE","empty":"tey ecnedive ohcE oN","header":"yrammus ecnedive sutats dnik kcit #"},"startupFileModal":{"title":"elif nepO","current_directory":"yrotcerid tnerruC","empty":"yrotcerid siht ni selif oN"},"settings":{"sections":{"appearance":"ecnaraeppA","editor":"rotidE","runtime":"emitnuR","keybindings":"sgnidnibyeK"},"rows":{"theme":{"label":"emehT","description":".semeht nevird-atad dellatsni neewteb hctiwS"},"theme_mode":{"label":"krad/thgiL","description":".noinapmoc krad ro thgil sti ot emeht tnerruc eht hctiwS"},"footer":{"label":"retooF","description":".egde mottob eht ta stnih dnammoc dna ,sucof ,edom wohS"},"markdown_preview":{"label":"weiverp nwodkraM","description":".weiverp dna ecruos neewteb reffub nwodkraM evitca eht hctiwS"},"diagnostics":{"label":"scitsongaiD","description":".gniriw emitnur lufroloC dna ,resrap ,tfarG tcepsnI"},"line_numbers":{"label":"srebmun eniL","description":".srebmun enil rotide evitaler-rosruc dna etulosba neewteb hctiwS"},"gutter_dimmed":{"label":"rettug miD","description":".tes nekot rettug demmid emeht eht esU"},"causal_gutter_basis":{"label":"srekram lasuaC","description":".srekram egnahc rettug yb desu sisab lasuac eht esoohC"},"leader":{"label":"redaeL","description":".crmij./~ ni devas ;egnahc ot retnE","capture":".slecnac csE ;yek eno sserP","capturing_value":"yek a sserP","space":"ecapS","backslash":"hsalskcaB","invalid_key":".slecnac csE ;yek elbatnirp eno esU","invalid":".yrter dna ti xif ;dilavni si crmij.","read_error":".crmij. daer ton dluoC","write_error":".yrter ;crmij. evas ton dluoC","unavailable":".tsoh siht ni devas eb tonnac sgnitteS","capture_hint":"sesolc 2F ;slecnac csE"}},"values":{"on":"nO","off":"ffO","theme_mode_dark":"kraD","theme_mode_light":"thgiL","line_numbers_absolute":"etulosbA","line_numbers_relative":"evitaleR","source":"ecruoS","preview":"weiverP","current":"tnerruC","open":"nepO","causal_gutter_last_save":"evas tsaL","causal_gutter_import":"tropmI","causal_gutter_selected_checkpoint":"tniopkcehc detceleS","causal_gutter_selected_tick":"kcit detceleS"},"toast":{"changed_title":"degnahc sgnitteS"}},"worldline":{"title":"senildlroW","empty":"tey senildlrow oN","header":"tcilfnoc atled daeh sisab eman dnik"},"why":{"range_obstructed_title":"detcurtsbo egnar yhW"}}; export const locales = ["en", "fr", "it", "de", "es", "ko", "ja", "zh-Hans", "pt-BR", "ru", "ar", "id", "tr", "hi", "nl", "pl", "vi", "th", "fa", "he", "me"] as const; diff --git a/src/ports/jim-settings.ts b/src/ports/jim-settings.ts new file mode 100644 index 00000000..0875b7ae --- /dev/null +++ b/src/ports/jim-settings.ts @@ -0,0 +1,24 @@ +import type { LeaderKey } from '../app/leader-key.js'; + +export const JimSettingsIssueKinds = Object.freeze({ + Input: Symbol('jim.settings.input'), + Invalid: Symbol('jim.settings.invalid'), + Read: Symbol('jim.settings.read'), + Write: Symbol('jim.settings.write'), + Unavailable: Symbol('jim.settings.unavailable'), +} as const); + +export interface JimSettingsIssue { + readonly kind: typeof JimSettingsIssueKinds[keyof typeof JimSettingsIssueKinds]; + readonly detail?: string; +} + +export interface JimSettingsSnapshot { + readonly leaderKey: LeaderKey; + readonly issue?: JimSettingsIssue; +} + +export interface JimSettingsPort { + load(): JimSettingsSnapshot; + saveLeader(leader: LeaderKey): JimSettingsIssue | undefined; +} diff --git a/src/ui/settings-drawer.ts b/src/ui/settings-drawer.ts index ff8c03bd..89071ad2 100644 --- a/src/ui/settings-drawer.ts +++ b/src/ui/settings-drawer.ts @@ -37,6 +37,7 @@ const SETTINGS_FALLBACK_CONTENT_X = 2; const SETTINGS_FALLBACK_CONTENT_Y = 1; export interface RenderSettingsDrawerOptions { + readonly closeHint?: string; readonly rows: readonly JeditSettingsRow[]; readonly selectedIndex: number; readonly theme: JeditTheme; @@ -101,7 +102,7 @@ function renderSettingsDrawerFallback(options: RenderSettingsDrawerOptions, cont function renderSettingsDrawerBody(options: RenderSettingsDrawerOptions): Surface { const surface = createSurface(options.width, options.height); fillSurface(surface, options.theme.surface.drawer); - paintText(surface, SETTINGS_CLOSE_HINT, SETTINGS_LEFT_PAD, SETTINGS_HINT_ROW, settingsHintToken(options)); + paintText(surface, options.closeHint ?? SETTINGS_CLOSE_HINT, SETTINGS_LEFT_PAD, SETTINGS_HINT_ROW, settingsHintToken(options)); const firstVisibleRow = firstVisibleSettingsRow(options.rows, options.selectedIndex, options.height); for (const layout of settingsRowLayouts(options.rows, firstVisibleRow)) { if (layout.section != null && layout.sectionY != null) { diff --git a/src/ui/workspace-chrome.ts b/src/ui/workspace-chrome.ts index 4a2b1ce3..c2b2a6ec 100644 --- a/src/ui/workspace-chrome.ts +++ b/src/ui/workspace-chrome.ts @@ -82,6 +82,8 @@ const FooterHintKeys = Object.freeze({ EscCancel: 'esc_cancel', }); +const LEADER_CAPTURE_FOOTER_KEY = 'settings.rows.leader.capture'; + type FooterHintTranslator = (key: string) => string; export interface WorkspaceTitleState { @@ -92,6 +94,7 @@ export interface WorkspaceTitleState { } export interface WorkspaceFooterState { + readonly leaderCaptureActive?: boolean; readonly i18n: I18nPort; readonly focusPane: FocusPane; readonly fileDrawerOpen: boolean; @@ -231,6 +234,9 @@ function editorModeKey(state: WorkspaceFooterState): string { function footerDetail(state: WorkspaceFooterState): string { const t = footerHintTranslator(state); + if (state.settingsOpen && state.leaderCaptureActive) { + return state.i18n.t(LEADER_CAPTURE_FOOTER_KEY); + } if (state.settingsOpen) { return footerHints([t(FooterHintKeys.JkMove), t(FooterHintKeys.EnterChange), t(FooterHintKeys.F2Close), t(FooterHintKeys.EscClose)]); } diff --git a/translations.json b/translations.json index 598abf31..aa45f4c5 100644 --- a/translations.json +++ b/translations.json @@ -110,7 +110,8 @@ "sections": { "appearance": "Appearance", "editor": "Editor", - "runtime": "Runtime" + "runtime": "Runtime", + "keybindings": "Keybindings" }, "rows": { "theme": { @@ -144,6 +145,20 @@ "causal_gutter_basis": { "label": "Causal markers", "description": "Choose the causal basis used by gutter change markers." + }, + "leader": { + "label": "Leader", + "description": "Enter to change; saved in ~/.jimrc.", + "capture": "Press one key; Esc cancels.", + "capturing_value": "Press a key", + "space": "Space", + "backslash": "Backslash", + "invalid_key": "Use one printable key; Esc cancels.", + "invalid": ".jimrc is invalid; fix it and retry.", + "read_error": "Could not read .jimrc.", + "write_error": "Could not save .jimrc; retry.", + "unavailable": "Settings cannot be saved in this host.", + "capture_hint": "Esc cancels; F2 closes" } }, "values": { @@ -286,7 +301,8 @@ "sections": { "appearance": "Appearance", "editor": "Editor", - "runtime": "Runtime" + "runtime": "Runtime", + "keybindings": "Raccourcis clavier" }, "rows": { "theme": { @@ -320,6 +336,20 @@ "causal_gutter_basis": { "label": "Causal markers", "description": "Choose the causal basis used by gutter change markers." + }, + "leader": { + "label": "Leader", + "description": "Entrée : modifier; fichier ~/.jimrc.", + "capture": "Appuyez sur une touche; Échap annule.", + "capturing_value": "Appuyez sur une touche", + "space": "Espace", + "backslash": "Barre oblique inverse", + "invalid_key": "Une touche imprimable; Échap annule.", + "invalid": ".jimrc invalide; corrigez et réessayez.", + "read_error": "Lecture de .jimrc impossible.", + "write_error": "Enregistrement de .jimrc impossible.", + "unavailable": "Ce mode ne peut pas enregistrer.", + "capture_hint": "Échap annule; F2 ferme" } }, "values": { @@ -462,7 +492,8 @@ "sections": { "appearance": "Appearance", "editor": "Editor", - "runtime": "Runtime" + "runtime": "Runtime", + "keybindings": "Scorciatoie da tastiera" }, "rows": { "theme": { @@ -496,6 +527,20 @@ "causal_gutter_basis": { "label": "Causal markers", "description": "Choose the causal basis used by gutter change markers." + }, + "leader": { + "label": "Leader", + "description": "Invio per cambiare; file ~/.jimrc.", + "capture": "Premi un tasto; Esc annulla.", + "capturing_value": "Premi un tasto", + "space": "Spazio", + "backslash": "Barra inversa", + "invalid_key": "Un tasto stampabile; Esc annulla.", + "invalid": ".jimrc non valido; correggi e riprova.", + "read_error": "Impossibile leggere .jimrc.", + "write_error": "Impossibile salvare .jimrc; riprova.", + "unavailable": "Questo ambiente non salva le impostazioni.", + "capture_hint": "Esc annulla; F2 chiude" } }, "values": { @@ -638,7 +683,8 @@ "sections": { "appearance": "Appearance", "editor": "Editor", - "runtime": "Runtime" + "runtime": "Runtime", + "keybindings": "Tastenkürzel" }, "rows": { "theme": { @@ -672,6 +718,20 @@ "causal_gutter_basis": { "label": "Causal markers", "description": "Choose the causal basis used by gutter change markers." + }, + "leader": { + "label": "Leader", + "description": "Enter zum Ändern; Datei ~/.jimrc.", + "capture": "Taste drücken; Esc bricht ab.", + "capturing_value": "Taste drücken", + "space": "Leertaste", + "backslash": "Rückstrich", + "invalid_key": "Ein druckbares Zeichen; Esc bricht ab.", + "invalid": ".jimrc ungültig; korrigieren und erneut versuchen.", + "read_error": ".jimrc konnte nicht gelesen werden.", + "write_error": ".jimrc konnte nicht gespeichert werden.", + "unavailable": "Diese Umgebung speichert keine Einstellungen.", + "capture_hint": "Esc abbricht; F2 schließt" } }, "values": { @@ -814,7 +874,8 @@ "sections": { "appearance": "Appearance", "editor": "Editor", - "runtime": "Runtime" + "runtime": "Runtime", + "keybindings": "Atajos de teclado" }, "rows": { "theme": { @@ -848,6 +909,20 @@ "causal_gutter_basis": { "label": "Causal markers", "description": "Choose the causal basis used by gutter change markers." + }, + "leader": { + "label": "Leader", + "description": "Enter para cambiar; archivo ~/.jimrc.", + "capture": "Pulsa una tecla; Esc cancela.", + "capturing_value": "Pulsa una tecla", + "space": "Espacio", + "backslash": "Barra inversa", + "invalid_key": "Una tecla imprimible; Esc cancela.", + "invalid": ".jimrc no válido; corrige y reintenta.", + "read_error": "No se pudo leer .jimrc.", + "write_error": "No se pudo guardar .jimrc; reintenta.", + "unavailable": "Este entorno no guarda ajustes.", + "capture_hint": "Esc cancela; F2 cierra" } }, "values": { @@ -990,7 +1065,8 @@ "sections": { "appearance": "Appearance", "editor": "Editor", - "runtime": "Runtime" + "runtime": "Runtime", + "keybindings": "키 바인딩" }, "rows": { "theme": { @@ -1024,6 +1100,20 @@ "causal_gutter_basis": { "label": "Causal markers", "description": "Choose the causal basis used by gutter change markers." + }, + "leader": { + "label": "Leader", + "description": "Enter로 변경; ~/.jimrc에 저장.", + "capture": "키를 누르세요; Esc로 취소.", + "capturing_value": "키를 누르세요", + "space": "스페이스", + "backslash": "백슬래시", + "invalid_key": "출력 가능한 키 하나; Esc로 취소.", + "invalid": ".jimrc가 잘못되었습니다. 수정 후 재시도하세요.", + "read_error": ".jimrc를 읽을 수 없습니다.", + "write_error": ".jimrc를 저장할 수 없습니다. 재시도하세요.", + "unavailable": "이 환경에서는 설정을 저장할 수 없습니다.", + "capture_hint": "Esc 취소; F2 닫기" } }, "values": { @@ -1166,7 +1256,8 @@ "sections": { "appearance": "Appearance", "editor": "Editor", - "runtime": "Runtime" + "runtime": "Runtime", + "keybindings": "キーバインド" }, "rows": { "theme": { @@ -1200,6 +1291,20 @@ "causal_gutter_basis": { "label": "Causal markers", "description": "Choose the causal basis used by gutter change markers." + }, + "leader": { + "label": "Leader", + "description": "Enterで変更、~/.jimrcに保存。", + "capture": "キーを押してください。Escで取消。", + "capturing_value": "キーを押す", + "space": "スペース", + "backslash": "バックスラッシュ", + "invalid_key": "表示可能なキー1つ。Escで取消。", + "invalid": ".jimrcが無効です。修正して再試行。", + "read_error": ".jimrcを読み込めません。", + "write_error": ".jimrcを保存できません。再試行。", + "unavailable": "この環境では設定を保存できません。", + "capture_hint": "Escで取消、F2で閉じる" } }, "values": { @@ -1342,7 +1447,8 @@ "sections": { "appearance": "Appearance", "editor": "Editor", - "runtime": "Runtime" + "runtime": "Runtime", + "keybindings": "按键绑定" }, "rows": { "theme": { @@ -1376,6 +1482,20 @@ "causal_gutter_basis": { "label": "Causal markers", "description": "Choose the causal basis used by gutter change markers." + }, + "leader": { + "label": "Leader", + "description": "按 Enter 修改;保存到 ~/.jimrc。", + "capture": "按一个键;Esc 取消。", + "capturing_value": "按一个键", + "space": "空格", + "backslash": "反斜杠", + "invalid_key": "使用一个可打印字符;Esc 取消。", + "invalid": ".jimrc 无效;修正后重试。", + "read_error": "无法读取 .jimrc。", + "write_error": "无法保存 .jimrc;请重试。", + "unavailable": "此环境无法保存设置。", + "capture_hint": "Esc 取消;F2 关闭" } }, "values": { @@ -1518,7 +1638,8 @@ "sections": { "appearance": "Appearance", "editor": "Editor", - "runtime": "Runtime" + "runtime": "Runtime", + "keybindings": "Atalhos de teclado" }, "rows": { "theme": { @@ -1552,6 +1673,20 @@ "causal_gutter_basis": { "label": "Causal markers", "description": "Choose the causal basis used by gutter change markers." + }, + "leader": { + "label": "Leader", + "description": "Enter para alterar; arquivo ~/.jimrc.", + "capture": "Pressione uma tecla; Esc cancela.", + "capturing_value": "Pressione uma tecla", + "space": "Espaço", + "backslash": "Barra invertida", + "invalid_key": "Uma tecla imprimível; Esc cancela.", + "invalid": ".jimrc inválido; corrija e tente novamente.", + "read_error": "Não foi possível ler .jimrc.", + "write_error": "Não foi possível salvar .jimrc.", + "unavailable": "Este ambiente não salva configurações.", + "capture_hint": "Esc cancela; F2 fecha" } }, "values": { @@ -1694,7 +1829,8 @@ "sections": { "appearance": "Appearance", "editor": "Editor", - "runtime": "Runtime" + "runtime": "Runtime", + "keybindings": "Сочетания клавиш" }, "rows": { "theme": { @@ -1728,6 +1864,20 @@ "causal_gutter_basis": { "label": "Causal markers", "description": "Choose the causal basis used by gutter change markers." + }, + "leader": { + "label": "Leader", + "description": "Enter: изменить; файл ~/.jimrc.", + "capture": "Нажмите клавишу; Esc отменяет.", + "capturing_value": "Нажмите клавишу", + "space": "Пробел", + "backslash": "Обратная косая черта", + "invalid_key": "Один печатный символ; Esc отменяет.", + "invalid": ".jimrc некорректен; исправьте и повторите.", + "read_error": "Не удалось прочитать .jimrc.", + "write_error": "Не удалось сохранить .jimrc; повторите.", + "unavailable": "В этой среде нельзя сохранять настройки.", + "capture_hint": "Esc отмена; F2 закрыть" } }, "values": { @@ -1870,7 +2020,8 @@ "sections": { "appearance": "Appearance", "editor": "Editor", - "runtime": "Runtime" + "runtime": "Runtime", + "keybindings": "اختصارات لوحة المفاتيح" }, "rows": { "theme": { @@ -1904,6 +2055,20 @@ "causal_gutter_basis": { "label": "Causal markers", "description": "Choose the causal basis used by gutter change markers." + }, + "leader": { + "label": "Leader", + "description": "Enter للتغيير؛ الحفظ في ~/.jimrc.", + "capture": "اضغط مفتاحًا؛ Esc للإلغاء.", + "capturing_value": "اضغط مفتاحًا", + "space": "مسافة", + "backslash": "شرطة مائلة عكسية", + "invalid_key": "حرف واحد قابل للطباعة؛ Esc للإلغاء.", + "invalid": "ملف .jimrc غير صالح؛ أصلحه وأعد المحاولة.", + "read_error": "تعذرت قراءة .jimrc.", + "write_error": "تعذر حفظ .jimrc؛ أعد المحاولة.", + "unavailable": "لا يمكن حفظ الإعدادات في هذه البيئة.", + "capture_hint": "Esc للإلغاء؛ F2 للإغلاق" } }, "values": { @@ -2046,7 +2211,8 @@ "sections": { "appearance": "Appearance", "editor": "Editor", - "runtime": "Runtime" + "runtime": "Runtime", + "keybindings": "Pintasan papan ketik" }, "rows": { "theme": { @@ -2080,6 +2246,20 @@ "causal_gutter_basis": { "label": "Causal markers", "description": "Choose the causal basis used by gutter change markers." + }, + "leader": { + "label": "Leader", + "description": "Enter untuk ubah; simpan ~/.jimrc.", + "capture": "Tekan satu tombol; Esc batal.", + "capturing_value": "Tekan tombol", + "space": "Spasi", + "backslash": "Garis miring terbalik", + "invalid_key": "Satu karakter tercetak; Esc batal.", + "invalid": ".jimrc tidak valid; perbaiki dan coba lagi.", + "read_error": "Tidak dapat membaca .jimrc.", + "write_error": "Tidak dapat menyimpan .jimrc; coba lagi.", + "unavailable": "Lingkungan ini tidak menyimpan pengaturan.", + "capture_hint": "Esc batal; F2 tutup" } }, "values": { @@ -2222,7 +2402,8 @@ "sections": { "appearance": "Appearance", "editor": "Editor", - "runtime": "Runtime" + "runtime": "Runtime", + "keybindings": "Tuş atamaları" }, "rows": { "theme": { @@ -2256,6 +2437,20 @@ "causal_gutter_basis": { "label": "Causal markers", "description": "Choose the causal basis used by gutter change markers." + }, + "leader": { + "label": "Leader", + "description": "Enter ile değiştir; dosya ~/.jimrc.", + "capture": "Bir tuşa basın; Esc iptal eder.", + "capturing_value": "Bir tuşa basın", + "space": "Boşluk", + "backslash": "Ters eğik çizgi", + "invalid_key": "Yazdırılabilir tek tuş; Esc iptal eder.", + "invalid": ".jimrc geçersiz; düzeltip yeniden deneyin.", + "read_error": ".jimrc okunamadı.", + "write_error": ".jimrc kaydedilemedi; yeniden deneyin.", + "unavailable": "Bu ortamda ayarlar kaydedilemez.", + "capture_hint": "Esc iptal; F2 kapat" } }, "values": { @@ -2398,7 +2593,8 @@ "sections": { "appearance": "Appearance", "editor": "Editor", - "runtime": "Runtime" + "runtime": "Runtime", + "keybindings": "की बाइंडिंग" }, "rows": { "theme": { @@ -2432,6 +2628,20 @@ "causal_gutter_basis": { "label": "Causal markers", "description": "Choose the causal basis used by gutter change markers." + }, + "leader": { + "label": "Leader", + "description": "Enter से बदलें; ~/.jimrc में सहेजें।", + "capture": "एक कुंजी दबाएँ; Esc से रद्द करें।", + "capturing_value": "कुंजी दबाएँ", + "space": "स्पेस", + "backslash": "बैकस्लैश", + "invalid_key": "एक मुद्रण योग्य कुंजी; Esc से रद्द करें।", + "invalid": ".jimrc अमान्य है; सुधारकर फिर कोशिश करें।", + "read_error": ".jimrc पढ़ा नहीं जा सका।", + "write_error": ".jimrc सहेजा नहीं जा सका; फिर कोशिश करें।", + "unavailable": "इस परिवेश में सेटिंग सहेजी नहीं जा सकती।", + "capture_hint": "Esc रद्द; F2 बंद" } }, "values": { @@ -2574,7 +2784,8 @@ "sections": { "appearance": "Appearance", "editor": "Editor", - "runtime": "Runtime" + "runtime": "Runtime", + "keybindings": "Sneltoetsen" }, "rows": { "theme": { @@ -2608,6 +2819,20 @@ "causal_gutter_basis": { "label": "Causal markers", "description": "Choose the causal basis used by gutter change markers." + }, + "leader": { + "label": "Leader", + "description": "Enter om te wijzigen; ~/.jimrc.", + "capture": "Druk op een toets; Esc annuleert.", + "capturing_value": "Druk op een toets", + "space": "Spatie", + "backslash": "Backslash", + "invalid_key": "Eén afdrukbare toets; Esc annuleert.", + "invalid": ".jimrc ongeldig; herstel en probeer opnieuw.", + "read_error": ".jimrc kon niet worden gelezen.", + "write_error": ".jimrc kon niet worden opgeslagen.", + "unavailable": "Deze omgeving kan instellingen niet opslaan.", + "capture_hint": "Esc annuleert; F2 sluit" } }, "values": { @@ -2750,7 +2975,8 @@ "sections": { "appearance": "Appearance", "editor": "Editor", - "runtime": "Runtime" + "runtime": "Runtime", + "keybindings": "Skróty klawiszowe" }, "rows": { "theme": { @@ -2784,6 +3010,20 @@ "causal_gutter_basis": { "label": "Causal markers", "description": "Choose the causal basis used by gutter change markers." + }, + "leader": { + "label": "Leader", + "description": "Enter zmienia; zapis w ~/.jimrc.", + "capture": "Naciśnij klawisz; Esc anuluje.", + "capturing_value": "Naciśnij klawisz", + "space": "Spacja", + "backslash": "Ukośnik odwrotny", + "invalid_key": "Jeden drukowalny znak; Esc anuluje.", + "invalid": ".jimrc niepoprawny; popraw i spróbuj ponownie.", + "read_error": "Nie można odczytać .jimrc.", + "write_error": "Nie można zapisać .jimrc; spróbuj ponownie.", + "unavailable": "To środowisko nie zapisuje ustawień.", + "capture_hint": "Esc anuluje; F2 zamyka" } }, "values": { @@ -2926,7 +3166,8 @@ "sections": { "appearance": "Appearance", "editor": "Editor", - "runtime": "Runtime" + "runtime": "Runtime", + "keybindings": "Phím tắt" }, "rows": { "theme": { @@ -2960,6 +3201,20 @@ "causal_gutter_basis": { "label": "Causal markers", "description": "Choose the causal basis used by gutter change markers." + }, + "leader": { + "label": "Leader", + "description": "Enter để đổi; lưu vào ~/.jimrc.", + "capture": "Nhấn một phím; Esc để hủy.", + "capturing_value": "Nhấn một phím", + "space": "Dấu cách", + "backslash": "Dấu gạch chéo ngược", + "invalid_key": "Một ký tự in được; Esc để hủy.", + "invalid": ".jimrc không hợp lệ; sửa rồi thử lại.", + "read_error": "Không đọc được .jimrc.", + "write_error": "Không lưu được .jimrc; thử lại.", + "unavailable": "Môi trường này không lưu được cài đặt.", + "capture_hint": "Esc hủy; F2 đóng" } }, "values": { @@ -3102,7 +3357,8 @@ "sections": { "appearance": "Appearance", "editor": "Editor", - "runtime": "Runtime" + "runtime": "Runtime", + "keybindings": "การผูกปุ่ม" }, "rows": { "theme": { @@ -3136,6 +3392,20 @@ "causal_gutter_basis": { "label": "Causal markers", "description": "Choose the causal basis used by gutter change markers." + }, + "leader": { + "label": "Leader", + "description": "Enter เพื่อเปลี่ยน; บันทึก ~/.jimrc.", + "capture": "กดหนึ่งปุ่ม; Esc เพื่อยกเลิก.", + "capturing_value": "กดหนึ่งปุ่ม", + "space": "เว้นวรรค", + "backslash": "แบ็กสแลช", + "invalid_key": "ใช้อักขระที่พิมพ์ได้หนึ่งตัว; Esc ยกเลิก.", + "invalid": ".jimrc ไม่ถูกต้อง; แก้ไขแล้วลองใหม่.", + "read_error": "อ่าน .jimrc ไม่ได้.", + "write_error": "บันทึก .jimrc ไม่ได้; ลองใหม่.", + "unavailable": "สภาพแวดล้อมนี้บันทึกการตั้งค่าไม่ได้.", + "capture_hint": "Esc ยกเลิก; F2 ปิด" } }, "values": { @@ -3278,7 +3548,8 @@ "sections": { "appearance": "Appearance", "editor": "Editor", - "runtime": "Runtime" + "runtime": "Runtime", + "keybindings": "میانبرهای صفحه‌کلید" }, "rows": { "theme": { @@ -3312,6 +3583,20 @@ "causal_gutter_basis": { "label": "Causal markers", "description": "Choose the causal basis used by gutter change markers." + }, + "leader": { + "label": "Leader", + "description": "Enter برای تغییر؛ ذخیره در ~/.jimrc.", + "capture": "یک کلید بزنید؛ Esc برای لغو.", + "capturing_value": "یک کلید بزنید", + "space": "فاصله", + "backslash": "بک‌اسلش", + "invalid_key": "یک نویسه قابل چاپ؛ Esc برای لغو.", + "invalid": ".jimrc نامعتبر است؛ اصلاح و دوباره تلاش کنید.", + "read_error": "خواندن .jimrc ممکن نیست.", + "write_error": "ذخیره .jimrc ممکن نیست؛ دوباره تلاش کنید.", + "unavailable": "در این محیط تنظیمات ذخیره نمی‌شوند.", + "capture_hint": "Esc لغو؛ F2 بستن" } }, "values": { @@ -3454,7 +3739,8 @@ "sections": { "appearance": "Appearance", "editor": "Editor", - "runtime": "Runtime" + "runtime": "Runtime", + "keybindings": "קיצורי מקשים" }, "rows": { "theme": { @@ -3488,6 +3774,20 @@ "causal_gutter_basis": { "label": "Causal markers", "description": "Choose the causal basis used by gutter change markers." + }, + "leader": { + "label": "Leader", + "description": "Enter לשינוי; שמירה ב־~/.jimrc.", + "capture": "לחצו על מקש; Esc לביטול.", + "capturing_value": "לחצו על מקש", + "space": "רווח", + "backslash": "לוכסן הפוך", + "invalid_key": "תו אחד הניתן להדפסה; Esc לביטול.", + "invalid": ".jimrc אינו תקין; תקנו ונסו שוב.", + "read_error": "לא ניתן לקרוא את .jimrc.", + "write_error": "לא ניתן לשמור את .jimrc; נסו שוב.", + "unavailable": "לא ניתן לשמור הגדרות בסביבה זו.", + "capture_hint": "Esc לביטול; F2 לסגירה" } }, "values": { From a90ddb7fbf27643dd898d98e425bae2302d21970 Mon Sep 17 00:00:00 2001 From: James Ross Date: Wed, 14 Oct 2026 01:10:03 -0700 Subject: [PATCH 3/8] Verify Leader settings in the live TUI and document .jimrc --- .../0159-leader-key-settings-narrow.svg | 2 +- docs/design/0159-leader-key-settings.md | 78 ++++++++++++++----- docs/topics/ui/settings-menu.md | 40 ++++++++++ spec/leader-settings.spec.mjs | 23 ++++++ 4 files changed, 123 insertions(+), 20 deletions(-) diff --git a/docs/design/0159-leader-key-settings-narrow.svg b/docs/design/0159-leader-key-settings-narrow.svg index 67b104fe..9f5e9632 100644 --- a/docs/design/0159-leader-key-settings-narrow.svg +++ b/docs/design/0159-leader-key-settings-narrow.svg @@ -1 +1 @@ -SettingsF2 / Esc closeKeybindings› Leader: Press a keyEsc cancelsSaved in ~/.jimrc \ No newline at end of file +SettingsF2 / Esc closeKeybindings› Leader: Press a keyEsc cancelsSaved in ~/.jimrc \ No newline at end of file diff --git a/docs/design/0159-leader-key-settings.md b/docs/design/0159-leader-key-settings.md index 2ead18cd..4af2cbdd 100644 --- a/docs/design/0159-leader-key-settings.md +++ b/docs/design/0159-leader-key-settings.md @@ -102,7 +102,7 @@ same heading, row, and description treatment as existing settings. ### Narrow UI Mockup -Existing drawer at 40 by 16. Selection scrolling keeps the Leader row visible; +Existing drawer at 60 by 16. Selection scrolling keeps the Leader row visible; labels and help use the existing clipping behavior. ![Leader row, narrow](0159-leader-key-settings-narrow.svg) @@ -123,8 +123,9 @@ No filesystem dependency enters the application core. ## Lower Modes -Plain-text rendering remains usable without color. Missing `.jimrc` uses the -default without creating a file. Hosts without a persistence adapter show a +Plain-text rendering remains usable without color. The workspace retains its +minimum of 60 columns by 12 rows; smaller terminals show the existing size +message. Missing `.jimrc` uses the default without creating a file. Hosts without a persistence adapter show a save error instead of claiming durability. Settings need no Echo text read or mutation for this preference. @@ -140,8 +141,9 @@ mutation for this preference. | Preservation | Unrelated root and keybindings fields survive saves; malformed existing files remain untouched. | Atomic replacement prevents partially written configuration. Saving rereads -the file to preserve unrelated edits made since startup. Live reload and -multi-process write arbitration are not part of this cycle. +the file to preserve unrelated edits made since startup. Symlinked dotfiles +retain their link and update the target. Live reload and multi-process write +arbitration are not part of this cycle. ## Accessibility Posture @@ -188,24 +190,24 @@ new modal or layout. ## Implementation Slices -- [x] Slice 1: Validated Leader preference and lossless `.jimrc` adapter. +- [x] Slice 1: Validated Leader preference and field-preserving `.jimrc` adapter. - [x] Slice 2: Settings capture, production wiring, and localized feedback. -- [ ] Slice 3: Runtime/render/restart witness, docs, and required validation. +- [x] Slice 3: Runtime/render/restart witness, docs, and required validation. ## Tests To Write First -- [ ] Settings render exposes the current preference and capture state. -- [ ] Runtime captures Space and custom keys; Esc/F2 cancel; invalid input stays in capture. -- [ ] Save/restart retains the key and preserves unrelated JSON fields. -- [ ] Malformed config and write failure retain previous data and visible errors. +- [x] Settings render exposes the current preference and capture state. +- [x] Runtime captures Space and custom keys; Esc/F2 cancel; invalid input stays in capture. +- [x] Save/restart retains the key and preserves unrelated JSON fields. +- [x] Malformed config and write failure retain previous data and visible errors. ## Acceptance Criteria -- [ ] User can configure the Leader key through Settings and restart with it. -- [ ] Configuration is optional, data-only, validated, and safely replaced. -- [ ] Rendered output and failure/cancellation paths have executable evidence. -- [ ] Catalogs, docs, issue, and PR reflect the scope. -- [ ] Required local checks pass; CI state is disclosed independently. +- [x] User can configure the Leader key through Settings and restart with it. +- [x] Configuration is optional, data-only, validated, and safely replaced. +- [x] Rendered output and failure/cancellation paths have executable evidence. +- [x] Catalogs, docs, issue, and PR reflect the scope. +- [x] Required local checks pass; CI state is disclosed independently. ## Validation Plan @@ -228,9 +230,47 @@ Existing hard-global key precedence must not steal printable capture input. ## Follow-On Debt -Leader command mappings remain separate from this preference-only cycle. -No new mapping behavior is promised by this design. +Leader command mappings remain separate from this preference-only cycle: +[issue #337](https://github.com/flyingrobots/jedit/issues/337) owns the requested +explorer toggle. No new mapping behavior is promised by this design. ## Retrospective -Pending implementation and validation. +The implementation follows the capture and data-only configuration design. +Symlink preservation was added after a failing filesystem witness showed that +ordinary atomic replacement would replace the link itself. Small-terminal +inspection confirmed the existing 60-column minimum, so the narrow mockup +assumption was corrected from 40 to 60 columns. Capture-specific drawer and +footer hints replace ordinary navigation hints while capture owns input. + +Ten specs in `spec/leader-settings.spec.mjs` prove validation, default loading, +capture/cancellation, special/modified-key rejection, save/restart through the +production app composition, unrelated-field preservation, symlink preservation, +malformed-file refusal, failed-write recovery, and persistent row errors. +The filesystem cases use temporary homes. The text dependency in the automated +composition case is an explicitly injected test double; no buffer edits occur. + +`npm run check` passed: 881 JavaScript tests passed, 12 skipped, 99 native Rust +tests passed, and the quality gate reported no regressions. The existing +tracked test-file size debt remains unchanged. Published implementation commit +`646d399a` also passed all twelve GitHub Actions checks, including the real Echo +release-gate witness. After adding the locale-rendering witness, all ten focused +specs and the quality gate also passed. + +A live PTY run used the production app and real native Echo host with an +injected temporary configuration directory and isolated WAL. At 100 by 32, +F2 → eight Down presses → Enter → Space saved the key and displayed its toast. +Enter → Esc canceled a second capture without changing the saved Space. +Restart at 60 by 16 loaded Space; the Leader row and capture-specific hints +remained visible. Ctrl+C restored the terminal and closed the host. Rendered +cell snapshots were visually inspected. Below 60 columns the existing minimum +size message appeared. + +New copy was authored for every supported locale and the mirror catalog was +regenerated. Automated localized rendering covers LTR and RTL catalogs; native +PTY dogfooding used English. Translation quality has not had native-speaker +review. Preference persistence is scoped to Leader; general settings +persistence, live config reload, and concurrent-process write arbitration are +not implemented. Leader sequence dispatch is tracked in #337. + +PR: https://github.com/flyingrobots/jedit/pull/336 diff --git a/docs/topics/ui/settings-menu.md b/docs/topics/ui/settings-menu.md index 18a48cdf..69e45d02 100644 --- a/docs/topics/ui/settings-menu.md +++ b/docs/topics/ui/settings-menu.md @@ -47,6 +47,7 @@ The settings drawer can include these rows, depending on runtime capabilities: | `Causal markers` | Choose the causal head used as the gutter comparison basis. | | `Markdown preview` | Toggle Markdown preview when supported for the current buffer. | | `Diagnostics` | Open or close the diagnostics panel. | +| `Leader` | Capture the key used as the Leader prefix and save it in `~/.jimrc`. | Language should stay compact because the locale list is long. The row should show only the current locale and position, for example: @@ -94,6 +95,43 @@ contrast for every gutter role. When a semantic palette color cannot meet that floor, theme construction falls back to the theme's named `ink` variable. No gutter role uses a hard-coded renderer color. +## Keybindings + +`Keybindings` currently contains one preference, `Leader`. The default is +backslash, matching Vim. To use Space: + +1. Open Settings with `F2`. +2. Select `Keybindings` → `Leader` with the arrow keys or `j`/`k`. +3. Press `Enter`, then `Space`. + +Activation starts single-key capture. The next printable character becomes the +Leader key. During capture, `j`, `k`, `q`, and backtick are valid choices rather +than navigation or closing commands. `Esc` cancels and leaves Settings open; +`F2` cancels and closes Settings. Modified keys and named special keys are +rejected with a visible message. `Ctrl+C` retains its quit behavior. + +The choice saves immediately to `~/.jimrc` and loads at the next startup. This +file contains JSON data, not Vimscript or executable code. For Space: + +```json +{ + "keybindings": { + "leader": " " + } +} +``` + +A missing file uses the default without creating a file. Jim creates the file +when a preference is successfully saved. Other JSON fields survive saves; +symlinked dotfiles keep their link and update the target. Jim replaces valid +configuration atomically and leaves malformed configuration untouched. If the +file is unreadable or invalid, the Leader row displays the error and startup +uses backslash. If saving fails, the active value stays unchanged. Fix the +file or filesystem access and activate the row again to retry. + +Only the Leader preference persists in this version. Leader command sequences +such as `Space e` are not implemented by this setting. + ## Setting Feedback Every setting change should create a short confirmation toast with: @@ -136,6 +174,8 @@ cursor. | --- | --- | | [`src/app/settings-session.ts`](../../../src/app/settings-session.ts) | Settings rows, focus movement, and key actions. | | [`src/app/workspace/settings.ts`](../../../src/app/workspace/settings.ts) | Workspace handlers for applying setting changes. | +| [`src/adapters/jim-settings.ts`](../../../src/adapters/jim-settings.ts) | Load and atomically save global JSON configuration. | +| [`src/app/workspace/leader-key-capture.ts`](../../../src/app/workspace/leader-key-capture.ts) | Capture, cancel, validate, and persist the Leader choice. | | [`src/app/workspace/settings-key-bindings.ts`](../../../src/app/workspace/settings-key-bindings.ts) | Settings key dispatch and change notifications. | | [`src/app/workspace/workspace-causal-gutter-basis.ts`](../../../src/app/workspace/workspace-causal-gutter-basis.ts) | Save/import comparison choices and evidence validation. | | [`src/app/workspace/workspace-causal-line-change-refresh.ts`](../../../src/app/workspace/workspace-causal-line-change-refresh.ts) | Bounded refresh commands and stale-result refusal. | diff --git a/spec/leader-settings.spec.mjs b/spec/leader-settings.spec.mjs index fad94f12..aac5fb15 100644 --- a/spec/leader-settings.spec.mjs +++ b/spec/leader-settings.spec.mjs @@ -222,3 +222,26 @@ test('production app composition saves and restores Leader through its settings assert.match(text(app.view(model)), /Leader.*Space/); assert.equal(createWorkspaceApp(options, textHost).init()[0].leaderKey.value, ' '); }); + +test('the Leader row and capture prompt render in LTR and RTL locales', async () => { + const m = await modules(); + for (const [locale, section, capture] of [ + ['en', 'Keybindings', 'Press a key'], + ['ar', 'اختصارات لوحة المفاتيح', 'اضغط مفتاحًا'], + ['he', 'קיצורי מקשים', 'לחצו על מקש'], + ]) { + const deps = mockRuntime(); + deps.initialModel.i18n = new m.i18n.BijouI18nAdapter().withLocale(locale); + deps.initialModel.jeditTheme = m.themes.availableJeditThemes()[0]; + const runtime = m.runtime.createWorkspaceRuntime(deps); + let model = selectLeader(runtime, m.settings, runtime.init()[0]); + model = runtime.update(key('enter'), model)[0]; + const rendered = text(m.drawer.renderSettingsDrawer({ + rows: m.settings.settingsRows(model), selectedIndex: model.settingsFocusIndex, + theme: model.jeditTheme, width: 60, height: 16, + })); + assert.ok(rendered.includes(section), locale); + assert.ok(rendered.includes(capture), locale); + assert.equal(model.leaderKey.value, '\\'); + } +}); From 23c2412182fd0a9c8807ff1cb98c543cc391e853 Mon Sep 17 00:00:00 2001 From: James Ross Date: Wed, 14 Oct 2026 01:14:31 -0700 Subject: [PATCH 4/8] Preserve configuration errors when Leader capture is canceled --- docs/design/0159-leader-key-settings.md | 9 ++++++--- spec/leader-settings.spec.mjs | 6 +++++- src/app/leader-settings-row.ts | 7 +++++-- src/app/workspace/leader-key-capture.ts | 7 +++---- src/app/workspace/model.ts | 1 + src/app/workspace/settings.ts | 2 ++ src/ports/jim-settings.ts | 1 - 7 files changed, 22 insertions(+), 11 deletions(-) diff --git a/docs/design/0159-leader-key-settings.md b/docs/design/0159-leader-key-settings.md index 4af2cbdd..ffa31c0f 100644 --- a/docs/design/0159-leader-key-settings.md +++ b/docs/design/0159-leader-key-settings.md @@ -242,6 +242,9 @@ ordinary atomic replacement would replace the link itself. Small-terminal inspection confirmed the existing 60-column minimum, so the narrow mockup assumption was corrected from 40 to 60 columns. Capture-specific drawer and footer hints replace ordinary navigation hints while capture owns input. +A final RED/GREEN regression separated temporary invalid-key feedback from +persistent configuration errors, so canceling capture cannot erase a `.jimrc` +error. Ten specs in `spec/leader-settings.spec.mjs` prove validation, default loading, capture/cancellation, special/modified-key rejection, save/restart through the @@ -250,12 +253,12 @@ malformed-file refusal, failed-write recovery, and persistent row errors. The filesystem cases use temporary homes. The text dependency in the automated composition case is an explicitly injected test double; no buffer edits occur. -`npm run check` passed: 881 JavaScript tests passed, 12 skipped, 99 native Rust +`npm run check` passed: 882 JavaScript tests passed, 12 skipped, 99 native Rust tests passed, and the quality gate reported no regressions. The existing tracked test-file size debt remains unchanged. Published implementation commit `646d399a` also passed all twelve GitHub Actions checks, including the real Echo -release-gate witness. After adding the locale-rendering witness, all ten focused -specs and the quality gate also passed. +release-gate witness. The final full check includes the locale-rendering witness and the +cancellation-error regression. All ten focused specs also passed. A live PTY run used the production app and real native Echo host with an injected temporary configuration directory and isolated WAL. At 100 by 32, diff --git a/spec/leader-settings.spec.mjs b/spec/leader-settings.spec.mjs index aac5fb15..7be454b2 100644 --- a/spec/leader-settings.spec.mjs +++ b/spec/leader-settings.spec.mjs @@ -95,7 +95,7 @@ test('invalid or modified keys remain in capture, and persistence failure retain model = runtime.update(event, model)[0]; assert.equal(model.leaderCaptureActive, true); assert.equal(model.leaderKey.value, '\\'); - assert.ok(model.leaderSettingsIssue); + assert.equal(model.leaderCaptureInvalid, true); } model = runtime.update(key(','), model)[0]; assert.equal(model.leaderCaptureActive, false); @@ -143,6 +143,10 @@ test('startup configuration errors stay visible and a repaired file can be saved let model = selectLeader(runtime, m.settings, runtime.init()[0]); assert.ok(model.leaderSettingsIssue); model = runtime.update(key('enter'), model)[0]; + model = runtime.update(key('up'), model)[0]; + model = runtime.update(key('escape'), model)[0]; + assert.match(m.settings.settingsRows(model).find((row) => row.id === 'leader').description, /\.jimrc is invalid/); + model = runtime.update(key('enter'), model)[0]; model = runtime.update(key('space'), model)[0]; assert.equal(model.leaderKey.value, '\\'); assert.equal(readFileSync(rc, 'utf8'), '{bad'); diff --git a/src/app/leader-settings-row.ts b/src/app/leader-settings-row.ts index 1f913710..bd2b8e3d 100644 --- a/src/app/leader-settings-row.ts +++ b/src/app/leader-settings-row.ts @@ -11,6 +11,7 @@ export const LEADER_SETTING_ROW_ID = 'leader'; export interface LeaderSettingsState { readonly leaderKey?: LeaderKey; readonly leaderCaptureActive?: boolean; + readonly leaderCaptureInvalid?: boolean; readonly leaderSettingsIssue?: JimSettingsIssue; } @@ -30,7 +31,6 @@ const COPY = Object.freeze({ } as const); const ISSUE_COPY = new Map([ - [JimSettingsIssueKinds.Input, COPY.Input], [JimSettingsIssueKinds.Invalid, COPY.Invalid], [JimSettingsIssueKinds.Read, COPY.Read], [JimSettingsIssueKinds.Write, COPY.Write], @@ -60,10 +60,13 @@ function leaderValueLabel(key: LeaderKey, i18n: JeditSettingsI18nState): string } function leaderDescription(state: LeaderSettingsState & { readonly i18n: JeditSettingsI18nState }): string { + if (state.leaderCaptureActive) { + return state.i18n.t(state.leaderCaptureInvalid ? COPY.Input : COPY.Capture); + } const issue = state.leaderSettingsIssue; if (issue != null) { const message = state.i18n.t(ISSUE_COPY.get(issue.kind) ?? COPY.Invalid); return issue.detail == null ? message : `${message} ${issue.detail}`; } - return state.i18n.t(state.leaderCaptureActive ? COPY.Capture : COPY.Description); + return state.i18n.t(COPY.Description); } diff --git a/src/app/workspace/leader-key-capture.ts b/src/app/workspace/leader-key-capture.ts index d9f5a6fa..929581c8 100644 --- a/src/app/workspace/leader-key-capture.ts +++ b/src/app/workspace/leader-key-capture.ts @@ -23,7 +23,7 @@ export function updateLeaderKeyCapture( const value = msg.key === LEADER_INPUT_KEYS.Space ? LEADER_INPUT_KEYS.LiteralSpace : msg.key; const leader = captureKey(msg, value); return leader == null - ? [{ ...model, leaderSettingsIssue: { kind: JimSettingsIssueKinds.Input } }, []] + ? [{ ...model, leaderCaptureInvalid: true }, []] : [saveLeader(model, leader, context), []]; } @@ -32,9 +32,7 @@ function cancelLeaderCapture(msg: KeyMsg, model: WorkspaceModel): WorkspaceModel ...model, settingsOpen: msg.key !== JEDIT_SETTINGS_TOGGLE_KEY, leaderCaptureActive: false, - leaderSettingsIssue: model.leaderSettingsIssue?.kind === JimSettingsIssueKinds.Input - ? undefined - : model.leaderSettingsIssue, + leaderCaptureInvalid: false, }; } @@ -47,6 +45,7 @@ function saveLeader(model: WorkspaceModel, leader: LeaderKey, context: Workspace ...model, leaderKey: issue == null ? leader : model.leaderKey, leaderCaptureActive: false, + leaderCaptureInvalid: false, leaderSettingsIssue: issue, }; } diff --git a/src/app/workspace/model.ts b/src/app/workspace/model.ts index 9c3b5772..58983514 100644 --- a/src/app/workspace/model.ts +++ b/src/app/workspace/model.ts @@ -67,6 +67,7 @@ export interface WorkspaceModel readonly settingsOpen: boolean; readonly leaderKey: LeaderKey; readonly leaderCaptureActive: boolean; + readonly leaderCaptureInvalid?: boolean; readonly leaderSettingsIssue?: JimSettingsIssue; readonly settingsFocusIndex: number; readonly settingsDiagnosticsOpen: boolean; diff --git a/src/app/workspace/settings.ts b/src/app/workspace/settings.ts index b8619e74..eec5eea2 100644 --- a/src/app/workspace/settings.ts +++ b/src/app/workspace/settings.ts @@ -31,6 +31,7 @@ export function settingsRows(model: WorkspaceModel): ReturnType ([{ ...model, leaderCaptureActive: true, + leaderCaptureInvalid: false, }, []]), cycleTheme: (model) => ([{ ...model, diff --git a/src/ports/jim-settings.ts b/src/ports/jim-settings.ts index 0875b7ae..22bbd5eb 100644 --- a/src/ports/jim-settings.ts +++ b/src/ports/jim-settings.ts @@ -1,7 +1,6 @@ import type { LeaderKey } from '../app/leader-key.js'; export const JimSettingsIssueKinds = Object.freeze({ - Input: Symbol('jim.settings.input'), Invalid: Symbol('jim.settings.invalid'), Read: Symbol('jim.settings.read'), Write: Symbol('jim.settings.write'), From 6713598a0bd2a654fba1699c93c798c639ca80a5 Mon Sep 17 00:00:00 2001 From: James Ross Date: Tue, 20 Oct 2026 01:35:29 -0700 Subject: [PATCH 5/8] Harden Leader capture and .jimrc rewrites per review - Move LeaderKey into the domain layer so the settings port depends inward, and enforce its invariants at runtime (factory-only construction, frozen instance with a private value). - Reject Shift-modified capture events; terminals report Shift+A as "a". - Keep mouse scrolling from moving Settings focus off an active capture. - Refuse to rewrite a .jimrc whose numbers JavaScript cannot represent exactly, instead of silently changing unrelated settings. - Restore the existing file mode on the replacement despite the umask. Co-Authored-By: Claude Opus 5.5 --- spec/leader-settings.spec.mjs | 46 ++++++++++++++++++++--- src/adapters/jim-settings.ts | 49 ++++++++++++++++++++++--- src/app/leader-input-keys.ts | 5 +++ src/app/leader-key.ts | 20 ---------- src/app/leader-settings-row.ts | 3 +- src/app/workspace/init.ts | 2 +- src/app/workspace/leader-key-capture.ts | 7 +++- src/app/workspace/model.ts | 2 +- src/app/workspace/mouse.ts | 2 + src/domain/leader-key.ts | 45 +++++++++++++++++++++++ src/ports/jim-settings.ts | 2 +- 11 files changed, 147 insertions(+), 36 deletions(-) create mode 100644 src/app/leader-input-keys.ts delete mode 100644 src/app/leader-key.ts create mode 100644 src/domain/leader-key.ts diff --git a/spec/leader-settings.spec.mjs b/spec/leader-settings.spec.mjs index 7be454b2..ee569170 100644 --- a/spec/leader-settings.spec.mjs +++ b/spec/leader-settings.spec.mjs @@ -1,5 +1,5 @@ import assert from 'node:assert/strict'; -import { mkdtempSync, readFileSync, writeFileSync, existsSync, readdirSync, mkdirSync, rmSync, symlinkSync, lstatSync } from 'node:fs'; +import { mkdtempSync, readFileSync, writeFileSync, existsSync, readdirSync, mkdirSync, rmSync, symlinkSync, lstatSync, chmodSync, statSync } from 'node:fs'; import { tmpdir } from 'node:os'; import path from 'node:path'; import test from 'node:test'; @@ -91,7 +91,7 @@ test('invalid or modified keys remain in capture, and persistence failure retain const runtime = m.runtime.createWorkspaceRuntime(deps); let model = selectLeader(runtime, m.settings, runtime.init()[0]); model = runtime.update(key('enter'), model)[0]; - for (const event of [key('up'), key('enter'), key('a', { ctrl: true }), key('a', { alt: true }), key('\n'), key('ab')]) { + for (const event of [key('up'), key('enter'), key('a', { ctrl: true }), key('a', { alt: true }), key('a', { shift: true }), key('\n'), key('ab')]) { model = runtime.update(event, model)[0]; assert.equal(model.leaderCaptureActive, true); assert.equal(model.leaderKey.value, '\\'); @@ -107,7 +107,7 @@ test('.jimrc preserves unrelated fields and never replaces malformed or unreadab const home = mkdtempSync(path.join(tmpdir(), 'jimrc-')); t.after(() => rmSync(home, { recursive: true, force: true })); const { createJimSettingsPort } = await importDist('adapters', 'jim-settings.js'); - const { LeaderKey } = await importDist('app', 'leader-key.js'); + const { LeaderKey } = await importDist('domain', 'leader-key.js'); const port = createJimSettingsPort(home); const rc = path.join(home, '.jimrc'); writeFileSync(rc, '{"theme":"custom","keybindings":{"other":[1,true],"leader":" "}}\n'); @@ -158,18 +158,54 @@ test('startup configuration errors stay visible and a repaired file can be saved }); test('Leader validation accepts printable single keys and rejects controls and named keys', async () => { - const { LeaderKey } = await importDist('app', 'leader-key.js'); + const { LeaderKey } = await importDist('domain', 'leader-key.js'); for (const value of [' ', '\\', ',', 'é', '🦀']) assert.equal(LeaderKey.from(value)?.value, value); for (const value of ['', 'enter', '\n', '\t', '\u001b', '\u007f', '\u200b', '\ud800', 'ab']) { assert.equal(LeaderKey.from(value), undefined); } + assert.throws(() => Reflect.construct(LeaderKey, [Symbol('forged'), 'enter'])); + assert.throws(() => { LeaderKey.Default.value = 'enter'; }); + assert.equal(LeaderKey.Default.value, '\\'); +}); + +test('.jimrc saves refuse documents whose numbers would change and keep the file mode', async (t) => { + const home = mkdtempSync(path.join(tmpdir(), 'jimrc-lossless-')); + const previousUmask = process.umask(0o077); + t.after(() => { process.umask(previousUmask); rmSync(home, { recursive: true, force: true }); }); + const { createJimSettingsPort } = await importDist('adapters', 'jim-settings.js'); + const { LeaderKey } = await importDist('domain', 'leader-key.js'); + const port = createJimSettingsPort(home); + const rc = path.join(home, '.jimrc'); + for (const source of ['{"id":9007199254740993}', '{"limit":1e400}', '{"ratio":0.10000000000000000001}']) { + writeFileSync(rc, source); + assert.ok(port.saveLeader(LeaderKey.from(',')), source); + assert.equal(readFileSync(rc, 'utf8'), source); + } + writeFileSync(rc, '{"n":1.0,"m":-0,"e":2.5e3,"s":"9007199254740993"}'); + chmodSync(rc, 0o664); + assert.equal(port.saveLeader(LeaderKey.from(',')), undefined); + assert.deepEqual(JSON.parse(readFileSync(rc, 'utf8')), { n: 1, m: 0, e: 2500, s: '9007199254740993', keybindings: { leader: ',' } }); + assert.equal(statSync(rc).mode & 0o777, 0o664); +}); + +test('mouse scrolling cannot move Settings focus away from an active Leader capture', async () => { + const m = await modules(); + const deps = mockRuntime(); + deps.initialModel.i18n = new m.i18n.BijouI18nAdapter(); + deps.initialModel.jeditTheme = m.themes.availableJeditThemes()[0]; + const runtime = m.runtime.createWorkspaceRuntime(deps); + const capturing = runtime.update(key('enter'), selectLeader(runtime, m.settings, runtime.init()[0]))[0]; + const scroll = { type: 'mouse', button: 'none', action: 'scroll-down', col: 4, row: 6, shift: false, alt: false, ctrl: false }; + const scrolled = runtime.update(scroll, capturing)[0]; + assert.equal(scrolled.settingsFocusIndex, capturing.settingsFocusIndex); + assert.equal(scrolled.leaderCaptureActive, true); }); test('saving a dotfile symlink updates its target and preserves the symlink', async (t) => { const home = mkdtempSync(path.join(tmpdir(), 'jimrc-link-')); t.after(() => rmSync(home, { recursive: true, force: true })); const { createJimSettingsPort } = await importDist('adapters', 'jim-settings.js'); - const { LeaderKey } = await importDist('app', 'leader-key.js'); + const { LeaderKey } = await importDist('domain', 'leader-key.js'); const target = path.join(home, 'settings.json'); writeFileSync(target, '{"keybindings":{"leader":","},"theme":"custom"}'); symlinkSync(target, path.join(home, '.jimrc')); diff --git a/src/adapters/jim-settings.ts b/src/adapters/jim-settings.ts index e958c9e2..cbe4180b 100644 --- a/src/adapters/jim-settings.ts +++ b/src/adapters/jim-settings.ts @@ -1,8 +1,8 @@ -import { closeSync, lstatSync, openSync, readFileSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync } from 'node:fs'; +import { closeSync, fchmodSync, lstatSync, openSync, readFileSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync } from 'node:fs'; import { homedir } from 'node:os'; import { join } from 'node:path'; import { randomUUID } from 'node:crypto'; -import { LeaderKey } from '../app/leader-key.js'; +import { LeaderKey } from '../domain/leader-key.js'; import { JimSettingsIssueKinds, type JimSettingsIssue, @@ -14,9 +14,18 @@ const CONFIG_ENCODING = 'utf8'; const CONFIG_INDENT = 2; const CONFIG_CREATE_MODE = 0o600; const MISSING_FILE_CODE = 'ENOENT'; +const PERMISSION_BITS = 0o7777; +// Strings are matched first so digits inside them are skipped; the remaining +// matches are exactly the document's number literals. +const JSON_STRING_OR_NUMBER = /"(?:[^"\\]|\\.)*"|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?/g; +const DECIMAL_PARTS = /^(-?)(\d+)(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/; +const JSON_STRING_QUOTE = '"'; +const DECIMAL_ZERO = '0'; +const LEADING_ZEROS = /^0+/; +const TRAILING_ZERO = '0'; type JsonValue = null | boolean | number | string | JsonValue[] | JsonObject; interface JsonObject { [key: string]: JsonValue } -interface ConfigFile { readonly document: JsonObject; readonly mode: number; readonly path: string } +interface ConfigFile { readonly document: JsonObject; readonly mode: number; readonly path: string; readonly lossless: boolean } // JSON is decoded only at this filesystem boundary. Arbitrary unrelated fields // remain JSON data; they never become application configuration or executable code. @@ -32,6 +41,9 @@ export function createJimSettingsPort(homeDirectory = homedir()): JimSettingsPor saveLeader: (leader) => { const file = readConfig(path); if (isIssue(file)) return file; + // Rewriting a document whose numbers JavaScript cannot represent exactly + // would silently change unrelated settings, so refuse instead. + if (!file.lossless || !(leader instanceof LeaderKey)) return { kind: JimSettingsIssueKinds.Invalid }; const bindings = file.document['keybindings']; const document = { ...file.document, @@ -60,7 +72,7 @@ function readConfig(path: string): ConfigFile | JimSettingsIssue { if (!isObject(value) || !validBindings(value['keybindings'])) { return { kind: JimSettingsIssueKinds.Invalid }; } - return { document: value, mode, path: resolvedPath }; + return { document: value, mode, path: resolvedPath, lossless: numbersSurviveRewrite(source) }; } catch { return { kind: JimSettingsIssueKinds.Invalid }; } @@ -72,7 +84,7 @@ function missingConfig(path: string): ConfigFile | JimSettingsIssue { return { kind: JimSettingsIssueKinds.Read }; } catch (cause) { return cause instanceof Error && 'code' in cause && cause.code === MISSING_FILE_CODE - ? { document: {}, mode: CONFIG_CREATE_MODE, path } + ? { document: {}, mode: CONFIG_CREATE_MODE, path, lossless: true } : { kind: JimSettingsIssueKinds.Read }; } } @@ -92,6 +104,31 @@ function leaderFromDocument(document: JsonObject): LeaderKey | undefined { : undefined; } +function numbersSurviveRewrite(source: string): boolean { + return Array.from(source.matchAll(JSON_STRING_OR_NUMBER), (match) => match[0]) + .filter((token) => !token.startsWith(JSON_STRING_QUOTE)) + .every((token) => { + const written = canonicalDecimal(String(Number(token))); + return written !== undefined && written === canonicalDecimal(token); + }); +} + +// Reduces a decimal literal to significant digits and exponent so that +// formatting differences (1.0, 1e0, 10e-1) compare equal but value changes do not. +function canonicalDecimal(literal: string): string | undefined { + const parts = DECIMAL_PARTS.exec(literal); + if (parts == null) return undefined; + const [, sign = '', whole = '', fraction = '', exponent = DECIMAL_ZERO] = parts; + let digits = `${whole}${fraction}`.replace(LEADING_ZEROS, ''); + let scale = Number(exponent) - fraction.length; + if (digits.length === 0) return DECIMAL_ZERO; + while (digits.endsWith(TRAILING_ZERO)) { + digits = digits.slice(0, -1); + scale += 1; + } + return `${sign}${digits}e${scale}`; +} + function isObject(value: JsonValue | undefined): value is JsonObject { return value != null && typeof value === 'object' && !Array.isArray(value); } @@ -107,6 +144,8 @@ function replaceConfig(path: string, source: string, mode: number): JimSettingsI const descriptor = openSync(temporary, 'wx', mode); created = true; try { + // openSync applies the process umask; restore the existing file's mode. + fchmodSync(descriptor, mode & PERMISSION_BITS); writeFileSync(descriptor, source, CONFIG_ENCODING); } finally { closeSync(descriptor); diff --git a/src/app/leader-input-keys.ts b/src/app/leader-input-keys.ts new file mode 100644 index 00000000..5694609e --- /dev/null +++ b/src/app/leader-input-keys.ts @@ -0,0 +1,5 @@ +export const LEADER_INPUT_KEYS = Object.freeze({ + Space: 'space', + LiteralSpace: ' ', + Escape: 'escape', +} as const); diff --git a/src/app/leader-key.ts b/src/app/leader-key.ts deleted file mode 100644 index 886429a2..00000000 --- a/src/app/leader-key.ts +++ /dev/null @@ -1,20 +0,0 @@ -const PRINTABLE_KEY_LENGTH = 1; -const NON_PRINTING_KEY = /[\p{Cc}\p{Cf}\p{Cs}\p{Zl}\p{Zp}]/u; - -export const LEADER_INPUT_KEYS = Object.freeze({ - Space: 'space', - LiteralSpace: ' ', - Escape: 'escape', -} as const); - -export class LeaderKey { - private constructor(readonly value: string) {} - - static from(value: string): LeaderKey | undefined { - return Array.from(value).length === PRINTABLE_KEY_LENGTH && !NON_PRINTING_KEY.test(value) - ? new LeaderKey(value) - : undefined; - } - - static readonly Default = new LeaderKey('\\'); -} diff --git a/src/app/leader-settings-row.ts b/src/app/leader-settings-row.ts index bd2b8e3d..450a52dc 100644 --- a/src/app/leader-settings-row.ts +++ b/src/app/leader-settings-row.ts @@ -1,4 +1,5 @@ -import { LeaderKey, LEADER_INPUT_KEYS } from './leader-key.js'; +import { LeaderKey } from '../domain/leader-key.js'; +import { LEADER_INPUT_KEYS } from './leader-input-keys.js'; import { JimSettingsIssueKinds, type JimSettingsIssue } from '../ports/jim-settings.js'; import { JEDIT_SETTING_ACTION, diff --git a/src/app/workspace/init.ts b/src/app/workspace/init.ts index 1045a119..2645f553 100644 --- a/src/app/workspace/init.ts +++ b/src/app/workspace/init.ts @@ -25,7 +25,7 @@ import { ViewModes } from "./view-mode.js"; import { TEXT_RUNTIME_PROFILE_ECHO_HOSTED } from "../text-runtime-profile.js"; import { createWorkspaceTextAuthority } from "./workspace-text-authority.js"; import { emptyWorkspaceBufferRegistry } from "./workspace-buffer-registry.js"; -import { LeaderKey } from '../leader-key.js'; +import { LeaderKey } from '../../domain/leader-key.js'; import { initialStartupFileModalState, INITIAL_WORKSPACE_CAUSAL_GUTTER_BASIS, diff --git a/src/app/workspace/leader-key-capture.ts b/src/app/workspace/leader-key-capture.ts index 929581c8..5db4ec93 100644 --- a/src/app/workspace/leader-key-capture.ts +++ b/src/app/workspace/leader-key-capture.ts @@ -1,5 +1,6 @@ import type { Cmd, KeyMsg } from '@flyingrobots/bijou-tui'; -import { LeaderKey, LEADER_INPUT_KEYS } from '../leader-key.js'; +import { LeaderKey } from '../../domain/leader-key.js'; +import { LEADER_INPUT_KEYS } from '../leader-input-keys.js'; import { JEDIT_SETTINGS_TOGGLE_KEY } from '../keybindings.js'; import { JimSettingsIssueKinds } from '../../ports/jim-settings.js'; import type { WorkspaceKeyBindingContext } from './key-binding-context.js'; @@ -59,5 +60,7 @@ function isForceQuit(msg: KeyMsg): boolean { } function captureKey(msg: KeyMsg, value: string): LeaderKey | undefined { - return msg.ctrl || msg.alt ? undefined : LeaderKey.from(value); + // Terminals report Shift+A as `a` with shift set; saving `a` would record a + // different key than the one pressed, so modified keys are rejected. + return msg.ctrl || msg.alt || msg.shift ? undefined : LeaderKey.from(value); } diff --git a/src/app/workspace/model.ts b/src/app/workspace/model.ts index 58983514..d29fbad3 100644 --- a/src/app/workspace/model.ts +++ b/src/app/workspace/model.ts @@ -33,7 +33,7 @@ import type { } from "./command-completion-preview.js"; import type { WorkspaceInlinePanel } from "./workspace-inline-panel.js"; import type { WorkspaceCausalGutterBasis } from "./workspace-causal-gutter-basis.js"; -import type { LeaderKey } from '../leader-key.js'; +import type { LeaderKey } from '../../domain/leader-key.js'; import type { JimSettingsIssue } from '../../ports/jim-settings.js'; export interface WorkspaceModel diff --git a/src/app/workspace/mouse.ts b/src/app/workspace/mouse.ts index 0309bba4..1ef2e9bd 100644 --- a/src/app/workspace/mouse.ts +++ b/src/app/workspace/mouse.ts @@ -163,6 +163,8 @@ function titleMouseLookEnabled(model: WorkspaceModel): boolean { function updateScrollableDrawerFromMouse(model: WorkspaceModel, deltaRows: number): WorkspaceModel | undefined { if (model.settingsOpen) { + // Capture saves the next key to the Leader row, so focus must stay on it. + if (model.leaderCaptureActive) return model; return { ...model, settingsFocusIndex: moveSettingsFocusIndex(model.settingsFocusIndex, deltaRows, settingsRows(model).length), diff --git a/src/domain/leader-key.ts b/src/domain/leader-key.ts new file mode 100644 index 00000000..efbe42c2 --- /dev/null +++ b/src/domain/leader-key.ts @@ -0,0 +1,45 @@ +import { JeditDomainError } from './errors.js'; + +const PRINTABLE_KEY_LENGTH = 1; +const NON_PRINTING_KEY = /[\p{Cc}\p{Cf}\p{Cs}\p{Zl}\p{Zp}]/u; +const DEFAULT_LEADER = '\\'; +const STRING_TYPE = 'string'; +const CONSTRUCTION_TOKEN = Symbol('jedit.leader-key.construction'); + +/** + * Thrown when a LeaderKey is constructed outside its validating factory. + */ +export class InvalidLeaderKeyError extends JeditDomainError {} + +// A single printable key. Construction is only reachable through `from`, which +// validates the value, and instances are frozen with a private field, so the +// invariant holds in emitted JavaScript as well as in the type system. +export class LeaderKey { + readonly #value: string; + + private constructor(token: symbol, value: string) { + if (token !== CONSTRUCTION_TOKEN || !isPrintableKey(value)) { + throw new InvalidLeaderKeyError('LeaderKey must be created with LeaderKey.from'); + } + this.#value = value; + Object.freeze(this); + } + + get value(): string { + return this.#value; + } + + static from(value: string): LeaderKey | undefined { + return isPrintableKey(value) ? new LeaderKey(CONSTRUCTION_TOKEN, value) : undefined; + } + + static readonly Default = new LeaderKey(CONSTRUCTION_TOKEN, DEFAULT_LEADER); +} + +Object.freeze(LeaderKey); + +function isPrintableKey(value: string): boolean { + return typeof value === STRING_TYPE && + Array.from(value).length === PRINTABLE_KEY_LENGTH && + !NON_PRINTING_KEY.test(value); +} diff --git a/src/ports/jim-settings.ts b/src/ports/jim-settings.ts index 22bbd5eb..b29fb320 100644 --- a/src/ports/jim-settings.ts +++ b/src/ports/jim-settings.ts @@ -1,4 +1,4 @@ -import type { LeaderKey } from '../app/leader-key.js'; +import type { LeaderKey } from '../domain/leader-key.js'; export const JimSettingsIssueKinds = Object.freeze({ Invalid: Symbol('jim.settings.invalid'), From 99d7c84183849563719d86f12aebe1c69711d598 Mon Sep 17 00:00:00 2001 From: James Ross Date: Tue, 20 Oct 2026 01:51:26 -0700 Subject: [PATCH 6/8] Let Ctrl+C quit while Leader capture is active The capture dispatch ran before the hard-global keys and the Settings reducer swallowed Ctrl+C. Handle force quit first. Co-Authored-By: Claude Opus 5.5 --- spec/leader-settings.spec.mjs | 1 + src/app/workspace/key-bindings.ts | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/spec/leader-settings.spec.mjs b/spec/leader-settings.spec.mjs index ee569170..9b6229d3 100644 --- a/spec/leader-settings.spec.mjs +++ b/spec/leader-settings.spec.mjs @@ -199,6 +199,7 @@ test('mouse scrolling cannot move Settings focus away from an active Leader capt const scrolled = runtime.update(scroll, capturing)[0]; assert.equal(scrolled.settingsFocusIndex, capturing.settingsFocusIndex); assert.equal(scrolled.leaderCaptureActive, true); + assert.equal(runtime.update(key('c', { ctrl: true }), capturing)[1].length, 1, 'Ctrl+C still quits during capture'); }); test('saving a dotfile symlink updates its target and preserves the symlink', async (t) => { diff --git a/src/app/workspace/key-bindings.ts b/src/app/workspace/key-bindings.ts index 4f453560..bc808715 100644 --- a/src/app/workspace/key-bindings.ts +++ b/src/app/workspace/key-bindings.ts @@ -2,6 +2,7 @@ import type { Cmd, KeyMsg } from '@flyingrobots/bijou-tui'; import { updateCommandLineKey } from './command-line-key-bindings.js'; import { updateFocusedPaneKey } from './focused-pane-key-bindings.js'; import { + updateForceQuitWorkspaceKey, updateGlobalWorkspaceKey, updateHardGlobalWorkspaceKey, updateQuitConfirmationKey, @@ -24,8 +25,7 @@ export function updateFromKey( model: WorkspaceModel, context: WorkspaceKeyBindingContext, ): KeyBindingResult { - const result = updateQuitConfirmationKey(msg, model) - ?? (model.settingsOpen && model.leaderCaptureActive ? updateSettingsKey(msg, model, context) : undefined) + const result = updatePriorityKey(msg, model, context) ?? updateHardGlobalWorkspaceKey(msg, model) ?? updateWorkspaceOverlayKey(msg, model, context) ?? updateGlobalWorkspaceKey(msg, model, context) @@ -36,6 +36,17 @@ export function updateFromKey( ]; } +// Ctrl+C must quit even while Leader capture owns every other key. +function updatePriorityKey( + msg: KeyMsg, + model: WorkspaceModel, + context: WorkspaceKeyBindingContext, +): KeyBindingResult | undefined { + return updateQuitConfirmationKey(msg, model) + ?? updateForceQuitWorkspaceKey(msg, model) + ?? (model.settingsOpen && model.leaderCaptureActive ? updateSettingsKey(msg, model, context) : undefined); +} + function updateWorkspaceOverlayKey( msg: KeyMsg, model: WorkspaceModel, From 6a5e0c1b34d4b6041ac88c7b049482be95c50118 Mon Sep 17 00:00:00 2001 From: James Ross Date: Tue, 20 Oct 2026 02:47:59 -0700 Subject: [PATCH 7/8] Flush the replacement .jimrc before renaming it Co-Authored-By: Claude Opus 5.5 --- src/adapters/jim-settings.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/adapters/jim-settings.ts b/src/adapters/jim-settings.ts index cbe4180b..d4c37a09 100644 --- a/src/adapters/jim-settings.ts +++ b/src/adapters/jim-settings.ts @@ -1,4 +1,4 @@ -import { closeSync, fchmodSync, lstatSync, openSync, readFileSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync } from 'node:fs'; +import { closeSync, fchmodSync, fsyncSync, lstatSync, openSync, readFileSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync } from 'node:fs'; import { homedir } from 'node:os'; import { join } from 'node:path'; import { randomUUID } from 'node:crypto'; @@ -147,6 +147,8 @@ function replaceConfig(path: string, source: string, mode: number): JimSettingsI // openSync applies the process umask; restore the existing file's mode. fchmodSync(descriptor, mode & PERMISSION_BITS); writeFileSync(descriptor, source, CONFIG_ENCODING); + // Durable before the rename, so a crash cannot leave a truncated .jimrc. + fsyncSync(descriptor); } finally { closeSync(descriptor); } From 5d2e36c57aedb5454fa6bf9689b04b2e3dcdff67 Mon Sep 17 00:00:00 2001 From: James Ross Date: Tue, 20 Oct 2026 04:54:14 -0700 Subject: [PATCH 8/8] Fix the German Leader capture hint grammar Co-Authored-By: Claude Opus 5.5 --- src/generated/i18n.ts | 2 +- translations.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/generated/i18n.ts b/src/generated/i18n.ts index d29b9446..6bca3e4f 100644 --- a/src/generated/i18n.ts +++ b/src/generated/i18n.ts @@ -210,7 +210,7 @@ export const fr: TranslationSchema = {"footer":{"mode":{"browse":"parcourir","in export const it: TranslationSchema = {"footer":{"mode":{"browse":"sfoglia","insert":"inserisci","normal":"normale","preview":"anteprima","settings":"impostazioni","files":"file","graft":"graft","history":"cronologia"},"context":{"settings":"impostazioni","graft_empty":"apri un file per ispezionarlo","history_empty":"nessuna prova Echo ancora","history_count":"Prove Echo: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k muovi","enter_change":"enter cambia","f2_close":"f2 chiudi","esc_close":"esc chiudi","j_k_scroll":"j/k scorri","f3_source":"f3 sorgente","f3_preview":"f3 anteprima","text_input":"testo","esc_normal":"esc normale","ctrl_s_save":"ctrl+s salva","ctrl_t_theme":"ctrl+t tema","tab_focus":"tab focus","tab_indent":"tab rientra","ctrl_b_files":"ctrl+b file","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h cronologia","enter_open":"enter apri","backspace_up":"backspace su","ctrl_b_close":"ctrl+b chiudi","enter_jump":"enter vai","r_refresh":"r aggiorna","ctrl_g_close":"ctrl+g chiudi","ctrl_h_close":"ctrl+h chiudi","i_insert":"i inserisci","o_open_line":"o apri riga","cc_line":"cc riga","cw_word":"cw parola","ce_word_end":"ce fine parola","c0_start":"c0 inizio","c_end":"c$ fine","dd_line":"dd riga","dw_word":"dw parola","de_word_end":"de fine parola","d0_start":"d0 inizio","d_end":"d$ fine","yy_line":"yy riga","yw_word":"yw parola","ye_word_end":"ye fine parola","y0_start":"y0 inizio","y_end":"y$ fine","gg_top":"gg cima","esc_cancel":"esc annulla","type_filter":"digita filtro","arrows_select":"↑/↓ seleziona","slash_filter":"/ filtra"},"evidence":{"head":"testa","tick":"tick","anchor":"ancora","coverage":"copertura","complete":"completa","partial":"parziale","unavailable":"non disponibile"}},"history":{"title":"Cronologia Echo","empty":"Nessuna prova Echo ancora","header":"# tick tipo stato prova riepilogo"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime","keybindings":"Scorciatoie da tastiera"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."},"leader":{"label":"Leader","description":"Invio per cambiare; file ~/.jimrc.","capture":"Premi un tasto; Esc annulla.","capturing_value":"Premi un tasto","space":"Spazio","backslash":"Barra inversa","invalid_key":"Un tasto stampabile; Esc annulla.","invalid":".jimrc non valido; correggi e riprova.","read_error":"Impossibile leggere .jimrc.","write_error":"Impossibile salvare .jimrc; riprova.","unavailable":"Questo ambiente non salva le impostazioni.","capture_hint":"Esc annulla; F2 chiude"}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"},"fileExplorer":{"search_label":"Cerca","placeholder":"Filtra file…","empty":"Nessun risultato","preview_label":"Anteprima","preview_loading":"Caricamento anteprima…","preview_binary":"File binario — anteprima non disponibile","preview_unavailable":"Anteprima non disponibile","preview_truncated":"Anteprima troncata"}}; -export const de: TranslationSchema = {"footer":{"mode":{"browse":"durchsuchen","insert":"einfügen","normal":"normal","preview":"vorschau","settings":"einstellungen","files":"dateien","graft":"graft","history":"verlauf"},"context":{"settings":"einstellungen","graft_empty":"datei öffnen zum prüfen","history_empty":"noch keine Echo-Nachweise","history_count":"Echo-Nachweise: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k bewegen","enter_change":"enter ändern","f2_close":"f2 schließen","esc_close":"esc schließen","j_k_scroll":"j/k scrollen","f3_source":"f3 quelltext","f3_preview":"f3 vorschau","text_input":"texteingabe","esc_normal":"esc normal","ctrl_s_save":"ctrl+s speichern","ctrl_t_theme":"ctrl+t theme","tab_focus":"tab fokus","tab_indent":"tab einrücken","ctrl_b_files":"ctrl+b dateien","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h verlauf","enter_open":"enter öffnen","backspace_up":"backspace hoch","ctrl_b_close":"ctrl+b schließen","enter_jump":"enter springen","r_refresh":"r aktualisieren","ctrl_g_close":"ctrl+g schließen","ctrl_h_close":"ctrl+h schließen","i_insert":"i einfügen","o_open_line":"o zeile öffnen","cc_line":"cc zeile","cw_word":"cw wort","ce_word_end":"ce wortende","c0_start":"c0 anfang","c_end":"c$ ende","dd_line":"dd zeile","dw_word":"dw wort","de_word_end":"de wortende","d0_start":"d0 anfang","d_end":"d$ ende","yy_line":"yy zeile","yw_word":"yw wort","ye_word_end":"ye wortende","y0_start":"y0 anfang","y_end":"y$ ende","gg_top":"gg oben","esc_cancel":"esc abbrechen","type_filter":"Filter eingeben","arrows_select":"↑/↓ auswählen","slash_filter":"/ filtern"},"evidence":{"head":"Kopf","tick":"Tick","anchor":"Anker","coverage":"Abdeckung","complete":"vollständig","partial":"teilweise","unavailable":"nicht verfügbar"}},"history":{"title":"Echo-Verlauf","empty":"Noch keine Echo-Nachweise","header":"# tick art status nachweis zusammenfassung"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime","keybindings":"Tastenkürzel"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."},"leader":{"label":"Leader","description":"Enter zum Ändern; Datei ~/.jimrc.","capture":"Taste drücken; Esc bricht ab.","capturing_value":"Taste drücken","space":"Leertaste","backslash":"Rückstrich","invalid_key":"Ein druckbares Zeichen; Esc bricht ab.","invalid":".jimrc ungültig; korrigieren und erneut versuchen.","read_error":".jimrc konnte nicht gelesen werden.","write_error":".jimrc konnte nicht gespeichert werden.","unavailable":"Diese Umgebung speichert keine Einstellungen.","capture_hint":"Esc abbricht; F2 schließt"}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"},"fileExplorer":{"search_label":"Suche","placeholder":"Dateien filtern…","empty":"Keine Treffer","preview_label":"Vorschau","preview_loading":"Vorschau wird geladen…","preview_binary":"Binärdatei — keine Vorschau","preview_unavailable":"Vorschau nicht verfügbar","preview_truncated":"Vorschau gekürzt"}}; +export const de: TranslationSchema = {"footer":{"mode":{"browse":"durchsuchen","insert":"einfügen","normal":"normal","preview":"vorschau","settings":"einstellungen","files":"dateien","graft":"graft","history":"verlauf"},"context":{"settings":"einstellungen","graft_empty":"datei öffnen zum prüfen","history_empty":"noch keine Echo-Nachweise","history_count":"Echo-Nachweise: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k bewegen","enter_change":"enter ändern","f2_close":"f2 schließen","esc_close":"esc schließen","j_k_scroll":"j/k scrollen","f3_source":"f3 quelltext","f3_preview":"f3 vorschau","text_input":"texteingabe","esc_normal":"esc normal","ctrl_s_save":"ctrl+s speichern","ctrl_t_theme":"ctrl+t theme","tab_focus":"tab fokus","tab_indent":"tab einrücken","ctrl_b_files":"ctrl+b dateien","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h verlauf","enter_open":"enter öffnen","backspace_up":"backspace hoch","ctrl_b_close":"ctrl+b schließen","enter_jump":"enter springen","r_refresh":"r aktualisieren","ctrl_g_close":"ctrl+g schließen","ctrl_h_close":"ctrl+h schließen","i_insert":"i einfügen","o_open_line":"o zeile öffnen","cc_line":"cc zeile","cw_word":"cw wort","ce_word_end":"ce wortende","c0_start":"c0 anfang","c_end":"c$ ende","dd_line":"dd zeile","dw_word":"dw wort","de_word_end":"de wortende","d0_start":"d0 anfang","d_end":"d$ ende","yy_line":"yy zeile","yw_word":"yw wort","ye_word_end":"ye wortende","y0_start":"y0 anfang","y_end":"y$ ende","gg_top":"gg oben","esc_cancel":"esc abbrechen","type_filter":"Filter eingeben","arrows_select":"↑/↓ auswählen","slash_filter":"/ filtern"},"evidence":{"head":"Kopf","tick":"Tick","anchor":"Anker","coverage":"Abdeckung","complete":"vollständig","partial":"teilweise","unavailable":"nicht verfügbar"}},"history":{"title":"Echo-Verlauf","empty":"Noch keine Echo-Nachweise","header":"# tick art status nachweis zusammenfassung"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime","keybindings":"Tastenkürzel"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."},"leader":{"label":"Leader","description":"Enter zum Ändern; Datei ~/.jimrc.","capture":"Taste drücken; Esc bricht ab.","capturing_value":"Taste drücken","space":"Leertaste","backslash":"Rückstrich","invalid_key":"Ein druckbares Zeichen; Esc bricht ab.","invalid":".jimrc ungültig; korrigieren und erneut versuchen.","read_error":".jimrc konnte nicht gelesen werden.","write_error":".jimrc konnte nicht gespeichert werden.","unavailable":"Diese Umgebung speichert keine Einstellungen.","capture_hint":"Esc bricht ab; F2 schließt"}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"},"fileExplorer":{"search_label":"Suche","placeholder":"Dateien filtern…","empty":"Keine Treffer","preview_label":"Vorschau","preview_loading":"Vorschau wird geladen…","preview_binary":"Binärdatei — keine Vorschau","preview_unavailable":"Vorschau nicht verfügbar","preview_truncated":"Vorschau gekürzt"}}; export const es: TranslationSchema = {"footer":{"mode":{"browse":"explorar","insert":"insertar","normal":"normal","preview":"vista previa","settings":"ajustes","files":"archivos","graft":"graft","history":"historial"},"context":{"settings":"ajustes","graft_empty":"abre un archivo para inspeccionarlo","history_empty":"sin evidencia Echo todavía","history_count":"Evidencia Echo: {count}"},"command":{"invalid":"Command not recognized; type :help for help","hints":{"tab_accept":"tab accept","enter_run":"enter run","esc_cancel":"esc cancel"},"details":{"edit":"Open a file","write":"Write the current file","quit":"Quit jedit","wq":"Write and quit","ttd":"Observe a causal tick without moving canonical head","strand":"Create, switch, or list copy-on-write strands","braid":"View, preview, or admit braid candidates","why":"Explain the last meaningful command","help":"Show command help"}},"hints":{"j_k_move":"j/k mover","enter_change":"enter cambiar","f2_close":"f2 cerrar","esc_close":"esc cerrar","j_k_scroll":"j/k desplazar","f3_source":"f3 fuente","f3_preview":"f3 vista previa","text_input":"texto","esc_normal":"esc normal","ctrl_s_save":"ctrl+s guardar","ctrl_t_theme":"ctrl+t tema","tab_focus":"tab foco","tab_indent":"tab sangría","ctrl_b_files":"ctrl+b archivos","ctrl_g_graft":"ctrl+g graft","ctrl_h_history":"ctrl+h historial","enter_open":"enter abrir","backspace_up":"backspace arriba","ctrl_b_close":"ctrl+b cerrar","enter_jump":"enter saltar","r_refresh":"r actualizar","ctrl_g_close":"ctrl+g cerrar","ctrl_h_close":"ctrl+h cerrar","i_insert":"i insertar","o_open_line":"o abrir línea","cc_line":"cc línea","cw_word":"cw palabra","ce_word_end":"ce fin palabra","c0_start":"c0 inicio","c_end":"c$ fin","dd_line":"dd línea","dw_word":"dw palabra","de_word_end":"de fin palabra","d0_start":"d0 inicio","d_end":"d$ fin","yy_line":"yy línea","yw_word":"yw palabra","ye_word_end":"ye fin palabra","y0_start":"y0 inicio","y_end":"y$ fin","gg_top":"gg arriba","esc_cancel":"esc cancelar","type_filter":"escribir filtro","arrows_select":"↑/↓ seleccionar","slash_filter":"/ filtrar"},"evidence":{"head":"cabeza","tick":"tick","anchor":"ancla","coverage":"cobertura","complete":"completa","partial":"parcial","unavailable":"no disponible"}},"history":{"title":"Historial Echo","empty":"Sin evidencia Echo todavía","header":"# tick tipo estado evidencia resumen"},"startupFileModal":{"title":"Open file","current_directory":"Current directory","empty":"No files in this directory"},"settings":{"sections":{"appearance":"Appearance","editor":"Editor","runtime":"Runtime","keybindings":"Atajos de teclado"},"rows":{"theme":{"label":"Theme","description":"Switch between installed data-driven themes."},"theme_mode":{"label":"Light/dark","description":"Switch the current theme to its light or dark companion."},"footer":{"label":"Footer","description":"Show mode, focus, and command hints at the bottom edge."},"markdown_preview":{"label":"Markdown preview","description":"Switch the active Markdown buffer between source and preview."},"diagnostics":{"label":"Diagnostics","description":"Inspect Graft, parser, and Colorful runtime wiring."},"line_numbers":{"label":"Line numbers","description":"Switch between absolute and cursor-relative editor line numbers."},"gutter_dimmed":{"label":"Dim gutter","description":"Use the theme dimmed gutter token set."},"causal_gutter_basis":{"label":"Causal markers","description":"Choose the causal basis used by gutter change markers."},"leader":{"label":"Leader","description":"Enter para cambiar; archivo ~/.jimrc.","capture":"Pulsa una tecla; Esc cancela.","capturing_value":"Pulsa una tecla","space":"Espacio","backslash":"Barra inversa","invalid_key":"Una tecla imprimible; Esc cancela.","invalid":".jimrc no válido; corrige y reintenta.","read_error":"No se pudo leer .jimrc.","write_error":"No se pudo guardar .jimrc; reintenta.","unavailable":"Este entorno no guarda ajustes.","capture_hint":"Esc cancela; F2 cierra"}},"values":{"on":"On","off":"Off","theme_mode_dark":"Dark","theme_mode_light":"Light","line_numbers_absolute":"Absolute","line_numbers_relative":"Relative","source":"Source","preview":"Preview","current":"Current","open":"Open","causal_gutter_last_save":"Last save","causal_gutter_import":"Import","causal_gutter_selected_checkpoint":"Selected checkpoint","causal_gutter_selected_tick":"Selected tick"},"toast":{"changed_title":"Settings changed"}},"worldline":{"title":"Worldlines","empty":"No worldlines yet","header":"kind name basis head delta conflict"},"why":{"range_obstructed_title":"Why range obstructed"},"fileExplorer":{"search_label":"Buscar","placeholder":"Filtrar archivos…","empty":"Sin resultados","preview_label":"Vista previa","preview_loading":"Cargando vista previa…","preview_binary":"Archivo binario — sin vista previa","preview_unavailable":"Vista previa no disponible","preview_truncated":"Vista previa truncada"}}; diff --git a/translations.json b/translations.json index 22db04e1..9d887cad 100644 --- a/translations.json +++ b/translations.json @@ -765,7 +765,7 @@ "read_error": ".jimrc konnte nicht gelesen werden.", "write_error": ".jimrc konnte nicht gespeichert werden.", "unavailable": "Diese Umgebung speichert keine Einstellungen.", - "capture_hint": "Esc abbricht; F2 schließt" + "capture_hint": "Esc bricht ab; F2 schließt" } }, "values": {