From cd5700e8ef8d00f2bfb8aa434b771f36d145750c Mon Sep 17 00:00:00 2001 From: James Ross Date: Wed, 14 Oct 2026 10:28:34 -0700 Subject: [PATCH 1/8] Design sticky explorer input and ephemeral path filtering --- .../0161-file-explorer-search-narrow.svg | 1 + .../design/0161-file-explorer-search-wide.svg | 1 + docs/design/0161-file-explorer-search.md | 233 ++++++++++++++++++ 3 files changed, 235 insertions(+) create mode 100644 docs/design/0161-file-explorer-search-narrow.svg create mode 100644 docs/design/0161-file-explorer-search-wide.svg create mode 100644 docs/design/0161-file-explorer-search.md diff --git a/docs/design/0161-file-explorer-search-narrow.svg b/docs/design/0161-file-explorer-search-narrow.svg new file mode 100644 index 00000000..7819f1c3 --- /dev/null +++ b/docs/design/0161-file-explorer-search-narrow.svg @@ -0,0 +1 @@ +Search: rn▌› render.ts renderer.test.ts↑/↓ select · Enter openInput stays pinned \ No newline at end of file diff --git a/docs/design/0161-file-explorer-search-wide.svg b/docs/design/0161-file-explorer-search-wide.svg new file mode 100644 index 00000000..61f1f398 --- /dev/null +++ b/docs/design/0161-file-explorer-search-wide.svg @@ -0,0 +1 @@ +Search: rn▌› render.ts renderer.test.ts↑/↓ select · Enter openInput stays pinned \ No newline at end of file diff --git a/docs/design/0161-file-explorer-search.md b/docs/design/0161-file-explorer-search.md new file mode 100644 index 00000000..cec8062e --- /dev/null +++ b/docs/design/0161-file-explorer-search.md @@ -0,0 +1,233 @@ +--- +title: "DX-0161 - Sticky explorer search" +legend: "DX" +lane: "design" +issue: "https://github.com/flyingrobots/jedit/issues/340" +status: "active" +owners: + - "@flyingrobots" +created: "2026-10-14" +updated: "2026-10-14" +--- + +# DX-0161 - Sticky explorer search + +## Linked Issue + +- https://github.com/flyingrobots/jedit/issues/340 + +## Decision Summary + +Give the Files drawer a sticky, focused search field and live fuzzy filtering +of the directory entries already loaded by the workspace. The query is a +session-only UI projection; there is no persistent index, SQLite, recursive +scan, semantic retrieval, or symbol extraction in this cycle. + +## Sponsored Human + +A keyboard user opens Files and wants to immediately type a partial file name +or relative path, see matching entries, and open one without switching modes. + +## Sponsored Agent + +An agent needs inspectable query/cursor state and one shared filtered-entry +projection for painting, navigation, opening, and pointer hit-testing. + +## Hill + +Ctrl+B opens the drawer with a visible caret in its pinned input. Typing updates +the displayed results immediately. Arrows select a visible match and Enter +opens exactly that match through the existing file-open path. + +## Current Truth + +The Files drawer renders only a scrollable list. Its focused keys perform +navigation and directory actions; there is no input or query state. See +[viewer-drawers.ts#14:5d7b0957a2511ac28b9ec9f47c9e6c432f89deea](https://github.com/flyingrobots/jedit/blob/5d7b0957a2511ac28b9ec9f47c9e6c432f89deea/src/app/workspace/viewer-drawers.ts#L14) +and [file-tree.ts#47:5d7b0957a2511ac28b9ec9f47c9e6c432f89deea](https://github.com/flyingrobots/jedit/blob/5d7b0957a2511ac28b9ec9f47c9e6c432f89deea/src/app/workspace/file-tree.ts#L47). + +Graft exposes per-file outline projections through `GraftSessionPort`, not a +ready-made workspace-wide symbol inventory. This PR is stacked on #339. + +## Problem + +Opening the explorer does not provide a place to type. Printable keys currently +perform actions such as navigation, refresh, quit, or perf toggling. + +## Scope + +- Pinned single-line input, automatic focus, visible caret, horizontal query scrolling. +- Case-insensitive ordered-subsequence matching of loaded names and relative paths. +- Shared filtered-entry identity for render, keys, wheel, click, and file opening. +- Query editing, empty results, directory transition, and focus/close behavior. + +## Non-Goals + +- Recursive workspace inventory, semantic retrieval, Graft symbol scan, SQLite, persistence. +- Leader configuration/dispatch or changes to authoritative Jim/Echo text state. + +## User Experience / Product Shape + +Ctrl+B opens Files and focuses the input. Its row remains pinned while results +scroll. The existing theme input/cursor tokens provide a stable block caret; +no blink timer is introduced. The query scrolls horizontally to keep the caret +visible. Printable keys, including j/k/q/r/h/l, colon, Space, and backtick, +enter text rather than invoke commands while the field owns focus. + +Left/Right/Home/End move the query caret. Backspace/Delete edit it. Ctrl+U +clears it. Up/Down select matching rows while input remains focused; Enter +opens the selected match. Ctrl+R refreshes the current directory. Alt+Up opens +the parent directory. Directory changes clear the query and select the first +entry. Ctrl+B and Esc close; Tab retains existing pane cycling. Closing and +reopening retains the session query and caret. F2 retains Settings ownership. + +A query edit selects the first matching entry. No matches shows a durable +message, and Enter/click cannot open a hidden file. Filtering never mutates +the loaded source list. Empty query shows the ordinary directory list. Parent +navigation is shown for empty query and excluded from search results. + +The search row can be clicked to focus/edit it; result clicks open their actual +filtered entry. Wheel scrolling changes selection only within visible matches. +The list reserves the same fixed input rows in painting and pointer mapping. + +### User Journey + +Open Files → type query → choose with arrows → Enter opens the match. Backspace +or Ctrl+U restores broader results; Esc closes and returns to the normal pane. + +### Wide UI Mockup + +100×32 workspace, existing drawer/theme. The input row and separator stay pinned. + +![Wide search field](0161-file-explorer-search-wide.svg) + +### Narrow UI Mockup + +60×16 workspace. The query scrolls horizontally; the selected row remains visible. + +![Narrow search field](0161-file-explorer-search-narrow.svg) + +### Accessibility Considerations + +Text label, visible caret, visible selected result, and an explicit no-match +message. Keyboard-only editing and opening; focus is never inferred from color +alone. Pointer mapping uses the same geometry as rendering. Existing minimum +terminal geometry remains unchanged. + +## Runtime / API Contract + +`WorkspaceModel.fileSearch` holds input and cursor coordinates. A pure selector +returns visible entries with their source indexes; `selectedIndex` retains the +loaded-entry index so existing file-opening identity is preserved. Query changes +select the first match or no selection. All filtered actions use that selector. +The input handler runs before plain global shortcuts when Files owns focus; +Ctrl+C, close, pane switching, and Settings retain their existing routes. + +## Lower Modes + +Input, query, selected rows, and no-match text remain visible without color. +The caret uses the existing theme token. Small viewports clip safely. No Graft +or optional database adapter is required; unreadable directory errors retain +existing file-system feedback and preserve the prior query/list. + +## Data / State Model + +| Category | Description | +| --- | --- | +| Source of truth | Existing loaded FileEntry list and session query/caret. | +| Derived state | Filtered entries, selection position, text window, list scroll offset. | +| Invalid states | Controls in query; opening an entry excluded by the current query. | +| Reset behavior | Successful directory change clears query; closing preserves it. | +| Persistence | None. A restart begins with an empty query. | +| Causal boundary | Disposable presentation over loaded file-system entries; no authoritative index or text mutation. | + +## Accessibility Posture + +Input focus stays on Files during arrow selection. Opening a file returns focus +to the existing editor/open path. Closing uses the existing pane restoration. +Empty results are readable rather than silent. Search is scoped to the loaded +directory, so it does not claim a complete workspace inventory. + +## Localization / Directionality Posture + +New search label, placeholder, empty-results text, and footer hints use catalog +keys for every authored locale and the generated mirror locale. File paths keep +their literal order; existing drawer directionality remains. Width calculations +use terminal display-cell lengths, and Unicode input boundaries are preserved. + +## Agent Inspectability / Explainability Posture + +Inspect query/caret, selected source index, and the visible-entry selector. +Runtime tests assert the requested path rather than a fabricated open result. +Render tests inspect fixed row positions and cursor tokens. Live PTY exercises +real input decoding and the native-host file-open route. + +## Linked Invariants + +- Source lists and buffers are not optimistically mutated by filtering. +- Derived UI indexes do not become causal or filesystem authority. +- One list projection and geometry serve all result interactions. +- Tests prove real runtime behavior and rendering. + +## Design Alternatives Considered + +An input-only field would not help locate files. A small deterministic fuzzy +filter requires no index or additional service. A repository crawler or symbol +scan needs bounded asynchronous inventory and stale-result rules, so it is +tracked separately in #341. SQLite has no demonstrated need in this slice. + +## Decision + +Ship the pinned input and ephemeral fuzzy filtering over loaded entries. Keep +workspace-wide and symbol search separate. + +## Implementation Slices + +- [ ] Slice 1: Input state, fuzzy projection, and keyboard/open behavior. +- [ ] Slice 2: Sticky input/caret rendering, pointer/wheel mapping, and localized help. +- [ ] Slice 3: Runtime/render/native-host verification and documentation. + +## Tests To Write First + +- [ ] Ctrl+B focuses a visible input; printable shortcut keys type query text. +- [ ] Fuzzy matching, no matches, editing, and Unicode caret movement behave correctly. +- [ ] Arrows/Enter/click open the displayed match rather than a hidden source entry. +- [ ] Header stays pinned while results scroll; mouse/wheel use the filtered list. +- [ ] Close/reopen, Settings, pane switching, refresh, and directory transitions preserve the contract. + +## Acceptance Criteria + +- [ ] Input is pinned, focused, and visibly editable on open. +- [ ] Loaded paths filter live with no persistent cache or index. +- [ ] All result interactions share the same projection and geometry. +- [ ] Empty results cannot open a hidden file. +- [ ] Full repository checks, localized rendering, and live PTY validation pass. + +## Validation Plan + +```bash +npm run check +npm run ci:verify-shards +``` + +## Playback / Witness + +Ctrl+B → type partial name → Down → Enter. Repeat with no matches, clear with +Ctrl+U, edit in the middle, scroll a long list, and click a filtered row. Inspect +at 100×32 and 60×16, including the real native-host file-open path. + +## Risks + +Existing j/k/r/Backspace navigation is intentionally replaced while input owns +focus; footer help must match. Filtering changes displayed indexes, so stale +raw-index pointer calculations can open the wrong file. Unknown-symbol and +workspace-inventory claims must remain outside this feature. + +## Follow-On Debt + +[Issue #341](https://github.com/flyingrobots/jedit/issues/341) owns evaluation of +workspace-wide file, semantic, and Graft symbol search with disposable indexes. + +## Retrospective + +Pending implementation and validation. From 23e98d39d52382b89439c32c75bba787d42ad67b Mon Sep 17 00:00:00 2001 From: James Ross Date: Wed, 14 Oct 2026 10:46:53 -0700 Subject: [PATCH 2/8] Add focused explorer search with sticky input and match highlights --- docs/design/0161-file-explorer-search.md | 9 +- docs/jim-component-ownership.md | 9 + docs/topics/ui/drawers-and-panels.md | 35 +++- spec/file-explorer-search.spec.mjs | 225 +++++++++++++++++++++ spec/workspace-drawer-scroll.spec.mjs | 6 +- spec/workspace-footer.spec.mjs | 4 +- spec/workspace-list-navigation.spec.mjs | 12 +- src/app/workspace/file-explorer-view.ts | 54 +++++ src/app/workspace/file-search-input-key.ts | 78 +++++++ src/app/workspace/file-search.ts | 90 +++++++++ src/app/workspace/file-tree.ts | 40 ++-- src/app/workspace/init.ts | 2 + src/app/workspace/key-bindings.ts | 2 + src/app/workspace/model.ts | 2 + src/app/workspace/mouse.ts | 49 ++++- src/app/workspace/viewer-drawers.ts | 25 +-- src/app/workspace/viewport.ts | 7 + src/generated/i18n.ts | 49 +++-- src/ui/file-search-input.ts | 81 ++++++++ src/ui/file-search-results.ts | 25 +++ src/ui/workspace-chrome.ts | 6 +- translations.json | 180 +++++++++++++++-- 22 files changed, 889 insertions(+), 101 deletions(-) create mode 100644 spec/file-explorer-search.spec.mjs create mode 100644 src/app/workspace/file-explorer-view.ts create mode 100644 src/app/workspace/file-search-input-key.ts create mode 100644 src/app/workspace/file-search.ts create mode 100644 src/ui/file-search-input.ts create mode 100644 src/ui/file-search-results.ts diff --git a/docs/design/0161-file-explorer-search.md b/docs/design/0161-file-explorer-search.md index cec8062e..e1c7d877 100644 --- a/docs/design/0161-file-explorer-search.md +++ b/docs/design/0161-file-explorer-search.md @@ -70,7 +70,10 @@ perform actions such as navigation, refresh, quit, or perf toggling. Ctrl+B opens Files and focuses the input. Its row remains pinned while results scroll. The existing theme input/cursor tokens provide a stable block caret; -no blink timer is introduced. The query scrolls horizontally to keep the caret +no blink timer is introduced. The field uses the theme current-line surface to distinguish it from the drawer. +Matched filename characters use the active-edge accent and bold weight. Name +matches are preferred over directory-prefix matches when the whole query fits +the filename. The query scrolls horizontally to keep the caret visible. Printable keys, including j/k/q/r/h/l, colon, Space, and backtick, enter text rather than invoke commands while the field owns focus. @@ -183,8 +186,8 @@ workspace-wide and symbol search separate. ## Implementation Slices -- [ ] Slice 1: Input state, fuzzy projection, and keyboard/open behavior. -- [ ] Slice 2: Sticky input/caret rendering, pointer/wheel mapping, and localized help. +- [x] Slice 1: Input state, fuzzy projection, and keyboard/open behavior. +- [x] Slice 2: Sticky input/caret rendering, pointer/wheel mapping, and localized help. - [ ] Slice 3: Runtime/render/native-host verification and documentation. ## Tests To Write First diff --git a/docs/jim-component-ownership.md b/docs/jim-component-ownership.md index 300236dd..e60d169d 100644 --- a/docs/jim-component-ownership.md +++ b/docs/jim-component-ownership.md @@ -285,6 +285,15 @@ the meaning of a checkpoint declaration, while Echo owns the Tick and receipt that witness its persistence. Jedit owns a screen cache, while neither that cache nor the screen becomes text authority. +The current Files drawer search is a transient Jedit presentation filter over +already loaded filesystem entries. Its query and caret are session-only shell +state; they are not persisted or replayed as Jim buffer-search state. It creates +no database or authoritative inventory. Filtering preserves source entry +identity, and opening a displayed file continues through the existing native +Echo text-import path. [DX-0161](design/0161-file-explorer-search.md) owns this +bounded implementation; a future workspace or symbol index must remain a +rebuildable projection and declare its observation basis and staleness policy. + > **Intuition to carry forward:** semantic ownership and storage authority are > different powers. Jim decides what command to perform, the text capability > defines what the text operation means, and Echo decides whether their diff --git a/docs/topics/ui/drawers-and-panels.md b/docs/topics/ui/drawers-and-panels.md index 671f4444..d3bad044 100644 --- a/docs/topics/ui/drawers-and-panels.md +++ b/docs/topics/ui/drawers-and-panels.md @@ -23,7 +23,40 @@ enough for detailed explanations. ## Files Drawer -Open the files drawer with `ctrl+b`. +Open the files drawer with `Ctrl+B`. Focus moves into the sticky search input at +its top, with a visible caret. Typing immediately fuzzy-filters the entries in +the currently loaded directory by name or workspace-relative path. Matching is +case-insensitive and accepts characters in order, so `rnt` can match +`render-tree.ts`. It preserves the directory's existing order. The input has a distinct theme +background, and the matched filename characters use the theme accent and bold +weight. + +| Key | Action | +| --- | --- | +| `Up` / `Down` | Select a displayed result. | +| `Enter` | Open the selected file or directory. | +| `Left` / `Right`, `Home` / `End` | Move the query caret. | +| `Backspace` / `Delete` | Edit the query. | +| `Ctrl+U` | Clear the query. | +| `Ctrl+R` | Refresh the current directory. | +| `Alt+Up` | Enter the parent directory. | +| `Ctrl+B` / `Esc` | Close the drawer. | +| `Tab` | Cycle visible panes. | + +Printable keys such as `j`, `k`, `q`, and Space enter the query while the field +has focus. The input stays pinned as results scroll. Clicking the input places +the caret; clicking a result opens that result. No matches displays an explicit +message and cannot open an excluded file. + +Closing and reopening preserves the session query. Entering another directory +clears it. Refreshing preserves it; a failed directory change preserves the +previous query and entries. A restart starts with an empty query. + +This search covers loaded directory entries. Workspace-wide discovery, semantic +retrieval, and Graft symbol search are deferred to +[issue #341](https://github.com/flyingrobots/jedit/issues/341). It creates no +SQLite database or persistent search index. The transient projection boundary +is recorded in [component ownership](../../jim-component-ownership.md). The files drawer is for browsing workspace entries. It should share file-opening behavior with `:edit`: selecting a file opens it, and selecting a directory diff --git a/spec/file-explorer-search.spec.mjs b/spec/file-explorer-search.spec.mjs new file mode 100644 index 00000000..d135feee --- /dev/null +++ b/spec/file-explorer-search.spec.mjs @@ -0,0 +1,225 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { QUIT } from '@flyingrobots/bijou-tui'; +import { importDist, mockRuntime } from './workspace-helpers.mjs'; + +const ENTRIES = [ + { kind: 'parent', name: '..', path: '/repo' }, + { kind: 'dir', name: 'components', path: '/repo/src/components' }, + { kind: 'file', name: 'alpha.txt', path: '/repo/src/alpha.txt' }, + { kind: 'file', name: 'index.ts', path: '/repo/src/index.ts' }, + { kind: 'file', name: 'render-tree.ts', path: '/repo/src/render-tree.ts' }, + { kind: 'file', name: 'readme.md', path: '/repo/src/readme.md' }, +]; +const key = (key, flags = {}) => ({ type: 'key', key, ...flags }); + +async function setup(options = {}) { + const { createWorkspaceRuntime } = await importDist('app', 'workspace', 'runtime.js'); + const { BijouI18nAdapter } = await importDist('adapters', 'bijou-i18n-adapter.js'); + const { availableJeditThemes } = await importDist('ui', 'jedit-themes.js'); + const deps = mockRuntime(options.deps); + deps.initialModel.entries = options.entries ?? ENTRIES; + deps.initialModel.i18n = new BijouI18nAdapter().withLocale(options.locale ?? 'en'); + deps.initialModel.jeditTheme = availableJeditThemes()[0]; + const runtime = createWorkspaceRuntime(deps); + const { openWorkspaceFileEntry } = await importDist('app', 'workspace', 'file-tree.js'); + deps.openEntry = (model, entry) => openWorkspaceFileEntry(model, entry, deps.nowMs, deps); + const initial = { ...runtime.init()[0], cwd: '/repo/src', startupIntroComplete: true }; + const [opened] = runtime.update(key('b', { ctrl: true }), initial); + return { runtime, initial, model: { ...opened, fileDrawerProgress: 1 } }; +} + +function type(runtime, model, input) { + for (const character of input) model = runtime.update(key(character), model)[0]; + return model; +} + +function text(surface) { + return Array.from({ length: surface.height }, (_, y) => ( + Array.from({ length: surface.width }, (_, x) => surface.get(x, y).char).join('') + )).join('\n'); +} + +async function drawer(model, width = 32, height = 12) { + const { renderDrawer } = await importDist('app', 'workspace', 'viewer-drawers.js'); + return renderDrawer('files', model, width, height); +} + +test('Ctrl+B focuses a sticky search input with a visible caret', async () => { + const { model } = await setup(); + assert.equal(model.focusPane, 'files'); + assert.deepEqual(model.fileSearch, { input: '', cursorIndex: 0 }); + const surface = await drawer(model); + assert.match(text(surface).split('\n')[1], /Search: Filter files/); + assert.equal(surface.get(9, 1).bg, model.jeditTheme.cursor.insert.bg); +}); + +test('typing fuzzy-filters loaded relative paths without mutating the source list', async () => { + const { runtime, model } = await setup(); + const next = type(runtime, model, 'SRC/RNT'); + assert.equal(next.fileSearch.input, 'SRC/RNT'); + assert.equal(next.entries, model.entries); + assert.equal(next.selectedIndex, 4); + const rendered = text(await drawer(next)); + assert.match(rendered, /render-tree\.ts/); + assert.doesNotMatch(rendered, /alpha\.txt|index\.ts|readme\.md|components\/|\.\.\//); +}); + +test('plain shortcut characters belong to the focused search field while Ctrl+C still quits', async () => { + const { runtime, model } = await setup(); + let next = model; + for (const character of 'qjkrh:l` ') { + const [updated, commands] = runtime.update(key(character), next); + assert.deepEqual(commands, []); + assert.equal(updated.quitConfirmOpen, false); + assert.equal(updated.perfVisible, false); + next = updated; + } + assert.equal(next.fileSearch.input, 'qjkrh:l` '); + const [, commands] = runtime.update(key('c', { ctrl: true }), next); + assert.equal(await commands[0](), QUIT); +}); + +test('the query caret edits Unicode text and supports clearing without leaving the directory', async () => { + const { runtime, model } = await setup(); + let next = type(runtime, model, 'a🦀界'); + next = runtime.update(key('left'), next)[0]; + next = runtime.update(key('backspace'), next)[0]; + assert.deepEqual(next.fileSearch, { input: 'a界', cursorIndex: 1 }); + next = runtime.update(key('delete'), next)[0]; + assert.deepEqual(next.fileSearch, { input: 'a', cursorIndex: 1 }); + next = runtime.update(key('home'), next)[0]; + next = type(runtime, next, 'é'); + assert.equal(next.fileSearch.input, 'éa'); + next = runtime.update(key('u', { ctrl: true }), next)[0]; + assert.deepEqual(next.fileSearch, { input: '', cursorIndex: 0 }); + assert.equal(next.cwd, '/repo/src'); + next = runtime.update(key('backspace'), next)[0]; + assert.equal(next.cwd, '/repo/src'); +}); + +test('arrows select visible matches and Enter opens the displayed file, including no-match safety', async () => { + const { runtime, model } = await setup(); + let next = type(runtime, model, '.ts'); + assert.equal(next.selectedIndex, 3); + next = runtime.update(key('down'), next)[0]; + assert.equal(next.selectedIndex, 4); + const [opening, commands] = runtime.update(key('enter'), next); + assert.equal(opening.textAuthority.filePath, '/repo/src/render-tree.ts'); + assert.equal(commands.length, 1); + const empty = type(runtime, model, 'not-a-match'); + assert.match(text(await drawer(empty)), /No matches/); + const [unchanged, none] = runtime.update(key('enter'), empty); + assert.equal(unchanged, empty); + assert.deepEqual(none, []); +}); + +test('the input stays pinned while the filtered list scrolls and caret text scrolls horizontally', async () => { + const entries = Array.from({ length: 30 }, (_, i) => ({ kind: 'file', name: `entry-${i}.ts`, path: `/repo/src/entry-${i}.ts` })); + const { runtime, model } = await setup({ entries }); + let next = type(runtime, model, 'en'); + const initialHeader = text(await drawer(next, 28, 10)).split('\n')[1]; + for (let i = 0; i < 22; i += 1) next = runtime.update(key('down'), next)[0]; + const rendered = text(await drawer(next, 28, 10)).split('\n'); + assert.equal(rendered[1], initialHeader); + assert.ok(rendered.some((line) => line.includes('› entry-22.ts'))); + next = runtime.update(key('u', { ctrl: true }), next)[0]; + next = type(runtime, next, 'a'.repeat(40) + 'tail'); + assert.match(text(await drawer(next, 28, 10)).split('\n')[1], /tail/); +}); + +test('filtered result clicks and wheel selection use the pinned-header geometry', async () => { + const { runtime, model } = await setup(); + const filtered = type(runtime, model, '.ts'); + const [clicked] = runtime.update({ type: 'mouse', action: 'press', button: 'left', col: 5, row: 6 }, filtered); + assert.equal(clicked.textAuthority.filePath, '/repo/src/render-tree.ts'); + const [wheeled] = runtime.update({ type: 'mouse', action: 'scroll-down', button: 'none', col: 5, row: 8 }, filtered); + assert.equal(wheeled.selectedIndex, 4); + const [inputClick, commands] = runtime.update({ type: 'mouse', action: 'press', button: 'left', col: 10, row: 3 }, { + ...filtered, focusPane: 'editor', + }); + assert.equal(inputClick.focusPane, 'files'); + assert.deepEqual(commands, []); + assert.equal(inputClick.textAuthority, model.textAuthority); +}); + +test('close/reopen retains the query, directory changes clear it, and failed changes retain it', async () => { + const { runtime, model } = await setup({ deps: { fileSystem: { + loadEntries: (cwd) => { if (cwd === '/repo/src/components') throw new Error('unreadable'); return ENTRIES; }, + dirname: () => '/repo', join: (...parts) => parts.join('/'), resolve: (...parts) => parts.join('/'), + describeDirectoryIssue: () => ({ title: 'Directory failed', message: 'unreadable' }), + } } }); + let next = type(runtime, model, 'rnt'); + next = runtime.update(key('escape'), next)[0]; + assert.equal(next.fileDrawerOpen, false); + next = runtime.update(key('b', { ctrl: true }), next)[0]; + assert.equal(next.focusPane, 'files'); + assert.equal(next.fileSearch.input, 'rnt'); + next = runtime.update(key('up', { alt: true }), next)[0]; + assert.equal(next.cwd, '/repo'); + assert.equal(next.fileSearch.input, ''); + const failed = type(runtime, model, 'components'); + const [after] = runtime.update(key('enter'), failed); + assert.equal(after.cwd, '/repo/src'); + assert.equal(after.fileSearch.input, 'components'); + assert.equal(after.entries, model.entries); +}); + +test('Settings and pane focus take ownership without changing the search query', async () => { + const { runtime, model } = await setup(); + const filtered = type(runtime, model, 'rnt'); + const settings = runtime.update(key('f2'), filtered)[0]; + assert.equal(settings.settingsOpen, true); + assert.equal(type(runtime, settings, 'q').fileSearch.input, 'rnt'); + const unfocused = { ...filtered, focusPane: 'editor' }; + const surface = await drawer(unfocused); + assert.equal(surface.get(12, 1).bg, model.jeditTheme.surface.currentLine.bg); + assert.equal(unfocused.fileSearch.input, 'rnt'); +}); + +test('refresh keeps the query and no-match pointer actions cannot open a hidden file', async () => { + const { runtime, model } = await setup(); + const filtered = type(runtime, model, 'rnt'); + const refreshed = runtime.update(key('r', { ctrl: true }), filtered)[0]; + assert.deepEqual(refreshed.fileSearch, filtered.fileSearch); + const empty = type(runtime, model, 'zzzzzz'); + for (const action of ['press', 'scroll-down', 'scroll-up']) { + const [next, commands] = runtime.update({ type: 'mouse', action, button: 'left', col: 5, row: 6 }, empty); + assert.equal(next.selectedIndex, -1); + assert.equal(next.textAuthority, empty.textAuthority); + assert.deepEqual(commands, []); + } +}); + +test('localized labels fit narrow fields and wide-character clicks use display columns', async () => { + const { fileSearchCursorAtColumn } = await importDist('ui', 'file-search-input.js'); + const { fileSearchInputOptions } = await importDist('app', 'workspace', 'file-explorer-view.js'); + for (const locale of ['en', 'ja', 'ar', 'me']) { + const { runtime, model } = await setup({ locale }); + const next = type(runtime, model, '界x'); + const options = fileSearchInputOptions(next, 28); + const surface = await drawer(next, 30, 8); + assert.match(text(surface).split('\n')[1], /界x/); + assert.ok(!options.label.includes('fileExplorer.')); + const { visibleLineLength } = await importDist('ui', 'fit-line.js'); + const prefix = visibleLineLength(`${options.label}: `); + assert.equal(fileSearchCursorAtColumn(options, prefix + 2), 1); + assert.equal(fileSearchCursorAtColumn(options, prefix + 3), 2); + } +}); + +test('the input has a distinct background and only fuzzy-matched letters receive the accent', async () => { + const { runtime, model } = await setup({ entries: [ENTRIES[4]] }); + const filtered = type(runtime, model, 'rnt'); + const surface = await drawer(filtered); + assert.notEqual(surface.get(2, 1).bg, surface.get(2, 3).bg); + assert.equal(surface.get(2, 1).bg, model.jeditTheme.surface.currentLine.bg); + for (const x of [3, 5, 10]) { + assert.equal(surface.get(x, 3).fg, model.jeditTheme.chrome.activeEdge.fg); + assert.ok(surface.get(x, 3).modifiers.includes('bold')); + } + assert.equal(surface.get(4, 3).fg, model.jeditTheme.surface.drawer.fg); + const cleared = runtime.update(key('u', { ctrl: true }), filtered)[0]; + const plain = await drawer(cleared); + assert.equal(plain.get(3, 3).fg, model.jeditTheme.surface.drawer.fg); +}); diff --git a/spec/workspace-drawer-scroll.spec.mjs b/spec/workspace-drawer-scroll.spec.mjs index 48b0c3ec..d850c9af 100644 --- a/spec/workspace-drawer-scroll.spec.mjs +++ b/spec/workspace-drawer-scroll.spec.mjs @@ -80,10 +80,10 @@ test("a click maps through the scroll offset to the right entry", async () => { rows: model.rows, footerVisible: model.footerVisible, }); - const listHeight = bodyHeight - (viewport.DRAWER_INNER_PAD * 2); + const listHeight = viewport.fileSearchListHeight(bodyHeight); const offset = viewport.listScrollOffset(60, entries.length, listHeight); const firstRow = - viewport.WORKSPACE_BODY_TOP_OFFSET + viewport.DRAWER_INNER_PAD; + viewport.WORKSPACE_BODY_TOP_OFFSET + viewport.FILE_SEARCH_LIST_TOP; const [next] = mouse.updateFromMouse( { @@ -126,7 +126,7 @@ test("clicking a file opens it, not just selects it", async () => { columns: 100, rows: 30, }); - const row = viewport.WORKSPACE_BODY_TOP_OFFSET + viewport.DRAWER_INNER_PAD + 1; + const row = viewport.WORKSPACE_BODY_TOP_OFFSET + viewport.FILE_SEARCH_LIST_TOP + 1; mouse.updateFromMouse( { type: "mouse", button: "left", action: "press", col: 2, row, shift: false, alt: false, ctrl: false }, diff --git a/spec/workspace-footer.spec.mjs b/spec/workspace-footer.spec.mjs index c744ce15..bae15f81 100644 --- a/spec/workspace-footer.spec.mjs +++ b/spec/workspace-footer.spec.mjs @@ -263,7 +263,7 @@ test('workspace footer shows file drawer controls and the selected file path', a graftSelection: undefined, }), [ - 'FILES [j/k move · enter open · backspace up · ctrl+b close · tab focus]', + 'FILES [type filter · arrows select · enter open · ctrl+b close · tab focus]', '/repo/notes/very-long-file-name.md', ], ); @@ -380,7 +380,7 @@ test('workspace footer obtains command hints from i18n', async () => { }); assert.equal(primary.includes(''), true); - assert.equal(primary.includes(''), true); + assert.equal(primary.includes(''), true); assert.equal(primary.includes(''), true); assert.equal(requestedKeys.includes('footer.hints.enter_open'), true); assert.equal(requestedKeys.includes('footer.hints.ctrl_b_close'), true); diff --git a/spec/workspace-list-navigation.spec.mjs b/spec/workspace-list-navigation.spec.mjs index b45d3cfd..bd15ec46 100644 --- a/spec/workspace-list-navigation.spec.mjs +++ b/spec/workspace-list-navigation.spec.mjs @@ -80,7 +80,7 @@ test("the file explorer selection wraps from the last entry to the first", async const model = await fileDrawerModel({ selectedIndex: LAST_INDEX }); const [next] = keyBindings.updateFromKey( - { key: "j" }, + { key: "down" }, model, mockKeyBindingContext(), ); @@ -93,7 +93,7 @@ test("the file explorer selection wraps from the first entry to the last", async const model = await fileDrawerModel({ selectedIndex: FIRST_INDEX }); const [next] = keyBindings.updateFromKey( - { key: "k" }, + { key: "up" }, model, mockKeyBindingContext(), ); @@ -106,7 +106,7 @@ test("an empty file explorer stays put instead of wrapping onto nothing", async const model = await fileDrawerModel({ entries: [], selectedIndex: 0 }); const [next] = keyBindings.updateFromKey( - { key: "j" }, + { key: "down" }, model, mockKeyBindingContext(), ); @@ -180,7 +180,7 @@ test("clicking a file explorer row selects that entry", async () => { importDist("app", "workspace", "viewport.js"), ]); const model = await fileDrawerModel({ columns: 100, rows: 30, fileDrawerProgress: 1 }); - const clickRow = viewport.WORKSPACE_BODY_TOP_OFFSET + viewport.DRAWER_INNER_PAD + 2; + const clickRow = viewport.WORKSPACE_BODY_TOP_OFFSET + viewport.FILE_SEARCH_LIST_TOP + 2; const [next] = mouse.updateFromMouse( { type: "mouse", button: "left", action: "press", col: 2, row: clickRow, shift: false, alt: false, ctrl: false }, @@ -197,7 +197,7 @@ test("clicking outside the file explorer leaves the selection alone", async () = importDist("app", "workspace", "viewport.js"), ]); const model = await fileDrawerModel({ columns: 100, rows: 30, fileDrawerProgress: 1, selectedIndex: 1 }); - const clickRow = viewport.WORKSPACE_BODY_TOP_OFFSET + viewport.DRAWER_INNER_PAD + 2; + const clickRow = viewport.WORKSPACE_BODY_TOP_OFFSET + viewport.FILE_SEARCH_LIST_TOP + 2; const [next] = mouse.updateFromMouse( { type: "mouse", button: "left", action: "press", col: 95, row: clickRow, shift: false, alt: false, ctrl: false }, @@ -214,7 +214,7 @@ test("clicking past the last entry leaves the selection alone", async () => { importDist("app", "workspace", "viewport.js"), ]); const model = await fileDrawerModel({ columns: 100, rows: 30, fileDrawerProgress: 1, selectedIndex: 1 }); - const clickRow = viewport.WORKSPACE_BODY_TOP_OFFSET + viewport.DRAWER_INNER_PAD + 20; + const clickRow = viewport.WORKSPACE_BODY_TOP_OFFSET + viewport.FILE_SEARCH_LIST_TOP + 20; const [next] = mouse.updateFromMouse( { type: "mouse", button: "left", action: "press", col: 2, row: clickRow, shift: false, alt: false, ctrl: false }, diff --git a/src/app/workspace/file-explorer-view.ts b/src/app/workspace/file-explorer-view.ts new file mode 100644 index 00000000..8773b620 --- /dev/null +++ b/src/app/workspace/file-explorer-view.ts @@ -0,0 +1,54 @@ +import { createSurface, stringToSurface, type Surface } from '@flyingrobots/bijou'; +import { fitBlock, formatTreeLine } from '../../ui/workspace-render.js'; +import { highlightFileSearchMatches } from '../../ui/file-search-results.js'; +import { renderFileSearchInput, type FileSearchInputOptions } from '../../ui/file-search-input.js'; +import type { WorkspaceModel } from './model.js'; +import { fileSearchEntries, fileSearchHasFocus, fileSearchState } from './file-search.js'; +import { + DRAWER_INNER_PAD, FILE_SEARCH_ROW, FILE_SEARCH_SEPARATOR_ROW, FILE_SEARCH_LIST_TOP, + fileSearchListHeight, listScrollOffset, +} from './viewport.js'; +import { applyBackground, fillSurface } from './surface-fill.js'; + +const PAD_MULTIPLIER = 2; +const MIN_WIDTH = 1; +const SEARCH_LABEL_KEY = 'fileExplorer.search_label'; +const SEARCH_PLACEHOLDER_KEY = 'fileExplorer.placeholder'; +const SEARCH_EMPTY_KEY = 'fileExplorer.empty'; +const INPUT_RULE = '─'; + +export function fileSearchInputOptions(model: WorkspaceModel, width: number): FileSearchInputOptions { + return { + state: fileSearchState(model), label: model.i18n.t(SEARCH_LABEL_KEY), + placeholder: model.i18n.t(SEARCH_PLACEHOLDER_KEY), focused: fileSearchHasFocus(model), + width, theme: model.jeditTheme, + }; +} + +export function renderFileExplorer(model: WorkspaceModel, width: number, height: number): Surface { + const surface = createSurface(width, height); + fillSurface(surface, model.jeditTheme.surface.drawer); + const innerWidth = Math.max(MIN_WIDTH, width - DRAWER_INNER_PAD * PAD_MULTIPLIER); + surface.blit(renderFileSearchInput(fileSearchInputOptions(model, innerWidth)), DRAWER_INNER_PAD, FILE_SEARCH_ROW); + const rule = stringToSurface(INPUT_RULE.repeat(innerWidth), innerWidth, 1); + applyBackground(rule, model.jeditTheme.surface.drawer); + surface.blit(rule, DRAWER_INNER_PAD, FILE_SEARCH_SEPARATOR_ROW); + paintFileSearchResults(surface, model, innerWidth, fileSearchListHeight(height)); + return surface; +} + +function paintFileSearchResults(surface: Surface, model: WorkspaceModel, width: number, height: number): void { + if (height <= 0) return; + const entries = fileSearchEntries(model); + const selected = entries.findIndex(({ index }) => index === model.selectedIndex); + const offset = listScrollOffset(selected, entries.length, height); + const lines = entries.length === 0 + ? [model.i18n.t(SEARCH_EMPTY_KEY)] + : entries.slice(offset, offset + height).map(({ entry, index }) => formatTreeLine(entry, { + selected: index === model.selectedIndex, + })); + const content = stringToSurface(fitBlock(lines.join('\n'), width, height), width, height); + applyBackground(content, model.jeditTheme.surface.drawer); + highlightFileSearchMatches(content, entries.slice(offset, offset + height), model.jeditTheme); + surface.blit(content, DRAWER_INNER_PAD, FILE_SEARCH_LIST_TOP); +} diff --git a/src/app/workspace/file-search-input-key.ts b/src/app/workspace/file-search-input-key.ts new file mode 100644 index 00000000..a97f04f9 --- /dev/null +++ b/src/app/workspace/file-search-input-key.ts @@ -0,0 +1,78 @@ +import type { Cmd, KeyMsg } from '@flyingrobots/bijou-tui'; +import { fileSearchHasFocus, fileSearchState, fileSearchWithInput, EMPTY_FILE_SEARCH, type FileSearchState } from './file-search.js'; +import type { WorkspaceModel } from './model.js'; +import type { WorkspaceMsg } from './msg.js'; + +const KEYS = Object.freeze({ + Left: 'left', Right: 'right', Home: 'home', End: 'end', Backspace: 'backspace', Delete: 'delete', + Space: 'space', Clear: 'u', +}); +const LITERAL_SPACE = ' '; +const SINGLE_KEY_LENGTH = 1; +const NON_PRINTABLE = /[\p{Cc}\p{Cf}\p{Cs}\p{Zl}\p{Zp}]/u; +const FORWARD_STEP = 1; +const BACKWARD_STEP = -1; +type Edit = (state: FileSearchState) => FileSearchState; +const EDITS = new Map([ + [KEYS.Left, (state) => moveCursor(state, BACKWARD_STEP)], + [KEYS.Right, (state) => moveCursor(state, FORWARD_STEP)], + [KEYS.Home, (state) => ({ ...state, cursorIndex: 0 })], + [KEYS.End, (state) => ({ ...state, cursorIndex: Array.from(state.input).length })], + [KEYS.Backspace, deleteBeforeCursor], + [KEYS.Delete, deleteAtCursor], +]); + +export function updateFileSearchInputKey( + msg: KeyMsg, + model: WorkspaceModel, +): [WorkspaceModel, Cmd[]] | undefined { + if (!fileSearchHasFocus(model)) return undefined; + const state = fileSearchState(model); + const next = editFromKey(msg, state); + if (next == null) return undefined; + if (next.input !== state.input) return [fileSearchWithInput(model, next), []]; + return [next.cursorIndex === state.cursorIndex ? model : { ...model, fileSearch: next }, []]; +} + +function editFromKey(msg: KeyMsg, state: FileSearchState): FileSearchState | undefined { + if (isClearKey(msg)) return EMPTY_FILE_SEARCH; + if (msg.ctrl || msg.alt) return undefined; + const edit = EDITS.get(msg.key); + if (edit != null) return edit(state); + const input = printableInput(msg); + return input == null ? undefined : insertAtCursor(state, input); +} + +function isClearKey(msg: KeyMsg): boolean { + return msg.ctrl === true && !msg.alt && msg.key === KEYS.Clear; +} + +function printableInput(msg: KeyMsg): string | undefined { + const value = msg.key === KEYS.Space ? LITERAL_SPACE : msg.key; + return Array.from(value).length === SINGLE_KEY_LENGTH && !NON_PRINTABLE.test(value) + ? (msg.shift ? value.toUpperCase() : value) + : undefined; +} + +function moveCursor(state: FileSearchState, delta: number): FileSearchState { + return { ...state, cursorIndex: Math.max(0, Math.min(Array.from(state.input).length, state.cursorIndex + delta)) }; +} + +function insertAtCursor(state: FileSearchState, input: string): FileSearchState { + const characters = Array.from(state.input); + characters.splice(state.cursorIndex, 0, ...Array.from(input)); + return { input: characters.join(''), cursorIndex: state.cursorIndex + Array.from(input).length }; +} + +function deleteBeforeCursor(state: FileSearchState): FileSearchState { + if (state.cursorIndex <= 0) return state; + const characters = Array.from(state.input); + characters.splice(state.cursorIndex - FORWARD_STEP, FORWARD_STEP); + return { input: characters.join(''), cursorIndex: state.cursorIndex - FORWARD_STEP }; +} + +function deleteAtCursor(state: FileSearchState): FileSearchState { + const characters = Array.from(state.input); + characters.splice(state.cursorIndex, FORWARD_STEP); + return { input: characters.join(''), cursorIndex: state.cursorIndex }; +} diff --git a/src/app/workspace/file-search.ts b/src/app/workspace/file-search.ts new file mode 100644 index 00000000..9d4f3197 --- /dev/null +++ b/src/app/workspace/file-search.ts @@ -0,0 +1,90 @@ +import type { FileEntry } from '../../ports/file-system.js'; +import { FileEntryKinds } from '../../ports/file-system.js'; +import { FocusPanes } from '../../ui/panel-focus.js'; +import { scrollIndexByRows } from '../../ui/mouse-scroll.js'; +import { wrapIndex } from '../list-index.js'; +import type { WorkspaceModel } from './model.js'; + +export interface FileSearchState { + readonly input: string; + readonly cursorIndex: number; +} + +export interface FileSearchEntry { + readonly entry: FileEntry; + readonly index: number; + readonly matchIndexes: readonly number[]; +} + +export const EMPTY_FILE_SEARCH: FileSearchState = Object.freeze({ input: '', cursorIndex: 0 }); +const PATH_SEPARATOR = '/'; +const NO_SELECTION = -1; + +export function fileSearchState(model: Pick): FileSearchState { + return model.fileSearch ?? EMPTY_FILE_SEARCH; +} + +export function fileSearchHasFocus(model: WorkspaceModel): boolean { + return model.fileDrawerOpen && model.focusPane === FocusPanes.Files && + !model.settingsOpen && !model.quitConfirmOpen && !model.startupFileModalOpen && !model.commandLine?.active; +} + +export function fileSearchEntries(model: Pick): readonly FileSearchEntry[] { + const query = fileSearchState(model).input.toLowerCase(); + return model.entries.flatMap((entry, index) => { + if (query.length === 0) return [{ entry, index, matchIndexes: [] }]; + if (entry.kind === FileEntryKinds.Parent) return []; + const path = fuzzyPathMatches(entry.name, query) != null ? entry.name : relativeFilePath(model.workspaceRoot, entry); + const matches = fuzzyPathMatches(path, query); + if (matches == null) return []; + const nameStart = Array.from(path).length - Array.from(entry.name).length; + return [{ entry, index, matchIndexes: matches.filter((offset) => offset >= nameStart).map((offset) => offset - nameStart) }]; + }); +} + +function relativeFilePath(root: string, entry: FileEntry): string { + const prefix = root?.endsWith(PATH_SEPARATOR) ? root : `${root ?? ''}${PATH_SEPARATOR}`; + const path = entry.path ?? entry.name; + return path.startsWith(prefix) ? path.slice(prefix.length) : path; +} + +function fuzzyPathMatches(path: string, query: string): readonly number[] | undefined { + const candidate = Array.from(path).flatMap((character, index) => ( + Array.from(character.toLowerCase()).map((value) => ({ value, index })) + )); + const matches: number[] = []; + let offset = 0; + for (const character of query) { + while (offset < candidate.length && candidate[offset]?.value !== character) offset += 1; + const matched = candidate[offset]; + if (matched == null) return undefined; + matches.push(matched.index); + offset += 1; + } + return matches; +} + +export function selectedFileSearchEntry(model: WorkspaceModel): FileEntry | undefined { + return fileSearchEntries(model).find(({ index }) => index === model.selectedIndex)?.entry; +} + +export function fileSearchWithInput(model: WorkspaceModel, search: FileSearchState): WorkspaceModel { + const next = { ...model, fileSearch: search }; + return { ...next, selectedIndex: fileSearchEntries(next)[0]?.index ?? NO_SELECTION }; +} + +export function moveFileSearchSelection(model: WorkspaceModel, delta: number): WorkspaceModel { + const entries = fileSearchEntries(model); + if (entries.length === 0) return model; + const position = entries.findIndex(({ index }) => index === model.selectedIndex); + const next = entries[wrapIndex(position + delta, entries.length)]; + return next == null ? model : { ...model, selectedIndex: next.index }; +} + +export function scrollFileSearchSelection(model: WorkspaceModel, delta: number): WorkspaceModel { + const entries = fileSearchEntries(model); + if (entries.length === 0) return model; + const position = entries.findIndex(({ index }) => index === model.selectedIndex); + const next = entries[scrollIndexByRows(position, entries.length, delta)]; + return next == null ? model : { ...model, selectedIndex: next.index }; +} diff --git a/src/app/workspace/file-tree.ts b/src/app/workspace/file-tree.ts index bb193929..139df40d 100644 --- a/src/app/workspace/file-tree.ts +++ b/src/app/workspace/file-tree.ts @@ -10,7 +10,6 @@ import type { EditorFilePort } from '../../ports/editor-file.js'; import type { GraftSessionPort } from '../../ports/graft-session.js'; import type { SourceHighlighter } from '../../ports/source-highlighter.js'; import { createNotificationTickCmd, pushErrorToast } from '../../ui/feedback.js'; -import { wrapIndex } from './viewport.js'; import { withFocusPane } from './focus.js'; import { isWorkspaceMarkdownFile } from './editor-session.js'; import { ViewModes } from './view-mode.js'; @@ -20,7 +19,9 @@ import { WorkspaceMessageTypes, type WorkspaceMsg } from './msg.js'; import type { ProductionTextSession } from './production-text-session.js'; import { createWorkspaceTextOpenCmd, defaultWorkspaceTextAperture } from './workspace-text-commands.js'; import { pendingWorkspaceTextOpen } from './workspace-text-authority.js'; +import { EMPTY_FILE_SEARCH, fileSearchState, fileSearchWithInput, moveFileSearchSelection, selectedFileSearchEntry } from './file-search.js'; import { + WorkspaceKeys, isWorkspaceBackKey, isWorkspaceDownKey, isWorkspaceOpenKey, @@ -35,6 +36,8 @@ import { } from './workspace-buffer-registry.js'; export const FILE_TREE_META_MIN = 0; +const FILE_TREE_FORWARD_STEP = 1; +const FILE_TREE_BACKWARD_STEP = -1; export interface UpdateTreeFromKeyDeps { readonly fileSystem: FileSystemPort; @@ -50,6 +53,8 @@ export function updateTreeFromKey( nowMs: () => number, deps: UpdateTreeFromKeyDeps, ): [WorkspaceModel, Cmd[]] { + const modified = updateModifiedTreeKey(msg, model, nowMs, deps.fileSystem); + if (modified != null) return modified; const navigation = updateTreeNavigationFromKey(msg, model); if (navigation != null) { return [navigation, []]; @@ -66,18 +71,29 @@ export function updateTreeFromKey( : [model, []]; } +function updateModifiedTreeKey( + msg: KeyMsg, + model: WorkspaceModel, + nowMs: () => number, + fileSystem: FileSystemPort, +): [WorkspaceModel, Cmd[]] | undefined { + if (msg.alt && msg.key === WorkspaceKeys.ArrowUp) { + return openParentDirectory(model, nowMs, fileSystem); + } + if (msg.ctrl || msg.alt) { + return msg.ctrl && msg.key === WorkspaceKeys.R + ? changeDirectory(model, model.cwd, DIRECTORY_ACTION_REFRESH, nowMs, fileSystem) + : [model, []]; + } + return undefined; +} + function updateTreeNavigationFromKey(msg: KeyMsg, model: WorkspaceModel): WorkspaceModel | undefined { if (isWorkspaceDownKey(msg)) { - return { - ...model, - selectedIndex: wrapIndex(model.selectedIndex + 1, model.entries.length), - }; + return moveFileSearchSelection(model, FILE_TREE_FORWARD_STEP); } if (isWorkspaceUpKey(msg)) { - return { - ...model, - selectedIndex: wrapIndex(model.selectedIndex - 1, model.entries.length), - }; + return moveFileSearchSelection(model, FILE_TREE_BACKWARD_STEP); } return undefined; } @@ -98,7 +114,7 @@ function openSelectedTreeEntry( nowMs: () => number, deps: UpdateTreeFromKeyDeps, ): [WorkspaceModel, Cmd[]] { - const entry = model.entries[model.selectedIndex]; + const entry = selectedFileSearchEntry(model); if (entry == null) { return [model, []]; } @@ -152,12 +168,12 @@ function openDirectory( cwd: string, fileSystem: FileSystemPort, ): WorkspaceModel { - return { + return fileSearchWithInput({ ...model, cwd, entries: fileSystem.loadEntries(cwd), selectedIndex: 0, - }; + }, cwd === model.cwd ? fileSearchState(model) : EMPTY_FILE_SEARCH); } function changeDirectory( diff --git a/src/app/workspace/init.ts b/src/app/workspace/init.ts index 51a13a6d..58bebcaf 100644 --- a/src/app/workspace/init.ts +++ b/src/app/workspace/init.ts @@ -9,6 +9,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 { EMPTY_FILE_SEARCH } from './file-search.js'; import { initialStartupFileModalState, INITIAL_WORKSPACE_CAUSAL_GUTTER_BASIS, @@ -42,6 +43,7 @@ export function createInitialModel( cwd, entries, selectedIndex: 0, + fileSearch: EMPTY_FILE_SEARCH, editor: undefined, ...initialBufferRegistryState(), textRuntimeProfile, diff --git a/src/app/workspace/key-bindings.ts b/src/app/workspace/key-bindings.ts index 7644d04b..a5440d67 100644 --- a/src/app/workspace/key-bindings.ts +++ b/src/app/workspace/key-bindings.ts @@ -13,6 +13,7 @@ import { clearWorkspaceInlinePanelAfterKey } from './workspace-inline-panel.js'; import { updateSettingsKey } from './settings-key-bindings.js'; import { updateStartupFileModalKey } from './startup-file-modal-key-bindings.js'; import { updateTitleScreenKey } from './title-screen-key-bindings.js'; +import { updateFileSearchInputKey } from './file-search-input-key.js'; export type { UpdateFromKeyDeps } from './key-binding-context.js'; @@ -24,6 +25,7 @@ export function updateFromKey( context: WorkspaceKeyBindingContext, ): KeyBindingResult { const result = updateQuitConfirmationKey(msg, model) + ?? updateFileSearchInputKey(msg, model) ?? updateHardGlobalWorkspaceKey(msg, model) ?? updateWorkspaceOverlayKey(msg, model, context) ?? updateGlobalWorkspaceKey(msg, model, context) diff --git a/src/app/workspace/model.ts b/src/app/workspace/model.ts index 618bf281..347c6687 100644 --- a/src/app/workspace/model.ts +++ b/src/app/workspace/model.ts @@ -21,6 +21,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 { FileSearchState } from './file-search.js'; export interface WorkspaceModel extends StartupFileModalState, @@ -30,6 +31,7 @@ export interface WorkspaceModel readonly cwd: string; readonly entries: readonly FileEntry[]; readonly selectedIndex: number; + readonly fileSearch: FileSearchState; readonly editor?: EditorState; readonly buffers: WorkspaceBufferRegistry; readonly activeBufferId?: string; diff --git a/src/app/workspace/mouse.ts b/src/app/workspace/mouse.ts index 3c0e507d..10a456d0 100644 --- a/src/app/workspace/mouse.ts +++ b/src/app/workspace/mouse.ts @@ -17,9 +17,15 @@ import { listScrollOffset, workspaceBodyHeight, WORKSPACE_BODY_TOP_OFFSET, + FILE_SEARCH_ROW, + FILE_SEARCH_LIST_TOP, + fileSearchListHeight, } from './viewport.js'; import { FocusPanes } from '../../ui/panel-focus.js'; import { beginWorkspaceSourceHighlightRefresh } from './workspace-source-highlight.js'; +import { fileSearchEntries, fileSearchState, scrollFileSearchSelection, selectedFileSearchEntry } from './file-search.js'; +import { fileSearchCursorAtColumn } from '../../ui/file-search-input.js'; +import { fileSearchInputOptions } from './file-explorer-view.js'; const DRAWER_PAD_MULTIPLIER = 2; const MOUSE_PRESS = 'press'; @@ -43,6 +49,8 @@ export function updateFromMouse( sourceHighlighter: SourceHighlighter, opener?: WorkspaceEntryOpener, ): [WorkspaceModel, Cmd[]] { + const input = updateFileSearchFromClick(msg, model); + if (input != null) return [input, []]; const clicked = updateFileDrawerFromClick(msg, model); if (clicked != null) { return openClickedEntry(clicked, opener); @@ -79,7 +87,7 @@ function fileDrawerEntryIndexAt( msg: MouseMsg, model: WorkspaceModel, ): number | undefined { - if (!model.fileDrawerOpen) { + if (!fileDrawerPointerEnabled(model)) { return undefined; } const drawer = resolveWorkspaceLayout( @@ -93,27 +101,29 @@ function fileDrawerEntryIndexAt( if (!withinDrawer) { return undefined; } - const row = msg.row - WORKSPACE_BODY_TOP_OFFSET - DRAWER_INNER_PAD; - const listHeight = workspaceBodyHeight({ + const row = msg.row - WORKSPACE_BODY_TOP_OFFSET - FILE_SEARCH_LIST_TOP; + const listHeight = fileSearchListHeight(workspaceBodyHeight({ rows: model.rows, footerVisible: model.footerVisible, - }) - (DRAWER_INNER_PAD * DRAWER_PAD_MULTIPLIER); + })); if (row < 0 || row >= listHeight) { return undefined; } + const entries = fileSearchEntries(model); + const position = entries.findIndex(({ index }) => index === model.selectedIndex); const index = row + listScrollOffset( - model.selectedIndex, - model.entries.length, + position, + entries.length, listHeight, ); - return index < model.entries.length ? index : undefined; + return entries[index]?.index; } function openClickedEntry( model: WorkspaceModel, opener: WorkspaceEntryOpener | undefined, ): [WorkspaceModel, Cmd[]] { - const entry = model.entries[model.selectedIndex]; + const entry = selectedFileSearchEntry(model); if (opener == null || entry == null) { return [model, []]; } @@ -128,7 +138,7 @@ function updateScrollableDrawerFromMouse(model: WorkspaceModel, deltaRows: numbe }; } if (model.focusPane === FocusPanes.Files && model.fileDrawerOpen) { - return { ...model, selectedIndex: scrollIndexByRows(model.selectedIndex, model.entries.length, deltaRows) }; + return scrollFileSearchSelection(model, deltaRows); } if (model.focusPane === FocusPanes.Graft && model.graftDrawerOpen) { return { @@ -139,6 +149,27 @@ function updateScrollableDrawerFromMouse(model: WorkspaceModel, deltaRows: numbe return undefined; } +function fileDrawerPointerEnabled(model: WorkspaceModel): boolean { + return model.fileDrawerOpen && !model.settingsOpen && !model.quitConfirmOpen && + !model.startupFileModalOpen && !model.commandLine?.active; +} + +function updateFileSearchFromClick(msg: MouseMsg, model: WorkspaceModel): WorkspaceModel | undefined { + if (msg.action !== MOUSE_PRESS || msg.button !== MOUSE_BUTTON_LEFT || !fileDrawerPointerEnabled(model)) return undefined; + if (msg.row !== WORKSPACE_BODY_TOP_OFFSET + FILE_SEARCH_ROW) return undefined; + const layout = resolveWorkspaceLayout(model.columns, model.fileDrawerProgress, model.graftDrawerProgress).fileDrawer; + const innerWidth = layout.width - DRAWER_INNER_PAD * DRAWER_PAD_MULTIPLIER; + const column = msg.col - layout.x - DRAWER_INNER_PAD; + if (column < 0 || column >= innerWidth) return undefined; + return { + ...model, focusPane: FocusPanes.Files, + fileSearch: { + ...fileSearchState(model), + cursorIndex: fileSearchCursorAtColumn(fileSearchInputOptions(model, innerWidth), column), + }, + }; +} + function updateEditorFromMouse( model: WorkspaceModel, deltaRows: number, diff --git a/src/app/workspace/viewer-drawers.ts b/src/app/workspace/viewer-drawers.ts index 5483dc56..57503612 100644 --- a/src/app/workspace/viewer-drawers.ts +++ b/src/app/workspace/viewer-drawers.ts @@ -3,11 +3,11 @@ import { DrawerKinds, type DrawerKind } from '../../ui/drawer-layout.js'; import { renderGraftDrawerLines } from '../../ui/graft-drawer.js'; import { fitBlock, - formatTreeLine, } from '../../ui/workspace-render.js'; import type { WorkspaceModel } from './model.js'; -import { DRAWER_INNER_PAD, listScrollOffset } from './viewport.js'; +import { DRAWER_INNER_PAD } from './viewport.js'; import { applyBackground, fillSurface } from './surface-fill.js'; +import { renderFileExplorer } from './file-explorer-view.js'; const MIN_VIEWPORT_DIMENSION = 1; const DRAWER_PAD_MULTIPLIER = 2; @@ -16,26 +16,7 @@ export function renderDrawer(kind: DrawerKind, model: WorkspaceModel, width: num if (kind === DrawerKinds.Graft) { return renderGraftDrawer(model, width, height); } - const surface = createSurface(width, height); - fillSurface(surface, model.jeditTheme.surface.drawer); - - const listWidth = Math.max(MIN_VIEWPORT_DIMENSION, width - (DRAWER_INNER_PAD * DRAWER_PAD_MULTIPLIER)); - const listHeight = Math.max(MIN_VIEWPORT_DIMENSION, height - (DRAWER_INNER_PAD * DRAWER_PAD_MULTIPLIER)); - const offset = listScrollOffset( - model.selectedIndex, - model.entries.length, - listHeight, - ); - const lines = model.entries - .slice(offset, offset + listHeight) - .map((entry, index) => formatTreeLine(entry, { - selected: index + offset === model.selectedIndex, - })); - const content = stringToSurface(fitBlock(lines.join('\n'), listWidth, listHeight), listWidth, listHeight); - applyBackground(content, model.jeditTheme.surface.drawer); - surface.blit(content, DRAWER_INNER_PAD, DRAWER_INNER_PAD); - - return surface; + return renderFileExplorer(model, width, height); } function renderGraftDrawer(model: WorkspaceModel, width: number, height: number): Surface { diff --git a/src/app/workspace/viewport.ts b/src/app/workspace/viewport.ts index b69fc0ea..892b61a0 100644 --- a/src/app/workspace/viewport.ts +++ b/src/app/workspace/viewport.ts @@ -8,6 +8,13 @@ export const MIN_ROWS = 12; export const VIEWER_LEFT_PAD = 4; export const VIEWER_TOP_PAD = 1; export const DRAWER_INNER_PAD = 1; +export const FILE_SEARCH_ROW = DRAWER_INNER_PAD; +export const FILE_SEARCH_SEPARATOR_ROW = FILE_SEARCH_ROW + 1; +export const FILE_SEARCH_LIST_TOP = FILE_SEARCH_SEPARATOR_ROW + 1; + +export function fileSearchListHeight(height: number): number { + return Math.max(0, height - FILE_SEARCH_LIST_TOP - DRAWER_INNER_PAD); +} // Rows above the workspace body: the title row and its rule. Shared so pointer // hit-testing and the renderer cannot disagree about where the body starts. export const WORKSPACE_BODY_TOP_OFFSET = 2; diff --git a/src/generated/i18n.ts b/src/generated/i18n.ts index 5cbbfc1b..89919743 100644 --- a/src/generated/i18n.ts +++ b/src/generated/i18n.ts @@ -78,6 +78,8 @@ export type TranslationSchema = { readonly "y_end": string; readonly "gg_top": string; readonly "esc_cancel": string; + readonly "type_filter": string; + readonly "arrows_select": string; }; readonly "evidence": { readonly "head": string; @@ -167,6 +169,11 @@ export type TranslationSchema = { readonly "why": { readonly "range_obstructed_title": string; }; + readonly "fileExplorer": { + readonly "search_label": string; + readonly "placeholder": string; + readonly "empty": string; + }; }; export type LocaleDirection = 'ltr' | 'rtl'; @@ -176,47 +183,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_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_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","type_filter":"type filter","arrows_select":"↑/↓ select"},"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"},"fileExplorer":{"search_label":"Search","placeholder":"Filter files…","empty":"No matches"}}; -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_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_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","type_filter":"saisir le filtre","arrows_select":"↑/↓ sélectionner"},"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"},"fileExplorer":{"search_label":"Recherche","placeholder":"Filtrer les fichiers…","empty":"Aucun résultat"}}; -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"},"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_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"},"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"},"fileExplorer":{"search_label":"Cerca","placeholder":"Filtra file…","empty":"Nessun risultato"}}; -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"},"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_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"},"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"},"fileExplorer":{"search_label":"Suche","placeholder":"Dateien filtern…","empty":"Keine Treffer"}}; -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"},"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_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"},"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"},"fileExplorer":{"search_label":"Buscar","placeholder":"Filtrar archivos…","empty":"Sin resultados"}}; -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_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_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 취소","type_filter":"필터 입력","arrows_select":"↑/↓ 선택"},"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"},"fileExplorer":{"search_label":"검색","placeholder":"파일 필터…","empty":"일치 항목 없음"}}; -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_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_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 取消","type_filter":"絞り込み入力","arrows_select":"↑/↓ 選択"},"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"},"fileExplorer":{"search_label":"検索","placeholder":"ファイルを絞り込む…","empty":"一致なし"}}; -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_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_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 取消","type_filter":"输入筛选","arrows_select":"↑/↓ 选择"},"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"},"fileExplorer":{"search_label":"搜索","placeholder":"筛选文件…","empty":"无匹配项"}}; -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_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_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","type_filter":"digitar filtro","arrows_select":"↑/↓ selecionar"},"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"},"fileExplorer":{"search_label":"Buscar","placeholder":"Filtrar arquivos…","empty":"Nenhum resultado"}}; -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_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_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 отмена","type_filter":"ввод фильтра","arrows_select":"↑/↓ выбор"},"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"},"fileExplorer":{"search_label":"Поиск","placeholder":"Фильтр файлов…","empty":"Нет совпадений"}}; -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_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_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 إلغاء","type_filter":"اكتب للتصفية","arrows_select":"↑/↓ تحديد"},"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"},"fileExplorer":{"search_label":"بحث","placeholder":"تصفية الملفات…","empty":"لا نتائج"}}; -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_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_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","type_filter":"ketik filter","arrows_select":"↑/↓ pilih"},"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"},"fileExplorer":{"search_label":"Cari","placeholder":"Filter berkas…","empty":"Tidak ada hasil"}}; -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_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_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","type_filter":"filtre yaz","arrows_select":"↑/↓ seç"},"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"},"fileExplorer":{"search_label":"Ara","placeholder":"Dosyaları filtrele…","empty":"Eşleşme yok"}}; -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_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_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 रद्द","type_filter":"फ़िल्टर लिखें","arrows_select":"↑/↓ चुनें"},"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"},"fileExplorer":{"search_label":"खोज","placeholder":"फ़ाइलें छाँटें…","empty":"कोई मेल नहीं"}}; -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_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_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","type_filter":"filter typen","arrows_select":"↑/↓ selecteren"},"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"},"fileExplorer":{"search_label":"Zoeken","placeholder":"Bestanden filteren…","empty":"Geen resultaten"}}; -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_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_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","type_filter":"wpisz filtr","arrows_select":"↑/↓ wybierz"},"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"},"fileExplorer":{"search_label":"Szukaj","placeholder":"Filtruj pliki…","empty":"Brak wyników"}}; -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_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_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","type_filter":"nhập bộ lọc","arrows_select":"↑/↓ chọn"},"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"},"fileExplorer":{"search_label":"Tìm","placeholder":"Lọc tệp…","empty":"Không khớp"}}; -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_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_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 ยกเลิก","type_filter":"พิมพ์ตัวกรอง","arrows_select":"↑/↓ เลือก"},"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"},"fileExplorer":{"search_label":"ค้นหา","placeholder":"กรองไฟล์…","empty":"ไม่พบผลลัพธ์"}}; -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_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_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 لغو","type_filter":"نوشتن فیلتر","arrows_select":"↑/↓ انتخاب"},"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"},"fileExplorer":{"search_label":"جستجو","placeholder":"فیلتر پرونده‌ها…","empty":"بدون نتیجه"}}; -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_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_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 בטל","type_filter":"הקלדת סינון","arrows_select":"↑/↓ בחירה"},"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"},"fileExplorer":{"search_label":"חיפוש","placeholder":"סינון קבצים…","empty":"אין התאמות"}}; -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_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_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","type_filter":"retlif epyt","arrows_select":"tceles ↓/↑"},"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"},"fileExplorer":{"search_label":"hcraeS","placeholder":"…selif retliF","empty":"sehctam oN"}}; 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/ui/file-search-input.ts b/src/ui/file-search-input.ts new file mode 100644 index 00000000..0887ddb2 --- /dev/null +++ b/src/ui/file-search-input.ts @@ -0,0 +1,81 @@ +import { createSurface, stringToSurface, type Surface } from '@flyingrobots/bijou'; +import { clipToWidth } from '@flyingrobots/bijou-tui'; +import type { FileSearchState } from '../app/workspace/file-search.js'; +import type { JeditTheme, JeditStyleToken } from './jedit-theme.js'; +import { fitLine, visibleLineLength } from './fit-line.js'; + +const INPUT_HEIGHT = 1; +const MIN_INPUT_WIDTH = 1; +const EMPTY_INPUT = ''; +const LABEL_SUFFIX = ': '; +const CHARACTER_STEP = 1; + +export interface FileSearchInputOptions { + readonly state: FileSearchState; + readonly label: string; + readonly placeholder: string; + readonly focused: boolean; + readonly width: number; + readonly theme: JeditTheme; +} + +interface FileSearchInputWindow { + readonly prefix: string; + readonly prefixWidth: number; + readonly text: string; + readonly startIndex: number; + readonly cursorColumn: number; +} + +export function renderFileSearchInput(options: FileSearchInputOptions): Surface { + if (options.width <= 0) return createSurface(0, INPUT_HEIGHT); + const window = inputWindow(options); + const content = options.state.input.length === 0 + ? clipToWidth(options.placeholder, options.width - window.prefixWidth) + : window.text; + const surface = stringToSurface(fitLine(`${window.prefix}${content}`, options.width), options.width, INPUT_HEIGHT); + applyInputToken(surface, options.theme.surface.currentLine); + if (options.focused && window.cursorColumn < options.width) { + const cell = surface.get(window.cursorColumn, 0); + surface.set(window.cursorColumn, 0, { ...cell, ...inputCellStyle(options.theme.cursor.insert), empty: false }); + } + return surface; +} + +function inputWindow(options: FileSearchInputOptions): FileSearchInputWindow { + const prefix = clipToWidth(`${options.label}${LABEL_SUFFIX}`, Math.max(0, options.width - MIN_INPUT_WIDTH)); + const prefixWidth = visibleLineLength(prefix); + const capacity = Math.max(MIN_INPUT_WIDTH, options.width - prefixWidth); + const characters = Array.from(options.state.input); + const cursor = Math.max(0, Math.min(characters.length, options.state.cursorIndex)); + let startIndex = 0; + while (startIndex < cursor && visibleLineLength(characters.slice(startIndex, cursor).join(EMPTY_INPUT)) >= capacity) { + startIndex += CHARACTER_STEP; + } + return { + prefix, prefixWidth, startIndex, + text: clipToWidth(characters.slice(startIndex).join(EMPTY_INPUT), capacity), + cursorColumn: prefixWidth + visibleLineLength(characters.slice(startIndex, cursor).join(EMPTY_INPUT)), + }; +} + +export function fileSearchCursorAtColumn(options: FileSearchInputOptions, column: number): number { + const window = inputWindow(options); + const characters = Array.from(options.state.input); + let position = window.prefixWidth; + for (let index = window.startIndex; index < characters.length; index += CHARACTER_STEP) { + if (column <= position) return index; + position += visibleLineLength(characters[index] ?? EMPTY_INPUT); + } + return characters.length; +} + +function applyInputToken(surface: Surface, token: JeditStyleToken): void { + for (let x = 0; x < surface.width; x += CHARACTER_STEP) { + surface.set(x, 0, { ...surface.get(x, 0), ...inputCellStyle(token), empty: false }); + } +} + +function inputCellStyle(token: JeditStyleToken) { + return { fg: token.fg, fgRGB: token.fgRGB, bg: token.bg, bgRGB: token.bgRGB, modifiers: token.modifiers == null ? undefined : [...token.modifiers] }; +} diff --git a/src/ui/file-search-results.ts b/src/ui/file-search-results.ts new file mode 100644 index 00000000..50df34c6 --- /dev/null +++ b/src/ui/file-search-results.ts @@ -0,0 +1,25 @@ +import type { Surface } from '@flyingrobots/bijou'; +import type { FileSearchEntry } from '../app/workspace/file-search.js'; +import type { JeditTheme } from './jedit-theme.js'; +import { JEDIT_TEXT_MODIFIER } from './jedit-theme.js'; +import { visibleLineLength } from './fit-line.js'; + +const ENTRY_PREFIX_WIDTH = 2; +const CHARACTER_STEP = 1; + +export function highlightFileSearchMatches(surface: Surface, entries: readonly FileSearchEntry[], theme: JeditTheme): void { + entries.forEach(({ entry, matchIndexes }, row) => { + const characters = Array.from(entry.name); + for (const index of matchIndexes) { + const column = ENTRY_PREFIX_WIDTH + visibleLineLength(characters.slice(0, index).join('')); + const width = visibleLineLength(characters[index] ?? ''); + for (let x = column; x < Math.min(surface.width, column + width); x += CHARACTER_STEP) { + const cell = surface.get(x, row); + surface.set(x, row, { + ...cell, fg: theme.chrome.activeEdge.fg, fgRGB: theme.chrome.activeEdge.fgRGB, + modifiers: [...(cell.modifiers ?? []), JEDIT_TEXT_MODIFIER.Bold], + }); + } + } + }); +} diff --git a/src/ui/workspace-chrome.ts b/src/ui/workspace-chrome.ts index 2c3307a2..a3e1dbca 100644 --- a/src/ui/workspace-chrome.ts +++ b/src/ui/workspace-chrome.ts @@ -40,6 +40,8 @@ const EditorModeKeys: Record = Object.freeze({ }); const FooterHintKeys = Object.freeze({ + TypeFilter: 'type_filter', + ArrowsSelect: 'arrows_select', JkMove: 'j_k_move', EnterChange: 'enter_change', F2Close: 'f2_close', @@ -322,9 +324,9 @@ function previewFooterHints(state: WorkspaceFooterState, t: FooterHintTranslator function fileDrawerFooterHints(state: WorkspaceFooterState, t: FooterHintTranslator): ReadonlyArray { return [ - t(FooterHintKeys.JkMove), + t(FooterHintKeys.TypeFilter), + t(FooterHintKeys.ArrowsSelect), t(FooterHintKeys.EnterOpen), - t(FooterHintKeys.BackspaceUp), t(FooterHintKeys.CtrlBClose), focusHint(state, t), ]; diff --git a/translations.json b/translations.json index d63a553f..a7f1c5b0 100644 --- a/translations.json +++ b/translations.json @@ -82,7 +82,9 @@ "y0_start": "y0 start", "y_end": "y$ end", "gg_top": "gg top", - "esc_cancel": "esc cancel" + "esc_cancel": "esc cancel", + "type_filter": "type filter", + "arrows_select": "↑/↓ select" }, "evidence": { "head": "head", @@ -171,6 +173,11 @@ }, "why": { "range_obstructed_title": "Why range obstructed" + }, + "fileExplorer": { + "search_label": "Search", + "placeholder": "Filter files…", + "empty": "No matches" } } }, @@ -256,7 +263,9 @@ "y0_start": "y0 début", "y_end": "y$ fin", "gg_top": "gg haut", - "esc_cancel": "esc annuler" + "esc_cancel": "esc annuler", + "type_filter": "saisir le filtre", + "arrows_select": "↑/↓ sélectionner" }, "evidence": { "head": "tête", @@ -345,6 +354,11 @@ }, "why": { "range_obstructed_title": "Why range obstructed" + }, + "fileExplorer": { + "search_label": "Recherche", + "placeholder": "Filtrer les fichiers…", + "empty": "Aucun résultat" } } }, @@ -430,7 +444,9 @@ "y0_start": "y0 inizio", "y_end": "y$ fine", "gg_top": "gg cima", - "esc_cancel": "esc annulla" + "esc_cancel": "esc annulla", + "type_filter": "digita filtro", + "arrows_select": "↑/↓ seleziona" }, "evidence": { "head": "testa", @@ -519,6 +535,11 @@ }, "why": { "range_obstructed_title": "Why range obstructed" + }, + "fileExplorer": { + "search_label": "Cerca", + "placeholder": "Filtra file…", + "empty": "Nessun risultato" } } }, @@ -604,7 +625,9 @@ "y0_start": "y0 anfang", "y_end": "y$ ende", "gg_top": "gg oben", - "esc_cancel": "esc abbrechen" + "esc_cancel": "esc abbrechen", + "type_filter": "Filter eingeben", + "arrows_select": "↑/↓ auswählen" }, "evidence": { "head": "Kopf", @@ -693,6 +716,11 @@ }, "why": { "range_obstructed_title": "Why range obstructed" + }, + "fileExplorer": { + "search_label": "Suche", + "placeholder": "Dateien filtern…", + "empty": "Keine Treffer" } } }, @@ -778,7 +806,9 @@ "y0_start": "y0 inicio", "y_end": "y$ fin", "gg_top": "gg arriba", - "esc_cancel": "esc cancelar" + "esc_cancel": "esc cancelar", + "type_filter": "escribir filtro", + "arrows_select": "↑/↓ seleccionar" }, "evidence": { "head": "cabeza", @@ -867,6 +897,11 @@ }, "why": { "range_obstructed_title": "Why range obstructed" + }, + "fileExplorer": { + "search_label": "Buscar", + "placeholder": "Filtrar archivos…", + "empty": "Sin resultados" } } }, @@ -952,7 +987,9 @@ "y0_start": "y0 시작", "y_end": "y$ 끝", "gg_top": "gg 맨 위", - "esc_cancel": "esc 취소" + "esc_cancel": "esc 취소", + "type_filter": "필터 입력", + "arrows_select": "↑/↓ 선택" }, "evidence": { "head": "헤드", @@ -1041,6 +1078,11 @@ }, "why": { "range_obstructed_title": "Why range obstructed" + }, + "fileExplorer": { + "search_label": "검색", + "placeholder": "파일 필터…", + "empty": "일치 항목 없음" } } }, @@ -1126,7 +1168,9 @@ "y0_start": "y0 先頭", "y_end": "y$ 末尾", "gg_top": "gg 先頭", - "esc_cancel": "esc 取消" + "esc_cancel": "esc 取消", + "type_filter": "絞り込み入力", + "arrows_select": "↑/↓ 選択" }, "evidence": { "head": "ヘッド", @@ -1215,6 +1259,11 @@ }, "why": { "range_obstructed_title": "Why range obstructed" + }, + "fileExplorer": { + "search_label": "検索", + "placeholder": "ファイルを絞り込む…", + "empty": "一致なし" } } }, @@ -1300,7 +1349,9 @@ "y0_start": "y0 开头", "y_end": "y$ 结尾", "gg_top": "gg 顶部", - "esc_cancel": "esc 取消" + "esc_cancel": "esc 取消", + "type_filter": "输入筛选", + "arrows_select": "↑/↓ 选择" }, "evidence": { "head": "头", @@ -1389,6 +1440,11 @@ }, "why": { "range_obstructed_title": "Why range obstructed" + }, + "fileExplorer": { + "search_label": "搜索", + "placeholder": "筛选文件…", + "empty": "无匹配项" } } }, @@ -1474,7 +1530,9 @@ "y0_start": "y0 início", "y_end": "y$ fim", "gg_top": "gg topo", - "esc_cancel": "esc cancelar" + "esc_cancel": "esc cancelar", + "type_filter": "digitar filtro", + "arrows_select": "↑/↓ selecionar" }, "evidence": { "head": "cabeça", @@ -1563,6 +1621,11 @@ }, "why": { "range_obstructed_title": "Why range obstructed" + }, + "fileExplorer": { + "search_label": "Buscar", + "placeholder": "Filtrar arquivos…", + "empty": "Nenhum resultado" } } }, @@ -1648,7 +1711,9 @@ "y0_start": "y0 начало", "y_end": "y$ конец", "gg_top": "gg верх", - "esc_cancel": "esc отмена" + "esc_cancel": "esc отмена", + "type_filter": "ввод фильтра", + "arrows_select": "↑/↓ выбор" }, "evidence": { "head": "вершина", @@ -1737,6 +1802,11 @@ }, "why": { "range_obstructed_title": "Why range obstructed" + }, + "fileExplorer": { + "search_label": "Поиск", + "placeholder": "Фильтр файлов…", + "empty": "Нет совпадений" } } }, @@ -1822,7 +1892,9 @@ "y0_start": "y0 بداية", "y_end": "y$ نهاية", "gg_top": "gg أعلى", - "esc_cancel": "esc إلغاء" + "esc_cancel": "esc إلغاء", + "type_filter": "اكتب للتصفية", + "arrows_select": "↑/↓ تحديد" }, "evidence": { "head": "الرأس", @@ -1911,6 +1983,11 @@ }, "why": { "range_obstructed_title": "Why range obstructed" + }, + "fileExplorer": { + "search_label": "بحث", + "placeholder": "تصفية الملفات…", + "empty": "لا نتائج" } } }, @@ -1996,7 +2073,9 @@ "y0_start": "y0 awal", "y_end": "y$ akhir", "gg_top": "gg atas", - "esc_cancel": "esc batal" + "esc_cancel": "esc batal", + "type_filter": "ketik filter", + "arrows_select": "↑/↓ pilih" }, "evidence": { "head": "kepala", @@ -2085,6 +2164,11 @@ }, "why": { "range_obstructed_title": "Why range obstructed" + }, + "fileExplorer": { + "search_label": "Cari", + "placeholder": "Filter berkas…", + "empty": "Tidak ada hasil" } } }, @@ -2170,7 +2254,9 @@ "y0_start": "y0 başlangıç", "y_end": "y$ son", "gg_top": "gg üst", - "esc_cancel": "esc iptal" + "esc_cancel": "esc iptal", + "type_filter": "filtre yaz", + "arrows_select": "↑/↓ seç" }, "evidence": { "head": "baş", @@ -2259,6 +2345,11 @@ }, "why": { "range_obstructed_title": "Why range obstructed" + }, + "fileExplorer": { + "search_label": "Ara", + "placeholder": "Dosyaları filtrele…", + "empty": "Eşleşme yok" } } }, @@ -2344,7 +2435,9 @@ "y0_start": "y0 शुरुआत", "y_end": "y$ अंत", "gg_top": "gg ऊपर", - "esc_cancel": "esc रद्द" + "esc_cancel": "esc रद्द", + "type_filter": "फ़िल्टर लिखें", + "arrows_select": "↑/↓ चुनें" }, "evidence": { "head": "शीर्ष", @@ -2433,6 +2526,11 @@ }, "why": { "range_obstructed_title": "Why range obstructed" + }, + "fileExplorer": { + "search_label": "खोज", + "placeholder": "फ़ाइलें छाँटें…", + "empty": "कोई मेल नहीं" } } }, @@ -2518,7 +2616,9 @@ "y0_start": "y0 begin", "y_end": "y$ einde", "gg_top": "gg boven", - "esc_cancel": "esc annuleren" + "esc_cancel": "esc annuleren", + "type_filter": "filter typen", + "arrows_select": "↑/↓ selecteren" }, "evidence": { "head": "kop", @@ -2607,6 +2707,11 @@ }, "why": { "range_obstructed_title": "Why range obstructed" + }, + "fileExplorer": { + "search_label": "Zoeken", + "placeholder": "Bestanden filteren…", + "empty": "Geen resultaten" } } }, @@ -2692,7 +2797,9 @@ "y0_start": "y0 początek", "y_end": "y$ koniec", "gg_top": "gg góra", - "esc_cancel": "esc anuluj" + "esc_cancel": "esc anuluj", + "type_filter": "wpisz filtr", + "arrows_select": "↑/↓ wybierz" }, "evidence": { "head": "głowa", @@ -2781,6 +2888,11 @@ }, "why": { "range_obstructed_title": "Why range obstructed" + }, + "fileExplorer": { + "search_label": "Szukaj", + "placeholder": "Filtruj pliki…", + "empty": "Brak wyników" } } }, @@ -2866,7 +2978,9 @@ "y0_start": "y0 đầu", "y_end": "y$ cuối", "gg_top": "gg đầu", - "esc_cancel": "esc hủy" + "esc_cancel": "esc hủy", + "type_filter": "nhập bộ lọc", + "arrows_select": "↑/↓ chọn" }, "evidence": { "head": "đầu", @@ -2955,6 +3069,11 @@ }, "why": { "range_obstructed_title": "Why range obstructed" + }, + "fileExplorer": { + "search_label": "Tìm", + "placeholder": "Lọc tệp…", + "empty": "Không khớp" } } }, @@ -3040,7 +3159,9 @@ "y0_start": "y0 เริ่ม", "y_end": "y$ จบ", "gg_top": "gg บน", - "esc_cancel": "esc ยกเลิก" + "esc_cancel": "esc ยกเลิก", + "type_filter": "พิมพ์ตัวกรอง", + "arrows_select": "↑/↓ เลือก" }, "evidence": { "head": "เฮด", @@ -3129,6 +3250,11 @@ }, "why": { "range_obstructed_title": "Why range obstructed" + }, + "fileExplorer": { + "search_label": "ค้นหา", + "placeholder": "กรองไฟล์…", + "empty": "ไม่พบผลลัพธ์" } } }, @@ -3214,7 +3340,9 @@ "y0_start": "y0 آغاز", "y_end": "y$ پایان", "gg_top": "gg بالا", - "esc_cancel": "esc لغو" + "esc_cancel": "esc لغو", + "type_filter": "نوشتن فیلتر", + "arrows_select": "↑/↓ انتخاب" }, "evidence": { "head": "سر", @@ -3303,6 +3431,11 @@ }, "why": { "range_obstructed_title": "Why range obstructed" + }, + "fileExplorer": { + "search_label": "جستجو", + "placeholder": "فیلتر پرونده‌ها…", + "empty": "بدون نتیجه" } } }, @@ -3388,7 +3521,9 @@ "y0_start": "y0 התחלה", "y_end": "y$ סוף", "gg_top": "gg למעלה", - "esc_cancel": "esc בטל" + "esc_cancel": "esc בטל", + "type_filter": "הקלדת סינון", + "arrows_select": "↑/↓ בחירה" }, "evidence": { "head": "ראש", @@ -3477,6 +3612,11 @@ }, "why": { "range_obstructed_title": "Why range obstructed" + }, + "fileExplorer": { + "search_label": "חיפוש", + "placeholder": "סינון קבצים…", + "empty": "אין התאמות" } } } From 055795d94a1a4eae93f06bb8c969d42ced020769 Mon Sep 17 00:00:00 2001 From: James Ross Date: Wed, 14 Oct 2026 10:48:45 -0700 Subject: [PATCH 3/8] Verify explorer search and preserve distinct inputs in light themes --- docs/design/0161-file-explorer-search.md | 47 +++++++++++++++++------- spec/file-explorer-search.spec.mjs | 9 +++++ src/ui/file-search-input.ts | 8 +++- 3 files changed, 50 insertions(+), 14 deletions(-) diff --git a/docs/design/0161-file-explorer-search.md b/docs/design/0161-file-explorer-search.md index e1c7d877..56f5811c 100644 --- a/docs/design/0161-file-explorer-search.md +++ b/docs/design/0161-file-explorer-search.md @@ -70,7 +70,8 @@ perform actions such as navigation, refresh, quit, or perf toggling. Ctrl+B opens Files and focuses the input. Its row remains pinned while results scroll. The existing theme input/cursor tokens provide a stable block caret; -no blink timer is introduced. The field uses the theme current-line surface to distinguish it from the drawer. +no blink timer is introduced. The field uses the theme current-line surface to distinguish it from the drawer, +falling back to the workspace surface when those background tokens coincide. Matched filename characters use the active-edge accent and bold weight. Name matches are preferred over directory-prefix matches when the whole query fits the filename. The query scrolls horizontally to keep the caret @@ -188,23 +189,23 @@ workspace-wide and symbol search separate. - [x] Slice 1: Input state, fuzzy projection, and keyboard/open behavior. - [x] Slice 2: Sticky input/caret rendering, pointer/wheel mapping, and localized help. -- [ ] Slice 3: Runtime/render/native-host verification and documentation. +- [x] Slice 3: Runtime/render/native-host verification and documentation. ## Tests To Write First -- [ ] Ctrl+B focuses a visible input; printable shortcut keys type query text. -- [ ] Fuzzy matching, no matches, editing, and Unicode caret movement behave correctly. -- [ ] Arrows/Enter/click open the displayed match rather than a hidden source entry. -- [ ] Header stays pinned while results scroll; mouse/wheel use the filtered list. -- [ ] Close/reopen, Settings, pane switching, refresh, and directory transitions preserve the contract. +- [x] Ctrl+B focuses a visible input; printable shortcut keys type query text. +- [x] Fuzzy matching, no matches, editing, and Unicode caret movement behave correctly. +- [x] Arrows/Enter/click open the displayed match rather than a hidden source entry. +- [x] Header stays pinned while results scroll; mouse/wheel use the filtered list. +- [x] Close/reopen, Settings, pane switching, refresh, and directory transitions preserve the contract. ## Acceptance Criteria -- [ ] Input is pinned, focused, and visibly editable on open. -- [ ] Loaded paths filter live with no persistent cache or index. -- [ ] All result interactions share the same projection and geometry. -- [ ] Empty results cannot open a hidden file. -- [ ] Full repository checks, localized rendering, and live PTY validation pass. +- [x] Input is pinned, focused, and visibly editable on open. +- [x] Loaded paths filter live with no persistent cache or index. +- [x] All result interactions share the same projection and geometry. +- [x] Empty results cannot open a hidden file. +- [x] Full repository checks, localized rendering, and live PTY validation pass. ## Validation Plan @@ -233,4 +234,24 @@ workspace-wide file, semantic, and Graft symbol search with disposable indexes. ## Retrospective -Pending implementation and validation. +Implemented with one shared filtered projection retaining original entry indexes. +The input owns printable keys while focus remains on Files; pointer geometry +reserves the same pinned rows as painting. Match highlights use the filter's +Unicode character positions, preferring filename matches when possible. + +The initial red tests exposed the missing input state and later the missing +visual distinction. Existing list-navigation and pointer tests were updated for +arrow navigation and the two extra pinned rows. Thirteen focused runtime/render +tests pass, including Japanese, Arabic, and mirrored labels, wide-character +caret mapping, and distinct input backgrounds across 16 built-in theme variants. +The full repository check passes, and shard coverage includes all 131 specs. + +Live native-host PTY sessions at 100×32 and 60×16 confirmed decoded input, +focused query editing, no-match Enter safety, and the selected fixture's actual +Echo import result with its expected source content. Terminal snapshots were +inspected at both sizes; the input remained at the top and matched characters +were accented. Quality reports no regressions and its debt baseline is unchanged. + +No persistent query, recursive inventory, symbols, semantic retrieval, or cache +was added. These remain explicit follow-on work in #341. This change is stacked +on the static-title removal in #339; neither PR is merged by this cycle. diff --git a/spec/file-explorer-search.spec.mjs b/spec/file-explorer-search.spec.mjs index d135feee..e7264139 100644 --- a/spec/file-explorer-search.spec.mjs +++ b/spec/file-explorer-search.spec.mjs @@ -223,3 +223,12 @@ test('the input has a distinct background and only fuzzy-matched letters receive const plain = await drawer(cleared); assert.equal(plain.get(3, 3).fg, model.jeditTheme.surface.drawer.fg); }); + +test('the input background remains distinct across built-in dark and light themes', async () => { + const { availableJeditThemes, oppositeJeditTheme } = await importDist('ui', 'jedit-themes.js'); + const { model } = await setup(); + for (const theme of availableJeditThemes().flatMap((theme) => [theme, oppositeJeditTheme(theme)])) { + const surface = await drawer({ ...model, jeditTheme: theme }); + assert.notEqual(surface.get(2, 1).bg, surface.get(2, 3).bg, theme.name); + } +}); diff --git a/src/ui/file-search-input.ts b/src/ui/file-search-input.ts index 0887ddb2..74605548 100644 --- a/src/ui/file-search-input.ts +++ b/src/ui/file-search-input.ts @@ -34,7 +34,7 @@ export function renderFileSearchInput(options: FileSearchInputOptions): Surface ? clipToWidth(options.placeholder, options.width - window.prefixWidth) : window.text; const surface = stringToSurface(fitLine(`${window.prefix}${content}`, options.width), options.width, INPUT_HEIGHT); - applyInputToken(surface, options.theme.surface.currentLine); + applyInputToken(surface, fileSearchInputToken(options.theme)); if (options.focused && window.cursorColumn < options.width) { const cell = surface.get(window.cursorColumn, 0); surface.set(window.cursorColumn, 0, { ...cell, ...inputCellStyle(options.theme.cursor.insert), empty: false }); @@ -79,3 +79,9 @@ function applyInputToken(surface: Surface, token: JeditStyleToken): void { function inputCellStyle(token: JeditStyleToken) { return { fg: token.fg, fgRGB: token.fgRGB, bg: token.bg, bgRGB: token.bgRGB, modifiers: token.modifiers == null ? undefined : [...token.modifiers] }; } + +function fileSearchInputToken(theme: JeditTheme): JeditStyleToken { + return theme.surface.currentLine.bg === theme.surface.drawer.bg + ? theme.surface.workspace + : theme.surface.currentLine; +} From 619d5e8fb2640cff2c353d4a938c1daaa6cef054 Mon Sep 17 00:00:00 2001 From: James Ross Date: Wed, 14 Oct 2026 11:26:41 -0700 Subject: [PATCH 4/8] Refine explorer design for modal navigation and file previews --- docs/design/0161-file-explorer-search.md | 78 ++++++++++++++++-------- 1 file changed, 52 insertions(+), 26 deletions(-) diff --git a/docs/design/0161-file-explorer-search.md b/docs/design/0161-file-explorer-search.md index 56f5811c..4f759540 100644 --- a/docs/design/0161-file-explorer-search.md +++ b/docs/design/0161-file-explorer-search.md @@ -68,35 +68,46 @@ perform actions such as navigation, refresh, quit, or perf toggling. ## User Experience / Product Shape -Ctrl+B opens Files and focuses the input. Its row remains pinned while results -scroll. The existing theme input/cursor tokens provide a stable block caret; -no blink timer is introduced. The field uses the theme current-line surface to distinguish it from the drawer, -falling back to the workspace surface when those background tokens coincide. -Matched filename characters use the active-edge accent and bold weight. Name -matches are preferred over directory-prefix matches when the whole query fits -the filename. The query scrolls horizontally to keep the caret -visible. Printable keys, including j/k/q/r/h/l, colon, Space, and backtick, -enter text rather than invoke commands while the field owns focus. - -Left/Right/Home/End move the query caret. Backspace/Delete edit it. Ctrl+U -clears it. Up/Down select matching rows while input remains focused; Enter -opens the selected match. Ctrl+R refreshes the current directory. Alt+Up opens -the parent directory. Directory changes clear the query and select the first -entry. Ctrl+B and Esc close; Tab retains existing pane cycling. Closing and -reopening retains the session query and caret. F2 retains Settings ownership. - -A query edit selects the first matching entry. No matches shows a durable -message, and Enter/click cannot open a hidden file. Filtering never mutates -the loaded source list. Empty query shows the ordinary directory list. Parent -navigation is shown for empty query and excluded from search results. - -The search row can be clicked to focus/edit it; result clicks open their actual -filtered entry. Wheel scrolling changes selection only within visible matches. -The list reserves the same fixed input rows in painting and pointer mapping. +The refinement replaces automatic input editing with explicit explorer modality. +Ctrl+B opens Files in navigation mode. j/k and Up/Down select results; `/` enters +filter editing. Clicking the input also enters editing. Esc or Enter in editing +returns to navigation while preserving the query. Esc in navigation closes the +drawer. Ctrl+B closes from either mode. Reopening starts in navigation with the +session query retained. F2 and Tab leave filter editing before changing focus. + +The search field remains pinned and uses a distinct theme background. Its caret +is visible only while editing and Files owns focus. Query edits select the first +match. Matched filename characters retain the accent and bold styling. Directory +changes clear the query. j/k/r/Backspace/h/l regain list actions in navigation; +Ctrl+R refreshes and Alt+Up goes to the parent in either mode. + +Pointer motion highlights the row under the cursor without changing selection +or loading a preview. Hover and selection have distinct theme backgrounds; +selection takes precedence if the cursor is over the selected row. Clicking a +row selects it and returns to navigation. A file selection previews it in the +main viewer while focus remains on Files. Clicking a directory only selects it; +Enter enters it. Enter on a file opens it through the native Echo path and moves +focus to the editor. Closing or leaving Files restores the existing editor. + +Preview is an explicitly labelled, read-only, bounded projection. The current +active buffer and other open buffer projections are preferred so unsaved text is +visible. Otherwise a filesystem adapter reads at most 64 KiB and 200 lines. +Binary, directory, unavailable, loading, and truncated previews are labelled. +Preview does not create an Echo buffer, change text authority, edit, or save. +Request identity and selected source entry reject late results. Selecting a +new item replaces the previous preview with loading immediately; no matches or +non-file selection clears it. No persistent preview cache is introduced. + +Terminal input must deliver Unicode scalars and modified arrows through a +Jedit boundary adapter; all-motion mouse reporting enables hover. Windows path +normalization occurs at the filesystem boundary with a portable search-path +field, leaving native paths intact for opening. Existing review findings on +these boundaries are included in this refinement. ### User Journey -Open Files → type query → choose with arrows → Enter opens the match. Backspace +Open Files → select with j/k or pointer → inspect preview → Enter opens. +Press / → type query → Esc → choose a filtered result. Backspace or Ctrl+U restores broader results; Esc closes and returns to the normal pane. ### Wide UI Mockup @@ -190,6 +201,10 @@ workspace-wide and symbol search separate. - [x] Slice 1: Input state, fuzzy projection, and keyboard/open behavior. - [x] Slice 2: Sticky input/caret rendering, pointer/wheel mapping, and localized help. - [x] Slice 3: Runtime/render/native-host verification and documentation. +- [ ] Slice 4: Explicit navigation/filter modes and mode-aware help. +- [ ] Slice 5: Pointer hover, click selection, and distinct row colors. +- [ ] Slice 6: Bounded read-only viewer previews and stale-result rejection. +- [ ] Slice 7: Real input boundary fixes, native verification, and refined docs. ## Tests To Write First @@ -255,3 +270,14 @@ were accented. Quality reports no regressions and its debt baseline is unchanged No persistent query, recursive inventory, symbols, semantic retrieval, or cache was added. These remain explicit follow-on work in #341. This change is stacked on the static-title removal in #339; neither PR is merged by this cycle. + +### Refinement acceptance (2026-10-14) + +- [ ] j/k navigates after opening; slash/click edits; Esc returns to navigation. +- [ ] Hover never selects or reads a file; click selects; colors remain distinct. +- [ ] Selected files preview without changing buffers, dirty text, or authority. +- [ ] Bounded, unavailable, binary, stale-result, and focus-return cases pass. +- [ ] Actual terminal Unicode, modified arrows, and hover packets are exercised. +- [ ] Full local checks and CI pass; documentation reflects final behavior. + +Refinement retrospective: pending implementation. From 3a940469654f88bd938744f11b85e327efed4dbd Mon Sep 17 00:00:00 2001 From: James Ross Date: Wed, 14 Oct 2026 11:39:35 -0700 Subject: [PATCH 5/8] Add modal explorer navigation, pointer selection, and bounded file previews --- docs/design/0161-file-explorer-search.md | 35 +-- docs/jim-component-ownership.md | 21 +- docs/topics/ui/drawers-and-panels.md | 83 +++++--- spec/file-explorer-preview.spec.mjs | 95 +++++++++ spec/file-explorer-search.spec.mjs | 88 ++++++-- spec/file-preview-source.spec.mjs | 44 ++++ spec/workspace-drawer-scroll.spec.mjs | 7 +- spec/workspace-footer.spec.mjs | 4 +- spec/workspace-terminal-input.spec.mjs | 43 ++++ src/adapters/file-preview.ts | 30 +++ src/adapters/filesystem.ts | 6 + src/adapters/terminal-input-stream.ts | 39 ++++ src/adapters/workspace-app.ts | 14 +- .../workspace-initial-model-snapshot.ts | 3 +- src/adapters/workspace-terminal-io.ts | 46 ++++ src/app/workspace/drawer.ts | 3 +- .../workspace/file-explorer-preview-view.ts | 46 ++++ src/app/workspace/file-explorer-preview.ts | 60 ++++++ src/app/workspace/file-explorer-view.ts | 4 +- src/app/workspace/file-search-input-key.ts | 17 +- src/app/workspace/file-search.ts | 18 +- src/app/workspace/global-key-bindings.ts | 2 +- src/app/workspace/init.ts | 4 + src/app/workspace/model.ts | 6 + src/app/workspace/mouse.ts | 43 ++-- src/app/workspace/msg.ts | 3 + src/app/workspace/runtime.ts | 9 +- src/app/workspace/settings-key-bindings.ts | 1 + src/app/workspace/viewer-content.ts | 3 + src/app/workspace/viewer.ts | 1 + .../workspace-runtime-dependencies.ts | 7 +- src/generated/i18n.ts | 48 +++-- src/main-workspace.ts | 5 +- src/ports/file-preview.ts | 12 ++ src/ports/file-system.ts | 1 + src/ui/file-search-results.ts | 43 +++- src/ui/terminal-mouse.ts | 1 + src/ui/workspace-chrome.ts | 8 +- translations.json | 200 ++++++++++++++---- 39 files changed, 896 insertions(+), 207 deletions(-) create mode 100644 spec/file-explorer-preview.spec.mjs create mode 100644 spec/file-preview-source.spec.mjs create mode 100644 spec/workspace-terminal-input.spec.mjs create mode 100644 src/adapters/file-preview.ts create mode 100644 src/adapters/terminal-input-stream.ts create mode 100644 src/adapters/workspace-terminal-io.ts create mode 100644 src/app/workspace/file-explorer-preview-view.ts create mode 100644 src/app/workspace/file-explorer-preview.ts create mode 100644 src/ports/file-preview.ts diff --git a/docs/design/0161-file-explorer-search.md b/docs/design/0161-file-explorer-search.md index 4f759540..b8ea011d 100644 --- a/docs/design/0161-file-explorer-search.md +++ b/docs/design/0161-file-explorer-search.md @@ -18,7 +18,8 @@ updated: "2026-10-14" ## Decision Summary -Give the Files drawer a sticky, focused search field and live fuzzy filtering +Give the Files drawer modal navigation, a sticky editable search field, pointer +hover/selection, read-only file previews, and live fuzzy filtering of the directory entries already loaded by the workspace. The query is a session-only UI projection; there is no persistent index, SQLite, recursive scan, semantic retrieval, or symbol extraction in this cycle. @@ -35,9 +36,9 @@ projection for painting, navigation, opening, and pointer hit-testing. ## Hill -Ctrl+B opens the drawer with a visible caret in its pinned input. Typing updates -the displayed results immediately. Arrows select a visible match and Enter -opens exactly that match through the existing file-open path. +Ctrl+B opens navigation; / enters the pinned filter. j/k, arrows, and clicks +select results and show read-only previews without replacing the active buffer. +Enter opens exactly the selected match through the existing file-open path. ## Current Truth @@ -131,11 +132,13 @@ terminal geometry remains unchanged. ## Runtime / API Contract -`WorkspaceModel.fileSearch` holds input and cursor coordinates. A pure selector +`WorkspaceModel.fileSearch` holds input and cursor coordinates; +`fileSearchEditing` declares filter mode and `fileHoveredIndex` is independent +of selection. `fileExplorerPreview` carries a selected entry and request id. A pure selector returns visible entries with their source indexes; `selectedIndex` retains the loaded-entry index so existing file-opening identity is preserved. Query changes select the first match or no selection. All filtered actions use that selector. -The input handler runs before plain global shortcuts when Files owns focus; +The input handler runs before plain global shortcuts only in filter mode; Ctrl+C, close, pane switching, and Settings retain their existing routes. ## Lower Modes @@ -149,11 +152,11 @@ existing file-system feedback and preserve the prior query/list. | Category | Description | | --- | --- | -| Source of truth | Existing loaded FileEntry list and session query/caret. | +| Source of truth | Loaded FileEntry list, session mode/query/caret, selected entry, and bounded preview observation. | | Derived state | Filtered entries, selection position, text window, list scroll offset. | | Invalid states | Controls in query; opening an entry excluded by the current query. | | Reset behavior | Successful directory change clears query; closing preserves it. | -| Persistence | None. A restart begins with an empty query. | +| Persistence | None. A restart begins with an empty query and navigation mode. | | Causal boundary | Disposable presentation over loaded file-system entries; no authoritative index or text mutation. | ## Accessibility Posture @@ -161,7 +164,9 @@ existing file-system feedback and preserve the prior query/list. Input focus stays on Files during arrow selection. Opening a file returns focus to the existing editor/open path. Closing uses the existing pane restoration. Empty results are readable rather than silent. Search is scoped to the loaded -directory, so it does not claim a complete workspace inventory. +directory, so it does not claim a complete workspace inventory. Hover has a +quiet background; selection has an accent background with contrast-adjusted +text. Existing theme variants keep them distinct. ## Localization / Directionality Posture @@ -201,9 +206,9 @@ workspace-wide and symbol search separate. - [x] Slice 1: Input state, fuzzy projection, and keyboard/open behavior. - [x] Slice 2: Sticky input/caret rendering, pointer/wheel mapping, and localized help. - [x] Slice 3: Runtime/render/native-host verification and documentation. -- [ ] Slice 4: Explicit navigation/filter modes and mode-aware help. -- [ ] Slice 5: Pointer hover, click selection, and distinct row colors. -- [ ] Slice 6: Bounded read-only viewer previews and stale-result rejection. +- [x] Slice 4: Explicit navigation/filter modes and mode-aware help. +- [x] Slice 5: Pointer hover, click selection, and distinct row colors. +- [x] Slice 6: Bounded read-only viewer previews and stale-result rejection. - [ ] Slice 7: Real input boundary fixes, native verification, and refined docs. ## Tests To Write First @@ -231,14 +236,14 @@ npm run ci:verify-shards ## Playback / Witness -Ctrl+B → type partial name → Down → Enter. Repeat with no matches, clear with +Ctrl+B → / → type partial name → Esc → j/k or click → inspect preview → Enter. Repeat with no matches, clear with Ctrl+U, edit in the middle, scroll a long list, and click a filtered row. Inspect at 100×32 and 60×16, including the real native-host file-open path. ## Risks -Existing j/k/r/Backspace navigation is intentionally replaced while input owns -focus; footer help must match. Filtering changes displayed indexes, so stale +Existing j/k/r/Backspace navigation is preserved in navigation and becomes +text editing only in filter mode; footer help must match. Filtering changes displayed indexes, so stale raw-index pointer calculations can open the wrong file. Unknown-symbol and workspace-inventory claims must remain outside this feature. diff --git a/docs/jim-component-ownership.md b/docs/jim-component-ownership.md index e60d169d..6a3c9ce6 100644 --- a/docs/jim-component-ownership.md +++ b/docs/jim-component-ownership.md @@ -285,14 +285,19 @@ the meaning of a checkpoint declaration, while Echo owns the Tick and receipt that witness its persistence. Jedit owns a screen cache, while neither that cache nor the screen becomes text authority. -The current Files drawer search is a transient Jedit presentation filter over -already loaded filesystem entries. Its query and caret are session-only shell -state; they are not persisted or replayed as Jim buffer-search state. It creates -no database or authoritative inventory. Filtering preserves source entry -identity, and opening a displayed file continues through the existing native -Echo text-import path. [DX-0161](design/0161-file-explorer-search.md) owns this -bounded implementation; a future workspace or symbol index must remain a -rebuildable projection and declare its observation basis and staleness policy. +The current Files drawer search and read-only preview are transient Jedit +presentation projections. Navigation/filter mode, query/caret, hover, and +selection are session-only shell state; they are not persisted or replayed as +Jim buffer-search state. The filter creates no database or authoritative +inventory. Selection previews bounded filesystem content or an already open +buffer projection without replacing the active buffer, editing, saving, or +creating an Echo buffer. Entry identity and request identity reject stale +preview results. Native file paths remain intact while portable search paths +are normalized at the filesystem boundary. Opening explicitly with Enter +continues through the native Echo text-import path. +[DX-0161](design/0161-file-explorer-search.md) owns this bounded implementation; +a future workspace or symbol index must remain a rebuildable projection and +declare its observation basis and staleness policy. > **Intuition to carry forward:** semantic ownership and storage authority are > different powers. Jim decides what command to perform, the text capability diff --git a/docs/topics/ui/drawers-and-panels.md b/docs/topics/ui/drawers-and-panels.md index d3bad044..a9b8029d 100644 --- a/docs/topics/ui/drawers-and-panels.md +++ b/docs/topics/ui/drawers-and-panels.md @@ -23,44 +23,57 @@ enough for detailed explanations. ## Files Drawer -Open the files drawer with `Ctrl+B`. Focus moves into the sticky search input at -its top, with a visible caret. Typing immediately fuzzy-filters the entries in -the currently loaded directory by name or workspace-relative path. Matching is -case-insensitive and accepts characters in order, so `rnt` can match -`render-tree.ts`. It preserves the directory's existing order. The input has a distinct theme -background, and the matched filename characters use the theme accent and bold -weight. - -| Key | Action | +Open the files drawer with `Ctrl+B`. It starts in navigation mode. The sticky +search field has a distinct background, but takes keyboard input only after `/` +or a click on the field. Its caret appears while editing. + +| Navigation key | Action | | --- | --- | -| `Up` / `Down` | Select a displayed result. | -| `Enter` | Open the selected file or directory. | -| `Left` / `Right`, `Home` / `End` | Move the query caret. | -| `Backspace` / `Delete` | Edit the query. | -| `Ctrl+U` | Clear the query. | -| `Ctrl+R` | Refresh the current directory. | -| `Alt+Up` | Enter the parent directory. | +| `j` / `k`, `Up` / `Down` | Select a displayed result and preview a file. | +| `/` | Edit the filter. | +| `Enter` / `l` | Open the selected file for editing, or enter a directory. | +| `Backspace` / `h`, `Alt+Up` | Enter the parent directory. | +| `r` / `Ctrl+R` | Refresh the current directory. | | `Ctrl+B` / `Esc` | Close the drawer. | | `Tab` | Cycle visible panes. | -Printable keys such as `j`, `k`, `q`, and Space enter the query while the field -has focus. The input stays pinned as results scroll. Clicking the input places -the caret; clicking a result opens that result. No matches displays an explicit -message and cannot open an excluded file. - -Closing and reopening preserves the session query. Entering another directory -clears it. Refreshing preserves it; a failed directory change preserves the -previous query and entries. A restart starts with an empty query. - -This search covers loaded directory entries. Workspace-wide discovery, semantic -retrieval, and Graft symbol search are deferred to -[issue #341](https://github.com/flyingrobots/jedit/issues/341). It creates no -SQLite database or persistent search index. The transient projection boundary -is recorded in [component ownership](../../jim-component-ownership.md). - -The files drawer is for browsing workspace entries. It should share file-opening -behavior with `:edit`: selecting a file opens it, and selecting a directory -enters that directory. +| Filter key | Action | +| --- | --- | +| Printable characters | Fuzzy-filter loaded entries as you type. | +| `Left` / `Right`, `Home` / `End` | Move the query caret. | +| `Backspace` / `Delete` | Edit the query. | +| `Ctrl+U` | Clear the query. | +| `Esc` / `Enter` | Return to navigation and retain the query. | +| `Up` / `Down` | Select a displayed result. | +| `Ctrl+B` | Close the drawer. | + +Matching is case-insensitive and accepts characters in order, so `rnt` can match +`render-tree.ts`. It preserves the directory's existing order. Matched filename +characters use accent color and bold weight; the selected row keeps a readable +foreground against its accent background. Empty results cannot open a hidden +file. Closing and reopening preserves the session query and starts in navigation. +Entering another directory clears it; refreshing preserves it. A restart starts +with an empty query. + +Mouse motion highlights the row under the cursor without selecting it or reading +a preview. Hover and selection have distinct backgrounds. A click selects the +row and leaves focus in Files. Selecting a file, by keyboard, click, wheel, or +filter change, shows a labelled read-only preview in the main viewer. Enter opens +it through the native Echo text-import path and focuses the editor. A directory +click selects it; Enter enters it. + +Previewing preserves the active buffer, including unsaved edits. Existing buffer +projections supply their current text; other files are read at most 64 KiB and +200 lines. Binary, unavailable, loading, and truncated previews are labelled. +Late results cannot replace the current selection's preview. Closing or leaving +Files restores the existing editor. Previews create no Echo buffers and have no +save action or persistent cache. The projection boundary is recorded in +[component ownership](../../jim-component-ownership.md). + +Search covers currently loaded directory entries. Workspace-wide discovery, +semantic retrieval, and Graft symbols are deferred to +[issue #341](https://github.com/flyingrobots/jedit/issues/341). No SQLite database +or persistent index is created. ## Graft Drawer @@ -100,4 +113,6 @@ replaces the explanation. | [`src/ui/graft-drawer.ts`](../../../src/ui/graft-drawer.ts) | Graft drawer rendering. | | [`src/ui/graft-diagnostics-panel.ts`](../../../src/ui/graft-diagnostics-panel.ts) | Diagnostics panel rendering. | | [`src/ui/why-inline-panel.ts`](../../../src/ui/why-inline-panel.ts) | Inline why explanation rendering. | +| [`src/app/workspace/file-explorer-preview.ts`](../../../src/app/workspace/file-explorer-preview.ts) | Selected-file preview lifecycle and stale-result protection. | +| [`src/adapters/file-preview.ts`](../../../src/adapters/file-preview.ts) | Bounded filesystem preview reads. | | [`src/app/workspace/file-tree.ts`](../../../src/app/workspace/file-tree.ts) | File tree state and entry behavior. | diff --git a/spec/file-explorer-preview.spec.mjs b/spec/file-explorer-preview.spec.mjs new file mode 100644 index 00000000..a504f3bf --- /dev/null +++ b/spec/file-explorer-preview.spec.mjs @@ -0,0 +1,95 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { importDist, mockRuntime } from './workspace-helpers.mjs'; + +const entries = [ + { kind: 'file', name: 'alpha.txt', path: '/repo/alpha.txt' }, + { kind: 'file', name: 'beta.txt', path: '/repo/beta.txt' }, +]; +const key = (key, flags = {}) => ({ type: 'key', key, ...flags }); +async function setup() { + const { createWorkspaceRuntime } = await importDist('app', 'workspace', 'runtime.js'); + const { BijouI18nAdapter } = await importDist('adapters', 'bijou-i18n-adapter.js'); + const reads = []; + const deps = mockRuntime(); + deps.initialModel.entries = entries; + deps.initialModel.i18n = new BijouI18nAdapter(); + const { availableJeditThemes } = await importDist('ui', 'jedit-themes.js'); + deps.initialModel.jeditTheme = availableJeditThemes()[0]; + deps.filePreviewSource = { loadFilePreview: (path) => { + reads.push(path); + return { kind: 'loaded', lines: [`PREVIEW ${path}`], truncated: false }; + } }; + const runtime = createWorkspaceRuntime(deps); + const model = { ...runtime.init()[0], startupIntroComplete: true }; + return { runtime, model, reads }; +} +function text(surface) { + return Array.from({ length: surface.height }, (_, y) => ( + Array.from({ length: surface.width }, (_, x) => surface.get(x, y).char).join('') + )).join('\n'); +} + +test('selection previews in the main viewer without replacing editor state or opening an Echo buffer', async () => { + const { runtime, model, reads } = await setup(); + const [opened, commands] = runtime.update(key('b', { ctrl: true }), model); + assert.equal(opened.focusPane, 'files'); + assert.equal(opened.textAuthority, model.textAuthority); + assert.ok(commands.length > 0); + const message = await commands.at(-1)(); + const [preview] = runtime.update(message, { ...opened, fileDrawerProgress: 1 }); + assert.match(text(runtime.view(preview)), /Preview.*alpha.txt/); + assert.match(text(runtime.view(preview)), /PREVIEW.*alpha.txt/); + assert.deepEqual(reads, ['/repo/alpha.txt']); + assert.equal(preview.editor, model.editor); + assert.equal(preview.textAuthority, model.textAuthority); + assert.equal(preview.textRequestId, model.textRequestId); + assert.equal(preview.focusPane, 'files'); + const [closed] = runtime.update(key('b', { ctrl: true }), preview); + assert.doesNotMatch(text(runtime.view(closed)), /PREVIEW/); +}); + +test('late preview results cannot paint a different selection and hover never reads a file', async () => { + const { runtime, model, reads } = await setup(); + const [opened, first] = runtime.update(key('b', { ctrl: true }), model); + const [next, second] = runtime.update(key('j'), opened); + const firstMessage = await first.at(-1)(); + const [late] = runtime.update(firstMessage, next); + assert.equal(late.fileExplorerPreview.entry.path, '/repo/beta.txt'); + assert.equal(late.fileExplorerPreview.result, undefined); + const secondMessage = await second.at(-1)(); + const [loaded] = runtime.update(secondMessage, next); + assert.equal(loaded.fileExplorerPreview.result.lines[0], 'PREVIEW /repo/beta.txt'); + const [, hoverCommands] = runtime.update({ type: 'mouse', action: 'move', button: 'none', col: 5, row: 5 }, { + ...loaded, fileDrawerProgress: 1, + }); + assert.deepEqual(hoverCommands, []); + assert.equal(reads.length, 2); +}); + +test('the active unsaved buffer projection is previewed without filesystem reads or mutation', async () => { + const { runtime, model, reads } = await setup(); + const editor = { path: '/repo/alpha.txt', lines: ['UNSAVED CONTENT'], dirty: true }; + const initial = { ...model, editor }; + const [opened] = runtime.update(key('b', { ctrl: true }), initial); + assert.equal(opened.fileExplorerPreview.result.lines[0], 'UNSAVED CONTENT'); + assert.deepEqual(reads, []); + assert.equal(opened.editor, editor); + assert.equal(opened.textAuthority, initial.textAuthority); +}); + + test('click selection previews without opening and closing rejects its outstanding result', async () => { + const { runtime, model } = await setup(); + const [opened] = runtime.update(key('b', { ctrl: true }), model); + const [selected, commands] = runtime.update({ type: 'mouse', action: 'press', button: 'left', col: 5, row: 6 }, { + ...opened, fileDrawerProgress: 1, + }); + assert.equal(selected.selectedIndex, 1); + assert.equal(selected.focusPane, 'files'); + assert.equal(selected.textRequestId, model.textRequestId); + const message = await commands.at(-1)(); + const [closed] = runtime.update(key('b', { ctrl: true }), selected); + const [late] = runtime.update(message, closed); + assert.equal(late.fileExplorerPreview, undefined); + assert.equal(late.textAuthority, model.textAuthority); +}); diff --git a/spec/file-explorer-search.spec.mjs b/spec/file-explorer-search.spec.mjs index e7264139..cd5036aa 100644 --- a/spec/file-explorer-search.spec.mjs +++ b/spec/file-explorer-search.spec.mjs @@ -22,11 +22,10 @@ async function setup(options = {}) { deps.initialModel.i18n = new BijouI18nAdapter().withLocale(options.locale ?? 'en'); deps.initialModel.jeditTheme = availableJeditThemes()[0]; const runtime = createWorkspaceRuntime(deps); - const { openWorkspaceFileEntry } = await importDist('app', 'workspace', 'file-tree.js'); - deps.openEntry = (model, entry) => openWorkspaceFileEntry(model, entry, deps.nowMs, deps); const initial = { ...runtime.init()[0], cwd: '/repo/src', startupIntroComplete: true }; const [opened] = runtime.update(key('b', { ctrl: true }), initial); - return { runtime, initial, model: { ...opened, fileDrawerProgress: 1 } }; + const focused = options.navigation ? opened : runtime.update(key('/'), opened)[0]; + return { runtime, initial, model: { ...focused, fileDrawerProgress: 1 } }; } function type(runtime, model, input) { @@ -45,7 +44,7 @@ async function drawer(model, width = 32, height = 12) { return renderDrawer('files', model, width, height); } -test('Ctrl+B focuses a sticky search input with a visible caret', async () => { +test('slash focuses a sticky search input with a visible caret', async () => { const { model } = await setup(); assert.equal(model.focusPane, 'files'); assert.deepEqual(model.fileSearch, { input: '', cursorIndex: 0 }); @@ -104,13 +103,15 @@ test('arrows select visible matches and Enter opens the displayed file, includin assert.equal(next.selectedIndex, 3); next = runtime.update(key('down'), next)[0]; assert.equal(next.selectedIndex, 4); + next = runtime.update(key('escape'), next)[0]; const [opening, commands] = runtime.update(key('enter'), next); assert.equal(opening.textAuthority.filePath, '/repo/src/render-tree.ts'); assert.equal(commands.length, 1); const empty = type(runtime, model, 'not-a-match'); assert.match(text(await drawer(empty)), /No matches/); const [unchanged, none] = runtime.update(key('enter'), empty); - assert.equal(unchanged, empty); + assert.equal(unchanged.textAuthority, empty.textAuthority); + assert.equal(unchanged.selectedIndex, -1); assert.deepEqual(none, []); }); @@ -132,7 +133,8 @@ test('filtered result clicks and wheel selection use the pinned-header geometry' const { runtime, model } = await setup(); const filtered = type(runtime, model, '.ts'); const [clicked] = runtime.update({ type: 'mouse', action: 'press', button: 'left', col: 5, row: 6 }, filtered); - assert.equal(clicked.textAuthority.filePath, '/repo/src/render-tree.ts'); + assert.equal(clicked.selectedIndex, 4); + assert.equal(clicked.textAuthority, model.textAuthority); const [wheeled] = runtime.update({ type: 'mouse', action: 'scroll-down', button: 'none', col: 5, row: 8 }, filtered); assert.equal(wheeled.selectedIndex, 4); const [inputClick, commands] = runtime.update({ type: 'mouse', action: 'press', button: 'left', col: 10, row: 3 }, { @@ -150,7 +152,7 @@ test('close/reopen retains the query, directory changes clear it, and failed cha describeDirectoryIssue: () => ({ title: 'Directory failed', message: 'unreadable' }), } } }); let next = type(runtime, model, 'rnt'); - next = runtime.update(key('escape'), next)[0]; + next = runtime.update(key('b', { ctrl: true }), next)[0]; assert.equal(next.fileDrawerOpen, false); next = runtime.update(key('b', { ctrl: true }), next)[0]; assert.equal(next.focusPane, 'files'); @@ -159,7 +161,7 @@ test('close/reopen retains the query, directory changes clear it, and failed cha assert.equal(next.cwd, '/repo'); assert.equal(next.fileSearch.input, ''); const failed = type(runtime, model, 'components'); - const [after] = runtime.update(key('enter'), failed); + const [after] = runtime.update(key('enter'), runtime.update(key('escape'), failed)[0]); assert.equal(after.cwd, '/repo/src'); assert.equal(after.fileSearch.input, 'components'); assert.equal(after.entries, model.entries); @@ -209,19 +211,20 @@ test('localized labels fit narrow fields and wide-character clicks use display c }); test('the input has a distinct background and only fuzzy-matched letters receive the accent', async () => { - const { runtime, model } = await setup({ entries: [ENTRIES[4]] }); + const { runtime, model } = await setup({ entries: [ENTRIES[4], { ...ENTRIES[4], name: 'render-test.ts', path: '/repo/src/render-test.ts' }] }); const filtered = type(runtime, model, 'rnt'); const surface = await drawer(filtered); assert.notEqual(surface.get(2, 1).bg, surface.get(2, 3).bg); assert.equal(surface.get(2, 1).bg, model.jeditTheme.surface.currentLine.bg); for (const x of [3, 5, 10]) { - assert.equal(surface.get(x, 3).fg, model.jeditTheme.chrome.activeEdge.fg); + assert.equal(surface.get(x, 3).fg, surface.get(4, 3).fg); + assert.equal(surface.get(x, 4).fg, model.jeditTheme.chrome.activeEdge.fg); assert.ok(surface.get(x, 3).modifiers.includes('bold')); } - assert.equal(surface.get(4, 3).fg, model.jeditTheme.surface.drawer.fg); + assert.equal(surface.get(4, 4).fg, model.jeditTheme.surface.drawer.fg); const cleared = runtime.update(key('u', { ctrl: true }), filtered)[0]; const plain = await drawer(cleared); - assert.equal(plain.get(3, 3).fg, model.jeditTheme.surface.drawer.fg); + assert.equal(plain.get(3, 4).fg, model.jeditTheme.surface.drawer.fg); }); test('the input background remains distinct across built-in dark and light themes', async () => { @@ -232,3 +235,64 @@ test('the input background remains distinct across built-in dark and light theme assert.notEqual(surface.get(2, 1).bg, surface.get(2, 3).bg, theme.name); } }); + + test('Files opens in navigation mode, slash edits, and Escape returns to navigation before closing', async () => { + const { runtime, model } = await setup({ navigation: true }); + assert.equal(model.fileSearchEditing, false); + let next = runtime.update(key('j'), model)[0]; + assert.equal(next.selectedIndex, 1); + assert.equal(next.fileSearch.input, ''); + next = runtime.update(key('k'), next)[0]; + assert.equal(next.selectedIndex, 0); + next = runtime.update(key('/'), next)[0]; + assert.equal(next.fileSearchEditing, true); + next = type(runtime, next, '.ts'); + next = runtime.update(key('escape'), next)[0]; + assert.equal(next.fileDrawerOpen, true); + assert.equal(next.fileSearchEditing, false); + assert.equal(next.fileSearch.input, '.ts'); + next = runtime.update(key('j'), next)[0]; + assert.equal(next.selectedIndex, 4); + next = runtime.update(key('escape'), next)[0]; + assert.equal(next.fileDrawerOpen, false); +}); + + test('mouse hover is separate from selection and clicking selects without opening', async () => { + const { runtime, model } = await setup({ navigation: true }); + const [hover, commands] = runtime.update({ type: 'mouse', action: 'move', button: 'none', col: 5, row: 7 }, model); + assert.equal(hover.fileHoveredIndex, 2); + assert.equal(hover.selectedIndex, 0); + assert.deepEqual(commands, []); + const surface = await drawer(hover); + assert.notEqual(surface.get(2, 3).bg, surface.get(2, 5).bg); + const [selected] = runtime.update({ type: 'mouse', action: 'press', button: 'left', col: 5, row: 7 }, hover); + assert.equal(selected.selectedIndex, 2); + assert.equal(selected.focusPane, 'files'); + assert.equal(selected.textAuthority, model.textAuthority); + const [outside] = runtime.update({ type: 'mouse', action: 'move', button: 'none', col: 80, row: 7 }, selected); + assert.equal(outside.fileHoveredIndex, undefined); +}); + + test('portable paths from the filesystem boundary exclude drive and workspace text from filtering', async () => { + const { portableFileSearchPath } = await importDist('adapters', 'filesystem.js'); + const { runtime, model } = await setup({ entries: [{ + kind: 'file', name: 'index.ts', path: 'C:\\repo\\src\\index.ts', + searchPath: portableFileSearchPath('C:\\repo\\src\\index.ts'), + }] }); + const initial = { ...model, workspaceRoot: 'C:\\repo', workspaceSearchRoot: portableFileSearchPath('C:\\repo') }; + assert.equal(type(runtime, initial, 'src/index').selectedIndex, 0); + assert.equal(type(runtime, initial, 'repo').selectedIndex, -1); +}); + + test('selected rows remain readable and distinct from hover across dark and light themes', async () => { + const { availableJeditThemes, oppositeJeditTheme } = await importDist('ui', 'jedit-themes.js'); + const { contrastRatio } = await importDist('ui', 'theme-contrast.js'); + const { model } = await setup({ navigation: true }); + for (const theme of availableJeditThemes().flatMap((theme) => [theme, oppositeJeditTheme(theme)])) { + const surface = await drawer({ ...model, jeditTheme: theme, selectedIndex: 1, fileHoveredIndex: 2 }); + const selected = surface.get(3, 4); + const hovered = surface.get(3, 5); + assert.notEqual(selected.bg, hovered.bg, theme.name); + assert.ok(contrastRatio(selected.fgRGB, selected.bgRGB) >= 4.45, theme.name); + } +}); diff --git a/spec/file-preview-source.spec.mjs b/spec/file-preview-source.spec.mjs new file mode 100644 index 00000000..646f26a0 --- /dev/null +++ b/spec/file-preview-source.spec.mjs @@ -0,0 +1,44 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { mkdtempSync, writeFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { tmpdir } from 'node:os'; +import { spawnSync } from 'node:child_process'; +import { importDist } from './dist-helpers.mjs'; + +test('filesystem previews bound bytes and lines and report binary or unavailable files', async () => { + const { filePreviewSource } = await importDist('adapters', 'file-preview.js'); + const dir = mkdtempSync(join(tmpdir(), 'jedit-preview-spec-')); + const large = join(dir, 'large.txt'); + writeFileSync(large, '界'.repeat(40000)); + const truncated = filePreviewSource.loadFilePreview(large); + assert.equal(truncated.kind, 'loaded'); + assert.equal(truncated.truncated, true); + assert.ok(Buffer.byteLength(truncated.lines.join('\n')) <= 65536); + assert.ok(!truncated.lines.join('').includes('�')); + const many = join(dir, 'many.txt'); + writeFileSync(many, 'line\n'.repeat(250)); + const lines = filePreviewSource.loadFilePreview(many); + assert.equal(lines.lines.length, 200); + assert.equal(lines.truncated, true); + const binary = join(dir, 'binary'); + writeFileSync(binary, Buffer.from([0, 1, 2])); + assert.equal(filePreviewSource.loadFilePreview(binary).kind, 'binary'); + assert.equal(filePreviewSource.loadFilePreview(join(dir, 'missing')).kind, 'unavailable'); + assert.equal(filePreviewSource.loadFilePreview(dir).kind, 'unavailable'); +}); + + test('a named pipe cannot block the preview reader', async (context) => { + const { filePreviewSource } = await importDist('adapters', 'file-preview.js'); + const dir = mkdtempSync(join(tmpdir(), 'jedit-preview-pipe-')); + const pipe = join(dir, 'pipe'); + const created = spawnSync('mkfifo', [pipe]); + if (created.error?.code === 'ENOENT') { context.skip('mkfifo unavailable'); return; } + assert.equal(created.status, 0); + const script = `import {filePreviewSource} from ${JSON.stringify(new URL('../dist/adapters/file-preview.js', import.meta.url).href)};` + + `process.stdout.write(JSON.stringify(filePreviewSource.loadFilePreview(${JSON.stringify(pipe)})));`; + const result = spawnSync(process.execPath, ['--input-type=module', '-e', script], { timeout: 1000, encoding: 'utf8' }); + assert.equal(result.error, undefined); + assert.equal(JSON.parse(result.stdout).kind, 'unavailable'); + assert.equal(filePreviewSource.loadFilePreview(dir).kind, 'unavailable'); +}); diff --git a/spec/workspace-drawer-scroll.spec.mjs b/spec/workspace-drawer-scroll.spec.mjs index d850c9af..86fd8de8 100644 --- a/spec/workspace-drawer-scroll.spec.mjs +++ b/spec/workspace-drawer-scroll.spec.mjs @@ -103,7 +103,7 @@ test("a click maps through the scroll offset to the right entry", async () => { assert.equal(next.selectedIndex, offset); }); -test("clicking a file opens it, not just selects it", async () => { +test("clicking a file selects it and leaves opening to Enter", async () => { const [mouse, viewport, panelFocus, titleScreen] = await Promise.all([ importDist("app", "workspace", "mouse.js"), importDist("app", "workspace", "viewport.js"), @@ -128,7 +128,7 @@ test("clicking a file opens it, not just selects it", async () => { }); const row = viewport.WORKSPACE_BODY_TOP_OFFSET + viewport.FILE_SEARCH_LIST_TOP + 1; - mouse.updateFromMouse( + const [selected] = mouse.updateFromMouse( { type: "mouse", button: "left", action: "press", col: 2, row, shift: false, alt: false, ctrl: false }, model, { highlight: () => undefined }, @@ -141,5 +141,6 @@ test("clicking a file opens it, not just selects it", async () => { }, ); - assert.deepEqual(opened, ["/w/beta.txt"]); + assert.equal(selected.selectedIndex, 1); + assert.deepEqual(opened, []); }); diff --git a/spec/workspace-footer.spec.mjs b/spec/workspace-footer.spec.mjs index bae15f81..56828db1 100644 --- a/spec/workspace-footer.spec.mjs +++ b/spec/workspace-footer.spec.mjs @@ -263,7 +263,7 @@ test('workspace footer shows file drawer controls and the selected file path', a graftSelection: undefined, }), [ - 'FILES [type filter · arrows select · enter open · ctrl+b close · tab focus]', + 'FILES [j/k move · slash filter · enter open · ctrl+b close · tab focus]', '/repo/notes/very-long-file-name.md', ], ); @@ -380,7 +380,7 @@ test('workspace footer obtains command hints from i18n', async () => { }); assert.equal(primary.includes(''), true); - assert.equal(primary.includes(''), true); + assert.equal(primary.includes(''), true); assert.equal(primary.includes(''), true); assert.equal(requestedKeys.includes('footer.hints.enter_open'), true); assert.equal(requestedKeys.includes('footer.hints.ctrl_b_close'), true); diff --git a/spec/workspace-terminal-input.spec.mjs b/spec/workspace-terminal-input.spec.mjs new file mode 100644 index 00000000..533756a5 --- /dev/null +++ b/spec/workspace-terminal-input.spec.mjs @@ -0,0 +1,43 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { createEventBus } from '@flyingrobots/bijou-tui'; +import { importDist } from './dist-helpers.mjs'; + + test('the production input boundary emits Unicode, modified arrows, and hover through the Bijou event bus', async () => { + const { workspaceTerminalIO } = await importDist('adapters', 'workspace-terminal-io.js'); + let input; + let disposed = false; + const rawIO = { rawInput: (handler) => { + input = handler; + return { dispose: () => { disposed = true; } }; + }, onResize: () => ({ dispose() {} }) }; + const bus = createEventBus(); + const received = []; + bus.on((msg) => received.push(msg)); + const connection = bus.connectIO(workspaceTerminalIO(rawIO), { mouse: true }); + input('/é界🦀'); + input('\x1b[1;3A'); + input('\x1b[<35;6;8M'); + assert.deepEqual(received.map((msg) => msg.key ?? msg.action), ['/', 'é', '界', '🦀', 'up', 'move']); + assert.equal(received[4].alt, true); + assert.equal(received[5].col, 5); + assert.equal(received[5].row, 7); + connection.dispose(); + assert.equal(disposed, true); +}); + + test('split escape sequences stay intact, ordinary keys preserve modifiers, and lone Escape is delivered', async () => { + const { workspaceTerminalIO } = await importDist('adapters', 'workspace-terminal-io.js'); + let input; + const rawIO = { rawInput: (handler) => { input = handler; return { dispose() {} }; }, onResize: () => ({ dispose() {} }) }; + const bus = createEventBus(); + const received = []; + bus.on((msg) => received.push(msg)); + const connection = bus.connectIO(workspaceTerminalIO(rawIO), { mouse: true }); + input('\x1b[1;'); input('3A'); input('J\x02'); input('\x1b'); + await new Promise((resolve) => setTimeout(resolve, 40)); + assert.deepEqual(received.map((msg) => msg.key), ['up', 'j', 'b', 'escape']); + assert.equal(received[1].shift, true); + assert.equal(received[2].ctrl, true); + connection.dispose(); +}); diff --git a/src/adapters/file-preview.ts b/src/adapters/file-preview.ts new file mode 100644 index 00000000..97c104df --- /dev/null +++ b/src/adapters/file-preview.ts @@ -0,0 +1,30 @@ +import { constants, closeSync, fstatSync, openSync, readSync } from 'node:fs'; +import { normalizeLines } from '../app/editor-lines.js'; +import { FilePreviewKinds, FILE_PREVIEW_MAX_BYTES, FILE_PREVIEW_MAX_LINES, type FilePreviewSource } from '../ports/file-preview.js'; + +const UTF8_ENCODING = 'utf-8'; +const NULL_BYTE = 0; + +export const filePreviewSource: FilePreviewSource = { + loadFilePreview(path) { + let descriptor: number | undefined; + try { + descriptor = openSync(path, constants.O_RDONLY | constants.O_NONBLOCK); + const stat = fstatSync(descriptor); + if (!stat.isFile()) return { kind: FilePreviewKinds.Unavailable }; + const bytes = Buffer.alloc(FILE_PREVIEW_MAX_BYTES); + const read = readSync(descriptor, bytes, 0, bytes.length, 0); + const content = bytes.subarray(0, read); + if (content.includes(NULL_BYTE)) return { kind: FilePreviewKinds.Binary }; + const lines = normalizeLines(new TextDecoder(UTF8_ENCODING).decode(content, { stream: read < stat.size })); + return { + kind: FilePreviewKinds.Loaded, lines: lines.slice(0, FILE_PREVIEW_MAX_LINES), + truncated: read < stat.size || lines.length > FILE_PREVIEW_MAX_LINES, + }; + } catch { + return { kind: FilePreviewKinds.Unavailable }; + } finally { + if (descriptor != null) closeSync(descriptor); + } + }, +}; diff --git a/src/adapters/filesystem.ts b/src/adapters/filesystem.ts index ba0a2f64..96ca61d1 100644 --- a/src/adapters/filesystem.ts +++ b/src/adapters/filesystem.ts @@ -52,6 +52,7 @@ export function loadEntries(cwd: string): readonly FileEntry[] { kind: 'parent', name: '..', path: parent, + searchPath: portableFileSearchPath(parent), }); } @@ -60,6 +61,7 @@ export function loadEntries(cwd: string): readonly FileEntry[] { kind: entry.isDirectory() ? 'dir' : 'file', name: entry.name, path: join(cwd, entry.name), + searchPath: portableFileSearchPath(join(cwd, entry.name)), })) .sort(compareEntries); @@ -129,3 +131,7 @@ function formatUnknownFilesystemError(cause: Throwable): string { ? cause.message : String(cause); } + +export function portableFileSearchPath(path: string): string { + return path.replaceAll('\\', '/'); +} diff --git a/src/adapters/terminal-input-stream.ts b/src/adapters/terminal-input-stream.ts new file mode 100644 index 00000000..078ff5e3 --- /dev/null +++ b/src/adapters/terminal-input-stream.ts @@ -0,0 +1,39 @@ +const ESCAPE = '\u001b'; +const CSI_START = `${ESCAPE}[`; +const SS3_START = `${ESCAPE}O`; +const CSI_FINAL = /[@-~]/u; +const ESCAPE_DELAY_MS = 25; + +export function terminalInputStream(emit: (token: string) => void) { + let pending = ''; + let timer: ReturnType | undefined; + const clearTimer = () => { if (timer != null) clearTimeout(timer); }; + return { + accept(raw: string) { + clearTimer(); + pending += raw; + while (pending.length > 0) { + const length = terminalTokenLength(pending); + if (length == null) break; + emit(pending.slice(0, length)); + pending = pending.slice(length); + } + if (pending === ESCAPE) { + timer = setTimeout(() => { pending = ''; emit(ESCAPE); }, ESCAPE_DELAY_MS); + } + }, + dispose() { clearTimer(); pending = ''; }, + }; +} + +function terminalTokenLength(raw: string): number | undefined { + if (raw === ESCAPE) return undefined; + if (raw.startsWith(CSI_START)) { + for (let index = CSI_START.length; index < raw.length; index += 1) { + if (CSI_FINAL.test(raw[index] ?? '')) return index + 1; + } + return undefined; + } + if (raw.startsWith(SS3_START)) return raw.length >= 3 ? 3 : undefined; + return Array.from(raw)[0]?.length; +} diff --git a/src/adapters/workspace-app.ts b/src/adapters/workspace-app.ts index e1a90503..a84a951f 100644 --- a/src/adapters/workspace-app.ts +++ b/src/adapters/workspace-app.ts @@ -1,3 +1,4 @@ +import { filePreviewSource } from './file-preview.js'; import { FileSystemPortAdapter } from './filesystem.js'; import { createWorkspaceRuntime } from '../app/workspace/runtime.js'; import { createRaytracerProfilerPort } from './raytracer-profiler.js'; @@ -16,8 +17,6 @@ import { createWorkspaceNotificationTickCmd, createWorkspaceTimeTickCmd, } from './workspace-animation-commands.js'; -import { openWorkspaceFileEntry } from '../app/workspace/file-tree.js'; -import type { WorkspaceModel } from '../app/workspace/model.js'; export interface WorkspaceAppOptions { initialColumns: number; @@ -54,6 +53,7 @@ function workspaceRuntimeDependencies( const graftSession = createGraftSessionPort(); const sourceHighlighter = createGraftSourceHighlighter(); return { + filePreviewSource, initialColumns: options.initialColumns, initialRows: options.initialRows, initialWorkingDirectory: options.initialWorkingDirectory, @@ -67,16 +67,6 @@ function workspaceRuntimeDependencies( profileOnStartup: options.profileEnabled ?? false, initialModel: options.seed ?? createInitialModelSnapshot(nowMs(), options.initialWorkingDirectory), nowMs, - openEntry: ( - model: WorkspaceModel, - entry: NonNullable, - ) => openWorkspaceFileEntry(model, entry, nowMs, { - fileSystem: FileSystemPortAdapter, - editorFile, - graftSession, - sourceHighlighter, - productionTextSession: productionText.productionTextSession, - }), createTimeTickCmd: createWorkspaceTimeTickCmd, createNotificationTickCmd: createWorkspaceNotificationTickCmd, createDrawerAnimationCmd: createWorkspaceDrawerAnimationCmd, diff --git a/src/adapters/workspace-initial-model-snapshot.ts b/src/adapters/workspace-initial-model-snapshot.ts index 251d7fed..3d5ec6d0 100644 --- a/src/adapters/workspace-initial-model-snapshot.ts +++ b/src/adapters/workspace-initial-model-snapshot.ts @@ -4,7 +4,7 @@ import { JEDIT_THEME_ENV, resolveInitialJeditTheme, } from "../ui/jedit-themes.js"; -import { loadEntries } from "./filesystem.js"; +import { loadEntries, portableFileSearchPath } from "./filesystem.js"; export function createInitialModelSnapshot( nowMs: number, @@ -12,6 +12,7 @@ export function createInitialModelSnapshot( ): WorkspaceInitialModelSnapshot { return { entries: loadEntries(cwd), + searchRoot: portableFileSearchPath(cwd), jeditTheme: resolveInitialJeditTheme(process.env[JEDIT_THEME_ENV]), i18n: new BijouI18nAdapter(), nowMs, diff --git a/src/adapters/workspace-terminal-io.ts b/src/adapters/workspace-terminal-io.ts new file mode 100644 index 00000000..294d7f5d --- /dev/null +++ b/src/adapters/workspace-terminal-io.ts @@ -0,0 +1,46 @@ +import type { IOPort } from '@flyingrobots/bijou'; +import type { KeyMsg } from '@flyingrobots/bijou-tui'; +import { terminalInputStream } from './terminal-input-stream.js'; + +const MODIFIED_ARROW = /^\u001b\[1;(\d+)([ABCDHF])$/u; +const ARROWS: Readonly> = { A: 'up', B: 'down', C: 'right', D: 'left', H: 'home', F: 'end' }; +const NON_ASCII_PRINTABLE = /^[^\x00-\x7f\p{Cc}\p{Cf}\p{Cs}\p{Zl}\p{Zp}]$/u; +const MODIFIER_BASE = 1; +const SHIFT_BIT = 1; +const ALT_BIT = 2; +const CTRL_BIT = 4; + +// Bijou's raw parser handles ASCII, mouse packets and ordinary escape keys. +// Its onData host-message port carries decoded keys that it cannot express. +export function workspaceTerminalIO(io: IOPort): IOPort { + const subscribers = new Set<(message: KeyMsg) => void>(); + return { + ...io, + rawInput(callback) { + const stream = terminalInputStream((raw) => { + const message = extendedTerminalKey(raw); + if (message == null) callback(raw); + else for (const subscriber of subscribers) subscriber(message); + }); + const handle = io.rawInput(stream.accept); + return { dispose() { stream.dispose(); handle.dispose(); } }; + }, + onData(callback) { + const subscriber = (message: KeyMsg) => callback(message); + subscribers.add(subscriber); + const handle = io.onData?.(callback); + return { dispose() { subscribers.delete(subscriber); handle?.dispose(); } }; + }, + }; +} + +function extendedTerminalKey(raw: string): KeyMsg | undefined { + if (NON_ASCII_PRINTABLE.test(raw)) return { type: 'key', key: raw, ctrl: false, alt: false, shift: false }; + const match = MODIFIED_ARROW.exec(raw); + if (match == null) return undefined; + const bits = Number(match[1]) - MODIFIER_BASE; + return { + type: 'key', key: ARROWS[match[2] ?? ''] ?? '', + shift: (bits & SHIFT_BIT) !== 0, alt: (bits & ALT_BIT) !== 0, ctrl: (bits & CTRL_BIT) !== 0, + }; +} diff --git a/src/app/workspace/drawer.ts b/src/app/workspace/drawer.ts index f62b5a69..f8553bb4 100644 --- a/src/app/workspace/drawer.ts +++ b/src/app/workspace/drawer.ts @@ -46,6 +46,7 @@ function openFileDrawer( const next = withFocusPane({ ...model, fileDrawerOpen: true, + fileSearchEditing: false, }, FocusPanes.Files); if (model.fileDrawerOpen) { @@ -77,7 +78,7 @@ export function closeDrawer( createDrawerAnimationCmd: CreateDrawerAnimationCmd, ): [WorkspaceModel, Cmd[]] { const next = kind === DrawerKinds.Files - ? { ...model, fileDrawerOpen: false } + ? { ...model, fileDrawerOpen: false, fileSearchEditing: false, fileHoveredIndex: undefined } : { ...model, graftDrawerOpen: false }; const focusState: Omit = { diff --git a/src/app/workspace/file-explorer-preview-view.ts b/src/app/workspace/file-explorer-preview-view.ts new file mode 100644 index 00000000..89debbd0 --- /dev/null +++ b/src/app/workspace/file-explorer-preview-view.ts @@ -0,0 +1,46 @@ +import { createSurface, stringToSurface, type Surface } from '@flyingrobots/bijou'; +import { FilePreviewKinds, type FilePreviewResult } from '../../ports/file-preview.js'; +import { fitBlock } from '../../ui/workspace-render.js'; +import { fileExplorerHasFocus, selectedFileSearchEntry } from './file-search.js'; +import { FileEntryKinds } from '../../ports/file-system.js'; +import type { WorkspaceModel } from './model.js'; +import { applyBackground, fillSurface } from './surface-fill.js'; + +const PREVIEW_PAD = 1; +const PAD_MULTIPLIER = 2; +const PREVIEW_BODY_TOP = 3; +const PREVIEW_LABEL = 'fileExplorer.preview_label'; +const PREVIEW_LOADING = 'fileExplorer.preview_loading'; +const PREVIEW_BINARY = 'fileExplorer.preview_binary'; +const PREVIEW_UNAVAILABLE = 'fileExplorer.preview_unavailable'; +const PREVIEW_TRUNCATED = 'fileExplorer.preview_truncated'; +const TAB_SPACES = ' '; +const UNSAFE_CONTROLS = /[\p{Cc}\p{Cf}]/gu; + +export function renderFileExplorerPreview(model: WorkspaceModel, width: number, height: number): Surface | undefined { + if (!fileExplorerHasFocus(model)) return undefined; + const entry = selectedFileSearchEntry(model); + if (entry?.kind !== FileEntryKinds.File) return undefined; + const surface = createSurface(width, height); + fillSurface(surface, model.jeditTheme.surface.workspace); + const innerWidth = Math.max(0, width - PREVIEW_PAD * PAD_MULTIPLIER); + const header = `${model.i18n.t(PREVIEW_LABEL)} · ${entry.name}`; + const label = stringToSurface(fitBlock(header, innerWidth, 1), innerWidth, 1); + applyBackground(label, model.jeditTheme.surface.header); + surface.blit(label, PREVIEW_PAD, PREVIEW_PAD); + const result = model.fileExplorerPreview?.entry === entry ? model.fileExplorerPreview.result : undefined; + const lines = previewLines(model, result); + const bodyHeight = Math.max(0, height - PREVIEW_BODY_TOP - PREVIEW_PAD); + const body = stringToSurface(fitBlock(lines.join('\n'), innerWidth, bodyHeight), innerWidth, bodyHeight); + applyBackground(body, model.jeditTheme.surface.workspace); + surface.blit(body, PREVIEW_PAD, PREVIEW_BODY_TOP); + return surface; +} + +function previewLines(model: WorkspaceModel, result: FilePreviewResult | undefined): readonly string[] { + if (result == null) return [model.i18n.t(PREVIEW_LOADING)]; + if (result.kind === FilePreviewKinds.Binary) return [model.i18n.t(PREVIEW_BINARY)]; + if (result.kind === FilePreviewKinds.Unavailable) return [model.i18n.t(PREVIEW_UNAVAILABLE)]; + const lines = result.lines.map((line) => line.replaceAll('\t', TAB_SPACES).replace(UNSAFE_CONTROLS, '')); + return result.truncated ? [model.i18n.t(PREVIEW_TRUNCATED), ...lines] : lines; +} diff --git a/src/app/workspace/file-explorer-preview.ts b/src/app/workspace/file-explorer-preview.ts new file mode 100644 index 00000000..16591f03 --- /dev/null +++ b/src/app/workspace/file-explorer-preview.ts @@ -0,0 +1,60 @@ +import type { Cmd } from '@flyingrobots/bijou-tui'; +import { FilePreviewKinds, FILE_PREVIEW_MAX_LINES, type FilePreviewSource, type FilePreviewResult } from '../../ports/file-preview.js'; +import { FileEntryKinds, type FileEntry } from '../../ports/file-system.js'; +import { fileExplorerHasFocus, selectedFileSearchEntry } from './file-search.js'; +import { findWorkspaceBufferRecordByPath } from './workspace-buffer-registry.js'; +import { WorkspaceMessageTypes, type WorkspaceMsg } from './msg.js'; +import type { WorkspaceModel } from './model.js'; + +export interface FileExplorerPreview { + readonly requestId: number; + readonly entry: FileEntry; + readonly result?: FilePreviewResult; +} + +export interface FileExplorerPreviewMessage extends FileExplorerPreview { + readonly result: FilePreviewResult; +} + +type Result = [WorkspaceModel, Cmd[]]; + +export function synchronizeFileExplorerPreview(result: Result, source: FilePreviewSource | undefined): Result { + const [model, commands] = result; + const entry = fileExplorerHasFocus(model) ? selectedFileSearchEntry(model) : undefined; + if (entry?.kind !== FileEntryKinds.File) return clearFileExplorerPreview(result); + if (model.fileExplorerPreview?.entry === entry || source == null) return result; + const requestId = (model.fileExplorerPreviewRequestId ?? 0) + 1; + const preview = { requestId, entry, result: bufferPreview(model, entry.path) }; + const next = { ...model, fileExplorerPreviewRequestId: requestId, fileExplorerPreview: preview }; + return preview.result != null ? [next, commands] : [next, [...commands, loadPreviewCmd(preview, source)]]; +} + +export function applyFileExplorerPreview(model: WorkspaceModel, msg: FileExplorerPreviewMessage): WorkspaceModel { + return model.fileExplorerPreview?.requestId === msg.requestId && + model.fileExplorerPreview.entry === msg.entry && fileExplorerHasFocus(model) + ? { ...model, fileExplorerPreview: msg } + : model; +} + +function clearFileExplorerPreview(result: Result): Result { + return result[0].fileExplorerPreview == null + ? result + : [{ ...result[0], fileExplorerPreview: undefined }, result[1]]; +} + +function bufferPreview(model: WorkspaceModel, path: string): FilePreviewResult | undefined { + const editor = model.editor?.path === path ? model.editor : findWorkspaceBufferRecordByPath(model, path)?.editorProjection; + return editor == null ? undefined : { + kind: FilePreviewKinds.Loaded, lines: editor.lines.slice(0, FILE_PREVIEW_MAX_LINES), + truncated: editor.lines.length > FILE_PREVIEW_MAX_LINES, + }; +} + +function loadPreviewCmd(preview: FileExplorerPreview, source: FilePreviewSource): Cmd { + return () => { + let result: FilePreviewResult; + try { result = source.loadFilePreview(preview.entry.path); } + catch { result = { kind: FilePreviewKinds.Unavailable }; } + return { type: WorkspaceMessageTypes.FileExplorerPreviewResult, ...preview, result }; + }; +} diff --git a/src/app/workspace/file-explorer-view.ts b/src/app/workspace/file-explorer-view.ts index 8773b620..5ef41c14 100644 --- a/src/app/workspace/file-explorer-view.ts +++ b/src/app/workspace/file-explorer-view.ts @@ -49,6 +49,8 @@ function paintFileSearchResults(surface: Surface, model: WorkspaceModel, width: })); const content = stringToSurface(fitBlock(lines.join('\n'), width, height), width, height); applyBackground(content, model.jeditTheme.surface.drawer); - highlightFileSearchMatches(content, entries.slice(offset, offset + height), model.jeditTheme); + highlightFileSearchMatches(content, entries.slice(offset, offset + height), model.jeditTheme, { + selectedIndex: model.selectedIndex, hoveredIndex: model.fileHoveredIndex, + }); surface.blit(content, DRAWER_INNER_PAD, FILE_SEARCH_LIST_TOP); } diff --git a/src/app/workspace/file-search-input-key.ts b/src/app/workspace/file-search-input-key.ts index a97f04f9..29a09d33 100644 --- a/src/app/workspace/file-search-input-key.ts +++ b/src/app/workspace/file-search-input-key.ts @@ -1,11 +1,11 @@ import type { Cmd, KeyMsg } from '@flyingrobots/bijou-tui'; -import { fileSearchHasFocus, fileSearchState, fileSearchWithInput, EMPTY_FILE_SEARCH, type FileSearchState } from './file-search.js'; +import { fileSearchHasFocus, fileExplorerHasFocus, fileSearchState, fileSearchWithInput, EMPTY_FILE_SEARCH, type FileSearchState } from './file-search.js'; import type { WorkspaceModel } from './model.js'; import type { WorkspaceMsg } from './msg.js'; const KEYS = Object.freeze({ Left: 'left', Right: 'right', Home: 'home', End: 'end', Backspace: 'backspace', Delete: 'delete', - Space: 'space', Clear: 'u', + Space: 'space', Clear: 'u', Filter: '/', Escape: 'escape', Enter: 'enter', }); const LITERAL_SPACE = ' '; const SINGLE_KEY_LENGTH = 1; @@ -26,6 +26,9 @@ export function updateFileSearchInputKey( msg: KeyMsg, model: WorkspaceModel, ): [WorkspaceModel, Cmd[]] | undefined { + if (!fileExplorerHasFocus(model)) return undefined; + const mode = updateFilterMode(msg, model); + if (mode != null) return mode; if (!fileSearchHasFocus(model)) return undefined; const state = fileSearchState(model); const next = editFromKey(msg, state); @@ -76,3 +79,13 @@ function deleteAtCursor(state: FileSearchState): FileSearchState { characters.splice(state.cursorIndex, FORWARD_STEP); return { input: characters.join(''), cursorIndex: state.cursorIndex }; } + +function updateFilterMode(msg: KeyMsg, model: WorkspaceModel): [WorkspaceModel, Cmd[]] | undefined { + if (msg.ctrl || msg.alt) return undefined; + if (!model.fileSearchEditing) { + return msg.key === KEYS.Filter ? [{ ...model, fileSearchEditing: true }, []] : undefined; + } + return msg.key === KEYS.Escape || msg.key === KEYS.Enter + ? [{ ...model, fileSearchEditing: false }, []] + : undefined; +} diff --git a/src/app/workspace/file-search.ts b/src/app/workspace/file-search.ts index 9d4f3197..055901a2 100644 --- a/src/app/workspace/file-search.ts +++ b/src/app/workspace/file-search.ts @@ -24,17 +24,21 @@ export function fileSearchState(model: Pick): File return model.fileSearch ?? EMPTY_FILE_SEARCH; } -export function fileSearchHasFocus(model: WorkspaceModel): boolean { +export function fileExplorerHasFocus(model: WorkspaceModel): boolean { return model.fileDrawerOpen && model.focusPane === FocusPanes.Files && !model.settingsOpen && !model.quitConfirmOpen && !model.startupFileModalOpen && !model.commandLine?.active; } -export function fileSearchEntries(model: Pick): readonly FileSearchEntry[] { +export function fileSearchHasFocus(model: WorkspaceModel): boolean { + return fileExplorerHasFocus(model) && model.fileSearchEditing === true; +} + +export function fileSearchEntries(model: Pick): readonly FileSearchEntry[] { const query = fileSearchState(model).input.toLowerCase(); return model.entries.flatMap((entry, index) => { if (query.length === 0) return [{ entry, index, matchIndexes: [] }]; if (entry.kind === FileEntryKinds.Parent) return []; - const path = fuzzyPathMatches(entry.name, query) != null ? entry.name : relativeFilePath(model.workspaceRoot, entry); + const path = fuzzyPathMatches(entry.name, query) != null ? entry.name : relativeFilePath(model.workspaceSearchRoot ?? model.workspaceRoot, entry); const matches = fuzzyPathMatches(path, query); if (matches == null) return []; const nameStart = Array.from(path).length - Array.from(entry.name).length; @@ -44,7 +48,7 @@ export function fileSearchEntries(model: Pick index === model.selectedIndex); const next = entries[wrapIndex(position + delta, entries.length)]; - return next == null ? model : { ...model, selectedIndex: next.index }; + return next == null ? model : { ...model, selectedIndex: next.index, fileHoveredIndex: undefined }; } export function scrollFileSearchSelection(model: WorkspaceModel, delta: number): WorkspaceModel { @@ -86,5 +90,5 @@ export function scrollFileSearchSelection(model: WorkspaceModel, delta: number): if (entries.length === 0) return model; const position = entries.findIndex(({ index }) => index === model.selectedIndex); const next = entries[scrollIndexByRows(position, entries.length, delta)]; - return next == null ? model : { ...model, selectedIndex: next.index }; + return next == null ? model : { ...model, selectedIndex: next.index, fileHoveredIndex: undefined }; } diff --git a/src/app/workspace/global-key-bindings.ts b/src/app/workspace/global-key-bindings.ts index 2de245f0..98bffbf8 100644 --- a/src/app/workspace/global-key-bindings.ts +++ b/src/app/workspace/global-key-bindings.ts @@ -91,7 +91,7 @@ function updateFocusKey(msg: KeyMsg, model: WorkspaceModel): KeyBindingResult | if (msg.key !== WorkspaceKeys.Tab || !hasFocusablePeers(focusState)) { return undefined; } - return [{ ...model, focusPane: cycleFocusPane(focusState) }, []]; + return [{ ...model, focusPane: cycleFocusPane(focusState), fileSearchEditing: false }, []]; } function updateDrawerKey( diff --git a/src/app/workspace/init.ts b/src/app/workspace/init.ts index 58bebcaf..a7fd4c33 100644 --- a/src/app/workspace/init.ts +++ b/src/app/workspace/init.ts @@ -27,6 +27,7 @@ export interface WorkspaceInitialModelSnapshot { readonly i18n: I18nPort; readonly entries: readonly FileEntry[]; readonly nowMs: number; + readonly searchRoot?: string; } export function createInitialModel( @@ -40,10 +41,13 @@ export function createInitialModel( return { i18n, workspaceRoot: cwd, + workspaceSearchRoot: snapshot.searchRoot, cwd, entries, selectedIndex: 0, fileSearch: EMPTY_FILE_SEARCH, + fileSearchEditing: false, + fileExplorerPreviewRequestId: 0, editor: undefined, ...initialBufferRegistryState(), textRuntimeProfile, diff --git a/src/app/workspace/model.ts b/src/app/workspace/model.ts index 347c6687..43deca39 100644 --- a/src/app/workspace/model.ts +++ b/src/app/workspace/model.ts @@ -21,6 +21,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 { FileExplorerPreview } from './file-explorer-preview.js'; import type { FileSearchState } from './file-search.js'; export interface WorkspaceModel @@ -28,10 +29,15 @@ export interface WorkspaceModel WorkspaceCommandLineFilePreviewState { readonly i18n: I18nPort; readonly workspaceRoot: string; + readonly workspaceSearchRoot?: string; readonly cwd: string; readonly entries: readonly FileEntry[]; readonly selectedIndex: number; readonly fileSearch: FileSearchState; + readonly fileSearchEditing: boolean; + readonly fileHoveredIndex?: number; + readonly fileExplorerPreview?: FileExplorerPreview; + readonly fileExplorerPreviewRequestId: number; readonly editor?: EditorState; readonly buffers: WorkspaceBufferRegistry; readonly activeBufferId?: string; diff --git a/src/app/workspace/mouse.ts b/src/app/workspace/mouse.ts index 10a456d0..06276881 100644 --- a/src/app/workspace/mouse.ts +++ b/src/app/workspace/mouse.ts @@ -23,37 +23,27 @@ import { } from './viewport.js'; import { FocusPanes } from '../../ui/panel-focus.js'; import { beginWorkspaceSourceHighlightRefresh } from './workspace-source-highlight.js'; -import { fileSearchEntries, fileSearchState, scrollFileSearchSelection, selectedFileSearchEntry } from './file-search.js'; +import { fileSearchEntries, fileSearchState, scrollFileSearchSelection } from './file-search.js'; import { fileSearchCursorAtColumn } from '../../ui/file-search-input.js'; import { fileSearchInputOptions } from './file-explorer-view.js'; const DRAWER_PAD_MULTIPLIER = 2; const MOUSE_PRESS = 'press'; +const MOUSE_MOVE = 'move'; const MOUSE_BUTTON_LEFT = 'left'; -// Opening on click needs the filesystem and Echo dependencies the pointer path -// does not otherwise carry. Injecting a named opener keeps that dependency -// explicit and lets the selection-only behaviour stand on its own when the -// caller has nothing to open with. -export interface WorkspaceEntryOpener { - readonly nowMs: () => number; - readonly openEntry: ( - model: WorkspaceModel, - entry: NonNullable, - ) => [WorkspaceModel, Cmd[]]; -} - export function updateFromMouse( msg: MouseMsg, model: WorkspaceModel, sourceHighlighter: SourceHighlighter, - opener?: WorkspaceEntryOpener, ): [WorkspaceModel, Cmd[]] { + const hover = updateFileDrawerHover(msg, model); + if (hover != null) return [hover, []]; const input = updateFileSearchFromClick(msg, model); if (input != null) return [input, []]; const clicked = updateFileDrawerFromClick(msg, model); if (clicked != null) { - return openClickedEntry(clicked, opener); + return [clicked, []]; } const deltaRows = mouseScrollDeltaRows(msg); if (deltaRows === 0) { @@ -66,10 +56,10 @@ export function updateFromMouse( return updateEditorFromMouse(model, deltaRows, sourceHighlighter); } -// Row and column map onto the list the renderer drew: the drawer is blitted at +// Row and column map onto the pinned list the renderer drew: the drawer is blitted at // (layout.fileDrawer.x, WORKSPACE_BODY_TOP_OFFSET) and its content is inset by -// DRAWER_INNER_PAD. Entry index equals line index because the list does not -// scroll -- long directories are truncated rather than paged today. +// DRAWER_INNER_PAD. Filtered positions map through the scroll offset back to +// original entry indexes. function updateFileDrawerFromClick( msg: MouseMsg, model: WorkspaceModel, @@ -80,7 +70,7 @@ function updateFileDrawerFromClick( const index = fileDrawerEntryIndexAt(msg, model); return index == null ? undefined - : { ...model, focusPane: FocusPanes.Files, selectedIndex: index }; + : { ...model, focusPane: FocusPanes.Files, selectedIndex: index, fileSearchEditing: false }; } function fileDrawerEntryIndexAt( @@ -119,15 +109,10 @@ function fileDrawerEntryIndexAt( return entries[index]?.index; } -function openClickedEntry( - model: WorkspaceModel, - opener: WorkspaceEntryOpener | undefined, -): [WorkspaceModel, Cmd[]] { - const entry = selectedFileSearchEntry(model); - if (opener == null || entry == null) { - return [model, []]; - } - return opener.openEntry(model, entry); +function updateFileDrawerHover(msg: MouseMsg, model: WorkspaceModel): WorkspaceModel | undefined { + if (msg.action !== MOUSE_MOVE) return undefined; + const index = fileDrawerEntryIndexAt(msg, model); + return model.fileHoveredIndex === index ? model : { ...model, fileHoveredIndex: index }; } function updateScrollableDrawerFromMouse(model: WorkspaceModel, deltaRows: number): WorkspaceModel | undefined { @@ -162,7 +147,7 @@ function updateFileSearchFromClick(msg: MouseMsg, model: WorkspaceModel): Worksp const column = msg.col - layout.x - DRAWER_INNER_PAD; if (column < 0 || column >= innerWidth) return undefined; return { - ...model, focusPane: FocusPanes.Files, + ...model, focusPane: FocusPanes.Files, fileSearchEditing: true, fileSearch: { ...fileSearchState(model), cursorIndex: fileSearchCursorAtColumn(fileSearchInputOptions(model, innerWidth), column), diff --git a/src/app/workspace/msg.ts b/src/app/workspace/msg.ts index 66f5b733..7e615f76 100644 --- a/src/app/workspace/msg.ts +++ b/src/app/workspace/msg.ts @@ -1,3 +1,4 @@ +import type { FileExplorerPreviewMessage } from './file-explorer-preview.js'; import type { RuntimeIssue } from "@flyingrobots/bijou-tui"; import type { GraftDiagnosticsReport } from "../../ports/graft-diagnostics.js"; import type { GraftInfo } from "../../ports/graft-session.js"; @@ -50,6 +51,7 @@ const WORKSPACE_INPUT_MESSAGE_KEY = "key"; const WORKSPACE_INPUT_MESSAGE_MOUSE = "mouse"; export const WorkspaceMessageTypes = Object.freeze({ + FileExplorerPreviewResult: 'file-explorer-preview-result', DrawerProgress: WORKSPACE_MESSAGE_DRAWER_PROGRESS, GraftDiagnostics: WORKSPACE_MESSAGE_GRAFT_DIAGNOSTICS, GraftInfo: WORKSPACE_MESSAGE_GRAFT_INFO, @@ -96,6 +98,7 @@ export type WorkspaceWhyRangeOutcome = }; export type WorkspaceMsg = + | ({ readonly type: typeof WorkspaceMessageTypes.FileExplorerPreviewResult } & FileExplorerPreviewMessage) | { type: typeof WorkspaceMessageTypes.DrawerProgress; kind: DrawerKind; diff --git a/src/app/workspace/runtime.ts b/src/app/workspace/runtime.ts index de5defe0..563a9a47 100644 --- a/src/app/workspace/runtime.ts +++ b/src/app/workspace/runtime.ts @@ -1,3 +1,4 @@ +import { applyFileExplorerPreview, synchronizeFileExplorerPreview } from './file-explorer-preview.js'; import { createInitialModel, workspaceAnimationIsActive } from "./init.js"; import type { WorkspaceModel } from "./model.js"; import { @@ -84,9 +85,9 @@ function updateWorkspaceRuntime( msg: WorkspaceRuntimeMsg, model: WorkspaceModel, ): WorkspaceRuntimeResult { - return syncWorkspaceRuntimeResult( + return synchronizeFileExplorerPreview(syncWorkspaceRuntimeResult( rebaseFrameClock(deps, model, updateWorkspaceRuntimeState(deps, msg, model)), - ); + ), deps.filePreviewSource); } // While the workspace is idle the tick handler returns the model untouched, so @@ -199,6 +200,7 @@ function updateWorkspaceStateMessage( msg: WorkspaceRuntimeMsg, model: WorkspaceModel, ): WorkspaceRuntimeResult | undefined { + if (msg.type === WorkspaceMessageTypes.FileExplorerPreviewResult) return [applyFileExplorerPreview(model, msg), []]; const animation = updateAnimationStateMessage(msg, model); if (animation != null) { return animation; @@ -400,9 +402,6 @@ function updateWorkspaceInputMessage( msg, model, deps.sourceHighlighter, - deps.openEntry == null - ? undefined - : { nowMs: deps.nowMs, openEntry: deps.openEntry }, ); } return msg.type === WorkspaceInputMessageTypes.Key diff --git a/src/app/workspace/settings-key-bindings.ts b/src/app/workspace/settings-key-bindings.ts index b6cdb59c..c6144fd1 100644 --- a/src/app/workspace/settings-key-bindings.ts +++ b/src/app/workspace/settings-key-bindings.ts @@ -35,6 +35,7 @@ export function updateSettingsKey( return [{ ...model, settingsOpen: !model.settingsOpen, + fileSearchEditing: false, settingsFocusIndex: 0, settingsDiagnosticsOpen: false, }, []]; diff --git a/src/app/workspace/viewer-content.ts b/src/app/workspace/viewer-content.ts index a02d4450..3369e689 100644 --- a/src/app/workspace/viewer-content.ts +++ b/src/app/workspace/viewer-content.ts @@ -1,3 +1,4 @@ +import { renderFileExplorerPreview } from './file-explorer-preview-view.js'; import { createSurface, type Surface } from '@flyingrobots/bijou'; import { paintMarkdownPreview } from '../../ui/markdown-preview.js'; import { renderSourceViewer } from '../../ui/source-viewer.js'; @@ -27,6 +28,8 @@ export function createViewerContentRenderer(): ViewerContentRenderer { } export function renderViewer(model: WorkspaceModel, width: number, height: number): Surface { + const explorerPreview = renderFileExplorerPreview(model, width, height); + if (explorerPreview != null) return explorerPreview; const editor = displayEditorForWorkspaceModel(model); if (editor == null) { return renderJimLogoScreen(width, height, model.jeditTheme); diff --git a/src/app/workspace/viewer.ts b/src/app/workspace/viewer.ts index 81b8cc9e..f372bf9b 100644 --- a/src/app/workspace/viewer.ts +++ b/src/app/workspace/viewer.ts @@ -132,6 +132,7 @@ function paintWorkspaceFooter(screen: Surface, model: WorkspaceModel): void { i18n: model.i18n, focusPane: model.focusPane, fileDrawerOpen: model.fileDrawerOpen, + fileSearchEditing: model.fileSearchEditing, graftDrawerOpen: model.graftDrawerOpen, viewMode: model.viewMode, markdownPreviewActive: isWorkspaceMarkdownPreviewAvailable(model), diff --git a/src/app/workspace/workspace-runtime-dependencies.ts b/src/app/workspace/workspace-runtime-dependencies.ts index 13a62c31..be362fb4 100644 --- a/src/app/workspace/workspace-runtime-dependencies.ts +++ b/src/app/workspace/workspace-runtime-dependencies.ts @@ -1,3 +1,4 @@ +import type { FilePreviewSource } from '../../ports/file-preview.js'; import type { Cmd, KeyMsg, MouseMsg, ResizeMsg, RuntimeIssue } from '@flyingrobots/bijou-tui'; import type { DrawerKind } from '../../ui/drawer-layout.js'; import type { EditorFilePort } from '../../ports/editor-file.js'; @@ -12,17 +13,13 @@ import type { WorkspaceMsg } from './msg.js'; 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'; export type WorkspaceRuntimeMsg = WorkspaceMsg | ResizeMsg | KeyMsg | MouseMsg; export type WorkspaceRuntimeResult = [WorkspaceModel, Cmd[]]; export type WorkspaceResizeMsg = ResizeMsg; export interface WorkspaceRuntimeDependencies { - // 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. - readonly openEntry?: WorkspaceEntryOpener['openEntry']; + readonly filePreviewSource?: FilePreviewSource; readonly initialColumns: number; readonly initialRows: number; readonly initialWorkingDirectory: string; diff --git a/src/generated/i18n.ts b/src/generated/i18n.ts index 89919743..b5b8fbec 100644 --- a/src/generated/i18n.ts +++ b/src/generated/i18n.ts @@ -80,6 +80,7 @@ export type TranslationSchema = { readonly "esc_cancel": string; readonly "type_filter": string; readonly "arrows_select": string; + readonly "slash_filter": string; }; readonly "evidence": { readonly "head": string; @@ -173,6 +174,11 @@ export type TranslationSchema = { readonly "search_label": string; readonly "placeholder": string; readonly "empty": string; + readonly "preview_label": string; + readonly "preview_loading": string; + readonly "preview_binary": string; + readonly "preview_unavailable": string; + readonly "preview_truncated": string; }; }; @@ -183,47 +189,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_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","type_filter":"type filter","arrows_select":"↑/↓ select"},"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"},"fileExplorer":{"search_label":"Search","placeholder":"Filter files…","empty":"No matches"}}; +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_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","type_filter":"type filter","arrows_select":"↑/↓ select","slash_filter":"/ filter"},"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"},"fileExplorer":{"search_label":"Search","placeholder":"Filter files…","empty":"No matches","preview_label":"Preview","preview_loading":"Loading preview…","preview_binary":"Binary file — preview unavailable","preview_unavailable":"Preview unavailable","preview_truncated":"Preview truncated"}}; -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_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","type_filter":"saisir le filtre","arrows_select":"↑/↓ sélectionner"},"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"},"fileExplorer":{"search_label":"Recherche","placeholder":"Filtrer les fichiers…","empty":"Aucun résultat"}}; +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_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","type_filter":"saisir le filtre","arrows_select":"↑/↓ sélectionner","slash_filter":"/ filtrer"},"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"},"fileExplorer":{"search_label":"Recherche","placeholder":"Filtrer les fichiers…","empty":"Aucun résultat","preview_label":"Aperçu","preview_loading":"Chargement de l’aperçu…","preview_binary":"Fichier binaire — aperçu indisponible","preview_unavailable":"Aperçu indisponible","preview_truncated":"Aperçu tronqué"}}; -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"},"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"},"fileExplorer":{"search_label":"Cerca","placeholder":"Filtra file…","empty":"Nessun risultato"}}; +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"},"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"},"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"},"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"},"fileExplorer":{"search_label":"Suche","placeholder":"Dateien filtern…","empty":"Keine Treffer"}}; +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"},"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"},"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"},"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"},"fileExplorer":{"search_label":"Buscar","placeholder":"Filtrar archivos…","empty":"Sin resultados"}}; +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"},"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"},"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"}}; -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_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 취소","type_filter":"필터 입력","arrows_select":"↑/↓ 선택"},"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"},"fileExplorer":{"search_label":"검색","placeholder":"파일 필터…","empty":"일치 항목 없음"}}; +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_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 취소","type_filter":"필터 입력","arrows_select":"↑/↓ 선택","slash_filter":"/ 필터"},"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"},"fileExplorer":{"search_label":"검색","placeholder":"파일 필터…","empty":"일치 항목 없음","preview_label":"미리 보기","preview_loading":"미리 보기 불러오는 중…","preview_binary":"바이너리 파일 — 미리 보기 불가","preview_unavailable":"미리 보기 불가","preview_truncated":"미리 보기 생략됨"}}; -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_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 取消","type_filter":"絞り込み入力","arrows_select":"↑/↓ 選択"},"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"},"fileExplorer":{"search_label":"検索","placeholder":"ファイルを絞り込む…","empty":"一致なし"}}; +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_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 取消","type_filter":"絞り込み入力","arrows_select":"↑/↓ 選択","slash_filter":"/ 絞り込み"},"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"},"fileExplorer":{"search_label":"検索","placeholder":"ファイルを絞り込む…","empty":"一致なし","preview_label":"プレビュー","preview_loading":"プレビューを読み込み中…","preview_binary":"バイナリファイル — プレビュー不可","preview_unavailable":"プレビュー不可","preview_truncated":"プレビューを省略"}}; -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_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 取消","type_filter":"输入筛选","arrows_select":"↑/↓ 选择"},"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"},"fileExplorer":{"search_label":"搜索","placeholder":"筛选文件…","empty":"无匹配项"}}; +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_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 取消","type_filter":"输入筛选","arrows_select":"↑/↓ 选择","slash_filter":"/ 筛选"},"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"},"fileExplorer":{"search_label":"搜索","placeholder":"筛选文件…","empty":"无匹配项","preview_label":"预览","preview_loading":"正在加载预览…","preview_binary":"二进制文件 — 无法预览","preview_unavailable":"无法预览","preview_truncated":"预览已截断"}}; -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_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","type_filter":"digitar filtro","arrows_select":"↑/↓ selecionar"},"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"},"fileExplorer":{"search_label":"Buscar","placeholder":"Filtrar arquivos…","empty":"Nenhum resultado"}}; +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_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","type_filter":"digitar filtro","arrows_select":"↑/↓ selecionar","slash_filter":"/ filtrar"},"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"},"fileExplorer":{"search_label":"Buscar","placeholder":"Filtrar arquivos…","empty":"Nenhum resultado","preview_label":"Prévia","preview_loading":"Carregando prévia…","preview_binary":"Arquivo binário — sem prévia","preview_unavailable":"Prévia indisponível","preview_truncated":"Prévia truncada"}}; -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_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 отмена","type_filter":"ввод фильтра","arrows_select":"↑/↓ выбор"},"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"},"fileExplorer":{"search_label":"Поиск","placeholder":"Фильтр файлов…","empty":"Нет совпадений"}}; +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_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 отмена","type_filter":"ввод фильтра","arrows_select":"↑/↓ выбор","slash_filter":"/ фильтр"},"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"},"fileExplorer":{"search_label":"Поиск","placeholder":"Фильтр файлов…","empty":"Нет совпадений","preview_label":"Просмотр","preview_loading":"Загрузка просмотра…","preview_binary":"Двоичный файл — просмотр недоступен","preview_unavailable":"Просмотр недоступен","preview_truncated":"Просмотр сокращён"}}; -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_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 إلغاء","type_filter":"اكتب للتصفية","arrows_select":"↑/↓ تحديد"},"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"},"fileExplorer":{"search_label":"بحث","placeholder":"تصفية الملفات…","empty":"لا نتائج"}}; +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_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 إلغاء","type_filter":"اكتب للتصفية","arrows_select":"↑/↓ تحديد","slash_filter":"/ تصفية"},"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"},"fileExplorer":{"search_label":"بحث","placeholder":"تصفية الملفات…","empty":"لا نتائج","preview_label":"معاينة","preview_loading":"جارٍ تحميل المعاينة…","preview_binary":"ملف ثنائي — المعاينة غير متاحة","preview_unavailable":"المعاينة غير متاحة","preview_truncated":"المعاينة مختصرة"}}; -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_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","type_filter":"ketik filter","arrows_select":"↑/↓ pilih"},"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"},"fileExplorer":{"search_label":"Cari","placeholder":"Filter berkas…","empty":"Tidak ada hasil"}}; +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_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","type_filter":"ketik filter","arrows_select":"↑/↓ pilih","slash_filter":"/ filter"},"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"},"fileExplorer":{"search_label":"Cari","placeholder":"Filter berkas…","empty":"Tidak ada hasil","preview_label":"Pratinjau","preview_loading":"Memuat pratinjau…","preview_binary":"Berkas biner — pratinjau tidak tersedia","preview_unavailable":"Pratinjau tidak tersedia","preview_truncated":"Pratinjau dipotong"}}; -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_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","type_filter":"filtre yaz","arrows_select":"↑/↓ seç"},"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"},"fileExplorer":{"search_label":"Ara","placeholder":"Dosyaları filtrele…","empty":"Eşleşme yok"}}; +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_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","type_filter":"filtre yaz","arrows_select":"↑/↓ seç","slash_filter":"/ filtre"},"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"},"fileExplorer":{"search_label":"Ara","placeholder":"Dosyaları filtrele…","empty":"Eşleşme yok","preview_label":"Önizleme","preview_loading":"Önizleme yükleniyor…","preview_binary":"İkili dosya — önizleme yok","preview_unavailable":"Önizleme kullanılamıyor","preview_truncated":"Önizleme kısaltıldı"}}; -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_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 रद्द","type_filter":"फ़िल्टर लिखें","arrows_select":"↑/↓ चुनें"},"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"},"fileExplorer":{"search_label":"खोज","placeholder":"फ़ाइलें छाँटें…","empty":"कोई मेल नहीं"}}; +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_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 रद्द","type_filter":"फ़िल्टर लिखें","arrows_select":"↑/↓ चुनें","slash_filter":"/ फ़िल्टर"},"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"},"fileExplorer":{"search_label":"खोज","placeholder":"फ़ाइलें छाँटें…","empty":"कोई मेल नहीं","preview_label":"पूर्वावलोकन","preview_loading":"पूर्वावलोकन लोड हो रहा है…","preview_binary":"बाइनरी फ़ाइल — पूर्वावलोकन उपलब्ध नहीं","preview_unavailable":"पूर्वावलोकन उपलब्ध नहीं","preview_truncated":"पूर्वावलोकन संक्षिप्त है"}}; -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_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","type_filter":"filter typen","arrows_select":"↑/↓ selecteren"},"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"},"fileExplorer":{"search_label":"Zoeken","placeholder":"Bestanden filteren…","empty":"Geen resultaten"}}; +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_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","type_filter":"filter typen","arrows_select":"↑/↓ selecteren","slash_filter":"/ filter"},"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"},"fileExplorer":{"search_label":"Zoeken","placeholder":"Bestanden filteren…","empty":"Geen resultaten","preview_label":"Voorbeeld","preview_loading":"Voorbeeld laden…","preview_binary":"Binair bestand — geen voorbeeld","preview_unavailable":"Voorbeeld niet beschikbaar","preview_truncated":"Voorbeeld ingekort"}}; -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_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","type_filter":"wpisz filtr","arrows_select":"↑/↓ wybierz"},"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"},"fileExplorer":{"search_label":"Szukaj","placeholder":"Filtruj pliki…","empty":"Brak wyników"}}; +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_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","type_filter":"wpisz filtr","arrows_select":"↑/↓ wybierz","slash_filter":"/ filtr"},"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"},"fileExplorer":{"search_label":"Szukaj","placeholder":"Filtruj pliki…","empty":"Brak wyników","preview_label":"Podgląd","preview_loading":"Wczytywanie podglądu…","preview_binary":"Plik binarny — brak podglądu","preview_unavailable":"Podgląd niedostępny","preview_truncated":"Podgląd skrócony"}}; -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_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","type_filter":"nhập bộ lọc","arrows_select":"↑/↓ chọn"},"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"},"fileExplorer":{"search_label":"Tìm","placeholder":"Lọc tệp…","empty":"Không khớp"}}; +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_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","type_filter":"nhập bộ lọc","arrows_select":"↑/↓ chọn","slash_filter":"/ lọc"},"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"},"fileExplorer":{"search_label":"Tìm","placeholder":"Lọc tệp…","empty":"Không khớp","preview_label":"Xem trước","preview_loading":"Đang tải bản xem trước…","preview_binary":"Tệp nhị phân — không thể xem trước","preview_unavailable":"Không thể xem trước","preview_truncated":"Bản xem trước bị cắt"}}; -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_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 ยกเลิก","type_filter":"พิมพ์ตัวกรอง","arrows_select":"↑/↓ เลือก"},"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"},"fileExplorer":{"search_label":"ค้นหา","placeholder":"กรองไฟล์…","empty":"ไม่พบผลลัพธ์"}}; +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_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 ยกเลิก","type_filter":"พิมพ์ตัวกรอง","arrows_select":"↑/↓ เลือก","slash_filter":"/ กรอง"},"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"},"fileExplorer":{"search_label":"ค้นหา","placeholder":"กรองไฟล์…","empty":"ไม่พบผลลัพธ์","preview_label":"ตัวอย่าง","preview_loading":"กำลังโหลดตัวอย่าง…","preview_binary":"ไฟล์ไบนารี — ไม่สามารถแสดงตัวอย่าง","preview_unavailable":"ไม่สามารถแสดงตัวอย่าง","preview_truncated":"ตัวอย่างถูกตัด"}}; -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_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 لغو","type_filter":"نوشتن فیلتر","arrows_select":"↑/↓ انتخاب"},"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"},"fileExplorer":{"search_label":"جستجو","placeholder":"فیلتر پرونده‌ها…","empty":"بدون نتیجه"}}; +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_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 لغو","type_filter":"نوشتن فیلتر","arrows_select":"↑/↓ انتخاب","slash_filter":"/ فیلتر"},"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"},"fileExplorer":{"search_label":"جستجو","placeholder":"فیلتر پرونده‌ها…","empty":"بدون نتیجه","preview_label":"پیش‌نمایش","preview_loading":"در حال بارگذاری پیش‌نمایش…","preview_binary":"فایل دودویی — پیش‌نمایش در دسترس نیست","preview_unavailable":"پیش‌نمایش در دسترس نیست","preview_truncated":"پیش‌نمایش کوتاه شده"}}; -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_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 בטל","type_filter":"הקלדת סינון","arrows_select":"↑/↓ בחירה"},"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"},"fileExplorer":{"search_label":"חיפוש","placeholder":"סינון קבצים…","empty":"אין התאמות"}}; +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_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 בטל","type_filter":"הקלדת סינון","arrows_select":"↑/↓ בחירה","slash_filter":"/ סינון"},"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"},"fileExplorer":{"search_label":"חיפוש","placeholder":"סינון קבצים…","empty":"אין התאמות","preview_label":"תצוגה מקדימה","preview_loading":"טוען תצוגה מקדימה…","preview_binary":"קובץ בינארי — אין תצוגה מקדימה","preview_unavailable":"תצוגה מקדימה אינה זמינה","preview_truncated":"תצוגה מקדימה מקוצרת"}}; -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_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","type_filter":"retlif epyt","arrows_select":"tceles ↓/↑"},"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"},"fileExplorer":{"search_label":"hcraeS","placeholder":"…selif retliF","empty":"sehctam oN"}}; +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_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","type_filter":"retlif epyt","arrows_select":"tceles ↓/↑","slash_filter":"retlif /"},"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"},"fileExplorer":{"search_label":"hcraeS","placeholder":"…selif retliF","empty":"sehctam oN","preview_label":"weiverP","preview_loading":"…weiverp gnidaoL","preview_binary":"elbaliavanu weiverp — elif yraniB","preview_unavailable":"elbaliavanu weiverP","preview_truncated":"detacnurt weiverP"}}; 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/main-workspace.ts b/src/main-workspace.ts index 0875cf2b..24732887 100644 --- a/src/main-workspace.ts +++ b/src/main-workspace.ts @@ -1,3 +1,5 @@ +import { getDefaultContext } from '@flyingrobots/bijou'; +import { workspaceTerminalIO } from './adapters/workspace-terminal-io.js'; import { initDefaultContext } from '@flyingrobots/bijou-node'; import { run } from '@flyingrobots/bijou-tui'; import { createWorkspaceApp } from './adapters/workspace-app.js'; @@ -72,7 +74,8 @@ export async function runJeditWorkspace(): Promise { initialWorkingDirectory: DEFAULT_WORKING_DIRECTORY, ...workspaceInstrumentationFromEnv(process.env), }, productionText); - await run(app, { mouse: JEDIT_TERMINAL_MOUSE_OPTIONS.mouse }); + const context = getDefaultContext(); + await run(app, { ...JEDIT_TERMINAL_MOUSE_OPTIONS, ctx: { ...context, io: workspaceTerminalIO(context.io) } }); }); } diff --git a/src/ports/file-preview.ts b/src/ports/file-preview.ts new file mode 100644 index 00000000..bdc15d8d --- /dev/null +++ b/src/ports/file-preview.ts @@ -0,0 +1,12 @@ +export const FilePreviewKinds = Object.freeze({ Loaded: 'loaded', Binary: 'binary', Unavailable: 'unavailable' } as const); +export const FILE_PREVIEW_MAX_BYTES = 65_536; +export const FILE_PREVIEW_MAX_LINES = 200; + +export type FilePreviewResult = + | { readonly kind: typeof FilePreviewKinds.Loaded; readonly lines: readonly string[]; readonly truncated: boolean } + | { readonly kind: typeof FilePreviewKinds.Binary } + | { readonly kind: typeof FilePreviewKinds.Unavailable }; + +export interface FilePreviewSource { + readonly loadFilePreview: (path: string) => FilePreviewResult; +} diff --git a/src/ports/file-system.ts b/src/ports/file-system.ts index f29ebf6d..138577bd 100644 --- a/src/ports/file-system.ts +++ b/src/ports/file-system.ts @@ -15,6 +15,7 @@ export interface FileEntry { readonly kind: FileEntryKind; readonly name: string; readonly path: string; + readonly searchPath?: string; } export interface DirectoryIssue { diff --git a/src/ui/file-search-results.ts b/src/ui/file-search-results.ts index 50df34c6..956c2405 100644 --- a/src/ui/file-search-results.ts +++ b/src/ui/file-search-results.ts @@ -1,14 +1,20 @@ import type { Surface } from '@flyingrobots/bijou'; import type { FileSearchEntry } from '../app/workspace/file-search.js'; -import type { JeditTheme } from './jedit-theme.js'; +import { legibleOn } from './theme-contrast.js'; +import type { JeditTheme, JeditStyleToken } from './jedit-theme.js'; import { JEDIT_TEXT_MODIFIER } from './jedit-theme.js'; import { visibleLineLength } from './fit-line.js'; const ENTRY_PREFIX_WIDTH = 2; const CHARACTER_STEP = 1; +const MIN_SELECTION_CONTRAST = 4.5; +const HEX_RADIX = 16; +const HEX_PAIR_WIDTH = 2; -export function highlightFileSearchMatches(surface: Surface, entries: readonly FileSearchEntry[], theme: JeditTheme): void { - entries.forEach(({ entry, matchIndexes }, row) => { +export function highlightFileSearchMatches(surface: Surface, entries: readonly FileSearchEntry[], theme: JeditTheme, selection: FileSearchSelection): void { + entries.forEach(({ entry, matchIndexes, index: sourceIndex }, row) => { + const selected = sourceIndex === selection.selectedIndex; + paintFileSearchRow(surface, row, theme, { selected, hovered: sourceIndex === selection.hoveredIndex }); const characters = Array.from(entry.name); for (const index of matchIndexes) { const column = ENTRY_PREFIX_WIDTH + visibleLineLength(characters.slice(0, index).join('')); @@ -16,10 +22,39 @@ export function highlightFileSearchMatches(surface: Surface, entries: readonly F for (let x = column; x < Math.min(surface.width, column + width); x += CHARACTER_STEP) { const cell = surface.get(x, row); surface.set(x, row, { - ...cell, fg: theme.chrome.activeEdge.fg, fgRGB: theme.chrome.activeEdge.fgRGB, + ...cell, fg: selected ? cell.fg : theme.chrome.activeEdge.fg, + fgRGB: selected ? cell.fgRGB : theme.chrome.activeEdge.fgRGB, modifiers: [...(cell.modifiers ?? []), JEDIT_TEXT_MODIFIER.Bold], }); } } }); } + +export interface FileSearchSelection { + readonly selectedIndex: number; + readonly hoveredIndex?: number; +} + +interface FileSearchRowState { + readonly selected: boolean; + readonly hovered: boolean; +} + +function paintFileSearchRow(surface: Surface, row: number, theme: JeditTheme, state: FileSearchRowState): void { + if (!state.selected && !state.hovered) return; + const token = state.selected ? fileSelectionToken(theme) : theme.surface.currentLine; + for (let x = 0; x < surface.width; x += CHARACTER_STEP) { + const cell = surface.get(x, row); + surface.set(x, row, { ...cell, fg: token.fg, fgRGB: token.fgRGB, bg: token.bg, bgRGB: token.bgRGB }); + } +} + +function fileSelectionToken(theme: JeditTheme): JeditStyleToken { + const background = theme.cursor.normal.bgRGB; + const foreground = theme.surface.workspace.bgRGB; + if (background == null || foreground == null) return theme.cursor.normal; + const fgRGB = legibleOn(foreground, [background], MIN_SELECTION_CONTRAST); + const fg = `#${fgRGB.map((channel) => channel.toString(HEX_RADIX).padStart(HEX_PAIR_WIDTH, '0')).join('')}`; + return { ...theme.cursor.normal, fg, fgRGB }; +} diff --git a/src/ui/terminal-mouse.ts b/src/ui/terminal-mouse.ts index d73490a9..2eab6a5f 100644 --- a/src/ui/terminal-mouse.ts +++ b/src/ui/terminal-mouse.ts @@ -1,3 +1,4 @@ export const JEDIT_TERMINAL_MOUSE_OPTIONS = { mouse: true, + mouseMode: 'any', } as const; diff --git a/src/ui/workspace-chrome.ts b/src/ui/workspace-chrome.ts index a3e1dbca..24eb4663 100644 --- a/src/ui/workspace-chrome.ts +++ b/src/ui/workspace-chrome.ts @@ -41,6 +41,7 @@ const EditorModeKeys: Record = Object.freeze({ const FooterHintKeys = Object.freeze({ TypeFilter: 'type_filter', + SlashFilter: 'slash_filter', ArrowsSelect: 'arrows_select', JkMove: 'j_k_move', EnterChange: 'enter_change', @@ -96,6 +97,7 @@ export interface WorkspaceFooterState { readonly i18n: I18nPort; readonly focusPane: FocusPane; readonly fileDrawerOpen: boolean; + readonly fileSearchEditing?: boolean; readonly graftDrawerOpen: boolean; readonly viewMode: ViewMode; readonly markdownPreviewActive: boolean; @@ -324,9 +326,9 @@ function previewFooterHints(state: WorkspaceFooterState, t: FooterHintTranslator function fileDrawerFooterHints(state: WorkspaceFooterState, t: FooterHintTranslator): ReadonlyArray { return [ - t(FooterHintKeys.TypeFilter), - t(FooterHintKeys.ArrowsSelect), - t(FooterHintKeys.EnterOpen), + t(state.fileSearchEditing ? FooterHintKeys.TypeFilter : FooterHintKeys.JkMove), + t(state.fileSearchEditing ? FooterHintKeys.EscNormal : FooterHintKeys.SlashFilter), + state.fileSearchEditing ? undefined : t(FooterHintKeys.EnterOpen), t(FooterHintKeys.CtrlBClose), focusHint(state, t), ]; diff --git a/translations.json b/translations.json index a7f1c5b0..e7060a50 100644 --- a/translations.json +++ b/translations.json @@ -84,7 +84,8 @@ "gg_top": "gg top", "esc_cancel": "esc cancel", "type_filter": "type filter", - "arrows_select": "↑/↓ select" + "arrows_select": "↑/↓ select", + "slash_filter": "/ filter" }, "evidence": { "head": "head", @@ -177,7 +178,12 @@ "fileExplorer": { "search_label": "Search", "placeholder": "Filter files…", - "empty": "No matches" + "empty": "No matches", + "preview_label": "Preview", + "preview_loading": "Loading preview…", + "preview_binary": "Binary file — preview unavailable", + "preview_unavailable": "Preview unavailable", + "preview_truncated": "Preview truncated" } } }, @@ -265,7 +271,8 @@ "gg_top": "gg haut", "esc_cancel": "esc annuler", "type_filter": "saisir le filtre", - "arrows_select": "↑/↓ sélectionner" + "arrows_select": "↑/↓ sélectionner", + "slash_filter": "/ filtrer" }, "evidence": { "head": "tête", @@ -358,7 +365,12 @@ "fileExplorer": { "search_label": "Recherche", "placeholder": "Filtrer les fichiers…", - "empty": "Aucun résultat" + "empty": "Aucun résultat", + "preview_label": "Aperçu", + "preview_loading": "Chargement de l’aperçu…", + "preview_binary": "Fichier binaire — aperçu indisponible", + "preview_unavailable": "Aperçu indisponible", + "preview_truncated": "Aperçu tronqué" } } }, @@ -446,7 +458,8 @@ "gg_top": "gg cima", "esc_cancel": "esc annulla", "type_filter": "digita filtro", - "arrows_select": "↑/↓ seleziona" + "arrows_select": "↑/↓ seleziona", + "slash_filter": "/ filtra" }, "evidence": { "head": "testa", @@ -539,7 +552,12 @@ "fileExplorer": { "search_label": "Cerca", "placeholder": "Filtra file…", - "empty": "Nessun risultato" + "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" } } }, @@ -627,7 +645,8 @@ "gg_top": "gg oben", "esc_cancel": "esc abbrechen", "type_filter": "Filter eingeben", - "arrows_select": "↑/↓ auswählen" + "arrows_select": "↑/↓ auswählen", + "slash_filter": "/ filtern" }, "evidence": { "head": "Kopf", @@ -720,7 +739,12 @@ "fileExplorer": { "search_label": "Suche", "placeholder": "Dateien filtern…", - "empty": "Keine Treffer" + "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" } } }, @@ -808,7 +832,8 @@ "gg_top": "gg arriba", "esc_cancel": "esc cancelar", "type_filter": "escribir filtro", - "arrows_select": "↑/↓ seleccionar" + "arrows_select": "↑/↓ seleccionar", + "slash_filter": "/ filtrar" }, "evidence": { "head": "cabeza", @@ -901,7 +926,12 @@ "fileExplorer": { "search_label": "Buscar", "placeholder": "Filtrar archivos…", - "empty": "Sin resultados" + "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" } } }, @@ -989,7 +1019,8 @@ "gg_top": "gg 맨 위", "esc_cancel": "esc 취소", "type_filter": "필터 입력", - "arrows_select": "↑/↓ 선택" + "arrows_select": "↑/↓ 선택", + "slash_filter": "/ 필터" }, "evidence": { "head": "헤드", @@ -1082,7 +1113,12 @@ "fileExplorer": { "search_label": "검색", "placeholder": "파일 필터…", - "empty": "일치 항목 없음" + "empty": "일치 항목 없음", + "preview_label": "미리 보기", + "preview_loading": "미리 보기 불러오는 중…", + "preview_binary": "바이너리 파일 — 미리 보기 불가", + "preview_unavailable": "미리 보기 불가", + "preview_truncated": "미리 보기 생략됨" } } }, @@ -1170,7 +1206,8 @@ "gg_top": "gg 先頭", "esc_cancel": "esc 取消", "type_filter": "絞り込み入力", - "arrows_select": "↑/↓ 選択" + "arrows_select": "↑/↓ 選択", + "slash_filter": "/ 絞り込み" }, "evidence": { "head": "ヘッド", @@ -1263,7 +1300,12 @@ "fileExplorer": { "search_label": "検索", "placeholder": "ファイルを絞り込む…", - "empty": "一致なし" + "empty": "一致なし", + "preview_label": "プレビュー", + "preview_loading": "プレビューを読み込み中…", + "preview_binary": "バイナリファイル — プレビュー不可", + "preview_unavailable": "プレビュー不可", + "preview_truncated": "プレビューを省略" } } }, @@ -1351,7 +1393,8 @@ "gg_top": "gg 顶部", "esc_cancel": "esc 取消", "type_filter": "输入筛选", - "arrows_select": "↑/↓ 选择" + "arrows_select": "↑/↓ 选择", + "slash_filter": "/ 筛选" }, "evidence": { "head": "头", @@ -1444,7 +1487,12 @@ "fileExplorer": { "search_label": "搜索", "placeholder": "筛选文件…", - "empty": "无匹配项" + "empty": "无匹配项", + "preview_label": "预览", + "preview_loading": "正在加载预览…", + "preview_binary": "二进制文件 — 无法预览", + "preview_unavailable": "无法预览", + "preview_truncated": "预览已截断" } } }, @@ -1532,7 +1580,8 @@ "gg_top": "gg topo", "esc_cancel": "esc cancelar", "type_filter": "digitar filtro", - "arrows_select": "↑/↓ selecionar" + "arrows_select": "↑/↓ selecionar", + "slash_filter": "/ filtrar" }, "evidence": { "head": "cabeça", @@ -1625,7 +1674,12 @@ "fileExplorer": { "search_label": "Buscar", "placeholder": "Filtrar arquivos…", - "empty": "Nenhum resultado" + "empty": "Nenhum resultado", + "preview_label": "Prévia", + "preview_loading": "Carregando prévia…", + "preview_binary": "Arquivo binário — sem prévia", + "preview_unavailable": "Prévia indisponível", + "preview_truncated": "Prévia truncada" } } }, @@ -1713,7 +1767,8 @@ "gg_top": "gg верх", "esc_cancel": "esc отмена", "type_filter": "ввод фильтра", - "arrows_select": "↑/↓ выбор" + "arrows_select": "↑/↓ выбор", + "slash_filter": "/ фильтр" }, "evidence": { "head": "вершина", @@ -1806,7 +1861,12 @@ "fileExplorer": { "search_label": "Поиск", "placeholder": "Фильтр файлов…", - "empty": "Нет совпадений" + "empty": "Нет совпадений", + "preview_label": "Просмотр", + "preview_loading": "Загрузка просмотра…", + "preview_binary": "Двоичный файл — просмотр недоступен", + "preview_unavailable": "Просмотр недоступен", + "preview_truncated": "Просмотр сокращён" } } }, @@ -1894,7 +1954,8 @@ "gg_top": "gg أعلى", "esc_cancel": "esc إلغاء", "type_filter": "اكتب للتصفية", - "arrows_select": "↑/↓ تحديد" + "arrows_select": "↑/↓ تحديد", + "slash_filter": "/ تصفية" }, "evidence": { "head": "الرأس", @@ -1987,7 +2048,12 @@ "fileExplorer": { "search_label": "بحث", "placeholder": "تصفية الملفات…", - "empty": "لا نتائج" + "empty": "لا نتائج", + "preview_label": "معاينة", + "preview_loading": "جارٍ تحميل المعاينة…", + "preview_binary": "ملف ثنائي — المعاينة غير متاحة", + "preview_unavailable": "المعاينة غير متاحة", + "preview_truncated": "المعاينة مختصرة" } } }, @@ -2075,7 +2141,8 @@ "gg_top": "gg atas", "esc_cancel": "esc batal", "type_filter": "ketik filter", - "arrows_select": "↑/↓ pilih" + "arrows_select": "↑/↓ pilih", + "slash_filter": "/ filter" }, "evidence": { "head": "kepala", @@ -2168,7 +2235,12 @@ "fileExplorer": { "search_label": "Cari", "placeholder": "Filter berkas…", - "empty": "Tidak ada hasil" + "empty": "Tidak ada hasil", + "preview_label": "Pratinjau", + "preview_loading": "Memuat pratinjau…", + "preview_binary": "Berkas biner — pratinjau tidak tersedia", + "preview_unavailable": "Pratinjau tidak tersedia", + "preview_truncated": "Pratinjau dipotong" } } }, @@ -2256,7 +2328,8 @@ "gg_top": "gg üst", "esc_cancel": "esc iptal", "type_filter": "filtre yaz", - "arrows_select": "↑/↓ seç" + "arrows_select": "↑/↓ seç", + "slash_filter": "/ filtre" }, "evidence": { "head": "baş", @@ -2349,7 +2422,12 @@ "fileExplorer": { "search_label": "Ara", "placeholder": "Dosyaları filtrele…", - "empty": "Eşleşme yok" + "empty": "Eşleşme yok", + "preview_label": "Önizleme", + "preview_loading": "Önizleme yükleniyor…", + "preview_binary": "İkili dosya — önizleme yok", + "preview_unavailable": "Önizleme kullanılamıyor", + "preview_truncated": "Önizleme kısaltıldı" } } }, @@ -2437,7 +2515,8 @@ "gg_top": "gg ऊपर", "esc_cancel": "esc रद्द", "type_filter": "फ़िल्टर लिखें", - "arrows_select": "↑/↓ चुनें" + "arrows_select": "↑/↓ चुनें", + "slash_filter": "/ फ़िल्टर" }, "evidence": { "head": "शीर्ष", @@ -2530,7 +2609,12 @@ "fileExplorer": { "search_label": "खोज", "placeholder": "फ़ाइलें छाँटें…", - "empty": "कोई मेल नहीं" + "empty": "कोई मेल नहीं", + "preview_label": "पूर्वावलोकन", + "preview_loading": "पूर्वावलोकन लोड हो रहा है…", + "preview_binary": "बाइनरी फ़ाइल — पूर्वावलोकन उपलब्ध नहीं", + "preview_unavailable": "पूर्वावलोकन उपलब्ध नहीं", + "preview_truncated": "पूर्वावलोकन संक्षिप्त है" } } }, @@ -2618,7 +2702,8 @@ "gg_top": "gg boven", "esc_cancel": "esc annuleren", "type_filter": "filter typen", - "arrows_select": "↑/↓ selecteren" + "arrows_select": "↑/↓ selecteren", + "slash_filter": "/ filter" }, "evidence": { "head": "kop", @@ -2711,7 +2796,12 @@ "fileExplorer": { "search_label": "Zoeken", "placeholder": "Bestanden filteren…", - "empty": "Geen resultaten" + "empty": "Geen resultaten", + "preview_label": "Voorbeeld", + "preview_loading": "Voorbeeld laden…", + "preview_binary": "Binair bestand — geen voorbeeld", + "preview_unavailable": "Voorbeeld niet beschikbaar", + "preview_truncated": "Voorbeeld ingekort" } } }, @@ -2799,7 +2889,8 @@ "gg_top": "gg góra", "esc_cancel": "esc anuluj", "type_filter": "wpisz filtr", - "arrows_select": "↑/↓ wybierz" + "arrows_select": "↑/↓ wybierz", + "slash_filter": "/ filtr" }, "evidence": { "head": "głowa", @@ -2892,7 +2983,12 @@ "fileExplorer": { "search_label": "Szukaj", "placeholder": "Filtruj pliki…", - "empty": "Brak wyników" + "empty": "Brak wyników", + "preview_label": "Podgląd", + "preview_loading": "Wczytywanie podglądu…", + "preview_binary": "Plik binarny — brak podglądu", + "preview_unavailable": "Podgląd niedostępny", + "preview_truncated": "Podgląd skrócony" } } }, @@ -2980,7 +3076,8 @@ "gg_top": "gg đầu", "esc_cancel": "esc hủy", "type_filter": "nhập bộ lọc", - "arrows_select": "↑/↓ chọn" + "arrows_select": "↑/↓ chọn", + "slash_filter": "/ lọc" }, "evidence": { "head": "đầu", @@ -3073,7 +3170,12 @@ "fileExplorer": { "search_label": "Tìm", "placeholder": "Lọc tệp…", - "empty": "Không khớp" + "empty": "Không khớp", + "preview_label": "Xem trước", + "preview_loading": "Đang tải bản xem trước…", + "preview_binary": "Tệp nhị phân — không thể xem trước", + "preview_unavailable": "Không thể xem trước", + "preview_truncated": "Bản xem trước bị cắt" } } }, @@ -3161,7 +3263,8 @@ "gg_top": "gg บน", "esc_cancel": "esc ยกเลิก", "type_filter": "พิมพ์ตัวกรอง", - "arrows_select": "↑/↓ เลือก" + "arrows_select": "↑/↓ เลือก", + "slash_filter": "/ กรอง" }, "evidence": { "head": "เฮด", @@ -3254,7 +3357,12 @@ "fileExplorer": { "search_label": "ค้นหา", "placeholder": "กรองไฟล์…", - "empty": "ไม่พบผลลัพธ์" + "empty": "ไม่พบผลลัพธ์", + "preview_label": "ตัวอย่าง", + "preview_loading": "กำลังโหลดตัวอย่าง…", + "preview_binary": "ไฟล์ไบนารี — ไม่สามารถแสดงตัวอย่าง", + "preview_unavailable": "ไม่สามารถแสดงตัวอย่าง", + "preview_truncated": "ตัวอย่างถูกตัด" } } }, @@ -3342,7 +3450,8 @@ "gg_top": "gg بالا", "esc_cancel": "esc لغو", "type_filter": "نوشتن فیلتر", - "arrows_select": "↑/↓ انتخاب" + "arrows_select": "↑/↓ انتخاب", + "slash_filter": "/ فیلتر" }, "evidence": { "head": "سر", @@ -3435,7 +3544,12 @@ "fileExplorer": { "search_label": "جستجو", "placeholder": "فیلتر پرونده‌ها…", - "empty": "بدون نتیجه" + "empty": "بدون نتیجه", + "preview_label": "پیش‌نمایش", + "preview_loading": "در حال بارگذاری پیش‌نمایش…", + "preview_binary": "فایل دودویی — پیش‌نمایش در دسترس نیست", + "preview_unavailable": "پیش‌نمایش در دسترس نیست", + "preview_truncated": "پیش‌نمایش کوتاه شده" } } }, @@ -3523,7 +3637,8 @@ "gg_top": "gg למעלה", "esc_cancel": "esc בטל", "type_filter": "הקלדת סינון", - "arrows_select": "↑/↓ בחירה" + "arrows_select": "↑/↓ בחירה", + "slash_filter": "/ סינון" }, "evidence": { "head": "ראש", @@ -3616,7 +3731,12 @@ "fileExplorer": { "search_label": "חיפוש", "placeholder": "סינון קבצים…", - "empty": "אין התאמות" + "empty": "אין התאמות", + "preview_label": "תצוגה מקדימה", + "preview_loading": "טוען תצוגה מקדימה…", + "preview_binary": "קובץ בינארי — אין תצוגה מקדימה", + "preview_unavailable": "תצוגה מקדימה אינה זמינה", + "preview_truncated": "תצוגה מקדימה מקוצרת" } } } From 44d28cb96458254076d9c181e16e38da927cfd01 Mon Sep 17 00:00:00 2001 From: James Ross Date: Wed, 14 Oct 2026 11:43:18 -0700 Subject: [PATCH 6/8] Record modal explorer verification and refined interaction mockups --- .../0161-file-explorer-search-narrow.svg | 17 ++++++- .../design/0161-file-explorer-search-wide.svg | 17 ++++++- docs/design/0161-file-explorer-search.md | 44 +++++++++++++++---- 3 files changed, 67 insertions(+), 11 deletions(-) diff --git a/docs/design/0161-file-explorer-search-narrow.svg b/docs/design/0161-file-explorer-search-narrow.svg index 7819f1c3..9df51b1f 100644 --- a/docs/design/0161-file-explorer-search-narrow.svg +++ b/docs/design/0161-file-explorer-search-narrow.svg @@ -1 +1,16 @@ -Search: rn▌› render.ts renderer.test.ts↑/↓ select · Enter openInput stays pinned \ No newline at end of file + + + + + + + + Search: rn + › render.ts + renderer.test.ts + Preview · render.ts + export const example = 42; + j/k move · / filter · Enter open + + + diff --git a/docs/design/0161-file-explorer-search-wide.svg b/docs/design/0161-file-explorer-search-wide.svg index 61f1f398..4b471cfb 100644 --- a/docs/design/0161-file-explorer-search-wide.svg +++ b/docs/design/0161-file-explorer-search-wide.svg @@ -1 +1,16 @@ -Search: rn▌› render.ts renderer.test.ts↑/↓ select · Enter openInput stays pinned \ No newline at end of file + + + + + + + + Search: rn + › render.ts + renderer.test.ts + Preview · render.ts + export const example = 42; + j/k move · / filter · Enter open + + + diff --git a/docs/design/0161-file-explorer-search.md b/docs/design/0161-file-explorer-search.md index b8ea011d..411ebcc3 100644 --- a/docs/design/0161-file-explorer-search.md +++ b/docs/design/0161-file-explorer-search.md @@ -209,7 +209,7 @@ workspace-wide and symbol search separate. - [x] Slice 4: Explicit navigation/filter modes and mode-aware help. - [x] Slice 5: Pointer hover, click selection, and distinct row colors. - [x] Slice 6: Bounded read-only viewer previews and stale-result rejection. -- [ ] Slice 7: Real input boundary fixes, native verification, and refined docs. +- [x] Slice 7: Real input boundary fixes, native verification, and refined docs. ## Tests To Write First @@ -278,11 +278,37 @@ on the static-title removal in #339; neither PR is merged by this cycle. ### Refinement acceptance (2026-10-14) -- [ ] j/k navigates after opening; slash/click edits; Esc returns to navigation. -- [ ] Hover never selects or reads a file; click selects; colors remain distinct. -- [ ] Selected files preview without changing buffers, dirty text, or authority. -- [ ] Bounded, unavailable, binary, stale-result, and focus-return cases pass. -- [ ] Actual terminal Unicode, modified arrows, and hover packets are exercised. -- [ ] Full local checks and CI pass; documentation reflects final behavior. - -Refinement retrospective: pending implementation. +- [x] j/k navigates after opening; slash/click edits; Esc returns to navigation. +- [x] Hover never selects or reads a file; click selects; colors remain distinct. +- [x] Selected files preview without changing buffers, dirty text, or authority. +- [x] Bounded, unavailable, binary, stale-result, and focus-return cases pass. +- [x] Actual terminal Unicode, modified arrows, and hover packets are exercised. +- [x] Full local checks and CI pass; documentation reflects final behavior. + +Refinement retrospective: implemented in `3a940469`, replacing always-editing +focus with navigation/filter modes. Mouse click now selects, and hover stays +independent. Selection shows a bounded, read-only preview; Enter remains the +explicit native Echo open boundary. The previous active buffer is preserved. +Preview results carry original entry identity and a request id, rejecting stale +selection and closed-drawer results. Existing open buffers provide live text. + +The focused tests prove navigation and filter transitions, filtered pointer +geometry, independent hover, preview rendering, stale-result rejection, unsaved +text preservation, bounded UTF-8 reads, binary/unavailable handling, and a +nonblocking named-pipe case. Event-bus tests exercise the production input +adapter with Unicode, modified arrows, split CSI, lone Escape, and SGR hover. +Portable paths are normalized at the filesystem boundary. Selection/hover stay +distinct across 16 theme variants, with selected text checked for contrast. + +The full local check passes: 855 JavaScript tests, 12 existing skips, and all 99 +native tests. Quality has no regressions and shard coverage includes 134 specs. +Live 100×32 and 60×16 PTYs confirm j/k navigation, hover without selection or +preview reads, click selection/preview, labelled main-viewer content, real +Unicode filtering/opening, and modified Alt+Up input. Terminal snapshots were +visually inspected at both sizes. The narrow witness opened the selected file +through Echo only after Enter. + +No persistent index/cache, workspace crawl, semantic retrieval, or Graft symbol +scan was added; #341 remains the follow-on. No buffer authority was delegated +to the preview. PR #342 remains stacked on #339 and requires separate merge +authorization. CI completion is recorded in the PR. From d0036f2c0f7e023c80ff48b3275128d208a17c1b Mon Sep 17 00:00:00 2001 From: James Ross Date: Tue, 20 Oct 2026 02:49:00 -0700 Subject: [PATCH 7/8] Search only names for entries outside the search root After following .. above the startup workspace, entries fell back to their absolute path, so ancestor directory names matched every entry. Co-Authored-By: Claude Opus 5.5 --- spec/file-explorer-search.spec.mjs | 3 +++ src/app/workspace/file-search.ts | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/spec/file-explorer-search.spec.mjs b/spec/file-explorer-search.spec.mjs index cd5036aa..6e8ef44b 100644 --- a/spec/file-explorer-search.spec.mjs +++ b/spec/file-explorer-search.spec.mjs @@ -282,6 +282,9 @@ test('the input background remains distinct across built-in dark and light theme const initial = { ...model, workspaceRoot: 'C:\\repo', workspaceSearchRoot: portableFileSearchPath('C:\\repo') }; assert.equal(type(runtime, initial, 'src/index').selectedIndex, 0); assert.equal(type(runtime, initial, 'repo').selectedIndex, -1); + const above = { ...initial, entries: [{ kind: 'file', name: 'notes.md', path: 'C:\\repo\\notes.md', searchPath: portableFileSearchPath('C:\\repo\\notes.md') }] }; + assert.equal(type(runtime, { ...above, workspaceSearchRoot: portableFileSearchPath('C:\\repo\\src') }, 'repo').selectedIndex, -1); + assert.equal(type(runtime, above, 'notes').selectedIndex, 0); }); test('selected rows remain readable and distinct from hover across dark and light themes', async () => { diff --git a/src/app/workspace/file-search.ts b/src/app/workspace/file-search.ts index 055901a2..148db04d 100644 --- a/src/app/workspace/file-search.ts +++ b/src/app/workspace/file-search.ts @@ -49,7 +49,9 @@ export function fileSearchEntries(model: Pick Date: Tue, 20 Oct 2026 04:06:25 -0700 Subject: [PATCH 8/8] Preview current buffer readings and recover from partial escapes - Explorer previews of open buffers use the text-authority reading instead of the editor projection, which can be a stale opening window, and report truncation from the reading total. A reading that misses the file head falls back to reading the file. - The terminal input stream flushes any unfinished escape sequence after the escape delay and ends a CSI prefix at a control byte, so Ctrl+C and Escape are no longer swallowed after ESC[. Co-Authored-By: Claude Opus 5.5 --- spec/file-explorer-preview.spec.mjs | 15 +++++++++ spec/terminal-input-stream.spec.mjs | 36 ++++++++++++++++++++++ src/adapters/terminal-input-stream.ts | 31 ++++++++++++++----- src/app/workspace/file-explorer-preview.ts | 28 ++++++++++++++--- 4 files changed, 97 insertions(+), 13 deletions(-) create mode 100644 spec/terminal-input-stream.spec.mjs diff --git a/spec/file-explorer-preview.spec.mjs b/spec/file-explorer-preview.spec.mjs index a504f3bf..8e034bb1 100644 --- a/spec/file-explorer-preview.spec.mjs +++ b/spec/file-explorer-preview.spec.mjs @@ -78,6 +78,21 @@ test('the active unsaved buffer projection is previewed without filesystem reads assert.equal(opened.textAuthority, initial.textAuthority); }); +test('an open buffer previews its current reading rather than a stale opening window', async () => { + const { runtime, model, reads } = await setup(); + const cache = { coverage: 'window', startLine: 0, lines: ['CURRENT'], totalLineCount: 500, truncated: false }; + const textAuthority = { kind: 'opened', filePath: '/repo/alpha.txt', cache }; + const initial = { ...model, editor: { path: '/repo/alpha.txt', lines: ['STALE'] }, textAuthority }; + const [opened] = runtime.update(key('b', { ctrl: true }), initial); + assert.deepEqual(opened.fileExplorerPreview.result, { kind: 'loaded', lines: ['CURRENT'], truncated: true }); + assert.deepEqual(reads, []); + const [scrolled, commands] = runtime.update(key('b', { ctrl: true }), { + ...initial, textAuthority: { ...textAuthority, cache: { ...cache, startLine: 40 } }, + }); + assert.equal(scrolled.fileExplorerPreview.result, undefined); + assert.ok(commands.length > 0, 'a reading that misses the head falls back to the file'); +}); + test('click selection previews without opening and closing rejects its outstanding result', async () => { const { runtime, model } = await setup(); const [opened] = runtime.update(key('b', { ctrl: true }), model); diff --git a/spec/terminal-input-stream.spec.mjs b/spec/terminal-input-stream.spec.mjs new file mode 100644 index 00000000..14373fe0 --- /dev/null +++ b/spec/terminal-input-stream.spec.mjs @@ -0,0 +1,36 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import { setTimeout as delay } from 'node:timers/promises'; +import { importDist } from './workspace-helpers.mjs'; + +const ESCAPE_SETTLE_MS = 60; + +async function stream() { + const { terminalInputStream } = await importDist('adapters', 'terminal-input-stream.js'); + const tokens = []; + return { tokens, input: terminalInputStream((token) => tokens.push(token)) }; +} + +test('complete escape sequences and printable keys are split into tokens', async () => { + const { tokens, input } = await stream(); + input.accept('a\u001b[Ab'); + assert.deepEqual(tokens, ['a', '\u001b[A', 'b']); + input.dispose(); +}); + +test('an unfinished CSI prefix is flushed after the escape delay', async () => { + const { tokens, input } = await stream(); + input.accept('\u001b['); + await delay(ESCAPE_SETTLE_MS); + input.accept('\u0003'); + assert.deepEqual(tokens, ['\u001b[', '\u0003']); + input.dispose(); +}); + +test('control keys inside an unfinished CSI prefix are delivered on their own', async () => { + const { tokens, input } = await stream(); + input.accept('\u001b['); + input.accept('\u0003'); + assert.deepEqual(tokens, ['\u001b[', '\u0003']); + input.dispose(); +}); diff --git a/src/adapters/terminal-input-stream.ts b/src/adapters/terminal-input-stream.ts index 078ff5e3..006ad8df 100644 --- a/src/adapters/terminal-input-stream.ts +++ b/src/adapters/terminal-input-stream.ts @@ -3,6 +3,8 @@ const CSI_START = `${ESCAPE}[`; const SS3_START = `${ESCAPE}O`; const CSI_FINAL = /[@-~]/u; const ESCAPE_DELAY_MS = 25; +const FIRST_SEQUENCE_CODE = 0x20; +const LAST_SEQUENCE_CODE = 0x7e; export function terminalInputStream(emit: (token: string) => void) { let pending = ''; @@ -18,8 +20,10 @@ export function terminalInputStream(emit: (token: string) => void) { emit(pending.slice(0, length)); pending = pending.slice(length); } - if (pending === ESCAPE) { - timer = setTimeout(() => { pending = ''; emit(ESCAPE); }, ESCAPE_DELAY_MS); + // A bare Escape or an unfinished sequence (e.g. Alt+[ sending ESC[) is + // flushed after the delay so it can never hold later input hostage. + if (pending.length > 0) { + timer = setTimeout(() => { const flushed = pending; pending = ''; emit(flushed); }, ESCAPE_DELAY_MS); } }, dispose() { clearTimer(); pending = ''; }, @@ -28,12 +32,23 @@ export function terminalInputStream(emit: (token: string) => void) { function terminalTokenLength(raw: string): number | undefined { if (raw === ESCAPE) return undefined; - if (raw.startsWith(CSI_START)) { - for (let index = CSI_START.length; index < raw.length; index += 1) { - if (CSI_FINAL.test(raw[index] ?? '')) return index + 1; - } - return undefined; - } + if (raw.startsWith(CSI_START)) return csiTokenLength(raw); if (raw.startsWith(SS3_START)) return raw.length >= 3 ? 3 : undefined; return Array.from(raw)[0]?.length; } + +function csiTokenLength(raw: string): number | undefined { + for (let index = CSI_START.length; index < raw.length; index += 1) { + const character = raw[index] ?? ''; + // A control byte (Ctrl+C, Escape) cannot belong to a CSI sequence: end the + // unfinished prefix there so the control key is delivered on its own. + if (!isSequenceByte(character)) return index; + if (CSI_FINAL.test(character)) return index + 1; + } + return undefined; +} + +function isSequenceByte(character: string): boolean { + const code = character.charCodeAt(0); + return code >= FIRST_SEQUENCE_CODE && code <= LAST_SEQUENCE_CODE; +} diff --git a/src/app/workspace/file-explorer-preview.ts b/src/app/workspace/file-explorer-preview.ts index 16591f03..9c6c622c 100644 --- a/src/app/workspace/file-explorer-preview.ts +++ b/src/app/workspace/file-explorer-preview.ts @@ -3,6 +3,8 @@ import { FilePreviewKinds, FILE_PREVIEW_MAX_LINES, type FilePreviewSource, type import { FileEntryKinds, type FileEntry } from '../../ports/file-system.js'; import { fileExplorerHasFocus, selectedFileSearchEntry } from './file-search.js'; import { findWorkspaceBufferRecordByPath } from './workspace-buffer-registry.js'; +import { isWorkspaceTextAuthorityOpened } from './workspace-text-authority.js'; +import type { WorkspaceTextReadingCache } from './workspace-text-reading-cache.js'; import { WorkspaceMessageTypes, type WorkspaceMsg } from './msg.js'; import type { WorkspaceModel } from './model.js'; @@ -17,6 +19,8 @@ export interface FileExplorerPreviewMessage extends FileExplorerPreview { } type Result = [WorkspaceModel, Cmd[]]; +const FIRST_LINE = 0; +type LoadedFilePreview = Extract; export function synchronizeFileExplorerPreview(result: Result, source: FilePreviewSource | undefined): Result { const [model, commands] = result; @@ -42,12 +46,26 @@ function clearFileExplorerPreview(result: Result): Result { : [{ ...result[0], fileExplorerPreview: undefined }, result[1]]; } +// Open buffers preview their current text-authority reading, not the editor +// projection, which can be a stale opening window. A reading that does not start +// at the first line cannot show the file's head, so the file is read instead. function bufferPreview(model: WorkspaceModel, path: string): FilePreviewResult | undefined { - const editor = model.editor?.path === path ? model.editor : findWorkspaceBufferRecordByPath(model, path)?.editorProjection; - return editor == null ? undefined : { - kind: FilePreviewKinds.Loaded, lines: editor.lines.slice(0, FILE_PREVIEW_MAX_LINES), - truncated: editor.lines.length > FILE_PREVIEW_MAX_LINES, - }; + const authority = isWorkspaceTextAuthorityOpened(model.textAuthority) && model.textAuthority.filePath === path + ? model.textAuthority + : findWorkspaceBufferRecordByPath(model, path)?.textAuthority; + if (authority != null) return readingPreview(authority.cache); + return model.editor?.path === path ? linesPreview(model.editor.lines, model.editor.lines.length) : undefined; +} + +function readingPreview(cache: WorkspaceTextReadingCache | undefined): FilePreviewResult | undefined { + if (cache == null || cache.startLine !== FIRST_LINE) return undefined; + const preview = linesPreview(cache.lines, cache.totalLineCount); + return cache.truncated ? { ...preview, truncated: true } : preview; +} + +function linesPreview(lines: readonly string[], totalLineCount: number): LoadedFilePreview { + const shown = lines.slice(0, FILE_PREVIEW_MAX_LINES); + return { kind: FilePreviewKinds.Loaded, lines: shown, truncated: totalLineCount > shown.length }; } function loadPreviewCmd(preview: FileExplorerPreview, source: FilePreviewSource): Cmd {