@bubblebrain-ai/bubble 0.0.52 → 0.0.53
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/README.md +4 -12
- package/dist/agent/routing-catalog.js +3 -2
- package/dist/agent/subagent/router.js +5 -0
- package/dist/agent/subagent/runtime.d.ts +1 -0
- package/dist/agent/subagent/runtime.js +13 -1
- package/dist/agent/subagent-control.d.ts +4 -0
- package/dist/agent/subagent-control.js +1 -0
- package/dist/agent/subagent-store.js +2 -0
- package/dist/agent/workflow/control.d.ts +2 -0
- package/dist/agent/workflow/runs.d.ts +1 -0
- package/dist/agent/workflow/runs.js +3 -0
- package/dist/agent.d.ts +11 -1
- package/dist/agent.js +158 -57
- package/dist/bin.js +2 -44
- package/dist/config.d.ts +14 -1
- package/dist/config.js +45 -4
- package/dist/context/budget.d.ts +7 -1
- package/dist/context/budget.js +25 -2
- package/dist/context/compact-llm.d.ts +1 -1
- package/dist/context/compact-llm.js +5 -2
- package/dist/context/overflow.js +2 -0
- package/dist/context/projector.d.ts +8 -0
- package/dist/context/projector.js +55 -6
- package/dist/context/tool-output-truncate.d.ts +17 -1
- package/dist/context/tool-output-truncate.js +171 -49
- package/dist/feishu/agent-host/run-driver.js +3 -0
- package/dist/feishu/serve.js +2 -0
- package/dist/goal/command.d.ts +1 -1
- package/dist/goal/command.js +7 -2
- package/dist/main.d.ts +1 -1
- package/dist/main.js +38 -8
- package/dist/memory/db.js +0 -7
- package/dist/model-catalog.js +25 -6
- package/dist/model-config.js +3 -2
- package/dist/model-pricing.d.ts +24 -5
- package/dist/model-pricing.js +0 -0
- package/dist/model-selection.js +13 -4
- package/dist/network/chatgpt-transport.js +0 -34
- package/dist/network/errors.d.ts +19 -0
- package/dist/network/errors.js +24 -0
- package/dist/network/provider-transport.d.ts +0 -4
- package/dist/network/provider-transport.js +3 -35
- package/dist/network/retry.d.ts +6 -1
- package/dist/network/retry.js +9 -0
- package/dist/provider-model-policy.d.ts +16 -0
- package/dist/provider-model-policy.js +33 -0
- package/dist/provider-registry.d.ts +1 -1
- package/dist/provider-registry.js +73 -10
- package/dist/provider-transform.js +32 -4
- package/dist/provider.d.ts +6 -2
- package/dist/provider.js +95 -6
- package/dist/question/controller.js +5 -4
- package/dist/question/normalize.d.ts +6 -0
- package/dist/question/normalize.js +11 -0
- package/dist/rewind.d.ts +20 -0
- package/dist/rewind.js +29 -0
- package/dist/sdk/index.d.ts +1 -1
- package/dist/sdk/index.js +11 -3
- package/dist/session.d.ts +7 -0
- package/dist/session.js +20 -0
- package/dist/skills/discovery.js +18 -0
- package/dist/skills/registry.d.ts +12 -2
- package/dist/skills/registry.js +40 -5
- package/dist/skills/types.d.ts +2 -0
- package/dist/slash-commands/commands.js +120 -81
- package/dist/slash-commands/registry.d.ts +2 -1
- package/dist/slash-commands/registry.js +3 -0
- package/dist/slash-commands/types.d.ts +22 -4
- package/dist/stats/usage.d.ts +3 -1
- package/dist/stats/usage.js +22 -13
- package/dist/tasks/manager.d.ts +4 -2
- package/dist/tasks/manager.js +34 -9
- package/dist/tasks/promotion.d.ts +2 -2
- package/dist/tasks/promotion.js +2 -2
- package/dist/tools/agent-lifecycle.js +4 -0
- package/dist/tools/bash.d.ts +3 -1
- package/dist/tools/bash.js +13 -2
- package/dist/tools/grep.d.ts +5 -1
- package/dist/tools/grep.js +318 -69
- package/dist/tools/index.d.ts +3 -1
- package/dist/tools/index.js +1 -0
- package/dist/tools/question.js +5 -4
- package/dist/tools/read.js +40 -21
- package/dist/tui/animation-clock.d.ts +14 -0
- package/dist/tui/animation-clock.js +34 -0
- package/dist/tui/app.d.ts +176 -0
- package/dist/tui/app.js +1647 -0
- package/dist/tui/components/approval-dialog.d.ts +43 -0
- package/dist/tui/components/approval-dialog.js +247 -0
- package/dist/tui/components/bottom-sheet.d.ts +9 -0
- package/dist/tui/components/bottom-sheet.js +32 -0
- package/dist/tui/components/context-info.d.ts +54 -0
- package/dist/tui/components/context-info.js +388 -0
- package/dist/tui/components/feedback-dialog.d.ts +56 -0
- package/dist/tui/components/feedback-dialog.js +262 -0
- package/dist/tui/components/feishu-setup-dialog.d.ts +65 -0
- package/dist/tui/components/feishu-setup-dialog.js +332 -0
- package/dist/tui/components/image-preview.d.ts +32 -0
- package/dist/tui/components/image-preview.js +130 -0
- package/dist/tui/components/plan-dialog.d.ts +34 -0
- package/dist/tui/components/plan-dialog.js +196 -0
- package/dist/tui/components/provider-key-input.d.ts +18 -0
- package/dist/tui/components/provider-key-input.js +49 -0
- package/dist/tui/components/question-dialog.d.ts +25 -0
- package/dist/tui/components/question-dialog.js +263 -0
- package/dist/tui/components/responsive-transcript.d.ts +31 -0
- package/dist/tui/components/responsive-transcript.js +77 -0
- package/dist/tui/components/rewind-picker.d.ts +62 -0
- package/dist/tui/components/rewind-picker.js +317 -0
- package/dist/tui/components/session-picker.d.ts +49 -0
- package/dist/tui/components/session-picker.js +303 -0
- package/dist/tui/components/skills-panel.d.ts +59 -0
- package/dist/tui/components/skills-panel.js +568 -0
- package/dist/tui/components/stats-panel.d.ts +40 -0
- package/dist/tui/components/stats-panel.js +301 -0
- package/dist/tui/components/streaming-message.d.ts +69 -0
- package/dist/tui/components/streaming-message.js +393 -0
- package/dist/tui/components/subagent-inspector.d.ts +28 -0
- package/dist/tui/components/subagent-inspector.js +127 -0
- package/dist/tui/components/task-inspector.d.ts +26 -0
- package/dist/tui/components/task-inspector.js +70 -0
- package/dist/tui/components/tasks-pane.d.ts +88 -0
- package/dist/tui/components/tasks-pane.js +416 -0
- package/dist/tui/components/transcript.d.ts +96 -0
- package/dist/tui/components/transcript.js +771 -0
- package/dist/tui/components/welcome.d.ts +20 -0
- package/dist/tui/components/welcome.js +133 -0
- package/dist/tui/components/workflow-inspector.d.ts +30 -0
- package/dist/tui/components/workflow-inspector.js +136 -0
- package/dist/tui/composer-autocomplete.d.ts +61 -0
- package/dist/tui/composer-autocomplete.js +383 -0
- package/dist/tui/composer-style.d.ts +7 -0
- package/dist/tui/composer-style.js +29 -0
- package/dist/tui/controller/agent-event-reducer.d.ts +75 -0
- package/dist/tui/controller/agent-event-reducer.js +238 -0
- package/dist/tui/controller/composer-controller.d.ts +95 -0
- package/dist/tui/controller/composer-controller.js +502 -0
- package/dist/tui/controller/controller.d.ts +194 -0
- package/dist/tui/controller/controller.js +1035 -0
- package/dist/tui/controller/effects.d.ts +71 -0
- package/dist/tui/controller/effects.js +1 -0
- package/dist/tui/controller/goal-runtime-controller.d.ts +47 -0
- package/dist/tui/controller/goal-runtime-controller.js +202 -0
- package/dist/tui/controller/input-queue-machine.d.ts +71 -0
- package/dist/tui/controller/input-queue-machine.js +121 -0
- package/dist/tui/controller/intents.d.ts +56 -0
- package/dist/tui/controller/intents.js +6 -0
- package/dist/tui/controller/overlay-controller.d.ts +64 -0
- package/dist/tui/controller/overlay-controller.js +129 -0
- package/dist/tui/controller/ports.d.ts +57 -0
- package/dist/tui/controller/ports.js +9 -0
- package/dist/tui/controller/session-transition.d.ts +54 -0
- package/dist/tui/controller/session-transition.js +68 -0
- package/dist/tui/controller/state.d.ts +14 -0
- package/dist/tui/controller/state.js +36 -0
- package/dist/tui/controller/task-runtime-controller.d.ts +66 -0
- package/dist/tui/controller/task-runtime-controller.js +158 -0
- package/dist/tui/footer.d.ts +37 -0
- package/dist/tui/footer.js +80 -0
- package/dist/{tui-ink → tui/formatting}/edit-diff.d.ts +1 -1
- package/dist/{tui-ink → tui/formatting}/edit-diff.js +1 -1
- package/dist/{tui-ink/app-helpers.d.ts → tui/formatting/summary.d.ts} +4 -4
- package/dist/{tui-ink/app-helpers.js → tui/formatting/summary.js} +2 -2
- package/dist/tui/fullscreen.d.ts +48 -0
- package/dist/tui/fullscreen.js +255 -0
- package/dist/tui/image-display.d.ts +4 -0
- package/dist/tui/image-display.js +30 -12
- package/dist/tui/markdown-style.d.ts +5 -0
- package/dist/tui/markdown-style.js +23 -0
- package/dist/{tui-ink → tui/model}/composer-buffer.js +1 -1
- package/dist/tui/model/composer-types.d.ts +17 -0
- package/dist/tui/model/composer-types.js +1 -0
- package/dist/{tui-ink → tui/model}/display-history.d.ts +7 -1
- package/dist/{tui-ink → tui/model}/display-reconstruct.d.ts +1 -1
- package/dist/{tui-ink → tui/model}/display-reconstruct.js +54 -6
- package/dist/{tui-ink → tui/model}/file-mentions.d.ts +2 -0
- package/dist/{tui-ink → tui/model}/file-mentions.js +4 -0
- package/dist/tui/model/image-attachment.d.ts +15 -0
- package/dist/tui/model/image-attachment.js +1 -0
- package/dist/{tui-ink → tui/model}/image-paste.d.ts +7 -1
- package/dist/{tui-ink → tui/model}/image-paste.js +46 -0
- package/dist/tui/model/input-history.d.ts +1 -0
- package/dist/tui/model/input-history.js +1 -0
- package/dist/{tui-ink → tui/model}/input-queue.d.ts +1 -1
- package/dist/{tui-ink → tui/model}/recent-activity.js +1 -1
- package/dist/{tui-ink → tui/model}/subagent-view.d.ts +17 -2
- package/dist/{tui-ink → tui/model}/subagent-view.js +125 -4
- package/dist/{tui-ink → tui/model}/submit-dedupe.d.ts +1 -1
- package/dist/tui/model/theme-style.d.ts +10 -0
- package/dist/tui/model/theme-style.js +79 -0
- package/dist/{tui-ink → tui/model}/theme.d.ts +11 -23
- package/dist/{tui-ink → tui/model}/theme.js +23 -38
- package/dist/{tui-ink → tui/model}/trace-groups.d.ts +2 -0
- package/dist/{tui-ink → tui/model}/trace-groups.js +91 -30
- package/dist/tui/model/trace-interaction.d.ts +63 -0
- package/dist/tui/model/trace-interaction.js +87 -0
- package/dist/tui/model-picker-data.d.ts +1 -2
- package/dist/tui/model-picker-data.js +8 -1
- package/dist/tui/model-switch.js +2 -0
- package/dist/{tui-ink → tui}/run-session-picker.d.ts +2 -3
- package/dist/tui/run-session-picker.js +64 -0
- package/dist/tui/run.d.ts +72 -0
- package/dist/tui/run.js +139 -0
- package/dist/tui/terminal/multiplexer.d.ts +5 -0
- package/dist/tui/terminal/multiplexer.js +14 -0
- package/dist/tui/testing/fake-agent.d.ts +24 -0
- package/dist/tui/testing/fake-agent.js +22 -0
- package/dist/tui/testing/spy-host.d.ts +21 -0
- package/dist/tui/testing/spy-host.js +55 -0
- package/dist/tui/trace-groups.js +27 -6
- package/dist/types.d.ts +7 -0
- package/node_modules/@bubblebrain-ai/pi-tui/LICENSE +21 -0
- package/node_modules/@bubblebrain-ai/pi-tui/README.md +854 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/alt-screen-search.d.ts +26 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/alt-screen-search.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/alt-screen-search.js +121 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/alt-screen-search.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/autocomplete.d.ts +86 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/autocomplete.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/autocomplete.js +649 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/autocomplete.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/alt-screen-flash.d.ts +13 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/alt-screen-flash.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/alt-screen-flash.js +36 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/alt-screen-flash.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/box.d.ts +22 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/box.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/box.js +99 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/box.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/cancellable-loader.d.ts +22 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/cancellable-loader.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/cancellable-loader.js +36 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/cancellable-loader.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/editor.d.ts +330 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/editor.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/editor.js +2344 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/editor.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/h-stack.d.ts +7 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/h-stack.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/h-stack.js +43 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/h-stack.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/image.d.ts +28 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/image.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/image.js +82 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/image.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/input.d.ts +48 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/input.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/input.js +390 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/input.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/loader.d.ts +31 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/loader.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/loader.js +67 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/loader.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/markdown.d.ts +103 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/markdown.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/markdown.js +797 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/markdown.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/scroll-view.d.ts +56 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/scroll-view.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/scroll-view.js +179 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/scroll-view.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/select-list.d.ts +53 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/select-list.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/select-list.js +175 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/select-list.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/settings-list.d.ts +50 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/settings-list.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/settings-list.js +174 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/settings-list.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/spacer.d.ts +12 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/spacer.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/spacer.js +22 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/spacer.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/stack.d.ts +32 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/stack.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/stack.js +109 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/stack.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/text.d.ts +19 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/text.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/text.js +81 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/text.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/truncated-text.d.ts +13 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/truncated-text.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/truncated-text.js +48 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/truncated-text.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/v-stack.d.ts +8 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/v-stack.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/v-stack.js +26 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/components/v-stack.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/editor-component.d.ts +39 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/editor-component.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/editor-component.js +2 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/editor-component.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/fuzzy.d.ts +16 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/fuzzy.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/fuzzy.js +110 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/fuzzy.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/index.d.ts +31 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/index.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/index.js +42 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/index.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keybindings.d.ts +273 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keybindings.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keybindings.js +231 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keybindings.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keys.d.ts +184 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keys.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keys.js +1174 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/keys.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/kill-ring.d.ts +28 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/kill-ring.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/kill-ring.js +46 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/kill-ring.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/latex.d.ts +10 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/latex.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/latex.js +1268 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/latex.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout-node.d.ts +40 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout-node.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout-node.js +6 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout-node.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout.d.ts +42 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout.js +335 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/layout.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/native-modifiers.d.ts +3 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/native-modifiers.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/native-modifiers.js +60 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/native-modifiers.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/stdin-buffer.d.ts +56 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/stdin-buffer.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/stdin-buffer.js +363 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/stdin-buffer.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-colors.d.ts +10 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-colors.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-colors.js +59 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-colors.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-image.d.ts +116 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-image.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-image.js +518 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal-image.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal.d.ts +142 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal.js +544 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/terminal.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/testing.d.ts +11 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/testing.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/testing.js +11 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/testing.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-alt-screen.d.ts +140 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-alt-screen.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-alt-screen.js +1334 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-alt-screen.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-main-screen.d.ts +39 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-main-screen.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-main-screen.js +536 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui-main-screen.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui.d.ts +338 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui.js +1016 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/tui.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/undo-stack.d.ts +17 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/undo-stack.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/undo-stack.js +27 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/undo-stack.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/utils.d.ts +96 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/utils.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/utils.js +1193 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/utils.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/word-navigation.d.ts +25 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/word-navigation.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/word-navigation.js +96 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/src/word-navigation.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/test/virtual-terminal.d.ts +73 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/test/virtual-terminal.d.ts.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/test/virtual-terminal.js +188 -0
- package/node_modules/@bubblebrain-ai/pi-tui/dist/test/virtual-terminal.js.map +1 -0
- package/node_modules/@bubblebrain-ai/pi-tui/native/darwin/README.md +20 -0
- package/node_modules/@bubblebrain-ai/pi-tui/native/darwin/build.sh +62 -0
- package/node_modules/@bubblebrain-ai/pi-tui/native/darwin/prebuilds/darwin-arm64/darwin-modifiers.node +0 -0
- package/node_modules/@bubblebrain-ai/pi-tui/native/darwin/prebuilds/darwin-x64/darwin-modifiers.node +0 -0
- package/node_modules/@bubblebrain-ai/pi-tui/native/darwin/src/darwin-modifiers.c +70 -0
- package/node_modules/@bubblebrain-ai/pi-tui/native/win32/README.md +22 -0
- package/node_modules/@bubblebrain-ai/pi-tui/native/win32/build.mjs +229 -0
- package/node_modules/@bubblebrain-ai/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
- package/node_modules/@bubblebrain-ai/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
- package/node_modules/@bubblebrain-ai/pi-tui/native/win32/src/win32-console-mode.c +119 -0
- package/node_modules/@bubblebrain-ai/pi-tui/package.json +66 -0
- package/node_modules/get-east-asian-width/index.d.ts +60 -0
- package/node_modules/get-east-asian-width/index.js +30 -0
- package/node_modules/get-east-asian-width/license +9 -0
- package/node_modules/get-east-asian-width/lookup-data.js +21 -0
- package/node_modules/get-east-asian-width/lookup.js +138 -0
- package/node_modules/get-east-asian-width/package.json +71 -0
- package/node_modules/get-east-asian-width/readme.md +65 -0
- package/node_modules/get-east-asian-width/utilities.js +24 -0
- package/package.json +23 -10
- package/packages/pi-tui/node_modules/marked/LICENSE +44 -0
- package/packages/pi-tui/node_modules/marked/README.md +115 -0
- package/packages/pi-tui/node_modules/marked/bin/main.js +284 -0
- package/packages/pi-tui/node_modules/marked/bin/marked.js +16 -0
- package/packages/pi-tui/node_modules/marked/lib/marked.d.ts +760 -0
- package/packages/pi-tui/node_modules/marked/lib/marked.esm.js +77 -0
- package/packages/pi-tui/node_modules/marked/lib/marked.esm.js.map +7 -0
- package/packages/pi-tui/node_modules/marked/lib/marked.umd.js +79 -0
- package/packages/pi-tui/node_modules/marked/lib/marked.umd.js.map +7 -0
- package/packages/pi-tui/node_modules/marked/man/marked.1 +113 -0
- package/packages/pi-tui/node_modules/marked/man/marked.1.md +93 -0
- package/packages/pi-tui/node_modules/marked/package.json +103 -0
- package/dist/tui-ink/app.d.ts +0 -76
- package/dist/tui-ink/app.js +0 -2377
- package/dist/tui-ink/approval/approval-dialog.d.ts +0 -13
- package/dist/tui-ink/approval/approval-dialog.js +0 -184
- package/dist/tui-ink/approval/diff-view.d.ts +0 -7
- package/dist/tui-ink/approval/diff-view.js +0 -44
- package/dist/tui-ink/approval/select.d.ts +0 -35
- package/dist/tui-ink/approval/select.js +0 -98
- package/dist/tui-ink/code-highlight.d.ts +0 -8
- package/dist/tui-ink/code-highlight.js +0 -121
- package/dist/tui-ink/command-palette.d.ts +0 -32
- package/dist/tui-ink/command-palette.js +0 -158
- package/dist/tui-ink/compaction-progress.d.ts +0 -19
- package/dist/tui-ink/compaction-progress.js +0 -74
- package/dist/tui-ink/detect-theme.d.ts +0 -1
- package/dist/tui-ink/detect-theme.js +0 -1
- package/dist/tui-ink/feedback-dialog.d.ts +0 -19
- package/dist/tui-ink/feedback-dialog.js +0 -133
- package/dist/tui-ink/feishu-setup-picker.d.ts +0 -5
- package/dist/tui-ink/feishu-setup-picker.js +0 -271
- package/dist/tui-ink/footer.d.ts +0 -26
- package/dist/tui-ink/footer.js +0 -51
- package/dist/tui-ink/input-box.d.ts +0 -189
- package/dist/tui-ink/input-box.js +0 -1279
- package/dist/tui-ink/input-history.d.ts +0 -1
- package/dist/tui-ink/input-history.js +0 -1
- package/dist/tui-ink/markdown.d.ts +0 -103
- package/dist/tui-ink/markdown.js +0 -747
- package/dist/tui-ink/message-list.d.ts +0 -49
- package/dist/tui-ink/message-list.js +0 -841
- package/dist/tui-ink/model-picker.d.ts +0 -143
- package/dist/tui-ink/model-picker.js +0 -966
- package/dist/tui-ink/plan-confirm.d.ts +0 -7
- package/dist/tui-ink/plan-confirm.js +0 -115
- package/dist/tui-ink/question-dialog.d.ts +0 -8
- package/dist/tui-ink/question-dialog.js +0 -135
- package/dist/tui-ink/rewind-picker.d.ts +0 -8
- package/dist/tui-ink/rewind-picker.js +0 -102
- package/dist/tui-ink/run-session-picker.js +0 -22
- package/dist/tui-ink/run.d.ts +0 -62
- package/dist/tui-ink/run.js +0 -103
- package/dist/tui-ink/session-picker.d.ts +0 -10
- package/dist/tui-ink/session-picker.js +0 -113
- package/dist/tui-ink/stats-panel.d.ts +0 -11
- package/dist/tui-ink/stats-panel.js +0 -65
- package/dist/tui-ink/subagent-inspector.d.ts +0 -21
- package/dist/tui-ink/subagent-inspector.js +0 -205
- package/dist/tui-ink/use-terminal-size.d.ts +0 -4
- package/dist/tui-ink/use-terminal-size.js +0 -48
- package/dist/tui-ink/waiting-indicator.d.ts +0 -12
- package/dist/tui-ink/waiting-indicator.js +0 -129
- package/dist/tui-ink/welcome.d.ts +0 -25
- package/dist/tui-ink/welcome.js +0 -88
- package/dist/tui-ink/wordmark.d.ts +0 -8
- package/dist/tui-ink/wordmark.js +0 -103
- /package/dist/{tui-ink → tui/model}/composer-buffer.d.ts +0 -0
- /package/dist/{tui-ink → tui/model}/display-history.js +0 -0
- /package/dist/{tui-ink → tui/model}/input-queue.js +0 -0
- /package/dist/{tui-ink → tui/model}/key-events.d.ts +0 -0
- /package/dist/{tui-ink → tui/model}/key-events.js +0 -0
- /package/dist/{tui-ink → tui/model}/paste-operation-tracker.d.ts +0 -0
- /package/dist/{tui-ink → tui/model}/paste-operation-tracker.js +0 -0
- /package/dist/{tui-ink → tui/model}/recent-activity.d.ts +0 -0
- /package/dist/{tui-ink → tui/model}/submit-dedupe.js +0 -0
- /package/dist/{tui-ink → tui/model}/sync-output.d.ts +0 -0
- /package/dist/{tui-ink → tui/model}/sync-output.js +0 -0
- /package/dist/{tui-ink → tui/model}/terminal-env.d.ts +0 -0
- /package/dist/{tui-ink → tui/model}/terminal-env.js +0 -0
- /package/dist/{tui-ink → tui/model}/terminal-mouse.d.ts +0 -0
- /package/dist/{tui-ink → tui/model}/terminal-mouse.js +0 -0
- /package/dist/{tui-ink → tui/model}/width.d.ts +0 -0
- /package/dist/{tui-ink → tui/model}/width.js +0 -0
package/dist/tui-ink/markdown.js
DELETED
|
@@ -1,747 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
/**
|
|
3
|
-
* Lightweight Markdown renderer for Ink TUI.
|
|
4
|
-
* Supports code blocks, inline formatting, and tables.
|
|
5
|
-
*/
|
|
6
|
-
import React from "react";
|
|
7
|
-
import { Box, Text } from "ink";
|
|
8
|
-
import { ambiguousIsWide, visualWidth, graphemeWidth } from "./width.js";
|
|
9
|
-
import { useTerminalSize } from "./use-terminal-size.js";
|
|
10
|
-
import { useTheme } from "./theme.js";
|
|
11
|
-
import { highlightCode, highlightCodeSync } from "./code-highlight.js";
|
|
12
|
-
const graphemeSegmenter = typeof Intl !== "undefined" && typeof Intl.Segmenter === "function"
|
|
13
|
-
? new Intl.Segmenter(undefined, { granularity: "grapheme" })
|
|
14
|
-
: null;
|
|
15
|
-
function splitGraphemes(text) {
|
|
16
|
-
if (!text)
|
|
17
|
-
return [];
|
|
18
|
-
if (graphemeSegmenter) {
|
|
19
|
-
const out = [];
|
|
20
|
-
for (const { segment } of graphemeSegmenter.segment(text))
|
|
21
|
-
out.push(segment);
|
|
22
|
-
return out;
|
|
23
|
-
}
|
|
24
|
-
return Array.from(text);
|
|
25
|
-
}
|
|
26
|
-
export function parseMarkdownBlocks(text) {
|
|
27
|
-
const lines = text.split("\n");
|
|
28
|
-
const blocks = [];
|
|
29
|
-
let i = 0;
|
|
30
|
-
while (i < lines.length) {
|
|
31
|
-
const line = lines[i];
|
|
32
|
-
// Code block
|
|
33
|
-
if (line.startsWith("```")) {
|
|
34
|
-
const lang = line.slice(3).trim();
|
|
35
|
-
i++;
|
|
36
|
-
const codeLines = [];
|
|
37
|
-
while (i < lines.length && !lines[i].startsWith("```")) {
|
|
38
|
-
codeLines.push(lines[i]);
|
|
39
|
-
i++;
|
|
40
|
-
}
|
|
41
|
-
blocks.push({ type: "code", lang, lines: codeLines });
|
|
42
|
-
i++;
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
// Table
|
|
46
|
-
if (line.trim().startsWith("|")) {
|
|
47
|
-
const tableLines = [];
|
|
48
|
-
while (i < lines.length && lines[i].trim().startsWith("|")) {
|
|
49
|
-
tableLines.push(lines[i]);
|
|
50
|
-
i++;
|
|
51
|
-
}
|
|
52
|
-
if (tableLines.length >= 2) {
|
|
53
|
-
const headers = parseTableRow(tableLines[0]);
|
|
54
|
-
if (headers.length > 0 && isTableSeparatorRow(tableLines[1], headers.length)) {
|
|
55
|
-
const rows = tableLines.slice(2).map((rowLine) => normalizeTableRow(parseTableRow(rowLine), headers.length));
|
|
56
|
-
blocks.push({ type: "table", headers, rows });
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
blocks.push({ type: "paragraph", lines: tableLines });
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
63
|
-
blocks.push({ type: "paragraph", lines: tableLines });
|
|
64
|
-
}
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
// Heading
|
|
68
|
-
const headingMatch = line.match(/^(#{1,6})\s+(.*)$/);
|
|
69
|
-
if (headingMatch) {
|
|
70
|
-
blocks.push({ type: "heading", level: headingMatch[1].length, text: headingMatch[2].trim() });
|
|
71
|
-
i++;
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
// Empty line -> skip
|
|
75
|
-
if (line.trim() === "") {
|
|
76
|
-
i++;
|
|
77
|
-
continue;
|
|
78
|
-
}
|
|
79
|
-
// Paragraph
|
|
80
|
-
const paraLines = [];
|
|
81
|
-
while (i < lines.length &&
|
|
82
|
-
lines[i].trim() !== "" &&
|
|
83
|
-
!lines[i].startsWith("```") &&
|
|
84
|
-
!lines[i].trim().startsWith("|")) {
|
|
85
|
-
paraLines.push(lines[i]);
|
|
86
|
-
i++;
|
|
87
|
-
}
|
|
88
|
-
blocks.push({ type: "paragraph", lines: paraLines });
|
|
89
|
-
}
|
|
90
|
-
return blocks;
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Return the byte offset where the LAST markdown block begins in `text`.
|
|
94
|
-
*
|
|
95
|
-
* Used by the streaming renderer to split incoming content into a "stable
|
|
96
|
-
* prefix" (everything before the in-flight block — already-closed blocks)
|
|
97
|
-
* and an "unstable suffix" (the block currently being typed by the model).
|
|
98
|
-
* Mirrors parseMarkdownBlocks's lexing rules so the boundary it produces is
|
|
99
|
-
* compatible with how MarkdownContent will later parse the prefix.
|
|
100
|
-
*
|
|
101
|
-
* Returns `text.length` when no blocks are present (empty / whitespace-only
|
|
102
|
-
* input), and `0` when the entire text is a single in-flight block.
|
|
103
|
-
*/
|
|
104
|
-
export function findLastBlockStart(text) {
|
|
105
|
-
if (text.length === 0)
|
|
106
|
-
return 0;
|
|
107
|
-
const lines = text.split("\n");
|
|
108
|
-
// `split("\n")` on "abc\n" yields ["abc", ""]; on "abc" yields ["abc"]. The
|
|
109
|
-
// trailing newline only contributes a length-1 separator for every line
|
|
110
|
-
// except the final element produced by split.
|
|
111
|
-
const lineLengthWithSeparator = (idx) => {
|
|
112
|
-
const len = lines[idx].length;
|
|
113
|
-
return idx < lines.length - 1 ? len + 1 : len;
|
|
114
|
-
};
|
|
115
|
-
let i = 0;
|
|
116
|
-
let offset = 0;
|
|
117
|
-
let lastBlockStart = text.length;
|
|
118
|
-
while (i < lines.length) {
|
|
119
|
-
const line = lines[i];
|
|
120
|
-
// Code block (fenced). Unclosed fences extend through EOF — that's exactly
|
|
121
|
-
// what we want for streaming, since marked-lexer-style "single token until
|
|
122
|
-
// close" keeps the in-flight code in the unstable suffix.
|
|
123
|
-
if (line.startsWith("```")) {
|
|
124
|
-
lastBlockStart = offset;
|
|
125
|
-
offset += lineLengthWithSeparator(i);
|
|
126
|
-
i++;
|
|
127
|
-
while (i < lines.length && !lines[i].startsWith("```")) {
|
|
128
|
-
offset += lineLengthWithSeparator(i);
|
|
129
|
-
i++;
|
|
130
|
-
}
|
|
131
|
-
if (i < lines.length) {
|
|
132
|
-
offset += lineLengthWithSeparator(i);
|
|
133
|
-
i++;
|
|
134
|
-
}
|
|
135
|
-
continue;
|
|
136
|
-
}
|
|
137
|
-
// Table — same shape as parseMarkdownBlocks: consume consecutive `|` lines.
|
|
138
|
-
if (line.trim().startsWith("|")) {
|
|
139
|
-
lastBlockStart = offset;
|
|
140
|
-
while (i < lines.length && lines[i].trim().startsWith("|")) {
|
|
141
|
-
offset += lineLengthWithSeparator(i);
|
|
142
|
-
i++;
|
|
143
|
-
}
|
|
144
|
-
continue;
|
|
145
|
-
}
|
|
146
|
-
// Heading.
|
|
147
|
-
if (/^#{1,6}\s+/.test(line)) {
|
|
148
|
-
lastBlockStart = offset;
|
|
149
|
-
offset += lineLengthWithSeparator(i);
|
|
150
|
-
i++;
|
|
151
|
-
continue;
|
|
152
|
-
}
|
|
153
|
-
// Blank line — does not start a block; just advances the offset.
|
|
154
|
-
if (line.trim() === "") {
|
|
155
|
-
offset += lineLengthWithSeparator(i);
|
|
156
|
-
i++;
|
|
157
|
-
continue;
|
|
158
|
-
}
|
|
159
|
-
// Paragraph — runs until blank line or start of another block.
|
|
160
|
-
lastBlockStart = offset;
|
|
161
|
-
while (i < lines.length &&
|
|
162
|
-
lines[i].trim() !== "" &&
|
|
163
|
-
!lines[i].startsWith("```") &&
|
|
164
|
-
!lines[i].trim().startsWith("|")) {
|
|
165
|
-
offset += lineLengthWithSeparator(i);
|
|
166
|
-
i++;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
return lastBlockStart;
|
|
170
|
-
}
|
|
171
|
-
function parseTableRow(line) {
|
|
172
|
-
let body = line.trim();
|
|
173
|
-
if (body.startsWith("|"))
|
|
174
|
-
body = body.slice(1);
|
|
175
|
-
if (endsWithUnescapedPipe(body))
|
|
176
|
-
body = body.slice(0, -1);
|
|
177
|
-
const cells = [];
|
|
178
|
-
let current = "";
|
|
179
|
-
let inCode = false;
|
|
180
|
-
for (let i = 0; i < body.length; i++) {
|
|
181
|
-
const char = body[i];
|
|
182
|
-
if (char === "\\" && i + 1 < body.length) {
|
|
183
|
-
current += body[i + 1];
|
|
184
|
-
i++;
|
|
185
|
-
continue;
|
|
186
|
-
}
|
|
187
|
-
if (char === "`") {
|
|
188
|
-
inCode = !inCode;
|
|
189
|
-
current += char;
|
|
190
|
-
continue;
|
|
191
|
-
}
|
|
192
|
-
if (char === "|" && !inCode) {
|
|
193
|
-
cells.push(current.trim());
|
|
194
|
-
current = "";
|
|
195
|
-
continue;
|
|
196
|
-
}
|
|
197
|
-
current += char;
|
|
198
|
-
}
|
|
199
|
-
cells.push(current.trim());
|
|
200
|
-
return cells;
|
|
201
|
-
}
|
|
202
|
-
function endsWithUnescapedPipe(text) {
|
|
203
|
-
if (!text.endsWith("|"))
|
|
204
|
-
return false;
|
|
205
|
-
let slashCount = 0;
|
|
206
|
-
for (let i = text.length - 2; i >= 0 && text[i] === "\\"; i--)
|
|
207
|
-
slashCount++;
|
|
208
|
-
return slashCount % 2 === 0;
|
|
209
|
-
}
|
|
210
|
-
function isTableSeparatorRow(line, expectedColumns) {
|
|
211
|
-
const cells = parseTableRow(line);
|
|
212
|
-
if (cells.length !== expectedColumns)
|
|
213
|
-
return false;
|
|
214
|
-
return cells.every((cell) => /^:?-{3,}:?$/.test(cell.replace(/\s+/g, "")));
|
|
215
|
-
}
|
|
216
|
-
function normalizeTableRow(row, colCount) {
|
|
217
|
-
const normalized = row.slice(0, colCount);
|
|
218
|
-
while (normalized.length < colCount)
|
|
219
|
-
normalized.push("");
|
|
220
|
-
return normalized;
|
|
221
|
-
}
|
|
222
|
-
// Inline formatting: bold, italic, inline code
|
|
223
|
-
export function parseMarkdownInlineSegments(text, style = {}) {
|
|
224
|
-
const segments = [];
|
|
225
|
-
let buffer = "";
|
|
226
|
-
let i = 0;
|
|
227
|
-
const flush = () => {
|
|
228
|
-
appendInlineSegment(segments, buffer, style);
|
|
229
|
-
buffer = "";
|
|
230
|
-
};
|
|
231
|
-
while (i < text.length) {
|
|
232
|
-
const char = text[i];
|
|
233
|
-
if (char === "\\" && i + 1 < text.length) {
|
|
234
|
-
buffer += text[i + 1];
|
|
235
|
-
i += 2;
|
|
236
|
-
continue;
|
|
237
|
-
}
|
|
238
|
-
if (char === "`") {
|
|
239
|
-
const close = findClosingMarker(text, "`", i + 1);
|
|
240
|
-
if (close !== -1) {
|
|
241
|
-
flush();
|
|
242
|
-
appendInlineSegment(segments, text.slice(i + 1, close), { ...style, code: true });
|
|
243
|
-
i = close + 1;
|
|
244
|
-
continue;
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
const marker = inlineMarkerAt(text, i);
|
|
248
|
-
if (marker) {
|
|
249
|
-
const close = findClosingMarker(text, marker, i + marker.length);
|
|
250
|
-
if (close !== -1 && close > i + marker.length) {
|
|
251
|
-
flush();
|
|
252
|
-
const inner = text.slice(i + marker.length, close);
|
|
253
|
-
const nextStyle = marker === "***"
|
|
254
|
-
? { ...style, bold: true, italic: true }
|
|
255
|
-
: marker === "**" || marker === "__"
|
|
256
|
-
? { ...style, bold: true }
|
|
257
|
-
: { ...style, italic: true };
|
|
258
|
-
for (const segment of parseMarkdownInlineSegments(inner, nextStyle)) {
|
|
259
|
-
appendInlineSegment(segments, segment.text, segment);
|
|
260
|
-
}
|
|
261
|
-
i = close + marker.length;
|
|
262
|
-
continue;
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
buffer += char;
|
|
266
|
-
i++;
|
|
267
|
-
}
|
|
268
|
-
flush();
|
|
269
|
-
return segments.length > 0 ? segments : [{ text, ...style }];
|
|
270
|
-
}
|
|
271
|
-
function inlineMarkerAt(text, index) {
|
|
272
|
-
for (const marker of ["***", "**", "__", "*", "_"]) {
|
|
273
|
-
if (!text.startsWith(marker, index))
|
|
274
|
-
continue;
|
|
275
|
-
if (marker.includes("_") && isIntraWordUnderscore(text, index, marker.length))
|
|
276
|
-
continue;
|
|
277
|
-
return marker;
|
|
278
|
-
}
|
|
279
|
-
return null;
|
|
280
|
-
}
|
|
281
|
-
function findClosingMarker(text, marker, start) {
|
|
282
|
-
for (let i = start; i <= text.length - marker.length; i++) {
|
|
283
|
-
if (text[i] === "\\") {
|
|
284
|
-
i++;
|
|
285
|
-
continue;
|
|
286
|
-
}
|
|
287
|
-
if (!text.startsWith(marker, i))
|
|
288
|
-
continue;
|
|
289
|
-
if (marker.includes("_") && isIntraWordUnderscore(text, i, marker.length))
|
|
290
|
-
continue;
|
|
291
|
-
return i;
|
|
292
|
-
}
|
|
293
|
-
return -1;
|
|
294
|
-
}
|
|
295
|
-
function isIntraWordUnderscore(text, index, markerLength) {
|
|
296
|
-
const before = text[index - 1];
|
|
297
|
-
const after = text[index + markerLength];
|
|
298
|
-
return isWordChar(before) && isWordChar(after);
|
|
299
|
-
}
|
|
300
|
-
function isWordChar(char) {
|
|
301
|
-
return !!char && /[A-Za-z0-9]/.test(char);
|
|
302
|
-
}
|
|
303
|
-
function appendInlineSegment(segments, text, style) {
|
|
304
|
-
if (!text)
|
|
305
|
-
return;
|
|
306
|
-
const previous = segments[segments.length - 1];
|
|
307
|
-
const next = {
|
|
308
|
-
text,
|
|
309
|
-
bold: style.bold || undefined,
|
|
310
|
-
italic: style.italic || undefined,
|
|
311
|
-
code: style.code || undefined,
|
|
312
|
-
};
|
|
313
|
-
if (previous &&
|
|
314
|
-
previous.bold === next.bold &&
|
|
315
|
-
previous.italic === next.italic &&
|
|
316
|
-
previous.code === next.code) {
|
|
317
|
-
previous.text += text;
|
|
318
|
-
}
|
|
319
|
-
else {
|
|
320
|
-
segments.push(next);
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
function renderSegmentNodes(segments, keyPrefix, codeColor) {
|
|
324
|
-
return segments.map((segment, index) => (_jsx(Text, { bold: segment.bold, italic: segment.italic, color: segment.code ? codeColor : undefined, children: segment.text }, `${keyPrefix}-${index}`)));
|
|
325
|
-
}
|
|
326
|
-
function renderInlineSegments(text, keyPrefix, codeColor, style = {}) {
|
|
327
|
-
return renderSegmentNodes(parseMarkdownInlineSegments(text, style), keyPrefix, codeColor);
|
|
328
|
-
}
|
|
329
|
-
function inlinePlainText(text) {
|
|
330
|
-
return parseMarkdownInlineSegments(text).map((segment) => segment.text).join("");
|
|
331
|
-
}
|
|
332
|
-
function InlineText({ text, maxWidth }) {
|
|
333
|
-
const theme = useTheme();
|
|
334
|
-
const { columns } = useTerminalSize();
|
|
335
|
-
// Pre-wrap CJK-aware so Ink's space-only wrap-ansi can't chop "、"-joined runs
|
|
336
|
-
// mid-token. A caller-supplied maxWidth is exact (it already nets out the
|
|
337
|
-
// gutter); otherwise fall back to the terminal width minus a generous margin
|
|
338
|
-
// for borders/padding so a nested card never over-estimates and overflows —
|
|
339
|
-
// wrapping a few cells early is invisible, a mid-token chop is not.
|
|
340
|
-
const effectiveWidth = maxWidth ?? (columns ? Math.max(20, columns - 10) : undefined);
|
|
341
|
-
if (effectiveWidth === undefined) {
|
|
342
|
-
return _jsx(Text, { children: renderInlineSegments(text, "inline", theme.inlineCode) });
|
|
343
|
-
}
|
|
344
|
-
const wrapped = wrapInlineSegments(parseMarkdownInlineSegments(text), effectiveWidth);
|
|
345
|
-
return (_jsx(_Fragment, { children: wrapped.map((segments, li) => (_jsx(Text, { children: segments.length ? renderSegmentNodes(segments, `inline-${li}`, theme.inlineCode) : " " }, li))) }));
|
|
346
|
-
}
|
|
347
|
-
function CodeBlock({ lang, lines, flushTop = false }) {
|
|
348
|
-
const theme = useTheme();
|
|
349
|
-
// Lazy init: try sync highlight when shiki is already warm so the very first
|
|
350
|
-
// paint carries highlighted output. Fall back to raw lines if shiki hasn't
|
|
351
|
-
// loaded yet; useEffect will refresh the mounted transcript item later.
|
|
352
|
-
const [highlighted, setHighlighted] = React.useState(() => {
|
|
353
|
-
const code = lines.join("\n");
|
|
354
|
-
if (!code)
|
|
355
|
-
return lines;
|
|
356
|
-
const sync = highlightCodeSync(code, lang || "text");
|
|
357
|
-
return sync ? sync.split("\n") : lines;
|
|
358
|
-
});
|
|
359
|
-
const upgraded = React.useRef(highlighted !== lines);
|
|
360
|
-
React.useEffect(() => {
|
|
361
|
-
if (upgraded.current)
|
|
362
|
-
return;
|
|
363
|
-
let cancelled = false;
|
|
364
|
-
const code = lines.join("\n");
|
|
365
|
-
if (!code)
|
|
366
|
-
return;
|
|
367
|
-
highlightCode(code, lang || "text")
|
|
368
|
-
.then((ansi) => {
|
|
369
|
-
if (cancelled)
|
|
370
|
-
return;
|
|
371
|
-
upgraded.current = true;
|
|
372
|
-
setHighlighted(ansi.split("\n"));
|
|
373
|
-
})
|
|
374
|
-
.catch(() => { });
|
|
375
|
-
return () => {
|
|
376
|
-
cancelled = true;
|
|
377
|
-
};
|
|
378
|
-
}, [lang, lines]);
|
|
379
|
-
return (_jsxs(Box, { flexDirection: "column", marginTop: flushTop ? 0 : 1, marginBottom: 1, children: [lang && _jsx(Text, { color: theme.muted, children: lang }), _jsx(Box, { flexDirection: "column", children: highlighted.map((line, i) => (_jsx(Text, { children: line || " " }, i))) })] }));
|
|
380
|
-
}
|
|
381
|
-
function TableBlock({ headers, rows, maxWidth, flushTop = false, }) {
|
|
382
|
-
const theme = useTheme();
|
|
383
|
-
const { columns: termWidth } = useTerminalSize();
|
|
384
|
-
const colCount = headers.length;
|
|
385
|
-
// Reserve a buffer so the table fits even when wrapped inside an indented
|
|
386
|
-
// box (e.g. the timeline gutter contributes marginLeft + "● " = 5 cells).
|
|
387
|
-
const budget = Math.max(20, (maxWidth ?? termWidth) - 8);
|
|
388
|
-
// Box-drawing ─│┌┬┼… are East Asian *Ambiguous*-width: on a terminal that
|
|
389
|
-
// renders them 2 cells wide, border rows would paint at twice the width the
|
|
390
|
-
// cell rows were budgeted for (and twice what Ink itself measures), so the
|
|
391
|
-
// terminal hard-wraps them into scattered fragments. There is no way to hit
|
|
392
|
-
// odd widths with 2-cell dashes, so on such terminals draw ASCII borders —
|
|
393
|
-
// the only glyphs whose width every layer agrees on.
|
|
394
|
-
const g = ambiguousIsWide()
|
|
395
|
-
? { h: "-", v: "|", tl: "+", tm: "+", tr: "+", ml: "+", mm: "+", mr: "+", bl: "+", bm: "+", br: "+" }
|
|
396
|
-
: { h: "─", v: "│", tl: "┌", tm: "┬", tr: "┐", ml: "├", mm: "┼", mr: "┤", bl: "└", bm: "┴", br: "┘" };
|
|
397
|
-
const maxWidths = headers.map((h, i) => {
|
|
398
|
-
let max = visualWidth(inlinePlainText(h));
|
|
399
|
-
for (const row of rows) {
|
|
400
|
-
const cell = row[i] || "";
|
|
401
|
-
max = Math.max(max, visualWidth(inlinePlainText(cell)));
|
|
402
|
-
}
|
|
403
|
-
return max;
|
|
404
|
-
});
|
|
405
|
-
const totalInnerWidth = maxWidths.reduce((a, b) => a + b, 0);
|
|
406
|
-
const separatorsWidth = colCount * 3 + 1; // " │ " separators + outer edges
|
|
407
|
-
const totalWidth = totalInnerWidth + separatorsWidth;
|
|
408
|
-
let widths = [...maxWidths];
|
|
409
|
-
if (totalWidth > budget) {
|
|
410
|
-
const available = Math.max(budget - separatorsWidth, colCount * 4);
|
|
411
|
-
widths = allocateColumnWidths(maxWidths, available);
|
|
412
|
-
}
|
|
413
|
-
const top = g.tl + widths.map((w) => g.h.repeat(w + 2)).join(g.tm) + g.tr;
|
|
414
|
-
const mid = g.ml + widths.map((w) => g.h.repeat(w + 2)).join(g.mm) + g.mr;
|
|
415
|
-
const bot = g.bl + widths.map((w) => g.h.repeat(w + 2)).join(g.bm) + g.br;
|
|
416
|
-
// A cell wider than its column wraps onto continuation lines (CJK-aware, so
|
|
417
|
-
// 、-joined user lists break cleanly); the row grows to its tallest cell.
|
|
418
|
-
const renderRow = (cells, keyPrefix, isHeader = false) => {
|
|
419
|
-
const wrapped = cells.map((c, i) => {
|
|
420
|
-
const width = widths[i] ?? 4;
|
|
421
|
-
let lines = wrapInlineSegments(parseMarkdownInlineSegments(c, { bold: isHeader }), width);
|
|
422
|
-
if (lines.length > MAX_TABLE_CELL_LINES) {
|
|
423
|
-
lines = lines.slice(0, MAX_TABLE_CELL_LINES);
|
|
424
|
-
const last = lines[MAX_TABLE_CELL_LINES - 1];
|
|
425
|
-
lines[MAX_TABLE_CELL_LINES - 1] = truncateInlineSegments([...last, { text: " …" }], width);
|
|
426
|
-
}
|
|
427
|
-
return lines;
|
|
428
|
-
});
|
|
429
|
-
const height = Math.max(1, ...wrapped.map((lines) => lines.length));
|
|
430
|
-
return (_jsx(Box, { flexDirection: "column", children: Array.from({ length: height }, (_, line) => (_jsxs(Text, { children: [`${g.v} `, wrapped.map((cellLines, i) => (_jsxs(React.Fragment, { children: [renderCellLine(cellLines[line] ?? [], widths[i] ?? 4, `${keyPrefix}-cell-${i}-${line}`, theme.inlineCode), i < colCount - 1 ? ` ${g.v} ` : ` ${g.v}`] }, i)))] }, `${keyPrefix}-line-${line}`))) }, keyPrefix));
|
|
431
|
-
};
|
|
432
|
-
return (_jsxs(Box, { flexDirection: "column", marginTop: flushTop ? 0 : 1, marginBottom: 1, children: [_jsx(Text, { children: top }), renderRow(headers, "header", true), _jsx(Text, { children: mid }), rows.map((row, ri) => renderRow(row, `row-${ri}`)), _jsx(Text, { children: bot })] }));
|
|
433
|
-
}
|
|
434
|
-
/** Cap a pathological cell (huge blob of text) at this many wrapped lines. */
|
|
435
|
-
const MAX_TABLE_CELL_LINES = 8;
|
|
436
|
-
/**
|
|
437
|
-
* Distribute `available` inner cells across columns. Columns whose natural
|
|
438
|
-
* width already fits their fair share keep it untouched (numbers and dates
|
|
439
|
-
* never wrap); only the wider columns split the remainder proportionally and
|
|
440
|
-
* wrap their content.
|
|
441
|
-
*/
|
|
442
|
-
export function allocateColumnWidths(natural, available) {
|
|
443
|
-
const count = natural.length;
|
|
444
|
-
const widths = new Array(count).fill(0);
|
|
445
|
-
const fixed = new Array(count).fill(false);
|
|
446
|
-
let remaining = available;
|
|
447
|
-
let unfixed = count;
|
|
448
|
-
// Each fixed column frees slack that can fit further columns — iterate to a
|
|
449
|
-
// fixpoint. Terminates: natural sum exceeds `available` (only caller path),
|
|
450
|
-
// so at least one column always stays unfixed.
|
|
451
|
-
let changed = true;
|
|
452
|
-
while (changed && unfixed > 0) {
|
|
453
|
-
changed = false;
|
|
454
|
-
const fair = remaining / unfixed;
|
|
455
|
-
for (let i = 0; i < count; i++) {
|
|
456
|
-
if (fixed[i] || natural[i] > fair)
|
|
457
|
-
continue;
|
|
458
|
-
fixed[i] = true;
|
|
459
|
-
widths[i] = natural[i];
|
|
460
|
-
remaining -= natural[i];
|
|
461
|
-
unfixed -= 1;
|
|
462
|
-
changed = true;
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
if (unfixed > 0) {
|
|
466
|
-
const wideTotal = natural.reduce((sum, w, i) => sum + (fixed[i] ? 0 : w), 0);
|
|
467
|
-
let assigned = 0;
|
|
468
|
-
let lastWide = -1;
|
|
469
|
-
for (let i = 0; i < count; i++) {
|
|
470
|
-
if (fixed[i])
|
|
471
|
-
continue;
|
|
472
|
-
widths[i] = Math.max(4, Math.floor((remaining * natural[i]) / wideTotal));
|
|
473
|
-
assigned += widths[i];
|
|
474
|
-
lastWide = i;
|
|
475
|
-
}
|
|
476
|
-
// Flooring leftovers go to the last wide column instead of being wasted.
|
|
477
|
-
if (lastWide >= 0 && assigned < remaining)
|
|
478
|
-
widths[lastWide] += remaining - assigned;
|
|
479
|
-
}
|
|
480
|
-
// On a very narrow budget the 4-cell floor can overshoot; shave the widest
|
|
481
|
-
// columns so the row never exceeds `available` and hard-wraps.
|
|
482
|
-
let excess = widths.reduce((a, b) => a + b, 0) - available;
|
|
483
|
-
while (excess > 0) {
|
|
484
|
-
let widest = -1;
|
|
485
|
-
for (let i = 0; i < count; i++) {
|
|
486
|
-
if (widths[i] > 4 && (widest === -1 || widths[i] > widths[widest]))
|
|
487
|
-
widest = i;
|
|
488
|
-
}
|
|
489
|
-
if (widest === -1)
|
|
490
|
-
break;
|
|
491
|
-
widths[widest] -= 1;
|
|
492
|
-
excess -= 1;
|
|
493
|
-
}
|
|
494
|
-
return widths;
|
|
495
|
-
}
|
|
496
|
-
function renderCellLine(segments, width, keyPrefix, codeColor) {
|
|
497
|
-
const padding = " ".repeat(Math.max(0, width - inlineSegmentsWidth(segments)));
|
|
498
|
-
return [
|
|
499
|
-
...renderSegmentNodes(segments, keyPrefix, codeColor),
|
|
500
|
-
_jsx(Text, { children: padding }, `${keyPrefix}-pad`),
|
|
501
|
-
];
|
|
502
|
-
}
|
|
503
|
-
function truncateInlineSegments(segments, width) {
|
|
504
|
-
if (inlineSegmentsWidth(segments) <= width)
|
|
505
|
-
return segments;
|
|
506
|
-
// The ellipsis is itself ambiguous-width (2 cells on an ambiguous-wide
|
|
507
|
-
// terminal) — reserve its real width or every truncated cell overflows.
|
|
508
|
-
const ellipsisWidth = graphemeWidth("…");
|
|
509
|
-
if (width <= ellipsisWidth)
|
|
510
|
-
return [{ text: "…" }];
|
|
511
|
-
const target = width - ellipsisWidth;
|
|
512
|
-
const output = [];
|
|
513
|
-
let used = 0;
|
|
514
|
-
for (const segment of segments) {
|
|
515
|
-
let text = "";
|
|
516
|
-
for (const grapheme of splitGraphemes(segment.text)) {
|
|
517
|
-
const gWidth = graphemeWidth(grapheme);
|
|
518
|
-
if (used + gWidth > target) {
|
|
519
|
-
if (text)
|
|
520
|
-
appendInlineSegment(output, text, segment);
|
|
521
|
-
appendInlineSegment(output, "…", {});
|
|
522
|
-
return output;
|
|
523
|
-
}
|
|
524
|
-
text += grapheme;
|
|
525
|
-
used += gWidth;
|
|
526
|
-
}
|
|
527
|
-
appendInlineSegment(output, text, segment);
|
|
528
|
-
}
|
|
529
|
-
appendInlineSegment(output, "…", {});
|
|
530
|
-
return output;
|
|
531
|
-
}
|
|
532
|
-
/**
|
|
533
|
-
* CJK-aware line wrap over styled inline segments.
|
|
534
|
-
*
|
|
535
|
-
* Ink wraps a <Text> via wrap-ansi with `hard: true`, which treats only ASCII
|
|
536
|
-
* spaces as break opportunities. Chinese joins items with ideographic
|
|
537
|
-
* punctuation like "、" and no ASCII space, so a long run such as
|
|
538
|
-
* `A、B、ProviderSendTurnInput` is seen as ONE unbreakable word and gets chopped
|
|
539
|
-
* mid-token at the column edge (e.g. `ProviderSendTurnInpu` + `t`), and the
|
|
540
|
-
* overflow spills past the box into the terminal's own hard wrap. We instead
|
|
541
|
-
* break at ASCII spaces AND after CJK punctuation (、,。!?;:)等 — keeping ASCII
|
|
542
|
-
* identifiers whole — and hard-split only a lone token wider than the line.
|
|
543
|
-
*/
|
|
544
|
-
export function wrapInlineSegments(segments, maxWidth) {
|
|
545
|
-
if (maxWidth <= 0)
|
|
546
|
-
return [segments];
|
|
547
|
-
const cells = [];
|
|
548
|
-
for (const segment of segments) {
|
|
549
|
-
const style = { bold: segment.bold, italic: segment.italic, code: segment.code };
|
|
550
|
-
for (const grapheme of splitGraphemes(segment.text)) {
|
|
551
|
-
cells.push({ grapheme, width: graphemeWidth(grapheme), style });
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
// CJK punctuation that acts as break opportunity AFTER it (like English comma/period).
|
|
555
|
-
const isCJKPunctuation = (g) => /^[、,。!?;:,!?;:]$/.test(g);
|
|
556
|
-
const chunks = [];
|
|
557
|
-
const chunkWidth = (run) => run.reduce((sum, cell) => sum + cell.width, 0);
|
|
558
|
-
let i = 0;
|
|
559
|
-
while (i < cells.length) {
|
|
560
|
-
const cell = cells[i];
|
|
561
|
-
if (cell.grapheme === " ") {
|
|
562
|
-
const run = [];
|
|
563
|
-
while (i < cells.length && cells[i].grapheme === " ")
|
|
564
|
-
run.push(cells[i++]);
|
|
565
|
-
chunks.push({ cells: run, width: chunkWidth(run), breakAfter: true });
|
|
566
|
-
}
|
|
567
|
-
else if (cell.width >= 2) {
|
|
568
|
-
chunks.push({ cells: [cell], width: cell.width, breakAfter: isCJKPunctuation(cell.grapheme) });
|
|
569
|
-
i++;
|
|
570
|
-
}
|
|
571
|
-
else {
|
|
572
|
-
const run = [];
|
|
573
|
-
while (i < cells.length && cells[i].grapheme !== " " && cells[i].width < 2)
|
|
574
|
-
run.push(cells[i++]);
|
|
575
|
-
chunks.push({ cells: run, width: chunkWidth(run), breakAfter: false });
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
// CJK line-break rules (禁則処理 / kinsoku): a closing bracket or a trailing
|
|
579
|
-
// punctuation mark must never begin a wrapped line (避头), and an opening
|
|
580
|
-
// bracket must never end one (避尾). Otherwise a line that fills up right
|
|
581
|
-
// before ")。" pushes that lone punctuation onto the next row — the jarring
|
|
582
|
-
// orphaned ")。" at the left margin. Glue each such mark to the chunk it must
|
|
583
|
-
// stay with, so the pair wraps as one unit (and may move down together).
|
|
584
|
-
const noStartGlyph = (g) => /^[、,。.!?;:))〕】}」』》〉…―ー]$/.test(g);
|
|
585
|
-
const noEndGlyph = (g) => /^[((〔【{「『《〈]$/.test(g);
|
|
586
|
-
const soleGlyph = (c) => (c.cells.length === 1 ? c.cells[0].grapheme : "");
|
|
587
|
-
const isSpaceChunk = (c) => c.cells[0]?.grapheme === " ";
|
|
588
|
-
const glued = [];
|
|
589
|
-
for (const chunk of chunks) {
|
|
590
|
-
const g = soleGlyph(chunk);
|
|
591
|
-
const prev = glued[glued.length - 1];
|
|
592
|
-
if (g && noStartGlyph(g) && prev && !isSpaceChunk(prev)) {
|
|
593
|
-
prev.cells.push(...chunk.cells);
|
|
594
|
-
prev.width += chunk.width;
|
|
595
|
-
prev.breakAfter = chunk.breakAfter; // inherit the mark's own break behavior
|
|
596
|
-
}
|
|
597
|
-
else {
|
|
598
|
-
glued.push({ cells: [...chunk.cells], width: chunk.width, breakAfter: chunk.breakAfter });
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
for (let k = glued.length - 2; k >= 0; k--) {
|
|
602
|
-
const g = soleGlyph(glued[k]);
|
|
603
|
-
if (g && noEndGlyph(g) && !isSpaceChunk(glued[k + 1])) {
|
|
604
|
-
const next = glued[k + 1];
|
|
605
|
-
next.cells.unshift(...glued[k].cells);
|
|
606
|
-
next.width += glued[k].width;
|
|
607
|
-
glued.splice(k, 1);
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
const lines = [];
|
|
611
|
-
let line = [];
|
|
612
|
-
let lineWidth = 0;
|
|
613
|
-
const flush = () => { lines.push(line); line = []; lineWidth = 0; };
|
|
614
|
-
for (let j = 0; j < glued.length; j++) {
|
|
615
|
-
const chunk = glued[j];
|
|
616
|
-
const isSpace = chunk.cells[0]?.grapheme === " ";
|
|
617
|
-
if (isSpace) {
|
|
618
|
-
if (lineWidth === 0)
|
|
619
|
-
continue;
|
|
620
|
-
if (lineWidth + chunk.width <= maxWidth) {
|
|
621
|
-
line.push(...chunk.cells);
|
|
622
|
-
lineWidth += chunk.width;
|
|
623
|
-
}
|
|
624
|
-
else {
|
|
625
|
-
flush();
|
|
626
|
-
}
|
|
627
|
-
continue;
|
|
628
|
-
}
|
|
629
|
-
if (lineWidth + chunk.width <= maxWidth) {
|
|
630
|
-
line.push(...chunk.cells);
|
|
631
|
-
lineWidth += chunk.width;
|
|
632
|
-
if (chunk.breakAfter && j < glued.length - 1) {
|
|
633
|
-
const nextChunk = glued[j + 1];
|
|
634
|
-
const nextIsSpace = nextChunk.cells[0]?.grapheme === " ";
|
|
635
|
-
if (!nextIsSpace && lineWidth + nextChunk.width > maxWidth)
|
|
636
|
-
flush();
|
|
637
|
-
}
|
|
638
|
-
continue;
|
|
639
|
-
}
|
|
640
|
-
if (lineWidth > 0)
|
|
641
|
-
flush();
|
|
642
|
-
if (chunk.width <= maxWidth) {
|
|
643
|
-
line.push(...chunk.cells);
|
|
644
|
-
lineWidth = chunk.width;
|
|
645
|
-
continue;
|
|
646
|
-
}
|
|
647
|
-
for (const cell of chunk.cells) {
|
|
648
|
-
if (lineWidth + cell.width > maxWidth && lineWidth > 0)
|
|
649
|
-
flush();
|
|
650
|
-
line.push(cell);
|
|
651
|
-
lineWidth += cell.width;
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
if (line.length > 0 || lines.length === 0)
|
|
655
|
-
lines.push(line);
|
|
656
|
-
return lines.map((lineCells) => {
|
|
657
|
-
const out = [];
|
|
658
|
-
for (const cell of lineCells)
|
|
659
|
-
appendInlineSegment(out, cell.grapheme, cell.style);
|
|
660
|
-
return out;
|
|
661
|
-
});
|
|
662
|
-
}
|
|
663
|
-
function inlineSegmentsWidth(segments) {
|
|
664
|
-
return segments.reduce((sum, segment) => sum + visualWidth(segment.text), 0);
|
|
665
|
-
}
|
|
666
|
-
function HeadingBlock({ level, text, flushTop = false }) {
|
|
667
|
-
const theme = useTheme();
|
|
668
|
-
const props = { bold: true };
|
|
669
|
-
if (level === 1) {
|
|
670
|
-
props.underline = true;
|
|
671
|
-
props.color = theme.accent;
|
|
672
|
-
}
|
|
673
|
-
else if (level === 2) {
|
|
674
|
-
props.color = theme.accent;
|
|
675
|
-
}
|
|
676
|
-
else if (level === 3) {
|
|
677
|
-
props.color = theme.warning;
|
|
678
|
-
}
|
|
679
|
-
return (_jsx(Box, { marginTop: flushTop ? 0 : 1, marginBottom: 1, children: _jsx(Text, { ...props, children: text }) }));
|
|
680
|
-
}
|
|
681
|
-
/**
|
|
682
|
-
* Streaming-aware wrapper around `MarkdownContent`.
|
|
683
|
-
*
|
|
684
|
-
* On every render, splits the incoming `content` into a stable prefix
|
|
685
|
-
* (everything before the in-flight block) and an unstable suffix (the block
|
|
686
|
-
* currently being typed). The two halves are rendered as two separate
|
|
687
|
-
* `MarkdownContent` instances; the stable one uses the same `content` prop
|
|
688
|
-
* across deltas, so its internal `useMemo([content])` short-circuits and
|
|
689
|
-
* does NOT re-parse on each token — which is the whole point. Only the
|
|
690
|
-
* shorter unstable suffix re-parses per delta.
|
|
691
|
-
*
|
|
692
|
-
* The boundary advances monotonically (the prefix only grows). A defensive
|
|
693
|
-
* reset handles the rare case where `content` is replaced wholesale (e.g.,
|
|
694
|
-
* the user re-enters a turn).
|
|
695
|
-
*/
|
|
696
|
-
export function StreamingMarkdown({ content, maxWidth, flushTop = false, }) {
|
|
697
|
-
const stablePrefixRef = React.useRef("");
|
|
698
|
-
if (!content.startsWith(stablePrefixRef.current)) {
|
|
699
|
-
stablePrefixRef.current = "";
|
|
700
|
-
}
|
|
701
|
-
const boundary = stablePrefixRef.current.length;
|
|
702
|
-
const tail = content.substring(boundary);
|
|
703
|
-
const advance = findLastBlockStart(tail);
|
|
704
|
-
if (advance > 0) {
|
|
705
|
-
stablePrefixRef.current = content.substring(0, boundary + advance);
|
|
706
|
-
}
|
|
707
|
-
const stablePrefix = stablePrefixRef.current;
|
|
708
|
-
const unstableSuffix = content.substring(stablePrefix.length);
|
|
709
|
-
return (_jsxs(Box, { flexDirection: "column", children: [stablePrefix && _jsx(MarkdownContent, { content: stablePrefix, maxWidth: maxWidth, flushTop: flushTop }), unstableSuffix && _jsx(MarkdownContent, { content: unstableSuffix, maxWidth: maxWidth, flushTop: flushTop && !stablePrefix })] }));
|
|
710
|
-
}
|
|
711
|
-
// A markdown list item: optional indent, a bullet (-, *, +) or ordered marker
|
|
712
|
-
// (1. / 2)), then whitespace, then the item text. The captured prefix is
|
|
713
|
-
// rendered in its own fixed column so wrapped continuation lines hang-indent
|
|
714
|
-
// under the item text instead of collapsing back to the left margin.
|
|
715
|
-
const LIST_ITEM_RE = /^(\s*)([-*+]|\d{1,9}[.)])(\s+)(\S.*)$/;
|
|
716
|
-
export function splitListItem(line) {
|
|
717
|
-
const m = LIST_ITEM_RE.exec(line);
|
|
718
|
-
if (!m)
|
|
719
|
-
return null;
|
|
720
|
-
const prefix = m[1] + m[2] + m[3];
|
|
721
|
-
return { prefix, content: m[4], indent: visualWidth(prefix) };
|
|
722
|
-
}
|
|
723
|
-
export function MarkdownContent({ content, maxWidth, flushTop = false, }) {
|
|
724
|
-
const blocks = React.useMemo(() => parseMarkdownBlocks(content), [content]);
|
|
725
|
-
return (_jsx(Box, { flexDirection: "column", children: blocks.map((block, i) => {
|
|
726
|
-
const blockFlushTop = flushTop && i === 0;
|
|
727
|
-
if (block.type === "code") {
|
|
728
|
-
return _jsx(CodeBlock, { lang: block.lang, lines: block.lines, flushTop: blockFlushTop }, i);
|
|
729
|
-
}
|
|
730
|
-
if (block.type === "table") {
|
|
731
|
-
return (_jsx(TableBlock, { headers: block.headers, rows: block.rows, maxWidth: maxWidth, flushTop: blockFlushTop }, i));
|
|
732
|
-
}
|
|
733
|
-
if (block.type === "heading") {
|
|
734
|
-
return _jsx(HeadingBlock, { level: block.level, text: block.text, flushTop: blockFlushTop }, i);
|
|
735
|
-
}
|
|
736
|
-
return (_jsx(Box, { flexDirection: "column", marginBottom: 1, children: block.lines.map((line, li) => {
|
|
737
|
-
const item = splitListItem(line);
|
|
738
|
-
if (item) {
|
|
739
|
-
// Marker in a non-shrinking column; the item text flows in a
|
|
740
|
-
// flex column to its right so every wrapped row aligns under it.
|
|
741
|
-
const contentWidth = maxWidth !== undefined ? Math.max(4, maxWidth - item.indent) : undefined;
|
|
742
|
-
return (_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { flexShrink: 0, children: _jsx(Text, { children: item.prefix }) }), _jsx(Box, { flexDirection: "column", flexGrow: 1, children: _jsx(InlineText, { text: item.content, maxWidth: contentWidth }) })] }, li));
|
|
743
|
-
}
|
|
744
|
-
return _jsx(InlineText, { text: line, maxWidth: maxWidth }, li);
|
|
745
|
-
}) }, i));
|
|
746
|
-
}) }));
|
|
747
|
-
}
|