@cueloop/client 0.1.0-alpha.10 → 0.1.0-alpha.100
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/native/build-pty.sh +51 -0
- package/native/build.sh +82 -0
- package/native/darwin-arm64/libcuelooppty.dylib +0 -0
- package/native/darwin-arm64/libcueloopvt.dylib +0 -0
- package/native/src/pty.zig +193 -0
- package/native/src/shim.zig +114 -0
- package/package.json +42 -34
- package/src/annotations/annotation-palette.ts +62 -0
- package/src/annotations/components/AnnotationCards.stories.tsx +86 -0
- package/src/annotations/components/AnnotationCards.test.tsx +42 -0
- package/src/annotations/components/AnnotationCards.tsx +364 -0
- package/src/annotations/components/DiscussionMarkerRail.stories.tsx +54 -0
- package/src/annotations/components/DiscussionMarkerRail.tsx +186 -0
- package/src/annotations/discussions.ts +77 -0
- package/src/annotations/thread-selection.test.ts +210 -0
- package/src/annotations/thread-selection.ts +242 -0
- package/src/annotations/use-annotation-surface.tsx +1238 -0
- package/src/app/App.appearance.test.tsx +53 -0
- package/src/{App.autoclose.test.tsx → app/App.autoclose.test.tsx} +77 -51
- package/src/app/App.deleted-scroll.test.tsx +187 -0
- package/src/app/App.diff.test.tsx +313 -0
- package/src/app/App.export.test.tsx +105 -0
- package/src/app/App.focus.test.ts +89 -0
- package/src/app/App.identity.test.tsx +84 -0
- package/src/app/App.plan-render.test.tsx +209 -0
- package/src/app/App.plan-review.test.tsx +428 -0
- package/src/app/App.prototype-markdown.test.tsx +128 -0
- package/src/app/App.prototype.test.tsx +176 -0
- package/src/app/App.readonly.test.tsx +210 -0
- package/src/app/App.regression.test.tsx +99 -0
- package/src/app/App.rendering.test.tsx +134 -0
- package/src/app/App.share-dialog.test.tsx +177 -0
- package/src/app/App.submit-confirm.test.tsx +267 -0
- package/src/app/App.test.tsx +496 -0
- package/src/app/App.thread-agent.test.ts +12 -0
- package/src/app/App.toast.test.tsx +124 -0
- package/src/app/App.tsx +1650 -0
- package/src/app/App.visual.test.tsx +59 -0
- package/src/app/App.walk.test.tsx +299 -0
- package/src/app/App.workbench.test.tsx +1149 -0
- package/src/app/app-key-state.ts +61 -0
- package/src/app/app-screens.tsx +606 -0
- package/src/app/app-view-model.ts +168 -0
- package/src/app/components/AppHeader.stories.tsx +50 -0
- package/src/app/components/AppHeader.tsx +81 -0
- package/src/app/components/AppShell.brand.test.tsx +58 -0
- package/src/app/components/AppShell.extensions.test.tsx +191 -0
- package/src/app/components/AppShell.stories.tsx +89 -0
- package/src/app/components/AppShell.tsx +358 -0
- package/src/app/components/AppShell.zoom.test.tsx +149 -0
- package/src/app/components/Breadcrumb.stories.tsx +19 -0
- package/src/app/components/Breadcrumb.tsx +45 -0
- package/src/app/components/MenuBar.stories.tsx +28 -0
- package/src/app/components/MenuBar.tsx +44 -0
- package/src/app/components/PaneGrid.stories.tsx +84 -0
- package/src/app/components/PaneGrid.tsx +128 -0
- package/src/app/components/PanelColumn.stories.tsx +25 -0
- package/src/app/components/PanelColumn.tsx +93 -0
- package/src/app/components/ShellHeader.stories.tsx +50 -0
- package/src/app/components/ShellHeader.tsx +137 -0
- package/src/app/components/WorkspacePanels.stories.tsx +36 -0
- package/src/app/components/WorkspacePanels.tsx +120 -0
- package/src/app/components/pages/Workspace.stories.tsx +181 -0
- package/src/app/intent-dispatch.test.ts +728 -0
- package/src/app/intent-dispatch.ts +687 -0
- package/src/app/launch-layout.test.ts +78 -0
- package/src/app/launch-layout.ts +57 -0
- package/src/app/ready-signal.test.ts +85 -0
- package/src/app/ready-signal.ts +52 -0
- package/src/app/run.ts +109 -0
- package/src/{serve.test.ts → app/serve.test.ts} +24 -12
- package/src/app/serve.ts +111 -0
- package/src/app/version.ts +5 -0
- package/src/appearance/components/theme-context.stories.tsx +46 -0
- package/src/appearance/components/theme-context.tsx +37 -0
- package/src/appearance/theme-presets.test.ts +51 -0
- package/src/appearance/theme-presets.ts +219 -0
- package/src/appearance/theme.ts +112 -0
- package/src/appearance/visual/contrast.test.ts +35 -0
- package/src/appearance/visual/contrast.ts +33 -0
- package/src/diff/components/AnnotatableFileView.stories.tsx +42 -0
- package/src/diff/components/AnnotatableFileView.test.tsx +157 -0
- package/src/diff/components/AnnotatableFileView.tsx +141 -0
- package/src/diff/components/BareWorkbenchFileView.stories.tsx +27 -0
- package/src/diff/components/BareWorkbenchFileView.tsx +63 -0
- package/src/diff/components/DiffContentView.scroll.test.tsx +434 -0
- package/src/diff/components/DiffContentView.split.test.tsx +140 -0
- package/src/diff/components/DiffContentView.stories.tsx +80 -0
- package/src/diff/components/DiffContentView.syntax.test.tsx +82 -0
- package/src/diff/components/DiffContentView.tsx +1055 -0
- package/src/diff/components/WalkWizard.stories.tsx +100 -0
- package/src/diff/components/WalkWizard.tsx +146 -0
- package/src/diff/components/diff-content-view-layout.test.ts +198 -0
- package/src/diff/components/diff-content-view-layout.ts +84 -0
- package/src/diff/diff-fold.test.ts +78 -0
- package/src/diff/diff-fold.ts +126 -0
- package/src/diff/diff-hunk-curate.test.ts +315 -0
- package/src/diff/diff-hunk-curate.ts +152 -0
- package/src/diff/diff-intraline.test.ts +239 -0
- package/src/diff/diff-intraline.ts +238 -0
- package/src/diff/diff-syntax.test.ts +125 -0
- package/src/diff/diff-syntax.ts +186 -0
- package/src/diff/split-diff.test.ts +110 -0
- package/src/diff/split-diff.ts +144 -0
- package/src/diff/view-diff.test.ts +325 -0
- package/src/diff/view-diff.ts +522 -0
- package/src/diff/walk.test.ts +121 -0
- package/src/diff/walk.ts +73 -0
- package/src/index.ts +31 -2
- package/src/integrations/attribution.test.ts +31 -0
- package/src/integrations/attribution.ts +42 -0
- package/src/integrations/client-extension-registry.test.tsx +61 -0
- package/src/integrations/client-extension-registry.ts +159 -0
- package/src/integrations/clipboard.test.ts +23 -0
- package/src/integrations/clipboard.ts +33 -0
- package/src/integrations/components/ExtensionZone.stories.tsx +28 -0
- package/src/integrations/components/ExtensionZone.tsx +145 -0
- package/src/integrations/components/ShareDialog.stories.tsx +121 -0
- package/src/integrations/components/ShareDialog.tsx +550 -0
- package/src/integrations/components/share-dialog-store.ts +55 -0
- package/src/integrations/editor.test.ts +149 -0
- package/src/integrations/editor.ts +160 -0
- package/src/integrations/github-identity.test.ts +58 -0
- package/src/integrations/github-identity.ts +41 -0
- package/src/integrations/herdr.test.ts +43 -0
- package/src/integrations/herdr.ts +38 -0
- package/src/integrations/integrations.test.ts +41 -0
- package/src/integrations/integrations.ts +32 -0
- package/src/integrations/pasted-image.test.ts +67 -0
- package/src/integrations/pasted-image.ts +60 -0
- package/src/integrations/share.ts +238 -0
- package/src/keyboard/components/CompletionOverlay.stories.tsx +37 -0
- package/src/keyboard/components/CompletionOverlay.tsx +75 -0
- package/src/keyboard/components/HandleChipsInput.stories.tsx +24 -0
- package/src/keyboard/components/HandleChipsInput.test.tsx +35 -0
- package/src/keyboard/components/HandleChipsInput.tsx +143 -0
- package/src/keyboard/components/KeybindsDialog.stories.tsx +31 -0
- package/src/keyboard/components/KeybindsDialog.tsx +103 -0
- package/src/keyboard/components/NavModeHint.stories.tsx +40 -0
- package/src/keyboard/components/NavModeHint.tsx +31 -0
- package/src/keyboard/components/SlashComposer.stories.tsx +37 -0
- package/src/keyboard/components/SlashComposer.tsx +87 -0
- package/src/keyboard/key-bindings.test.ts +156 -0
- package/src/keyboard/key-bindings.ts +459 -0
- package/src/keyboard/keymap.test.ts +611 -0
- package/src/keyboard/keymap.ts +395 -0
- package/src/keyboard/skills.test.ts +74 -0
- package/src/keyboard/skills.ts +75 -0
- package/src/keyboard/slash-palette.test.ts +103 -0
- package/src/keyboard/slash-palette.ts +129 -0
- package/src/keyboard/thread-chords.test.ts +79 -0
- package/src/keyboard/thread-chords.ts +163 -0
- package/src/keyboard/use-shared-keyboard.ts +55 -0
- package/src/markdown/__snapshots__/view-plan.regression.test.ts.snap +100 -0
- package/src/markdown/components/MarkdownGridBlock.stories.tsx +33 -0
- package/src/markdown/components/MarkdownGridBlock.tsx +72 -0
- package/src/markdown/components/MarkdownThreadEditor.stories.tsx +15 -0
- package/src/markdown/components/MarkdownThreadEditor.test.tsx +98 -0
- package/src/markdown/components/MarkdownThreadEditor.tsx +160 -0
- package/src/markdown/components/ThreadView.selection.test.tsx +951 -0
- package/src/markdown/components/ThreadView.slash.test.tsx +146 -0
- package/src/markdown/components/ThreadView.stories.tsx +26 -0
- package/src/markdown/components/ThreadView.tsx +560 -0
- package/src/markdown/mark-runs.ts +121 -0
- package/src/markdown/markdown-editor-syntax-style.ts +50 -0
- package/src/markdown/markdown-grid.test.ts +101 -0
- package/src/markdown/markdown-grid.ts +203 -0
- package/src/markdown/markdown-highlight.test.ts +61 -0
- package/src/markdown/markdown-highlight.ts +124 -0
- package/src/markdown/thread-code-syntax.ts +45 -0
- package/src/markdown/use-thread-body-editing.tsx +54 -0
- package/src/markdown/view-plan.regression.test.ts +60 -0
- package/src/markdown/view-plan.test.ts +545 -0
- package/src/markdown/view-plan.ts +1009 -0
- package/src/performance/otlp-trace.test.ts +119 -0
- package/src/performance/otlp-trace.ts +134 -0
- package/src/performance/perf-report.ts +18 -0
- package/src/performance/perf-timings.test.ts +18 -0
- package/src/performance/perf-timings.ts +39 -0
- package/src/prototype/components/PrototypeContentView.stories.tsx +19 -0
- package/src/prototype/components/PrototypeContentView.tsx +442 -0
- package/src/prototype/prototype-browser.test.ts +81 -0
- package/src/prototype/prototype-browser.ts +321 -0
- package/src/prototype/prototype-pixels.tsx +35 -0
- package/src/settings/components/SettingsDialog.stories.tsx +117 -0
- package/src/settings/components/SettingsDialog.tsx +183 -0
- package/src/settings/components/SettingsRows.stories.tsx +18 -0
- package/src/settings/components/SettingsRows.tsx +112 -0
- package/src/settings/components/quick-actions-editor.stories.tsx +58 -0
- package/src/settings/components/quick-actions-editor.test.tsx +120 -0
- package/src/settings/components/quick-actions-editor.tsx +222 -0
- package/src/settings/config-identity.test.ts +78 -0
- package/src/settings/config.test.ts +676 -0
- package/src/settings/config.ts +628 -0
- package/src/settings/use-settings-dialog.tsx +351 -0
- package/src/stories/__snapshots__/stories.test.tsx.snap +2358 -0
- package/src/stories/stories-app.tsx +196 -0
- package/src/stories/stories.test.tsx +74 -0
- package/src/stories/story-fixtures.ts +102 -0
- package/src/stories/story-tree.test.ts +108 -0
- package/src/stories/story.ts +154 -0
- package/src/terminal/ansi-screen.test.ts +28 -0
- package/src/terminal/ansi-screen.ts +72 -0
- package/src/terminal/components/AnsiScreen.stories.tsx +12 -0
- package/src/terminal/components/AnsiScreen.tsx +28 -0
- package/src/terminal/components/terminal-pane.test.tsx +40 -0
- package/src/terminal/components/terminal-pane.ts +196 -0
- package/src/terminal/ghostty-terminal.test.ts +85 -0
- package/src/terminal/ghostty-terminal.ts +197 -0
- package/src/terminal/kitty-image.test.ts +66 -0
- package/src/terminal/kitty-image.ts +137 -0
- package/src/terminal/pty.test.ts +81 -0
- package/src/terminal/pty.ts +246 -0
- package/src/testing/test-support.test.ts +264 -0
- package/src/testing/test-support.ts +367 -0
- package/src/thread/agent-continuation.test.ts +32 -0
- package/src/thread/agent-continuation.ts +13 -0
- package/src/thread/agent-transcript.test.ts +161 -0
- package/src/thread/agent-transcript.ts +336 -0
- package/src/thread/components/AgentConfigControls.stories.tsx +39 -0
- package/src/thread/components/AgentConfigControls.test.tsx +112 -0
- package/src/thread/components/AgentConfigControls.tsx +114 -0
- package/src/thread/components/AgentThreadPane.stories.tsx +105 -0
- package/src/thread/components/AgentThreadPane.test.tsx +829 -0
- package/src/thread/components/AgentThreadPane.tsx +367 -0
- package/src/thread/components/ThreadFooter.stories.tsx +30 -0
- package/src/thread/components/ThreadFooter.test.tsx +49 -0
- package/src/thread/components/ThreadFooter.tsx +77 -0
- package/src/thread/components/ThreadTree.stories.tsx +46 -0
- package/src/thread/components/ThreadTree.test.tsx +41 -0
- package/src/thread/components/ThreadTree.tsx +267 -0
- package/src/thread/components/thread-tree.test.ts +137 -0
- package/src/thread/components/thread-tree.ts +112 -0
- package/src/thread/thread-controller.curate.test.ts +502 -0
- package/src/thread/thread-controller.derivation.test.ts +62 -0
- package/src/thread/thread-controller.live-diff.test.ts +357 -0
- package/src/thread/thread-controller.optimistic.test.ts +69 -0
- package/src/thread/thread-controller.share.test.ts +573 -0
- package/src/thread/thread-controller.switch-cache.test.ts +174 -0
- package/src/thread/thread-controller.tree.test.ts +263 -0
- package/src/thread/thread-controller.ts +2018 -0
- package/src/thread/tree-view.test.ts +128 -0
- package/src/thread/tree-view.ts +146 -0
- package/src/thread/use-thread-agent.test.tsx +208 -0
- package/src/thread/use-thread-agent.ts +111 -0
- package/src/ui/components/CodeBlock.stories.tsx +42 -0
- package/src/ui/components/CodeBlock.tsx +80 -0
- package/src/ui/components/ConfirmCard.stories.tsx +37 -0
- package/src/ui/components/ConfirmCard.tsx +151 -0
- package/src/ui/components/ConfirmDialog.stories.tsx +21 -0
- package/src/ui/components/ConfirmDialog.tsx +72 -0
- package/src/ui/components/OverlayScrollbar.stories.tsx +33 -0
- package/src/ui/components/OverlayScrollbar.test.tsx +139 -0
- package/src/ui/components/OverlayScrollbar.tsx +129 -0
- package/src/ui/components/PromptDialog.stories.tsx +36 -0
- package/src/ui/components/PromptDialog.test.tsx +65 -0
- package/src/ui/components/PromptDialog.tsx +99 -0
- package/src/ui/components/RootOverlay.stories.tsx +45 -0
- package/src/ui/components/RootOverlay.tsx +49 -0
- package/src/ui/components/ScrollArea.stories.tsx +20 -0
- package/src/ui/components/ScrollArea.tsx +61 -0
- package/src/ui/components/Toast.stories.tsx +19 -0
- package/src/ui/components/Toast.tsx +54 -0
- package/src/ui/components/Tooltip.stories.tsx +26 -0
- package/src/ui/components/Tooltip.tsx +72 -0
- package/src/ui/components/menu-control.stories.tsx +35 -0
- package/src/ui/components/menu-control.tsx +43 -0
- package/src/ui/components/primitives/Button.stories.tsx +32 -0
- package/src/ui/components/primitives/Button.tsx +57 -0
- package/src/ui/components/primitives/Card.stories.tsx +25 -0
- package/src/ui/components/primitives/Card.tsx +62 -0
- package/src/ui/components/primitives/Dialog.stories.tsx +24 -0
- package/src/ui/components/primitives/Dialog.test.tsx +38 -0
- package/src/ui/components/primitives/Dialog.tsx +69 -0
- package/src/ui/components/primitives/DialogActions.stories.tsx +20 -0
- package/src/ui/components/primitives/DialogActions.tsx +39 -0
- package/src/ui/components/primitives/IconButton.stories.tsx +36 -0
- package/src/ui/components/primitives/IconButton.tsx +58 -0
- package/src/ui/components/primitives/StatusBar.stories.tsx +11 -0
- package/src/ui/components/primitives/StatusBar.tsx +23 -0
- package/src/ui/components/primitives/Switch.stories.tsx +17 -0
- package/src/ui/components/primitives/Switch.tsx +32 -0
- package/src/ui/components/primitives/Tabs.stories.tsx +29 -0
- package/src/ui/components/primitives/Tabs.tsx +101 -0
- package/src/ui/components/primitives/Toolbar.stories.tsx +17 -0
- package/src/ui/components/primitives/Toolbar.tsx +14 -0
- package/src/ui/components/primitives/Tree.stories.tsx +124 -0
- package/src/ui/components/primitives/Tree.tsx +133 -0
- package/src/ui/components/primitives/frame.test.ts +12 -0
- package/src/ui/components/primitives/frame.ts +17 -0
- package/src/ui/components/primitives/icons.ts +61 -0
- package/src/ui/components/primitives/tree-model.test.ts +99 -0
- package/src/ui/components/primitives/tree-model.ts +137 -0
- package/src/ui/components/syntax-highlight.ts +133 -0
- package/src/ui/components/truncate-text.ts +7 -0
- package/src/ui/components/truncate-title.test.ts +31 -0
- package/src/ui/components/truncate-title.ts +15 -0
- package/src/ui/use-frame-measure.ts +74 -0
- package/src/ui/use-terminal-virtualizer.ts +259 -0
- package/src/workbench/components/ChangesColumn.stories.tsx +44 -0
- package/src/workbench/components/ChangesColumn.tsx +203 -0
- package/src/workbench/components/ChangesFileTree.test.tsx +181 -0
- package/src/workbench/components/EditorGrid.split.test.tsx +118 -0
- package/src/workbench/components/EditorGrid.stories.tsx +86 -0
- package/src/workbench/components/EditorGrid.tabs.test.tsx +118 -0
- package/src/workbench/components/EditorGrid.tsx +544 -0
- package/src/workbench/components/EditorGrid.zoom.test.tsx +132 -0
- package/src/workbench/components/FileContentsView.stories.tsx +16 -0
- package/src/workbench/components/FileContentsView.test.tsx +29 -0
- package/src/workbench/components/FileContentsView.tsx +100 -0
- package/src/workbench/components/GridTabContent.actions.test.tsx +151 -0
- package/src/workbench/components/GridTabContent.stories.tsx +59 -0
- package/src/workbench/components/GridTabContent.tsx +193 -0
- package/src/workbench/components/ProjectTreeView.stories.tsx +25 -0
- package/src/workbench/components/ProjectTreeView.test.tsx +236 -0
- package/src/workbench/components/ProjectTreeView.tsx +151 -0
- package/src/workbench/components/TreePane.stories.tsx +106 -0
- package/src/workbench/components/TreePane.tsx +155 -0
- package/src/workbench/components/WelcomePlayground.stories.tsx +12 -0
- package/src/workbench/components/WelcomePlayground.test.tsx +83 -0
- package/src/workbench/components/WelcomePlayground.tsx +138 -0
- package/src/workbench/components/editor-grid.test.ts +184 -0
- package/src/workbench/components/editor-grid.ts +281 -0
- package/src/workbench/components/file-tree.test.ts +60 -0
- package/src/workbench/components/file-tree.ts +112 -0
- package/src/workbench/use-changes-workbench.ts +205 -0
- package/src/workbench/use-remember-layout.ts +31 -0
- package/src/workbench/use-repo-changes.ts +91 -0
- package/src/workbench/workbench-snapshot.test.ts +99 -0
- package/src/workbench/workbench-snapshot.ts +59 -0
- package/src/App.diff.test.tsx +0 -99
- package/src/App.export.test.tsx +0 -88
- package/src/App.plan-review.test.tsx +0 -259
- package/src/App.readonly.test.tsx +0 -148
- package/src/App.rendering.test.tsx +0 -108
- package/src/App.submit-confirm.test.tsx +0 -183
- package/src/App.test.tsx +0 -218
- package/src/App.tsx +0 -1249
- package/src/config.test.ts +0 -116
- package/src/config.ts +0 -150
- package/src/editor.ts +0 -34
- package/src/herdr.test.ts +0 -68
- package/src/herdr.ts +0 -34
- package/src/keymap.test.ts +0 -270
- package/src/keymap.ts +0 -173
- package/src/run.ts +0 -30
- package/src/serve.ts +0 -75
- package/src/session-controller.ts +0 -398
- package/src/syntax.test.ts +0 -41
- package/src/syntax.ts +0 -88
- package/src/theme.ts +0 -46
- package/src/view-diff.test.ts +0 -55
- package/src/view-diff.ts +0 -86
- package/src/view.test.ts +0 -157
- package/src/view.ts +0 -364
package/src/syntax.test.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { filetypeFor, highlightCode } from "./syntax";
|
|
3
|
-
|
|
4
|
-
const TS = `export function gate(full: number) {
|
|
5
|
-
// threshold
|
|
6
|
-
return full < 0.6 ? "blocked" : "clear";
|
|
7
|
-
}`;
|
|
8
|
-
|
|
9
|
-
describe("highlightCode", () => {
|
|
10
|
-
test("token text concatenates back to the exact source (verbatim rendering)", async () => {
|
|
11
|
-
const lines = await highlightCode(TS, "ts");
|
|
12
|
-
expect(lines).not.toBeNull();
|
|
13
|
-
const rebuilt = lines!.map((l) => l.map((t) => t.content).join("")).join("\n");
|
|
14
|
-
expect(rebuilt).toBe(TS);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
test("keywords, strings, and comments get distinct colors", async () => {
|
|
18
|
-
const lines = await highlightCode(TS, "ts");
|
|
19
|
-
const colorOf = (needle: string) => {
|
|
20
|
-
for (const line of lines!) for (const t of line) if (t.content.includes(needle)) return t.color;
|
|
21
|
-
return undefined;
|
|
22
|
-
};
|
|
23
|
-
const kw = colorOf("export");
|
|
24
|
-
const str = colorOf("blocked");
|
|
25
|
-
const comment = colorOf("threshold");
|
|
26
|
-
expect(kw).toBeDefined();
|
|
27
|
-
expect(str).toBeDefined();
|
|
28
|
-
expect(comment).toBeDefined();
|
|
29
|
-
expect(new Set([kw, str, comment]).size).toBe(3);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test("unknown languages return null and render unstyled", async () => {
|
|
33
|
-
expect(await highlightCode("whatever", "brainfog")).toBeNull();
|
|
34
|
-
expect(await highlightCode("plain", undefined)).toBeNull();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
test("fence aliases map to real grammars", async () => {
|
|
38
|
-
expect(await highlightCode("x = 1", "py")).not.toBeNull();
|
|
39
|
-
expect(await highlightCode('echo "hi"', "sh")).not.toBeNull();
|
|
40
|
-
});
|
|
41
|
-
});
|
package/src/syntax.ts
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Code-block syntax highlighting via Shiki (#67): TextMate-grammar tokens
|
|
3
|
-
* colored by a theme built from cueloop's tokens. The highlighter loads
|
|
4
|
-
* lazily on the first code block; unknown languages render unstyled.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { DARK as T } from "./theme";
|
|
8
|
-
|
|
9
|
-
export interface CodeToken {
|
|
10
|
-
content: string;
|
|
11
|
-
color?: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
type Highlighter = {
|
|
15
|
-
codeToTokensBase(code: string, opts: { lang: string; theme: string }): { content: string; color?: string }[][];
|
|
16
|
-
getLoadedLanguages(): string[];
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const LANGS = ["typescript", "tsx", "javascript", "jsx", "json", "bash", "python", "rust", "go", "yaml", "diff", "markdown", "toml", "sql", "html", "css"];
|
|
20
|
-
|
|
21
|
-
/** Markdown fence info → Shiki language id. */
|
|
22
|
-
export function filetypeFor(lang?: string): string {
|
|
23
|
-
const l = (lang ?? "").toLowerCase();
|
|
24
|
-
const map: Record<string, string> = {
|
|
25
|
-
ts: "typescript",
|
|
26
|
-
js: "javascript",
|
|
27
|
-
py: "python",
|
|
28
|
-
rb: "ruby",
|
|
29
|
-
rs: "rust",
|
|
30
|
-
sh: "bash",
|
|
31
|
-
shell: "bash",
|
|
32
|
-
zsh: "bash",
|
|
33
|
-
yml: "yaml",
|
|
34
|
-
};
|
|
35
|
-
return map[l] ?? l;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function cueloopTheme() {
|
|
39
|
-
const scope = (scopes: string, foreground: string, fontStyle?: string) => ({
|
|
40
|
-
scope: scopes.split(", "),
|
|
41
|
-
settings: fontStyle ? { foreground, fontStyle } : { foreground },
|
|
42
|
-
});
|
|
43
|
-
return {
|
|
44
|
-
name: "cueloop",
|
|
45
|
-
type: "dark" as const,
|
|
46
|
-
colors: { "editor.background": T.elevated, "editor.foreground": T.textMuted },
|
|
47
|
-
settings: [
|
|
48
|
-
{ settings: { foreground: T.textMuted } },
|
|
49
|
-
scope("comment, punctuation.definition.comment", T.textDim, "italic"),
|
|
50
|
-
scope("string, string.quoted, punctuation.definition.string", T.green),
|
|
51
|
-
scope("constant.numeric, constant.language, constant.character", T.blue),
|
|
52
|
-
scope("keyword, storage, storage.type, storage.modifier, keyword.control", T.accent),
|
|
53
|
-
scope("entity.name.function, support.function, meta.function-call", T.text),
|
|
54
|
-
scope("entity.name.type, entity.name.class, support.type, support.class", T.blue),
|
|
55
|
-
scope("variable, variable.other, variable.parameter", T.textMuted),
|
|
56
|
-
scope("entity.name.tag", T.accent),
|
|
57
|
-
scope("entity.other.attribute-name", T.blue),
|
|
58
|
-
scope("punctuation, meta.brace", T.textDim),
|
|
59
|
-
scope("keyword.operator", T.textDim),
|
|
60
|
-
],
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
let highlighterPromise: Promise<Highlighter | null> | null = null;
|
|
65
|
-
|
|
66
|
-
function getHighlighter(): Promise<Highlighter | null> {
|
|
67
|
-
highlighterPromise ??= import("shiki")
|
|
68
|
-
.then((shiki) => shiki.createHighlighter({ themes: [cueloopTheme()], langs: LANGS }) as Promise<Highlighter>)
|
|
69
|
-
.catch(() => null); // highlighting is an enhancement, never a blocker
|
|
70
|
-
return highlighterPromise;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Tokenize code into styled lines. Token text concatenates back to the exact
|
|
75
|
-
* source line, so verbatim rendering (indentation, no wrap) is preserved.
|
|
76
|
-
* Returns null when the language is unknown or the highlighter is unavailable.
|
|
77
|
-
*/
|
|
78
|
-
export async function highlightCode(content: string, lang?: string): Promise<CodeToken[][] | null> {
|
|
79
|
-
const language = filetypeFor(lang);
|
|
80
|
-
if (!language) return null;
|
|
81
|
-
const highlighter = await getHighlighter();
|
|
82
|
-
if (!highlighter || !highlighter.getLoadedLanguages().includes(language)) return null;
|
|
83
|
-
try {
|
|
84
|
-
return highlighter.codeToTokensBase(content, { lang: language, theme: "cueloop" });
|
|
85
|
-
} catch {
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
88
|
-
}
|
package/src/theme.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Semantic theme tokens (#22). Values carry over from the design-system
|
|
3
|
-
* prototypes (tokens.css). One built-in dark theme for now; the theme
|
|
4
|
-
* registry (palette-derived themes, terminal theme, user overrides) layers
|
|
5
|
-
* on top of these slots without touching consumers.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export interface Theme {
|
|
9
|
-
bg: string;
|
|
10
|
-
panel: string;
|
|
11
|
-
elevated: string;
|
|
12
|
-
border: string;
|
|
13
|
-
text: string;
|
|
14
|
-
textMuted: string;
|
|
15
|
-
textDim: string;
|
|
16
|
-
accent: string;
|
|
17
|
-
accentInk: string;
|
|
18
|
-
green: string;
|
|
19
|
-
red: string;
|
|
20
|
-
blue: string;
|
|
21
|
-
cursorBg: string;
|
|
22
|
-
markCommentBg: string;
|
|
23
|
-
markSuggestionBg: string;
|
|
24
|
-
insFg: string;
|
|
25
|
-
delFg: string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export const DARK: Theme = {
|
|
29
|
-
bg: "#1f1f29",
|
|
30
|
-
panel: "#1a1a23",
|
|
31
|
-
elevated: "#23232f",
|
|
32
|
-
border: "#3a3a4a",
|
|
33
|
-
text: "#e4e6ec",
|
|
34
|
-
textMuted: "#b1b6c4",
|
|
35
|
-
textDim: "#6b7280",
|
|
36
|
-
accent: "#f5a3a3",
|
|
37
|
-
accentInk: "#2a1416",
|
|
38
|
-
green: "#62d96b",
|
|
39
|
-
red: "#ff6b6b",
|
|
40
|
-
blue: "#84a6e8",
|
|
41
|
-
cursorBg: "#33262a",
|
|
42
|
-
markCommentBg: "#3d2a2e",
|
|
43
|
-
markSuggestionBg: "#20351f",
|
|
44
|
-
insFg: "#62d96b",
|
|
45
|
-
delFg: "#ff6b6b",
|
|
46
|
-
}
|
package/src/view-diff.test.ts
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { diffRowAnchor, diffRows } from "./view-diff";
|
|
3
|
-
|
|
4
|
-
const PATCH = `diff --git a/src/store.ts b/src/store.ts
|
|
5
|
-
index 111..222 100644
|
|
6
|
-
--- a/src/store.ts
|
|
7
|
-
+++ b/src/store.ts
|
|
8
|
-
@@ -1,5 +1,6 @@
|
|
9
|
-
import { join } from "node:path";
|
|
10
|
-
export class Store {
|
|
11
|
-
- private items = [];
|
|
12
|
-
+ private items = new Map();
|
|
13
|
-
+ private ready = false;
|
|
14
|
-
constructor() {}
|
|
15
|
-
@@ -20,3 +21,3 @@
|
|
16
|
-
export function helper() {
|
|
17
|
-
- return 1;
|
|
18
|
-
+ return 2;
|
|
19
|
-
}
|
|
20
|
-
`;
|
|
21
|
-
|
|
22
|
-
describe("diffRows", () => {
|
|
23
|
-
test("flattens files, hunks, and signed lines with line numbers", () => {
|
|
24
|
-
const rows = diffRows(PATCH);
|
|
25
|
-
expect(rows[0]).toMatchObject({ t: "file", file: "src/store.ts" });
|
|
26
|
-
expect(rows[1]!.t).toBe("hunk");
|
|
27
|
-
const del = rows.find((r) => r.t === "del")!;
|
|
28
|
-
expect(del.text).toContain("private items = [];");
|
|
29
|
-
expect(del.oldLine).toBe(3);
|
|
30
|
-
const adds = rows.filter((r) => r.t === "add");
|
|
31
|
-
expect(adds[0]!.text).toContain("new Map()");
|
|
32
|
-
expect(adds[0]!.newLine).toBe(3);
|
|
33
|
-
expect(adds[1]!.newLine).toBe(4);
|
|
34
|
-
// two hunks
|
|
35
|
-
expect(rows.filter((r) => r.t === "hunk").length).toBe(2);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
test("context lines carry both line numbers", () => {
|
|
39
|
-
const rows = diffRows(PATCH);
|
|
40
|
-
const ctx = rows.find((r) => r.t === "ctx")!;
|
|
41
|
-
expect(ctx.oldLine).toBe(1);
|
|
42
|
-
expect(ctx.newLine).toBe(1);
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
describe("diffRowAnchor", () => {
|
|
47
|
-
test("quote is the line, neighbors are the context selectors", () => {
|
|
48
|
-
const rows = diffRows(PATCH);
|
|
49
|
-
const idx = rows.findIndex((r) => r.text.includes("new Map()"));
|
|
50
|
-
const anchor = diffRowAnchor(rows, idx);
|
|
51
|
-
expect(anchor.quote).toContain("new Map()");
|
|
52
|
-
expect(anchor.prefix.length).toBeGreaterThan(0);
|
|
53
|
-
expect(anchor.suffix.length).toBeGreaterThan(0);
|
|
54
|
-
});
|
|
55
|
-
});
|
package/src/view-diff.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Diff artifact projection (#18 slice 3): flatten @pierre/diffs' parsed
|
|
3
|
-
* patch model into render rows. Line-anchored annotations use the same
|
|
4
|
-
* quote-primary anchors as plans: quote = the line content, prefix/suffix =
|
|
5
|
-
* the neighbor lines - so the whole anchor/feedback pipeline is shared.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { parsePatchFiles, type FileDiffMetadata } from "@pierre/diffs";
|
|
9
|
-
|
|
10
|
-
export type DiffRowType = "file" | "hunk" | "ctx" | "add" | "del";
|
|
11
|
-
|
|
12
|
-
export interface DiffRow {
|
|
13
|
-
t: DiffRowType;
|
|
14
|
-
text: string;
|
|
15
|
-
file: string;
|
|
16
|
-
oldLine?: number;
|
|
17
|
-
newLine?: number;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function diffRows(patchText: string): DiffRow[] {
|
|
21
|
-
const rows: DiffRow[] = [];
|
|
22
|
-
const patches = parsePatchFiles(patchText);
|
|
23
|
-
for (const patch of patches) {
|
|
24
|
-
for (const file of patch.files) {
|
|
25
|
-
rows.push({ t: "file", text: fileLabel(file), file: file.name });
|
|
26
|
-
for (const hunk of file.hunks) {
|
|
27
|
-
rows.push({ t: "hunk", text: hunk.hunkSpecs ?? "@@", file: file.name });
|
|
28
|
-
let oldLine = hunk.deletionStart;
|
|
29
|
-
let newLine = hunk.additionStart;
|
|
30
|
-
for (const seg of hunk.hunkContent) {
|
|
31
|
-
if (seg.type === "context") {
|
|
32
|
-
for (let i = 0; i < seg.lines; i++) {
|
|
33
|
-
rows.push({
|
|
34
|
-
t: "ctx",
|
|
35
|
-
text: file.additionLines[seg.additionLineIndex + i] ?? "",
|
|
36
|
-
file: file.name,
|
|
37
|
-
oldLine: oldLine++,
|
|
38
|
-
newLine: newLine++,
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
} else {
|
|
42
|
-
for (let i = 0; i < seg.deletions; i++) {
|
|
43
|
-
rows.push({
|
|
44
|
-
t: "del",
|
|
45
|
-
text: file.deletionLines[seg.deletionLineIndex + i] ?? "",
|
|
46
|
-
file: file.name,
|
|
47
|
-
oldLine: oldLine++,
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
for (let i = 0; i < seg.additions; i++) {
|
|
51
|
-
rows.push({
|
|
52
|
-
t: "add",
|
|
53
|
-
text: file.additionLines[seg.additionLineIndex + i] ?? "",
|
|
54
|
-
file: file.name,
|
|
55
|
-
newLine: newLine++,
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return rows;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function fileLabel(file: FileDiffMetadata): string {
|
|
67
|
-
return file.prevName && file.prevName !== file.name ? `${file.prevName} → ${file.name}` : file.name;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/** Quote-primary anchor for a diff row: neighbors as context selectors. */
|
|
71
|
-
export function diffRowAnchor(rows: DiffRow[], idx: number): { quote: string; prefix: string; suffix: string } {
|
|
72
|
-
const row = rows[idx]!;
|
|
73
|
-
const prev = rows[idx - 1];
|
|
74
|
-
const next = rows[idx + 1];
|
|
75
|
-
return {
|
|
76
|
-
quote: row.text,
|
|
77
|
-
prefix: prev && (prev.t === "ctx" || prev.t === "add" || prev.t === "del") ? prev.text.slice(-24) : "",
|
|
78
|
-
suffix: next && (next.t === "ctx" || next.t === "add" || next.t === "del") ? next.text.slice(0, 24) : "",
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/** Location label for the rail and feedback: file:newLine (or old for del). */
|
|
83
|
-
export function diffRowLocation(row: DiffRow): string {
|
|
84
|
-
const line = row.newLine ?? row.oldLine;
|
|
85
|
-
return line !== undefined ? `${row.file}:${line}` : row.file;
|
|
86
|
-
}
|
package/src/view.test.ts
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import {
|
|
3
|
-
blockRuns,
|
|
4
|
-
buildDisplay,
|
|
5
|
-
displayText,
|
|
6
|
-
marksByDisplay,
|
|
7
|
-
nextWorkBlock,
|
|
8
|
-
overlayMarks,
|
|
9
|
-
spanKey,
|
|
10
|
-
startSpan,
|
|
11
|
-
wrapRuns,
|
|
12
|
-
} from "./view";
|
|
13
|
-
import { cutBlock, parseBlocks, restoreBlock, restoreLine, type Annotation } from "@cueloop/schema";
|
|
14
|
-
|
|
15
|
-
const BASE = `# Plan
|
|
16
|
-
|
|
17
|
-
## Context
|
|
18
|
-
|
|
19
|
-
The daemon persists sessions to disk atomically.
|
|
20
|
-
|
|
21
|
-
- first item
|
|
22
|
-
- second item
|
|
23
|
-
`;
|
|
24
|
-
|
|
25
|
-
describe("buildDisplay reconciliation", () => {
|
|
26
|
-
test("no working copy: everything is same", () => {
|
|
27
|
-
const d = buildDisplay(BASE);
|
|
28
|
-
expect(d.every((x) => x.type === "same")).toBe(true);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
test("reworded block pairs as mod (similar)", () => {
|
|
32
|
-
const working = BASE.replace("persists sessions to disk", "writes sessions to disk");
|
|
33
|
-
const d = buildDisplay(BASE, working);
|
|
34
|
-
const mod = d.find((x) => x.type === "mod")!;
|
|
35
|
-
expect(mod.base!.text).toContain("persists");
|
|
36
|
-
expect(mod.work!.text).toContain("writes");
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
test("unrelated cut + insert do NOT merge into a mod", () => {
|
|
40
|
-
const working = BASE.replace("- second item\n", "") + "\nA totally new closing note.\n";
|
|
41
|
-
const d = buildDisplay(BASE, working);
|
|
42
|
-
expect(d.some((x) => x.type === "del" && x.base!.text === "second item")).toBe(true);
|
|
43
|
-
expect(d.some((x) => x.type === "add" && x.work!.text.includes("closing note"))).toBe(true);
|
|
44
|
-
expect(d.some((x) => x.type === "mod")).toBe(false);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
test("ordered list items get running numbers", () => {
|
|
48
|
-
const d = buildDisplay("1. one\n2. two\n3. three\n");
|
|
49
|
-
expect(d.map((x) => x.oliNum)).toEqual([1, 2, 3]);
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
describe("blockRuns + overlayMarks", () => {
|
|
54
|
-
test("mod blocks emit word-diff runs that reconstruct both sides", () => {
|
|
55
|
-
const d = buildDisplay("plain old text here\n", "plain new text here\n")[0]!;
|
|
56
|
-
const runs = blockRuns(d, true);
|
|
57
|
-
const oldSide = runs.filter((r) => r.role !== "ins").map((r) => r.text).join("");
|
|
58
|
-
const newSide = runs.filter((r) => r.role !== "del").map((r) => r.text).join("");
|
|
59
|
-
expect(oldSide).toBe("plain old text here");
|
|
60
|
-
expect(newSide).toBe("plain new text here");
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
test("marks bind to working-text offsets and skip del runs", () => {
|
|
64
|
-
const d = buildDisplay("old words linger\n", "new words linger\n")[0]!;
|
|
65
|
-
const runs = blockRuns(d, true);
|
|
66
|
-
const marked = overlayMarks(runs, [{ start: 4, end: 9, role: "mark-comment" }]); // "words"
|
|
67
|
-
const markedText = marked.filter((r) => r.role === "mark-comment").map((r) => r.text).join("");
|
|
68
|
-
expect(markedText).toBe("words");
|
|
69
|
-
// del run text untouched
|
|
70
|
-
expect(marked.some((r) => r.role === "del" && r.text === "old")).toBe(true);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
test("marks never override ins emphasis", () => {
|
|
74
|
-
const d = buildDisplay("keep\n", "keep added\n")[0]!;
|
|
75
|
-
const runs = overlayMarks(blockRuns(d, true), [{ start: 0, end: 10, role: "mark-comment" }]);
|
|
76
|
-
expect(runs.some((r) => r.role === "ins")).toBe(true);
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
describe("marksByDisplay", () => {
|
|
81
|
-
test("annotations resolve into display coordinates", () => {
|
|
82
|
-
const working = BASE.replace("first item", "first item edited beyond recognition of it");
|
|
83
|
-
const display = buildDisplay(BASE, working);
|
|
84
|
-
const ann: Annotation = {
|
|
85
|
-
id: "a1",
|
|
86
|
-
kind: "comment",
|
|
87
|
-
anchor: { quote: "atomically", prefix: "to disk ", suffix: "." },
|
|
88
|
-
body: "define atomically",
|
|
89
|
-
createdAt: "",
|
|
90
|
-
};
|
|
91
|
-
const map = marksByDisplay([ann], display);
|
|
92
|
-
const [dispIdx, marks] = [...map.entries()][0]!;
|
|
93
|
-
expect(displayText(display[dispIdx]!)).toContain("atomically");
|
|
94
|
-
expect(marks[0]!.annotationId).toBe("a1");
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
test("orphaned annotations produce no marks", () => {
|
|
98
|
-
const display = buildDisplay(BASE);
|
|
99
|
-
const ann: Annotation = {
|
|
100
|
-
id: "a1",
|
|
101
|
-
kind: "comment",
|
|
102
|
-
anchor: { quote: "text that is gone", prefix: "", suffix: "" },
|
|
103
|
-
body: "",
|
|
104
|
-
createdAt: "",
|
|
105
|
-
};
|
|
106
|
-
expect(marksByDisplay([ann], display).size).toBe(0);
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
describe("wrapRuns", () => {
|
|
111
|
-
test("wraps at word boundaries within width", () => {
|
|
112
|
-
const lines = wrapRuns([{ text: "alpha beta gamma delta", role: "plain", start: 0 }], 11);
|
|
113
|
-
const rendered = lines.map((l) => l.map((r) => r.text).join(""));
|
|
114
|
-
expect(rendered.every((l) => l.length <= 11)).toBe(true);
|
|
115
|
-
expect(rendered.join(" ").replace(/\s+/g, " ").trim()).toBe("alpha beta gamma delta");
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
test("hard-splits words longer than the width", () => {
|
|
119
|
-
const lines = wrapRuns([{ text: "abcdefghij", role: "plain", start: 0 }], 4);
|
|
120
|
-
expect(lines.length).toBeGreaterThan(1);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
test("newlines force line breaks (code blocks)", () => {
|
|
124
|
-
const lines = wrapRuns([{ text: "line1\nline2", role: "plain", start: 0 }], 80);
|
|
125
|
-
expect(lines.length).toBe(2);
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
describe("span mode", () => {
|
|
130
|
-
const text = "one two three four";
|
|
131
|
-
test("v selects the first word; l grows; w slides; $ to end", () => {
|
|
132
|
-
let s = startSpan(0, text)!;
|
|
133
|
-
expect(text.slice(s.start, s.end)).toBe("one");
|
|
134
|
-
s = spanKey(s, "l", text);
|
|
135
|
-
expect(text.slice(s.start, s.end)).toBe("one two");
|
|
136
|
-
s = spanKey(s, "w", text);
|
|
137
|
-
expect(text.slice(s.start, s.end)).toBe("two three");
|
|
138
|
-
s = spanKey(s, "$", text);
|
|
139
|
-
expect(text.slice(s.start, s.end)).toBe("two three four");
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
describe("cut / restore round-trip", () => {
|
|
144
|
-
test("cut removes the block lines; restore puts them back", () => {
|
|
145
|
-
const blocks = parseBlocks(BASE);
|
|
146
|
-
const li = blocks.find((b) => b.text === "second item")!;
|
|
147
|
-
const cut = cutBlock(BASE, li);
|
|
148
|
-
expect(cut).not.toContain("second item");
|
|
149
|
-
const display = buildDisplay(BASE, cut);
|
|
150
|
-
const delIdx = display.findIndex((d) => d.type === "del");
|
|
151
|
-
const line = restoreLine(nextWorkBlock(display, delIdx), cut.split("\n").length);
|
|
152
|
-
// undefined = the working copy round-tripped back to the submitted revision
|
|
153
|
-
const restored = restoreBlock(BASE, cut, li, line);
|
|
154
|
-
expect(restored).toBeUndefined();
|
|
155
|
-
expect(buildDisplay(BASE, restored).every((d) => d.type === "same")).toBe(true);
|
|
156
|
-
});
|
|
157
|
-
});
|