@adea-ai/ui 0.140.0 → 0.142.0
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/dist/components/composites/appearance-editor/appearance-editor.js +2 -2
- package/dist/components/composites/appearance-editor/appearance-editor.js.map +1 -1
- package/dist/components/composites/catalog-browser/catalog-browser-group.js +1 -1
- package/dist/components/composites/chain-of-thought/chain-of-thought.d.ts +56 -0
- package/dist/components/composites/chain-of-thought/chain-of-thought.d.ts.map +1 -0
- package/dist/components/composites/chain-of-thought/chain-of-thought.js +117 -0
- package/dist/components/composites/chain-of-thought/chain-of-thought.js.map +1 -0
- package/dist/components/composites/chain-of-thought/index.d.ts +2 -0
- package/dist/components/composites/chain-of-thought/index.d.ts.map +1 -0
- package/dist/components/composites/chain-of-thought/index.js +2 -0
- package/dist/components/composites/chat/chat.d.ts +67 -0
- package/dist/components/composites/chat/chat.d.ts.map +1 -0
- package/dist/components/composites/chat/chat.js +179 -0
- package/dist/components/composites/chat/chat.js.map +1 -0
- package/dist/components/composites/chat/index.d.ts +2 -0
- package/dist/components/composites/chat/index.d.ts.map +1 -0
- package/dist/components/composites/chat/index.js +2 -0
- package/dist/components/composites/checkpoint/checkpoint-model.d.ts +34 -0
- package/dist/components/composites/checkpoint/checkpoint-model.d.ts.map +1 -0
- package/dist/components/composites/checkpoint/checkpoint-model.js +48 -0
- package/dist/components/composites/checkpoint/checkpoint-model.js.map +1 -0
- package/dist/components/composites/checkpoint/checkpoint.d.ts +48 -0
- package/dist/components/composites/checkpoint/checkpoint.d.ts.map +1 -0
- package/dist/components/composites/checkpoint/checkpoint.js +160 -0
- package/dist/components/composites/checkpoint/checkpoint.js.map +1 -0
- package/dist/components/composites/checkpoint/index.d.ts +3 -0
- package/dist/components/composites/checkpoint/index.d.ts.map +1 -0
- package/dist/components/composites/checkpoint/index.js +3 -0
- package/dist/components/composites/confirmation/confirmation-model.d.ts +45 -0
- package/dist/components/composites/confirmation/confirmation-model.d.ts.map +1 -0
- package/dist/components/composites/confirmation/confirmation-model.js +51 -0
- package/dist/components/composites/confirmation/confirmation-model.js.map +1 -0
- package/dist/components/composites/confirmation/confirmation.d.ts +72 -0
- package/dist/components/composites/confirmation/confirmation.d.ts.map +1 -0
- package/dist/components/composites/confirmation/confirmation.js +300 -0
- package/dist/components/composites/confirmation/confirmation.js.map +1 -0
- package/dist/components/composites/confirmation/index.d.ts +3 -0
- package/dist/components/composites/confirmation/index.d.ts.map +1 -0
- package/dist/components/composites/confirmation/index.js +3 -0
- package/dist/components/composites/context/context-model.d.ts +69 -0
- package/dist/components/composites/context/context-model.d.ts.map +1 -0
- package/dist/components/composites/context/context-model.js +122 -0
- package/dist/components/composites/context/context-model.js.map +1 -0
- package/dist/components/composites/context/context.d.ts +38 -0
- package/dist/components/composites/context/context.d.ts.map +1 -0
- package/dist/components/composites/context/context.js +234 -0
- package/dist/components/composites/context/context.js.map +1 -0
- package/dist/components/composites/context/index.d.ts +3 -0
- package/dist/components/composites/context/index.d.ts.map +1 -0
- package/dist/components/composites/context/index.js +3 -0
- package/dist/components/composites/file-tree/build-file-tree.d.ts +138 -0
- package/dist/components/composites/file-tree/build-file-tree.d.ts.map +1 -0
- package/dist/components/composites/file-tree/build-file-tree.js +325 -0
- package/dist/components/composites/file-tree/build-file-tree.js.map +1 -0
- package/dist/components/composites/file-tree/file-tree.d.ts +74 -0
- package/dist/components/composites/file-tree/file-tree.d.ts.map +1 -0
- package/dist/components/composites/file-tree/file-tree.js +389 -0
- package/dist/components/composites/file-tree/file-tree.js.map +1 -0
- package/dist/components/composites/file-tree/index.d.ts +3 -0
- package/dist/components/composites/file-tree/index.d.ts.map +1 -0
- package/dist/components/composites/file-tree/index.js +3 -0
- package/dist/components/composites/inline-citation/citation.d.ts +37 -0
- package/dist/components/composites/inline-citation/citation.d.ts.map +1 -0
- package/dist/components/composites/inline-citation/citation.js +52 -0
- package/dist/components/composites/inline-citation/citation.js.map +1 -0
- package/dist/components/composites/inline-citation/index.d.ts +3 -0
- package/dist/components/composites/inline-citation/index.d.ts.map +1 -0
- package/dist/components/composites/inline-citation/index.js +3 -0
- package/dist/components/composites/inline-citation/inline-citation.d.ts +64 -0
- package/dist/components/composites/inline-citation/inline-citation.d.ts.map +1 -0
- package/dist/components/composites/inline-citation/inline-citation.js +272 -0
- package/dist/components/composites/inline-citation/inline-citation.js.map +1 -0
- package/dist/components/composites/message-scroller/index.d.ts +3 -0
- package/dist/components/composites/message-scroller/index.d.ts.map +1 -0
- package/dist/components/composites/message-scroller/index.js +3 -0
- package/dist/components/composites/message-scroller/message-scroller.d.ts +55 -0
- package/dist/components/composites/message-scroller/message-scroller.d.ts.map +1 -0
- package/dist/components/composites/message-scroller/message-scroller.js +201 -0
- package/dist/components/composites/message-scroller/message-scroller.js.map +1 -0
- package/dist/components/composites/message-scroller/scroll-anchor.d.ts +66 -0
- package/dist/components/composites/message-scroller/scroll-anchor.d.ts.map +1 -0
- package/dist/components/composites/message-scroller/scroll-anchor.js +122 -0
- package/dist/components/composites/message-scroller/scroll-anchor.js.map +1 -0
- package/dist/components/composites/model-selector/index.d.ts +3 -0
- package/dist/components/composites/model-selector/index.d.ts.map +1 -0
- package/dist/components/composites/model-selector/index.js +3 -0
- package/dist/components/composites/model-selector/model-selector.d.ts +55 -0
- package/dist/components/composites/model-selector/model-selector.d.ts.map +1 -0
- package/dist/components/composites/model-selector/model-selector.js +490 -0
- package/dist/components/composites/model-selector/model-selector.js.map +1 -0
- package/dist/components/composites/model-selector/models.d.ts +51 -0
- package/dist/components/composites/model-selector/models.d.ts.map +1 -0
- package/dist/components/composites/model-selector/models.js +71 -0
- package/dist/components/composites/model-selector/models.js.map +1 -0
- package/dist/components/composites/plan/index.d.ts +2 -0
- package/dist/components/composites/plan/index.d.ts.map +1 -0
- package/dist/components/composites/plan/index.js +2 -0
- package/dist/components/composites/plan/plan.d.ts +61 -0
- package/dist/components/composites/plan/plan.d.ts.map +1 -0
- package/dist/components/composites/plan/plan.js +130 -0
- package/dist/components/composites/plan/plan.js.map +1 -0
- package/dist/components/composites/queue/index.d.ts +2 -0
- package/dist/components/composites/queue/index.d.ts.map +1 -0
- package/dist/components/composites/queue/index.js +2 -0
- package/dist/components/composites/queue/queue.d.ts +77 -0
- package/dist/components/composites/queue/queue.d.ts.map +1 -0
- package/dist/components/composites/queue/queue.js +271 -0
- package/dist/components/composites/queue/queue.js.map +1 -0
- package/dist/components/composites/reasoning/index.d.ts +2 -0
- package/dist/components/composites/reasoning/index.d.ts.map +1 -0
- package/dist/components/composites/reasoning/index.js +2 -0
- package/dist/components/composites/reasoning/reasoning.d.ts +62 -0
- package/dist/components/composites/reasoning/reasoning.d.ts.map +1 -0
- package/dist/components/composites/reasoning/reasoning.js +156 -0
- package/dist/components/composites/reasoning/reasoning.js.map +1 -0
- package/dist/components/composites/sources/index.d.ts +3 -0
- package/dist/components/composites/sources/index.d.ts.map +1 -0
- package/dist/components/composites/sources/index.js +3 -0
- package/dist/components/composites/sources/source.d.ts +46 -0
- package/dist/components/composites/sources/source.d.ts.map +1 -0
- package/dist/components/composites/sources/source.js +74 -0
- package/dist/components/composites/sources/source.js.map +1 -0
- package/dist/components/composites/sources/sources.d.ts +45 -0
- package/dist/components/composites/sources/sources.d.ts.map +1 -0
- package/dist/components/composites/sources/sources.js +175 -0
- package/dist/components/composites/sources/sources.js.map +1 -0
- package/dist/components/composites/stack-trace/index.d.ts +3 -0
- package/dist/components/composites/stack-trace/index.d.ts.map +1 -0
- package/dist/components/composites/stack-trace/index.js +3 -0
- package/dist/components/composites/stack-trace/parse-stack-trace.d.ts +118 -0
- package/dist/components/composites/stack-trace/parse-stack-trace.d.ts.map +1 -0
- package/dist/components/composites/stack-trace/parse-stack-trace.js +376 -0
- package/dist/components/composites/stack-trace/parse-stack-trace.js.map +1 -0
- package/dist/components/composites/stack-trace/stack-trace.d.ts +63 -0
- package/dist/components/composites/stack-trace/stack-trace.d.ts.map +1 -0
- package/dist/components/composites/stack-trace/stack-trace.js +491 -0
- package/dist/components/composites/stack-trace/stack-trace.js.map +1 -0
- package/dist/components/composites/subagent/index.d.ts +2 -0
- package/dist/components/composites/subagent/index.d.ts.map +1 -0
- package/dist/components/composites/subagent/index.js +2 -0
- package/dist/components/composites/subagent/subagent.d.ts +55 -0
- package/dist/components/composites/subagent/subagent.d.ts.map +1 -0
- package/dist/components/composites/subagent/subagent.js +138 -0
- package/dist/components/composites/subagent/subagent.js.map +1 -0
- package/dist/components/composites/suggestion/index.d.ts +2 -0
- package/dist/components/composites/suggestion/index.d.ts.map +1 -0
- package/dist/components/composites/suggestion/index.js +2 -0
- package/dist/components/composites/suggestion/suggestion.d.ts +37 -0
- package/dist/components/composites/suggestion/suggestion.d.ts.map +1 -0
- package/dist/components/composites/suggestion/suggestion.js +100 -0
- package/dist/components/composites/suggestion/suggestion.js.map +1 -0
- package/dist/components/composites/task/index.d.ts +2 -0
- package/dist/components/composites/task/index.d.ts.map +1 -0
- package/dist/components/composites/task/index.js +2 -0
- package/dist/components/composites/task/task.d.ts +96 -0
- package/dist/components/composites/task/task.d.ts.map +1 -0
- package/dist/components/composites/task/task.js +311 -0
- package/dist/components/composites/task/task.js.map +1 -0
- package/dist/components/composites/terminal/ansi.d.ts +101 -0
- package/dist/components/composites/terminal/ansi.d.ts.map +1 -0
- package/dist/components/composites/terminal/ansi.js +544 -0
- package/dist/components/composites/terminal/ansi.js.map +1 -0
- package/dist/components/composites/terminal/index.d.ts +4 -0
- package/dist/components/composites/terminal/index.d.ts.map +1 -0
- package/dist/components/composites/terminal/index.js +4 -0
- package/dist/components/composites/terminal/terminal-model.d.ts +63 -0
- package/dist/components/composites/terminal/terminal-model.d.ts.map +1 -0
- package/dist/components/composites/terminal/terminal-model.js +69 -0
- package/dist/components/composites/terminal/terminal-model.js.map +1 -0
- package/dist/components/composites/terminal/terminal.d.ts +88 -0
- package/dist/components/composites/terminal/terminal.d.ts.map +1 -0
- package/dist/components/composites/terminal/terminal.js +500 -0
- package/dist/components/composites/terminal/terminal.js.map +1 -0
- package/dist/components/composites/test-results/index.d.ts +3 -0
- package/dist/components/composites/test-results/index.d.ts.map +1 -0
- package/dist/components/composites/test-results/index.js +3 -0
- package/dist/components/composites/test-results/test-results-model.d.ts +154 -0
- package/dist/components/composites/test-results/test-results-model.d.ts.map +1 -0
- package/dist/components/composites/test-results/test-results-model.js +252 -0
- package/dist/components/composites/test-results/test-results-model.js.map +1 -0
- package/dist/components/composites/test-results/test-results.d.ts +60 -0
- package/dist/components/composites/test-results/test-results.d.ts.map +1 -0
- package/dist/components/composites/test-results/test-results.js +631 -0
- package/dist/components/composites/test-results/test-results.js.map +1 -0
- package/dist/components/composites/tool/format-tool-value.d.ts +37 -0
- package/dist/components/composites/tool/format-tool-value.d.ts.map +1 -0
- package/dist/components/composites/tool/format-tool-value.js +135 -0
- package/dist/components/composites/tool/format-tool-value.js.map +1 -0
- package/dist/components/composites/tool/index.d.ts +3 -0
- package/dist/components/composites/tool/index.d.ts.map +1 -0
- package/dist/components/composites/tool/index.js +3 -0
- package/dist/components/composites/tool/tool.d.ts +69 -0
- package/dist/components/composites/tool/tool.d.ts.map +1 -0
- package/dist/components/composites/tool/tool.js +214 -0
- package/dist/components/composites/tool/tool.js.map +1 -0
- package/dist/components/composites/update-dialog/update-dialog.js +1 -1
- package/dist/components/conversation/conversation-surface.d.ts +7 -0
- package/dist/components/conversation/conversation-surface.d.ts.map +1 -1
- package/dist/components/conversation/conversation-surface.js +2 -0
- package/dist/components/conversation/conversation-surface.js.map +1 -1
- package/dist/components/conversation/scroll-follow-core.d.ts +24 -0
- package/dist/components/conversation/scroll-follow-core.d.ts.map +1 -1
- package/dist/components/conversation/scroll-follow-core.js +23 -1
- package/dist/components/conversation/scroll-follow-core.js.map +1 -1
- package/dist/components/conversation/scroll-follow.d.ts +3 -0
- package/dist/components/conversation/scroll-follow.d.ts.map +1 -1
- package/dist/components/conversation/scroll-follow.js +80 -1
- package/dist/components/conversation/scroll-follow.js.map +1 -1
- package/dist/components/ui/attachment/attachment.d.ts +58 -0
- package/dist/components/ui/attachment/attachment.d.ts.map +1 -0
- package/dist/components/ui/attachment/attachment.js +279 -0
- package/dist/components/ui/attachment/attachment.js.map +1 -0
- package/dist/components/ui/attachment/index.d.ts +2 -0
- package/dist/components/ui/attachment/index.d.ts.map +1 -0
- package/dist/components/ui/attachment/index.js +2 -0
- package/dist/components/ui/badge/badge.d.ts +1 -1
- package/dist/components/ui/bubble/bubble.d.ts +33 -0
- package/dist/components/ui/bubble/bubble.d.ts.map +1 -0
- package/dist/components/ui/bubble/bubble.js +95 -0
- package/dist/components/ui/bubble/bubble.js.map +1 -0
- package/dist/components/ui/bubble/index.d.ts +2 -0
- package/dist/components/ui/bubble/index.d.ts.map +1 -0
- package/dist/components/ui/bubble/index.js +2 -0
- package/dist/components/ui/button/button.d.ts +1 -1
- package/dist/components/ui/code-block/code-block.d.ts +56 -3
- package/dist/components/ui/code-block/code-block.d.ts.map +1 -1
- package/dist/components/ui/code-block/code-block.js +231 -50
- package/dist/components/ui/code-block/code-block.js.map +1 -1
- package/dist/components/ui/code-block/index.d.ts +1 -1
- package/dist/components/ui/code-block/index.d.ts.map +1 -1
- package/dist/components/ui/code-block/index.js +2 -2
- package/dist/components/ui/field/number-field.d.ts +21 -4
- package/dist/components/ui/field/number-field.d.ts.map +1 -1
- package/dist/components/ui/field/number-field.js +22 -11
- package/dist/components/ui/field/number-field.js.map +1 -1
- package/dist/components/ui/marker/index.d.ts +2 -0
- package/dist/components/ui/marker/index.d.ts.map +1 -0
- package/dist/components/ui/marker/index.js +2 -0
- package/dist/components/ui/marker/marker.d.ts +35 -0
- package/dist/components/ui/marker/marker.d.ts.map +1 -0
- package/dist/components/ui/marker/marker.js +123 -0
- package/dist/components/ui/marker/marker.js.map +1 -0
- package/dist/components/ui/message/cite-ranges.d.ts +42 -0
- package/dist/components/ui/message/cite-ranges.d.ts.map +1 -0
- package/dist/components/ui/message/cite-ranges.js +105 -0
- package/dist/components/ui/message/cite-ranges.js.map +1 -0
- package/dist/components/ui/message/index.d.ts +4 -0
- package/dist/components/ui/message/index.d.ts.map +1 -0
- package/dist/components/ui/message/index.js +4 -0
- package/dist/components/ui/message/message.d.ts +95 -0
- package/dist/components/ui/message/message.d.ts.map +1 -0
- package/dist/components/ui/message/message.js +443 -0
- package/dist/components/ui/message/message.js.map +1 -0
- package/dist/components/ui/message/parse-message-text.d.ts +93 -0
- package/dist/components/ui/message/parse-message-text.d.ts.map +1 -0
- package/dist/components/ui/message/parse-message-text.js +365 -0
- package/dist/components/ui/message/parse-message-text.js.map +1 -0
- package/dist/components/ui/questionnaire/index.d.ts +2 -0
- package/dist/components/ui/questionnaire/index.d.ts.map +1 -0
- package/dist/components/ui/questionnaire/index.js +2 -0
- package/dist/components/ui/questionnaire/questionnaire.d.ts +83 -0
- package/dist/components/ui/questionnaire/questionnaire.d.ts.map +1 -0
- package/dist/components/ui/questionnaire/questionnaire.js +470 -0
- package/dist/components/ui/questionnaire/questionnaire.js.map +1 -0
- package/dist/components/ui/status-chip/status-chip.d.ts +1 -1
- package/dist/components/ui/switch/switch.js +11 -11
- package/dist/components/ui/switch/switch.js.map +1 -1
- package/dist/components/ui/trace/index.d.ts +2 -0
- package/dist/components/ui/trace/index.d.ts.map +1 -0
- package/dist/components/ui/trace/index.js +2 -0
- package/dist/components/ui/trace/trace.d.ts +72 -0
- package/dist/components/ui/trace/trace.d.ts.map +1 -0
- package/dist/components/ui/trace/trace.js +340 -0
- package/dist/components/ui/trace/trace.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +46 -5
- package/dist/r/attachment.json +25 -0
- package/dist/r/bubble.json +22 -0
- package/dist/r/chain-of-thought.json +26 -0
- package/dist/r/chat.json +24 -0
- package/dist/r/checkpoint.json +35 -0
- package/dist/r/confirmation.json +34 -0
- package/dist/r/context.json +33 -0
- package/dist/r/file-tree.json +32 -0
- package/dist/r/inline-citation.json +35 -0
- package/dist/r/marker.json +22 -0
- package/dist/r/message-scroller.json +31 -0
- package/dist/r/message.json +33 -0
- package/dist/r/model-selector.json +34 -0
- package/dist/r/plan.json +26 -0
- package/dist/r/questionnaire.json +28 -0
- package/dist/r/queue.json +26 -0
- package/dist/r/reasoning.json +26 -0
- package/dist/r/registry.json +811 -52
- package/dist/r/sources.json +32 -0
- package/dist/r/src/components/composites/chain-of-thought/chain-of-thought.tsx +141 -0
- package/dist/r/src/components/composites/chain-of-thought/index.ts +11 -0
- package/dist/r/src/components/composites/chat/chat.tsx +190 -0
- package/dist/r/src/components/composites/chat/index.ts +11 -0
- package/dist/r/src/components/composites/checkpoint/checkpoint-model.ts +71 -0
- package/dist/r/src/components/composites/checkpoint/checkpoint.tsx +214 -0
- package/dist/r/src/components/composites/checkpoint/index.ts +8 -0
- package/dist/r/src/components/composites/confirmation/confirmation-model.ts +83 -0
- package/dist/r/src/components/composites/confirmation/confirmation.tsx +311 -0
- package/dist/r/src/components/composites/confirmation/index.ts +9 -0
- package/dist/r/src/components/composites/context/context-model.ts +168 -0
- package/dist/r/src/components/composites/context/context.tsx +187 -0
- package/dist/r/src/components/composites/context/index.ts +18 -0
- package/dist/r/src/components/composites/file-tree/build-file-tree.ts +456 -0
- package/dist/r/src/components/composites/file-tree/file-tree.tsx +405 -0
- package/dist/r/src/components/composites/file-tree/index.ts +25 -0
- package/dist/r/src/components/composites/inline-citation/citation.ts +78 -0
- package/dist/r/src/components/composites/inline-citation/index.ts +9 -0
- package/dist/r/src/components/composites/inline-citation/inline-citation.tsx +285 -0
- package/dist/r/src/components/composites/message-scroller/index.ts +2 -0
- package/dist/r/src/components/composites/message-scroller/message-scroller.tsx +301 -0
- package/dist/r/src/components/composites/message-scroller/scroll-anchor.ts +170 -0
- package/dist/r/src/components/composites/model-selector/index.ts +14 -0
- package/dist/r/src/components/composites/model-selector/model-selector.tsx +420 -0
- package/dist/r/src/components/composites/model-selector/models.ts +103 -0
- package/dist/r/src/components/composites/plan/index.ts +1 -0
- package/dist/r/src/components/composites/plan/plan.tsx +155 -0
- package/dist/r/src/components/composites/queue/index.ts +10 -0
- package/dist/r/src/components/composites/queue/queue.tsx +264 -0
- package/dist/r/src/components/composites/reasoning/index.ts +1 -0
- package/dist/r/src/components/composites/reasoning/reasoning.tsx +176 -0
- package/dist/r/src/components/composites/sources/index.ts +11 -0
- package/dist/r/src/components/composites/sources/source.ts +116 -0
- package/dist/r/src/components/composites/sources/sources.tsx +163 -0
- package/dist/r/src/components/composites/stack-trace/index.ts +19 -0
- package/dist/r/src/components/composites/stack-trace/parse-stack-trace.ts +507 -0
- package/dist/r/src/components/composites/stack-trace/stack-trace.tsx +395 -0
- package/dist/r/src/components/composites/subagent/index.ts +1 -0
- package/dist/r/src/components/composites/subagent/subagent.tsx +159 -0
- package/dist/r/src/components/composites/suggestion/index.ts +1 -0
- package/dist/r/src/components/composites/suggestion/suggestion.tsx +140 -0
- package/dist/r/src/components/composites/task/index.ts +14 -0
- package/dist/r/src/components/composites/task/task.tsx +317 -0
- package/dist/r/src/components/composites/terminal/ansi.ts +674 -0
- package/dist/r/src/components/composites/terminal/index.ts +20 -0
- package/dist/r/src/components/composites/terminal/terminal-model.ts +111 -0
- package/dist/r/src/components/composites/terminal/terminal.tsx +574 -0
- package/dist/r/src/components/composites/test-results/index.ts +20 -0
- package/dist/r/src/components/composites/test-results/test-results-model.ts +368 -0
- package/dist/r/src/components/composites/test-results/test-results.tsx +586 -0
- package/dist/r/src/components/composites/tool/format-tool-value.ts +221 -0
- package/dist/r/src/components/composites/tool/index.ts +2 -0
- package/dist/r/src/components/composites/tool/tool.tsx +228 -0
- package/dist/r/src/components/conversation/conversation-surface.tsx +9 -0
- package/dist/r/src/components/conversation/scroll-follow-core.ts +31 -0
- package/dist/r/src/components/conversation/scroll-follow.ts +92 -0
- package/dist/r/src/components/ui/attachment/attachment.tsx +282 -0
- package/dist/r/src/components/ui/attachment/index.ts +10 -0
- package/dist/r/src/components/ui/bubble/bubble.tsx +86 -0
- package/dist/r/src/components/ui/bubble/index.ts +1 -0
- package/dist/r/src/components/ui/code-block/code-block.tsx +256 -26
- package/dist/r/src/components/ui/code-block/index.ts +9 -1
- package/dist/r/src/components/ui/field/number-field.tsx +51 -14
- package/dist/r/src/components/ui/marker/index.ts +1 -0
- package/dist/r/src/components/ui/marker/marker.tsx +90 -0
- package/dist/r/src/components/ui/message/cite-ranges.ts +144 -0
- package/dist/r/src/components/ui/message/index.ts +23 -0
- package/dist/r/src/components/ui/message/message.tsx +434 -0
- package/dist/r/src/components/ui/message/parse-message-text.ts +450 -0
- package/dist/r/src/components/ui/questionnaire/index.ts +10 -0
- package/dist/r/src/components/ui/questionnaire/questionnaire.tsx +446 -0
- package/dist/r/src/components/ui/trace/index.ts +9 -0
- package/dist/r/src/components/ui/trace/trace.tsx +334 -0
- package/dist/r/src/styles/base.css +34 -0
- package/dist/r/stack-trace.json +32 -0
- package/dist/r/subagent.json +27 -0
- package/dist/r/suggestion.json +24 -0
- package/dist/r/task.json +28 -0
- package/dist/r/terminal.json +35 -0
- package/dist/r/test-results.json +33 -0
- package/dist/r/tool.json +31 -0
- package/dist/r/trace.json +25 -0
- package/package.json +6 -3
- package/registry.json +811 -52
- package/src/components/composites/chain-of-thought/chain-of-thought.tsx +141 -0
- package/src/components/composites/chain-of-thought/index.ts +11 -0
- package/src/components/composites/chat/chat.tsx +190 -0
- package/src/components/composites/chat/index.ts +11 -0
- package/src/components/composites/checkpoint/checkpoint-model.ts +71 -0
- package/src/components/composites/checkpoint/checkpoint.tsx +214 -0
- package/src/components/composites/checkpoint/index.ts +8 -0
- package/src/components/composites/confirmation/confirmation-model.ts +83 -0
- package/src/components/composites/confirmation/confirmation.tsx +311 -0
- package/src/components/composites/confirmation/index.ts +9 -0
- package/src/components/composites/context/context-model.ts +168 -0
- package/src/components/composites/context/context.tsx +187 -0
- package/src/components/composites/context/index.ts +18 -0
- package/src/components/composites/file-tree/build-file-tree.ts +456 -0
- package/src/components/composites/file-tree/file-tree.tsx +405 -0
- package/src/components/composites/file-tree/index.ts +25 -0
- package/src/components/composites/inline-citation/citation.ts +78 -0
- package/src/components/composites/inline-citation/index.ts +9 -0
- package/src/components/composites/inline-citation/inline-citation.tsx +285 -0
- package/src/components/composites/message-scroller/index.ts +2 -0
- package/src/components/composites/message-scroller/message-scroller.tsx +301 -0
- package/src/components/composites/message-scroller/scroll-anchor.ts +170 -0
- package/src/components/composites/model-selector/index.ts +14 -0
- package/src/components/composites/model-selector/model-selector.tsx +420 -0
- package/src/components/composites/model-selector/models.ts +103 -0
- package/src/components/composites/plan/index.ts +1 -0
- package/src/components/composites/plan/plan.tsx +155 -0
- package/src/components/composites/queue/index.ts +10 -0
- package/src/components/composites/queue/queue.tsx +264 -0
- package/src/components/composites/reasoning/index.ts +1 -0
- package/src/components/composites/reasoning/reasoning.tsx +176 -0
- package/src/components/composites/sources/index.ts +11 -0
- package/src/components/composites/sources/source.ts +116 -0
- package/src/components/composites/sources/sources.tsx +163 -0
- package/src/components/composites/stack-trace/index.ts +19 -0
- package/src/components/composites/stack-trace/parse-stack-trace.ts +507 -0
- package/src/components/composites/stack-trace/stack-trace.tsx +395 -0
- package/src/components/composites/subagent/index.ts +1 -0
- package/src/components/composites/subagent/subagent.tsx +159 -0
- package/src/components/composites/suggestion/index.ts +1 -0
- package/src/components/composites/suggestion/suggestion.tsx +140 -0
- package/src/components/composites/task/index.ts +14 -0
- package/src/components/composites/task/task.tsx +317 -0
- package/src/components/composites/terminal/ansi.ts +674 -0
- package/src/components/composites/terminal/index.ts +20 -0
- package/src/components/composites/terminal/terminal-model.ts +111 -0
- package/src/components/composites/terminal/terminal.tsx +574 -0
- package/src/components/composites/test-results/index.ts +20 -0
- package/src/components/composites/test-results/test-results-model.ts +368 -0
- package/src/components/composites/test-results/test-results.tsx +586 -0
- package/src/components/composites/tool/format-tool-value.ts +221 -0
- package/src/components/composites/tool/index.ts +2 -0
- package/src/components/composites/tool/tool.tsx +228 -0
- package/src/components/conversation/conversation-surface.tsx +9 -0
- package/src/components/conversation/scroll-follow-core.ts +31 -0
- package/src/components/conversation/scroll-follow.ts +92 -0
- package/src/components/ui/attachment/attachment.tsx +282 -0
- package/src/components/ui/attachment/index.ts +10 -0
- package/src/components/ui/bubble/bubble.tsx +86 -0
- package/src/components/ui/bubble/index.ts +1 -0
- package/src/components/ui/code-block/code-block.tsx +256 -26
- package/src/components/ui/code-block/index.ts +9 -1
- package/src/components/ui/field/number-field.tsx +51 -14
- package/src/components/ui/marker/index.ts +1 -0
- package/src/components/ui/marker/marker.tsx +90 -0
- package/src/components/ui/message/cite-ranges.ts +144 -0
- package/src/components/ui/message/index.ts +23 -0
- package/src/components/ui/message/message.tsx +434 -0
- package/src/components/ui/message/parse-message-text.ts +450 -0
- package/src/components/ui/questionnaire/index.ts +10 -0
- package/src/components/ui/questionnaire/questionnaire.tsx +446 -0
- package/src/components/ui/trace/index.ts +9 -0
- package/src/components/ui/trace/trace.tsx +334 -0
- package/src/index.ts +26 -0
- package/src/styles/base.css +34 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { OctagonAlert, TriangleAlert } from 'lucide-solid'
|
|
2
|
+
import { For, Show, splitProps, type ComponentProps } from 'solid-js'
|
|
3
|
+
import { cn } from '#lib/utils'
|
|
4
|
+
import { Button } from '../../ui/button'
|
|
5
|
+
import { Popover, PopoverContent, PopoverTitle, PopoverTrigger } from '../../ui/popover'
|
|
6
|
+
import { Progress } from '../../ui/progress'
|
|
7
|
+
import {
|
|
8
|
+
breakdownRows,
|
|
9
|
+
contextLabel,
|
|
10
|
+
contextLevel,
|
|
11
|
+
contextLevelWord,
|
|
12
|
+
contextPercent,
|
|
13
|
+
contextShort,
|
|
14
|
+
formatCost,
|
|
15
|
+
formatExact,
|
|
16
|
+
hasContextFigures,
|
|
17
|
+
isFigure,
|
|
18
|
+
type ContextFigures,
|
|
19
|
+
type ContextLevel,
|
|
20
|
+
} from './context-model'
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Context.
|
|
24
|
+
*
|
|
25
|
+
* How much of the model's context window is in use, as a compact trigger that
|
|
26
|
+
* opens the figures behind it. It sits next to the composer, where the reader
|
|
27
|
+
* decides whether to keep going or start fresh, so the trigger is small and the
|
|
28
|
+
* detail is a press away rather than permanent chrome.
|
|
29
|
+
*
|
|
30
|
+
* ## Every figure is a claim, so every figure is optional
|
|
31
|
+
*
|
|
32
|
+
* A host passes whatever it knows: `used`, `total`, a `breakdown` (input, output,
|
|
33
|
+
* cache read and write, reasoning), a `cost`. A missing value is omitted — never
|
|
34
|
+
* drawn as 0, which would say "empty" about a window nobody measured — and when
|
|
35
|
+
* *nothing* is known the trigger is not rendered at all (`hasContextFigures`).
|
|
36
|
+
* Nothing is estimated here; the one derived row, "Remaining", appears only when
|
|
37
|
+
* both `used` and `total` are known and `used` does not exceed `total`.
|
|
38
|
+
*
|
|
39
|
+
* ## Never colour alone
|
|
40
|
+
*
|
|
41
|
+
* The ring shows the fraction; past 75% the ring gives way to a warning glyph
|
|
42
|
+
* (triangle, then octagon at 90% and above), the popover says "Getting full",
|
|
43
|
+
* "Nearly full" or "Full" in words, and the trigger's accessible name speaks the
|
|
44
|
+
* same. An overrun is reported as it is ("112%"); only the ring saturates.
|
|
45
|
+
*
|
|
46
|
+
* It is a `Popover`, not a hover card: the figures must be reachable by keyboard
|
|
47
|
+
* and by touch, and Escape closes it and returns focus to the trigger.
|
|
48
|
+
*/
|
|
49
|
+
export type ContextProps = ContextFigures & {
|
|
50
|
+
/** BCP 47 tag for number formatting. Defaults to `en-US`, so output is stable. */
|
|
51
|
+
locale?: string
|
|
52
|
+
placement?: ComponentProps<typeof Popover>['placement']
|
|
53
|
+
class?: string
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// A circle of radius 6 in a 16-unit box; the dash array is the fraction of this.
|
|
57
|
+
const RING_RADIUS = 6
|
|
58
|
+
const RING_CIRCUMFERENCE = 2 * Math.PI * RING_RADIUS
|
|
59
|
+
|
|
60
|
+
export function Context(props: ContextProps) {
|
|
61
|
+
const [local, figures] = splitProps(props, ['locale', 'placement', 'class'])
|
|
62
|
+
const locale = () => local.locale ?? 'en-US'
|
|
63
|
+
const percent = () => contextPercent(figures.used, figures.total)
|
|
64
|
+
const level = (): ContextLevel => contextLevel(percent())
|
|
65
|
+
const levelWord = () => contextLevelWord(level())
|
|
66
|
+
const rows = () => breakdownRows(figures.breakdown)
|
|
67
|
+
const remaining = () =>
|
|
68
|
+
isFigure(figures.used) && isFigure(figures.total) && figures.used <= figures.total
|
|
69
|
+
? figures.total - figures.used
|
|
70
|
+
: undefined
|
|
71
|
+
const cost = () => (figures.cost && isFigure(figures.cost.amount) ? figures.cost : undefined)
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<Show when={hasContextFigures(figures)}>
|
|
75
|
+
<Popover placement={local.placement ?? 'top'} gutter={6}>
|
|
76
|
+
<PopoverTrigger
|
|
77
|
+
as={Button}
|
|
78
|
+
variant="ghost"
|
|
79
|
+
size="xs"
|
|
80
|
+
data-slot="context"
|
|
81
|
+
data-level={level()}
|
|
82
|
+
aria-label={contextLabel(figures, locale())}
|
|
83
|
+
class={local.class}
|
|
84
|
+
>
|
|
85
|
+
<Show
|
|
86
|
+
when={level() !== 'normal'}
|
|
87
|
+
fallback={
|
|
88
|
+
<Show when={percent() !== undefined}>
|
|
89
|
+
<Ring percent={percent() ?? 0} />
|
|
90
|
+
</Show>
|
|
91
|
+
}
|
|
92
|
+
>
|
|
93
|
+
<span
|
|
94
|
+
aria-hidden="true"
|
|
95
|
+
class={cn({
|
|
96
|
+
'text-warning': level() === 'high',
|
|
97
|
+
'text-destructive': level() === 'critical' || level() === 'full',
|
|
98
|
+
})}
|
|
99
|
+
>
|
|
100
|
+
<Show when={level() === 'high'} fallback={<OctagonAlert />}>
|
|
101
|
+
<TriangleAlert />
|
|
102
|
+
</Show>
|
|
103
|
+
</span>
|
|
104
|
+
</Show>
|
|
105
|
+
<span aria-hidden="true" class="tabular-nums">
|
|
106
|
+
{contextShort(figures, locale())}
|
|
107
|
+
</span>
|
|
108
|
+
</PopoverTrigger>
|
|
109
|
+
<PopoverContent class="w-72 max-w-full">
|
|
110
|
+
<PopoverTitle>Context window</PopoverTitle>
|
|
111
|
+
<div class="flex flex-col gap-2 px-2 pb-2">
|
|
112
|
+
<Show when={percent() !== undefined}>
|
|
113
|
+
<Progress
|
|
114
|
+
value={Math.min(percent() ?? 0, 100)}
|
|
115
|
+
minValue={0}
|
|
116
|
+
maxValue={100}
|
|
117
|
+
label="Context used"
|
|
118
|
+
getValueLabel={() => `${percent()}% used`}
|
|
119
|
+
/>
|
|
120
|
+
</Show>
|
|
121
|
+
<Show when={levelWord()}>
|
|
122
|
+
<p
|
|
123
|
+
class={cn('m-0 flex items-center gap-1.5 text-sm font-medium', {
|
|
124
|
+
'text-warning': level() === 'high',
|
|
125
|
+
'text-destructive': level() === 'critical' || level() === 'full',
|
|
126
|
+
})}
|
|
127
|
+
>
|
|
128
|
+
<Show when={level() === 'high'} fallback={<OctagonAlert class="size-4" />}>
|
|
129
|
+
<TriangleAlert class="size-4" />
|
|
130
|
+
</Show>
|
|
131
|
+
<span class="text-foreground">{levelWord()}</span>
|
|
132
|
+
</p>
|
|
133
|
+
</Show>
|
|
134
|
+
<dl class="m-0 flex flex-col gap-1 text-sm">
|
|
135
|
+
<Show when={isFigure(figures.used)}>
|
|
136
|
+
<Row label="Used" value={`${formatExact(figures.used ?? 0, locale())} tokens`} />
|
|
137
|
+
</Show>
|
|
138
|
+
<Show when={isFigure(figures.total)}>
|
|
139
|
+
<Row label="Window" value={`${formatExact(figures.total ?? 0, locale())} tokens`} />
|
|
140
|
+
</Show>
|
|
141
|
+
<Show when={remaining() !== undefined}>
|
|
142
|
+
<Row
|
|
143
|
+
label="Remaining"
|
|
144
|
+
value={`${formatExact(remaining() ?? 0, locale())} tokens`}
|
|
145
|
+
/>
|
|
146
|
+
</Show>
|
|
147
|
+
<For each={rows()}>
|
|
148
|
+
{(row) => <Row label={row.label} value={formatExact(row.value, locale())} />}
|
|
149
|
+
</For>
|
|
150
|
+
<Show when={cost()}>
|
|
151
|
+
{(value) => <Row label="Cost" value={formatCost(value(), locale())} />}
|
|
152
|
+
</Show>
|
|
153
|
+
</dl>
|
|
154
|
+
</div>
|
|
155
|
+
</PopoverContent>
|
|
156
|
+
</Popover>
|
|
157
|
+
</Show>
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function Ring(props: { percent: number }) {
|
|
162
|
+
const filled = () => (RING_CIRCUMFERENCE * Math.min(Math.max(props.percent, 0), 100)) / 100
|
|
163
|
+
return (
|
|
164
|
+
<svg aria-hidden="true" viewBox="0 0 16 16" fill="none" class="size-4">
|
|
165
|
+
<circle cx="8" cy="8" r={RING_RADIUS} stroke-width="2" class="stroke-border" />
|
|
166
|
+
<circle
|
|
167
|
+
cx="8"
|
|
168
|
+
cy="8"
|
|
169
|
+
r={RING_RADIUS}
|
|
170
|
+
stroke-width="2"
|
|
171
|
+
stroke-linecap="round"
|
|
172
|
+
stroke-dasharray={`${filled()} ${RING_CIRCUMFERENCE}`}
|
|
173
|
+
transform="rotate(-90 8 8)"
|
|
174
|
+
class="stroke-primary"
|
|
175
|
+
/>
|
|
176
|
+
</svg>
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function Row(props: { label: string; value: string }) {
|
|
181
|
+
return (
|
|
182
|
+
<div class="flex items-baseline justify-between gap-4">
|
|
183
|
+
<dt class="text-muted-foreground">{props.label}</dt>
|
|
184
|
+
<dd class="m-0 text-end tabular-nums">{props.value}</dd>
|
|
185
|
+
</div>
|
|
186
|
+
)
|
|
187
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export { Context, type ContextProps } from './context'
|
|
2
|
+
export {
|
|
3
|
+
breakdownRows,
|
|
4
|
+
contextLabel,
|
|
5
|
+
contextLevel,
|
|
6
|
+
contextLevelWord,
|
|
7
|
+
contextPercent,
|
|
8
|
+
contextShort,
|
|
9
|
+
formatCost,
|
|
10
|
+
formatExact,
|
|
11
|
+
formatTokens,
|
|
12
|
+
hasContextFigures,
|
|
13
|
+
isFigure,
|
|
14
|
+
type ContextBreakdown,
|
|
15
|
+
type ContextCost,
|
|
16
|
+
type ContextFigures,
|
|
17
|
+
type ContextLevel,
|
|
18
|
+
} from './context-model'
|
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The model behind the changed-files tree: paths in, a folder tree out, and the
|
|
3
|
+
* small pure rules the view needs (what is visible, what to say about the
|
|
4
|
+
* changes, what a typed letter jumps to).
|
|
5
|
+
*
|
|
6
|
+
* Paths come from an agent, which means from a model, which means they are
|
|
7
|
+
* untrusted text. They are used here as labels and as ids, never as places to
|
|
8
|
+
* read or write, and the builder is strict about what counts as a path at all:
|
|
9
|
+
* a `..` segment, a control character or a bidirectional override rejects the
|
|
10
|
+
* path rather than being cleaned up, because a cleaned-up path is a different
|
|
11
|
+
* path and the tree would be making a claim the input did not. Rejected paths
|
|
12
|
+
* are returned, not dropped silently, so the view can say how many it ignored.
|
|
13
|
+
*
|
|
14
|
+
* Everything is iterative. A path with ten thousand segments, or a tree five
|
|
15
|
+
* thousand files wide, must not be able to overflow the stack or stall the page.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export type FileChangeKind = 'added' | 'modified' | 'deleted' | 'renamed'
|
|
19
|
+
|
|
20
|
+
export type FileChangeInput = Readonly<{
|
|
21
|
+
path: string
|
|
22
|
+
/** Absent means the host did not say; the tree makes no statement about the file. */
|
|
23
|
+
change?: FileChangeKind
|
|
24
|
+
/** For a rename, where the file was. Shown as text only. */
|
|
25
|
+
renamedFrom?: string
|
|
26
|
+
additions?: number
|
|
27
|
+
deletions?: number
|
|
28
|
+
}>
|
|
29
|
+
|
|
30
|
+
export type FileTreeNode = Readonly<{
|
|
31
|
+
/** The normalised path, `/`-separated. Unique within a tree. */
|
|
32
|
+
id: string
|
|
33
|
+
/** The label. For a compacted folder chain this is `a/b/c`. */
|
|
34
|
+
name: string
|
|
35
|
+
kind: 'file' | 'directory'
|
|
36
|
+
change?: FileChangeKind
|
|
37
|
+
renamedFrom?: string
|
|
38
|
+
additions?: number
|
|
39
|
+
deletions?: number
|
|
40
|
+
children: readonly FileTreeNode[]
|
|
41
|
+
/** Files at or below this node. A file counts itself. */
|
|
42
|
+
fileCount: number
|
|
43
|
+
}>
|
|
44
|
+
|
|
45
|
+
export type FileTreeBuild = Readonly<{
|
|
46
|
+
roots: readonly FileTreeNode[]
|
|
47
|
+
/** Inputs that were not accepted as paths, exactly as given. */
|
|
48
|
+
rejected: readonly string[]
|
|
49
|
+
/** Inputs that named a path already present. The first one wins. */
|
|
50
|
+
duplicates: number
|
|
51
|
+
fileCount: number
|
|
52
|
+
}>
|
|
53
|
+
|
|
54
|
+
export type BuildFileTreeOptions = {
|
|
55
|
+
/** Merge a folder with a single folder child into one row (`src/lib`). Default true. */
|
|
56
|
+
compact?: boolean
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Deeper paths are rejected: no real repository nests this far, and a row's indent would be unusable. */
|
|
60
|
+
export const FILE_TREE_MAX_DEPTH = 256
|
|
61
|
+
const MAX_SEGMENT_LENGTH = 1024
|
|
62
|
+
|
|
63
|
+
function hasForbiddenCharacter(value: string): boolean {
|
|
64
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
65
|
+
const code = value.charCodeAt(i)
|
|
66
|
+
if (
|
|
67
|
+
code < 0x20 ||
|
|
68
|
+
(code >= 0x7f && code <= 0x9f) ||
|
|
69
|
+
(code >= 0x202a && code <= 0x202e) ||
|
|
70
|
+
(code >= 0x2066 && code <= 0x2069) ||
|
|
71
|
+
code === 0x2028 ||
|
|
72
|
+
code === 0x2029
|
|
73
|
+
) {
|
|
74
|
+
return true
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return false
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* A path as its segments, or `null` when it is not acceptable. Both separators
|
|
82
|
+
* are accepted, empty and `.` segments are dropped (`a//b`, `./a`, trailing `/`),
|
|
83
|
+
* and a leading separator is dropped, so an absolute path becomes relative to
|
|
84
|
+
* the tree's root rather than escaping it.
|
|
85
|
+
*/
|
|
86
|
+
export function normalizePath(path: unknown): string[] | null {
|
|
87
|
+
if (typeof path !== 'string' || hasForbiddenCharacter(path)) return null
|
|
88
|
+
const segments: string[] = []
|
|
89
|
+
let start = 0
|
|
90
|
+
for (let i = 0; i <= path.length; i += 1) {
|
|
91
|
+
if (i !== path.length && path[i] !== '/' && path[i] !== '\\') continue
|
|
92
|
+
const segment = path.slice(start, i)
|
|
93
|
+
start = i + 1
|
|
94
|
+
if (segment === '' || segment === '.') continue
|
|
95
|
+
if (segment === '..' || segment.length > MAX_SEGMENT_LENGTH) return null
|
|
96
|
+
segments.push(segment)
|
|
97
|
+
if (segments.length > FILE_TREE_MAX_DEPTH) return null
|
|
98
|
+
}
|
|
99
|
+
return segments.length === 0 ? null : segments
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
type Draft = {
|
|
103
|
+
id: string
|
|
104
|
+
name: string
|
|
105
|
+
children: Map<string, Draft>
|
|
106
|
+
change?: FileChangeKind
|
|
107
|
+
renamedFrom?: string
|
|
108
|
+
additions?: number
|
|
109
|
+
deletions?: number
|
|
110
|
+
explicit: boolean
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function newDraft(id: string, name: string): Draft {
|
|
114
|
+
return { id, name, children: new Map(), explicit: false }
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function compareNodes(a: FileTreeNode, b: FileTreeNode): number {
|
|
118
|
+
if (a.kind !== b.kind) return a.kind === 'directory' ? -1 : 1
|
|
119
|
+
const left = a.name.toLowerCase()
|
|
120
|
+
const right = b.name.toLowerCase()
|
|
121
|
+
if (left !== right) return left < right ? -1 : 1
|
|
122
|
+
return a.name < b.name ? -1 : a.name > b.name ? 1 : 0
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function safeCount(value: unknown): number | undefined {
|
|
126
|
+
return typeof value === 'number' && Number.isFinite(value) && value >= 0
|
|
127
|
+
? Math.floor(value)
|
|
128
|
+
: undefined
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const CHANGE_KINDS: ReadonlySet<string> = new Set(['added', 'modified', 'deleted', 'renamed'])
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Build the tree. Folders sort before files, then by name without regard to
|
|
135
|
+
* case. A path that is both a file and the parent of another path is a folder
|
|
136
|
+
* that keeps the file entry's change (a file replaced by a directory is a real
|
|
137
|
+
* thing to report).
|
|
138
|
+
*/
|
|
139
|
+
export function buildFileTree(
|
|
140
|
+
paths: readonly (string | FileChangeInput)[],
|
|
141
|
+
options: BuildFileTreeOptions = {}
|
|
142
|
+
): FileTreeBuild {
|
|
143
|
+
const root = newDraft('', '')
|
|
144
|
+
const rejected: string[] = []
|
|
145
|
+
let duplicates = 0
|
|
146
|
+
|
|
147
|
+
for (const input of paths) {
|
|
148
|
+
const entry: FileChangeInput | undefined =
|
|
149
|
+
typeof input === 'string'
|
|
150
|
+
? { path: input }
|
|
151
|
+
: input && typeof input === 'object'
|
|
152
|
+
? input
|
|
153
|
+
: undefined
|
|
154
|
+
const segments = normalizePath(entry?.path)
|
|
155
|
+
if (!entry || !segments) {
|
|
156
|
+
rejected.push(
|
|
157
|
+
typeof input === 'string' ? input : String((input as { path?: unknown })?.path ?? '')
|
|
158
|
+
)
|
|
159
|
+
continue
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
let node = root
|
|
163
|
+
let id = ''
|
|
164
|
+
for (const segment of segments) {
|
|
165
|
+
id = id === '' ? segment : `${id}/${segment}`
|
|
166
|
+
let child = node.children.get(segment)
|
|
167
|
+
if (!child) {
|
|
168
|
+
child = newDraft(id, segment)
|
|
169
|
+
node.children.set(segment, child)
|
|
170
|
+
}
|
|
171
|
+
node = child
|
|
172
|
+
}
|
|
173
|
+
if (node.explicit) {
|
|
174
|
+
duplicates += 1
|
|
175
|
+
continue
|
|
176
|
+
}
|
|
177
|
+
node.explicit = true
|
|
178
|
+
if (entry.change && CHANGE_KINDS.has(entry.change)) node.change = entry.change
|
|
179
|
+
if (typeof entry.renamedFrom === 'string' && !hasForbiddenCharacter(entry.renamedFrom)) {
|
|
180
|
+
node.renamedFrom = entry.renamedFrom
|
|
181
|
+
}
|
|
182
|
+
node.additions = safeCount(entry.additions)
|
|
183
|
+
node.deletions = safeCount(entry.deletions)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Pre-order flatten, then fold back in reverse: a child always has a larger
|
|
187
|
+
// index than its parent, so one backwards pass builds every node after its
|
|
188
|
+
// children without recursion.
|
|
189
|
+
const order: Draft[] = []
|
|
190
|
+
const parentOf: number[] = []
|
|
191
|
+
const stack: [Draft, number][] = [...root.children.values()].map((draft) => [draft, -1])
|
|
192
|
+
while (stack.length > 0) {
|
|
193
|
+
const [draft, parent] = stack.pop()!
|
|
194
|
+
const index = order.length
|
|
195
|
+
order.push(draft)
|
|
196
|
+
parentOf.push(parent)
|
|
197
|
+
for (const child of draft.children.values()) stack.push([child, index])
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const built: FileTreeNode[][] = order.map(() => [])
|
|
201
|
+
const roots: FileTreeNode[] = []
|
|
202
|
+
const compact = options.compact !== false
|
|
203
|
+
let fileCount = 0
|
|
204
|
+
|
|
205
|
+
for (let index = order.length - 1; index >= 0; index -= 1) {
|
|
206
|
+
const draft = order[index]!
|
|
207
|
+
const children = built[index]!.toSorted(compareNodes)
|
|
208
|
+
let node: FileTreeNode
|
|
209
|
+
const only = children.length === 1 ? children[0]! : undefined
|
|
210
|
+
if (compact && only?.kind === 'directory' && draft.change === undefined) {
|
|
211
|
+
// A folder holding only another folder reads as one row: `src/lib`, not two.
|
|
212
|
+
node = { ...only, name: `${draft.name}/${only.name}` }
|
|
213
|
+
} else if (children.length > 0) {
|
|
214
|
+
node = {
|
|
215
|
+
id: draft.id,
|
|
216
|
+
name: draft.name,
|
|
217
|
+
kind: 'directory',
|
|
218
|
+
...(draft.change && { change: draft.change }),
|
|
219
|
+
...(draft.renamedFrom !== undefined && { renamedFrom: draft.renamedFrom }),
|
|
220
|
+
children,
|
|
221
|
+
fileCount: children.reduce((sum, child) => sum + child.fileCount, 0),
|
|
222
|
+
}
|
|
223
|
+
} else {
|
|
224
|
+
fileCount += 1
|
|
225
|
+
node = {
|
|
226
|
+
id: draft.id,
|
|
227
|
+
name: draft.name,
|
|
228
|
+
kind: 'file',
|
|
229
|
+
...(draft.change && { change: draft.change }),
|
|
230
|
+
...(draft.renamedFrom !== undefined && { renamedFrom: draft.renamedFrom }),
|
|
231
|
+
...(draft.additions !== undefined && { additions: draft.additions }),
|
|
232
|
+
...(draft.deletions !== undefined && { deletions: draft.deletions }),
|
|
233
|
+
children: [],
|
|
234
|
+
fileCount: 1,
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
const parent = parentOf[index]!
|
|
238
|
+
if (parent === -1) roots.push(node)
|
|
239
|
+
else built[parent]!.push(node)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
roots.sort(compareNodes)
|
|
243
|
+
return { roots, rejected, duplicates, fileCount }
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// ---------------------------------------------------------------------------
|
|
247
|
+
// Projection
|
|
248
|
+
// ---------------------------------------------------------------------------
|
|
249
|
+
|
|
250
|
+
export type FileTreeRow = Readonly<{
|
|
251
|
+
node: FileTreeNode
|
|
252
|
+
/** One-based depth. */
|
|
253
|
+
level: number
|
|
254
|
+
parentId: string | null
|
|
255
|
+
expanded: boolean
|
|
256
|
+
}>
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* The rows that are on screen, in reading order. A collapsed folder's subtree
|
|
260
|
+
* is never visited, so toggling a folder in a 5 000-file tree costs the size of
|
|
261
|
+
* what is visible, not of what exists.
|
|
262
|
+
*/
|
|
263
|
+
export function flattenFileTree(
|
|
264
|
+
roots: readonly FileTreeNode[],
|
|
265
|
+
expanded: ReadonlySet<string>
|
|
266
|
+
): FileTreeRow[] {
|
|
267
|
+
const rows: FileTreeRow[] = []
|
|
268
|
+
const stack: [FileTreeNode, number, string | null][] = []
|
|
269
|
+
for (let i = roots.length - 1; i >= 0; i -= 1) stack.push([roots[i]!, 1, null])
|
|
270
|
+
while (stack.length > 0) {
|
|
271
|
+
const [node, level, parentId] = stack.pop()!
|
|
272
|
+
const open = node.kind === 'directory' && expanded.has(node.id)
|
|
273
|
+
rows.push({ node, level, parentId, expanded: open })
|
|
274
|
+
if (open) {
|
|
275
|
+
for (let i = node.children.length - 1; i >= 0; i -= 1) {
|
|
276
|
+
stack.push([node.children[i]!, level + 1, node.id])
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return rows
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/** Every node by id, for turning a row id back into its node. */
|
|
284
|
+
export function indexFileTree(roots: readonly FileTreeNode[]): Map<string, FileTreeNode> {
|
|
285
|
+
const index = new Map<string, FileTreeNode>()
|
|
286
|
+
const stack = [...roots]
|
|
287
|
+
while (stack.length > 0) {
|
|
288
|
+
const node = stack.pop()!
|
|
289
|
+
index.set(node.id, node)
|
|
290
|
+
for (const child of node.children) stack.push(child)
|
|
291
|
+
}
|
|
292
|
+
return index
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export type FileTreeExpansion = boolean | number | readonly string[] | undefined
|
|
296
|
+
|
|
297
|
+
/** Tree this small opens fully by default; a larger one opens only its top level. */
|
|
298
|
+
export const FILE_TREE_AUTO_EXPAND_LIMIT = 100
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Which folders start open. `true` opens every folder, a number opens folders up
|
|
302
|
+
* to that depth (1 is the top level), a list opens exactly those ids, `false`
|
|
303
|
+
* none. Unset is "auto": everything for a small tree, the top level for a large
|
|
304
|
+
* one, because a tree that opens 5 000 rows is the freeze this exists to avoid.
|
|
305
|
+
*/
|
|
306
|
+
export function initialExpanded(
|
|
307
|
+
roots: readonly FileTreeNode[],
|
|
308
|
+
spec: FileTreeExpansion,
|
|
309
|
+
fileCount: number
|
|
310
|
+
): Set<string> {
|
|
311
|
+
const open = new Set<string>()
|
|
312
|
+
if (Array.isArray(spec)) {
|
|
313
|
+
for (const id of spec) open.add(id)
|
|
314
|
+
return open
|
|
315
|
+
}
|
|
316
|
+
const depth =
|
|
317
|
+
spec === true
|
|
318
|
+
? Infinity
|
|
319
|
+
: spec === false
|
|
320
|
+
? 0
|
|
321
|
+
: typeof spec === 'number'
|
|
322
|
+
? Math.max(0, Math.floor(spec))
|
|
323
|
+
: fileCount <= FILE_TREE_AUTO_EXPAND_LIMIT
|
|
324
|
+
? Infinity
|
|
325
|
+
: 1
|
|
326
|
+
const stack: [FileTreeNode, number][] = roots.map((node) => [node, 1])
|
|
327
|
+
while (stack.length > 0) {
|
|
328
|
+
const [node, level] = stack.pop()!
|
|
329
|
+
if (node.kind !== 'directory' || level > depth) continue
|
|
330
|
+
open.add(node.id)
|
|
331
|
+
for (const child of node.children) stack.push([child, level + 1])
|
|
332
|
+
}
|
|
333
|
+
return open
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// ---------------------------------------------------------------------------
|
|
337
|
+
// Mounted window
|
|
338
|
+
// ---------------------------------------------------------------------------
|
|
339
|
+
|
|
340
|
+
/** The slice of visible rows that is mounted: `start` inclusive, `end` exclusive. */
|
|
341
|
+
export type RowWindow = Readonly<{ start: number; end: number }>
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* A window clamped to what exists, and never narrower than one page when that
|
|
345
|
+
* many rows are there. Rows collapse and files are removed while a window is
|
|
346
|
+
* held, so every read goes through this.
|
|
347
|
+
*/
|
|
348
|
+
export function clampWindow(window: RowWindow, total: number, page: number): RowWindow {
|
|
349
|
+
if (total <= 0) return { start: 0, end: 0 }
|
|
350
|
+
const start = Math.min(Math.max(0, window.start), total - 1)
|
|
351
|
+
return { start, end: Math.min(total, Math.max(window.end, start + Math.max(1, page))) }
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* The window after the keyboard (or typeahead) asks for a row. A row inside the
|
|
356
|
+
* window changes nothing. A row just outside it — an arrow key at the edge — grows
|
|
357
|
+
* the window by one page, so walking the tree costs a page at a time. Anything
|
|
358
|
+
* further is a jump (End, Home, a typed letter): the window moves to the page that
|
|
359
|
+
* holds the row instead of mounting every row between, which is what keeps End on
|
|
360
|
+
* a 5 000-row tree from being a multi-second freeze.
|
|
361
|
+
*/
|
|
362
|
+
export function revealInWindow(
|
|
363
|
+
window: RowWindow,
|
|
364
|
+
index: number,
|
|
365
|
+
total: number,
|
|
366
|
+
page: number
|
|
367
|
+
): RowWindow {
|
|
368
|
+
const current = clampWindow(window, total, page)
|
|
369
|
+
if (index < 0 || index >= total) return current
|
|
370
|
+
if (index >= current.start && index < current.end) return current
|
|
371
|
+
if (index === current.start - 1) {
|
|
372
|
+
return { start: Math.max(0, current.start - page), end: current.end }
|
|
373
|
+
}
|
|
374
|
+
if (index === current.end)
|
|
375
|
+
return { start: current.start, end: Math.min(total, current.end + page) }
|
|
376
|
+
const start = Math.floor(index / page) * page
|
|
377
|
+
return { start, end: Math.min(total, start + page) }
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// ---------------------------------------------------------------------------
|
|
381
|
+
// Words
|
|
382
|
+
// ---------------------------------------------------------------------------
|
|
383
|
+
|
|
384
|
+
export type ChangeCounts = Readonly<{
|
|
385
|
+
total: number
|
|
386
|
+
added: number
|
|
387
|
+
modified: number
|
|
388
|
+
deleted: number
|
|
389
|
+
renamed: number
|
|
390
|
+
/** Files with no change kind supplied. */
|
|
391
|
+
unspecified: number
|
|
392
|
+
}>
|
|
393
|
+
|
|
394
|
+
export function countChanges(roots: readonly FileTreeNode[]): ChangeCounts {
|
|
395
|
+
const counts = { total: 0, added: 0, modified: 0, deleted: 0, renamed: 0, unspecified: 0 }
|
|
396
|
+
const stack = [...roots]
|
|
397
|
+
while (stack.length > 0) {
|
|
398
|
+
const node = stack.pop()!
|
|
399
|
+
for (const child of node.children) stack.push(child)
|
|
400
|
+
if (node.kind !== 'file') continue
|
|
401
|
+
counts.total += 1
|
|
402
|
+
if (node.change) counts[node.change] += 1
|
|
403
|
+
else counts.unspecified += 1
|
|
404
|
+
}
|
|
405
|
+
return counts
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* The summary in words: "12 files changed: 3 added, 8 modified, 1 deleted". Only
|
|
410
|
+
* kinds that occur are named, and files with no kind are not folded into any of
|
|
411
|
+
* them. Empty is empty, not "0 files changed".
|
|
412
|
+
*/
|
|
413
|
+
export function describeChanges(counts: ChangeCounts): string {
|
|
414
|
+
if (counts.total === 0) return 'No changed files'
|
|
415
|
+
const parts: string[] = []
|
|
416
|
+
for (const kind of ['added', 'modified', 'deleted', 'renamed'] as const) {
|
|
417
|
+
if (counts[kind] > 0) parts.push(`${counts[kind].toLocaleString()} ${kind}`)
|
|
418
|
+
}
|
|
419
|
+
const noun = counts.total === 1 ? 'file' : 'files'
|
|
420
|
+
const head = `${counts.total.toLocaleString()} ${noun} changed`
|
|
421
|
+
return parts.length > 0 ? `${head}: ${parts.join(', ')}` : head
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/** The words for a change, which are drawn on the row and so also spoken. */
|
|
425
|
+
export const FILE_CHANGE_LABELS: Readonly<Record<FileChangeKind, string>> = {
|
|
426
|
+
added: 'Added',
|
|
427
|
+
modified: 'Modified',
|
|
428
|
+
deleted: 'Deleted',
|
|
429
|
+
renamed: 'Renamed',
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// ---------------------------------------------------------------------------
|
|
433
|
+
// Typeahead
|
|
434
|
+
// ---------------------------------------------------------------------------
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* The row a typed prefix jumps to. One typed letter searches from the row after
|
|
438
|
+
* the active one, so pressing it again cycles; a longer string searches from the
|
|
439
|
+
* active row, so refining a match does not skip past it. Matching ignores case.
|
|
440
|
+
*/
|
|
441
|
+
export function typeaheadIndex(
|
|
442
|
+
labels: readonly string[],
|
|
443
|
+
activeIndex: number,
|
|
444
|
+
query: string
|
|
445
|
+
): number {
|
|
446
|
+
const needle = query.toLowerCase()
|
|
447
|
+
if (needle === '' || labels.length === 0) return -1
|
|
448
|
+
const repeated = needle.length > 1 && [...needle].every((char) => char === needle[0])
|
|
449
|
+
const prefix = repeated ? needle[0]! : needle
|
|
450
|
+
const first = needle.length === 1 || repeated ? activeIndex + 1 : Math.max(0, activeIndex)
|
|
451
|
+
for (let offset = 0; offset < labels.length; offset += 1) {
|
|
452
|
+
const index = (first + offset) % labels.length
|
|
453
|
+
if (labels[index]!.toLowerCase().startsWith(prefix)) return index
|
|
454
|
+
}
|
|
455
|
+
return -1
|
|
456
|
+
}
|