@xp266/dshtui 0.1.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.
Files changed (159) hide show
  1. package/bin/dshtui.js +65 -0
  2. package/cordis.patch.yml +81 -0
  3. package/lib/contract/index.d.mts +2 -0
  4. package/lib/contract/index.mjs +5 -0
  5. package/lib/dialog.d.mts +156 -0
  6. package/lib/dialog.mjs +3 -0
  7. package/lib/index-DqnaSXqP.d.mts +823 -0
  8. package/lib/index.d.mts +17 -0
  9. package/lib/index.mjs +11404 -0
  10. package/lib/prod-react-Dzof4qJx.mjs +4 -0
  11. package/lib/surface-DdxzIgIY.mjs +3126 -0
  12. package/lib/vendor.d.mts +3 -0
  13. package/lib/vendor.mjs +4 -0
  14. package/package.json +128 -0
  15. package/src/apply-theme.ts +8 -0
  16. package/src/boot-log.ts +35 -0
  17. package/src/chat/blocks.ts +41 -0
  18. package/src/chat/bridge.ts +1076 -0
  19. package/src/chat/builtin-tool-views.ts +53 -0
  20. package/src/chat/chat-face.ts +16 -0
  21. package/src/chat/chat-nodes.ts +53 -0
  22. package/src/chat/efforts.ts +4 -0
  23. package/src/chat/interactions.ts +329 -0
  24. package/src/chat/models.ts +263 -0
  25. package/src/chat/partial-json.ts +125 -0
  26. package/src/chat/presets.ts +26 -0
  27. package/src/chat/question-view.ts +106 -0
  28. package/src/chat/retry-status.ts +33 -0
  29. package/src/chat/session-groups.ts +34 -0
  30. package/src/chat/session-list.ts +311 -0
  31. package/src/chat/store.ts +636 -0
  32. package/src/chat/todo-view.ts +66 -0
  33. package/src/chat/tool-view.ts +311 -0
  34. package/src/chat/tool-views.ts +114 -0
  35. package/src/contract/index.ts +704 -0
  36. package/src/contract/upstream.ts +165 -0
  37. package/src/core/caret-nonce.ts +19 -0
  38. package/src/core/composer-layout.ts +65 -0
  39. package/src/core/edit.ts +152 -0
  40. package/src/core/field-view.ts +52 -0
  41. package/src/core/fields.ts +185 -0
  42. package/src/core/metrics.ts +26 -0
  43. package/src/core/paste.ts +149 -0
  44. package/src/core/segments.ts +112 -0
  45. package/src/core/text.ts +290 -0
  46. package/src/dialog.ts +11 -0
  47. package/src/env.ts +38 -0
  48. package/src/harness-home.ts +22 -0
  49. package/src/hot-theme.ts +56 -0
  50. package/src/index.tsx +185 -0
  51. package/src/kernel/registry.ts +102 -0
  52. package/src/kernel/surface.ts +33 -0
  53. package/src/log.ts +249 -0
  54. package/src/model/message.ts +89 -0
  55. package/src/model/selection.ts +63 -0
  56. package/src/performance-guard.ts +28 -0
  57. package/src/runtime/prod-react.ts +10 -0
  58. package/src/terminal/background.ts +66 -0
  59. package/src/terminal/capabilities.ts +54 -0
  60. package/src/terminal/clipboard-backends.ts +69 -0
  61. package/src/terminal/clipboard.ts +161 -0
  62. package/src/terminal/cursor-shape.ts +13 -0
  63. package/src/terminal/glyphs.ts +44 -0
  64. package/src/terminal/mouse.ts +154 -0
  65. package/src/terminal/probe.ts +61 -0
  66. package/src/terminal/screen.ts +349 -0
  67. package/src/theme-settings.ts +31 -0
  68. package/src/theme.ts +319 -0
  69. package/src/ui/app.tsx +454 -0
  70. package/src/ui/chrome/caps.tsx +9 -0
  71. package/src/ui/chrome/hint-service.ts +87 -0
  72. package/src/ui/chrome/input-status.ts +27 -0
  73. package/src/ui/chrome/key-gate.tsx +29 -0
  74. package/src/ui/chrome/status-bar.tsx +147 -0
  75. package/src/ui/contributions.ts +47 -0
  76. package/src/ui/dialog/defaults-dialog.tsx +152 -0
  77. package/src/ui/dialog/dialog-item.tsx +38 -0
  78. package/src/ui/dialog/dialog.tsx +386 -0
  79. package/src/ui/dialog/effort-dialog.tsx +45 -0
  80. package/src/ui/dialog/geometry.ts +131 -0
  81. package/src/ui/dialog/items.ts +124 -0
  82. package/src/ui/dialog/list-dialog.tsx +63 -0
  83. package/src/ui/dialog/models-dialog.tsx +203 -0
  84. package/src/ui/dialog/presets-dialog.tsx +46 -0
  85. package/src/ui/dialog/providers-dialog.tsx +313 -0
  86. package/src/ui/dialog/sessions-dialog.tsx +163 -0
  87. package/src/ui/dialog/sizes.ts +7 -0
  88. package/src/ui/dialog/status-lines.ts +11 -0
  89. package/src/ui/dialog/todo-dialog.tsx +29 -0
  90. package/src/ui/dialog/use-dialog-input.ts +186 -0
  91. package/src/ui/extension-point.ts +188 -0
  92. package/src/ui/home-logo.ts +132 -0
  93. package/src/ui/hooks/use-async-action.ts +25 -0
  94. package/src/ui/hooks/use-async-list.ts +71 -0
  95. package/src/ui/hooks/use-caret.ts +24 -0
  96. package/src/ui/hooks/use-chat-events.ts +153 -0
  97. package/src/ui/hooks/use-mouse-selection.ts +352 -0
  98. package/src/ui/hooks/use-scroll.ts +49 -0
  99. package/src/ui/hooks/use-terminal-size.ts +20 -0
  100. package/src/ui/input/commands.ts +118 -0
  101. package/src/ui/input/composer-bus.ts +14 -0
  102. package/src/ui/input/composer-fields.ts +111 -0
  103. package/src/ui/input/composer-keys.ts +21 -0
  104. package/src/ui/input/composer-paste.ts +20 -0
  105. package/src/ui/input/input-bar.tsx +279 -0
  106. package/src/ui/input/use-composer.ts +495 -0
  107. package/src/ui/key-arbiter.ts +37 -0
  108. package/src/ui/keymap.ts +15 -0
  109. package/src/ui/layout-service.ts +77 -0
  110. package/src/ui/message/layout.ts +763 -0
  111. package/src/ui/message/md/block.ts +247 -0
  112. package/src/ui/message/md/engine.ts +430 -0
  113. package/src/ui/message/md/extensions.ts +58 -0
  114. package/src/ui/message/md/highlight.ts +626 -0
  115. package/src/ui/message/md/index.ts +14 -0
  116. package/src/ui/message/md/inline.ts +97 -0
  117. package/src/ui/message/md/palette.ts +87 -0
  118. package/src/ui/message/message-list.tsx +69 -0
  119. package/src/ui/message/message-row.tsx +137 -0
  120. package/src/ui/message/message-views.ts +16 -0
  121. package/src/ui/message/renderers.ts +39 -0
  122. package/src/ui/message/tool-diff.ts +188 -0
  123. package/src/ui/message/warmup.ts +41 -0
  124. package/src/ui/message/wave.ts +98 -0
  125. package/src/ui/overlay.ts +22 -0
  126. package/src/ui/panels/approval-panel.tsx +193 -0
  127. package/src/ui/panels/exports.ts +3 -0
  128. package/src/ui/panels/question-model.ts +424 -0
  129. package/src/ui/panels/question-panel.tsx +154 -0
  130. package/src/ui/panels/surface.tsx +106 -0
  131. package/src/ui/panels-builtin.tsx +86 -0
  132. package/src/ui/pointer/builtins.ts +281 -0
  133. package/src/ui/pointer/registry.ts +25 -0
  134. package/src/ui/region.tsx +31 -0
  135. package/src/ui/selection/service.ts +92 -0
  136. package/src/ui/selection-registry.ts +88 -0
  137. package/src/ui/selection.tsx +148 -0
  138. package/src/ui/spinner-tick.tsx +24 -0
  139. package/src/ui/use-available-windows.ts +31 -0
  140. package/src/ui/widgets/actions.tsx +84 -0
  141. package/src/ui/widgets/button.tsx +57 -0
  142. package/src/ui/widgets/checkbox.tsx +58 -0
  143. package/src/ui/widgets/header.tsx +36 -0
  144. package/src/ui/widgets/index.ts +8 -0
  145. package/src/ui/widgets/input.tsx +76 -0
  146. package/src/ui/widgets/registry.ts +32 -0
  147. package/src/ui/widgets/search.tsx +59 -0
  148. package/src/ui/widgets/select.tsx +97 -0
  149. package/src/ui/widgets/static.tsx +37 -0
  150. package/src/ui/widgets/types.ts +1 -0
  151. package/src/ui/window-services-bridge.ts +71 -0
  152. package/src/ui/window-services.ts +20 -0
  153. package/src/ui/windows/models-window.tsx +15 -0
  154. package/src/ui/windows/providers-window.tsx +14 -0
  155. package/src/ui/windows/service-window.tsx +18 -0
  156. package/src/ui/windows/sessions-window.tsx +25 -0
  157. package/src/ui/windows-builtin.tsx +94 -0
  158. package/src/ui/windows.ts +18 -0
  159. package/src/vendor.ts +12 -0
@@ -0,0 +1,97 @@
1
+ import { Box, Text } from 'ink'
2
+ import { COLORS } from '../../theme.ts'
3
+ import { glyphs } from '../../terminal/glyphs.ts'
4
+ import { selectBlock, CAROUSEL_BUTTON_WIDTH } from '../dialog/geometry.ts'
5
+ import { textWidth } from '../../core/text.ts'
6
+ import { SelectableText } from '../selection.tsx'
7
+ import { BUILTIN_WIDGET_ORDER, registerWidget } from './registry.ts'
8
+ import type { PaintArgs, ClickHit, ClickActions, WidgetKeyApi } from './types.ts'
9
+ import type { DialogItem } from '../dialog/items.ts'
10
+
11
+ type SelectItem = Extract<DialogItem, { type: 'select' }>
12
+
13
+ export function carouselHit(item: SelectItem, width: number, localX: number): -1 | 1 | null {
14
+ if (item.options.length <= 1) return null
15
+ const block = selectBlock(width, item.value)
16
+ if (localX >= block.blockStart && localX < block.blockStart + CAROUSEL_BUTTON_WIDTH) return -1
17
+ if (localX >= block.blockStart + block.blockWidth - CAROUSEL_BUTTON_WIDTH && localX < block.blockStart + block.blockWidth) return 1
18
+ return null
19
+ }
20
+
21
+ function paintSelect(paint: PaintArgs<SelectItem>) {
22
+ const { item, width, y, x } = paint
23
+ const block = selectBlock(width, item.value)
24
+ const hasArrows = item.options.length > 1
25
+ const pad = hasArrows ? block.leftPad : block.fullLeftPad
26
+ const inner = block.blockWidth - (hasArrows ? CAROUSEL_BUTTON_WIDTH * 2 : 0)
27
+ const valueCol = x + block.blockStart + (hasArrows ? CAROUSEL_BUTTON_WIDTH : 0)
28
+ const valueText = ' '.repeat(pad) + block.text + ' '.repeat(Math.max(0, inner - pad - textWidth(block.text)))
29
+ const carousel = (
30
+ <Box width={width} justifyContent="space-between">
31
+ <SelectableText y={y} col={x} text={`${item.label}:`} />
32
+ <Box width={block.blockWidth} flexDirection="row">
33
+ {hasArrows && (
34
+ <Text color={COLORS.ink} backgroundColor={paint.pressed === 'left' ? COLORS.carouselButtonPressedBg : COLORS.carouselButtonBg}>{` ${glyphs.carouselLeft} `}</Text>
35
+ )}
36
+ <SelectableText
37
+ y={y}
38
+ col={valueCol}
39
+ text={valueText}
40
+ color={paint.focused ? COLORS.carouselSelectedText : COLORS.ink}
41
+ backgroundColor={COLORS.carouselCurrentBg}
42
+ />
43
+ {hasArrows && (
44
+ <Text color={COLORS.ink} backgroundColor={paint.pressed === 'right' ? COLORS.carouselButtonPressedBg : COLORS.carouselButtonBg}>{` ${glyphs.carouselRight} `}</Text>
45
+ )}
46
+ </Box>
47
+ </Box>
48
+ )
49
+ if (!item.spaced) return carousel
50
+ return (
51
+ <Box flexDirection="column">
52
+ {paint.clip === 0 && carousel}
53
+ <Box height={1}>
54
+ <Text> </Text>
55
+ </Box>
56
+ </Box>
57
+ )
58
+ }
59
+
60
+ function cycle(item: SelectItem, direction: -1 | 1): void {
61
+ const index = Math.max(0, item.options.indexOf(item.value))
62
+ const length = item.options.length
63
+ const next = item.options[(index + direction + length) % length]
64
+ if (next !== undefined) item.onChange(next)
65
+ }
66
+
67
+ registerWidget<SelectItem>('select', {
68
+ selectable: true,
69
+ height(item) {
70
+ return item.spaced === true ? 2 : 1
71
+ },
72
+ paintWidth(item) {
73
+ return textWidth(item.label)
74
+ },
75
+ render: paintSelect,
76
+ onLeftRight(item, direction) {
77
+ if (item.options.length <= 1) return false
78
+ cycle(item, direction)
79
+ return true
80
+ },
81
+ onEnter(item, api) {
82
+ if (item.onEnter !== undefined) {
83
+ item.onEnter()
84
+ return true
85
+ }
86
+ api.navigate('down')
87
+ return true
88
+ },
89
+ onClick(item, hit: ClickHit, actions: ClickActions) {
90
+ if (hit.localY !== 0) return false
91
+ const direction = carouselHit(item, hit.width, hit.localX)
92
+ if (direction === null) return false
93
+ cycle(item, direction)
94
+ actions.flash(direction === -1 ? 'left' : 'right')
95
+ return true
96
+ },
97
+ }, { order: BUILTIN_WIDGET_ORDER })
@@ -0,0 +1,37 @@
1
+ import { Box } from 'ink'
2
+ import { COLORS } from '../../theme.ts'
3
+ import { textWidth, wrapLines } from '../../core/text.ts'
4
+ import { SelectableText } from '../selection.tsx'
5
+ import { BUILTIN_WIDGET_ORDER, registerWidget } from './registry.ts'
6
+ import type { DialogItem } from '../dialog/items.ts'
7
+
8
+ type StaticItem = Extract<DialogItem, { type: 'static' }>
9
+
10
+ registerWidget<StaticItem>('static', {
11
+ selectable: false,
12
+ height(item, width) {
13
+ return Math.max(1, wrapLines(item.label, Math.max(4, width)).length)
14
+ },
15
+ paintWidth(item) {
16
+ return textWidth(item.label)
17
+ },
18
+ render({ item, width, y, x }) {
19
+ const lines = item.label === '' ? [''] : wrapLines(item.label, Math.max(4, width))
20
+ if (lines.length === 1) {
21
+ return <SelectableText y={y} col={x} text={item.label === '' ? ' ' : item.label} color={item.label === '' ? undefined : COLORS.toolBodyText} />
22
+ }
23
+ return (
24
+ <Box flexDirection="column">
25
+ {lines.map((line, index) => (
26
+ <SelectableText
27
+ key={index}
28
+ y={y + index}
29
+ col={x}
30
+ text={line === '' ? ' ' : line}
31
+ color={line === '' ? undefined : COLORS.toolBodyText}
32
+ />
33
+ ))}
34
+ </Box>
35
+ )
36
+ },
37
+ }, { order: BUILTIN_WIDGET_ORDER })
@@ -0,0 +1 @@
1
+ export type { ClickActions, ClickHit, PaintArgs, WidgetDef, WidgetKeyApi } from '../../contract/index.ts'
@@ -0,0 +1,71 @@
1
+ import { registerWindowService } from './window-services.ts'
2
+ import type { ChatBridge } from '../chat/bridge.ts'
3
+ import type { ModelApi } from './dialog/models-dialog.tsx'
4
+ import type { SessionsApi } from './dialog/sessions-dialog.tsx'
5
+ import type { PresetsApi } from './dialog/presets-dialog.tsx'
6
+ import type { EffortsApi } from './dialog/effort-dialog.tsx'
7
+ import type { DefaultsApi } from './dialog/defaults-dialog.tsx'
8
+ import type { TodoItemLike } from '../chat/todo-view.ts'
9
+
10
+ /** Builtin window services sit in a high-order layer; plugin services override by default. */
11
+ export const BUILTIN_SERVICE_ORDER = 500
12
+
13
+ export function registerWindowServices(bridge: ChatBridge): () => void {
14
+ const models: ModelApi = {
15
+ listModels: bridge.listModels,
16
+ selectModel: bridge.selectModel,
17
+ fetchCustomModels: bridge.fetchCustomModels,
18
+ saveCustomProvider: bridge.saveCustomProvider,
19
+ listProviderDirectory: bridge.listProviderDirectory,
20
+ fetchProviderModels: bridge.fetchProviderModels,
21
+ saveProviderKey: bridge.saveProviderKey,
22
+ saveProviderModels: bridge.saveProviderModels,
23
+ deleteProvider: bridge.deleteProvider,
24
+ readModelEntries: bridge.readModelEntries,
25
+ describeModel: bridge.describeModel,
26
+ saveModelEntry: bridge.saveModelEntry,
27
+ deleteModelEntry: bridge.deleteModelEntry,
28
+ }
29
+ const sessions: SessionsApi = {
30
+ listSessions: bridge.listSessions,
31
+ onSessionsChanged: bridge.onSessionListChanged,
32
+ openSession: bridge.openSession,
33
+ newSession: bridge.newSession,
34
+ archiveSession: bridge.archiveSession,
35
+ activeSessionId: bridge.activeSessionId,
36
+ }
37
+ const presets: PresetsApi = {
38
+ listPresets: bridge.listPresets,
39
+ currentPreset: bridge.currentPreset,
40
+ selectPreset: bridge.selectPreset,
41
+ }
42
+ const efforts: EffortsApi = {
43
+ listEfforts: bridge.listEfforts,
44
+ currentEffort: bridge.currentEffort,
45
+ selectEffort: bridge.selectEffort,
46
+ }
47
+ const defaults: DefaultsApi = {
48
+ listPresets: bridge.listPresets,
49
+ defaultPresetId: bridge.defaultPresetId,
50
+ setDefaultPreset: bridge.setDefaultPreset,
51
+ listPermissionPresets: bridge.listPermissionPresets,
52
+ defaultPermission: bridge.defaultPermission,
53
+ setDefaultPermission: bridge.setDefaultPermission,
54
+ themeMode: bridge.themePreference,
55
+ setThemeMode: bridge.setThemePreference,
56
+ }
57
+ const disposers = [
58
+ registerWindowService('models', models, { order: BUILTIN_SERVICE_ORDER }),
59
+ registerWindowService('sessions', sessions, { order: BUILTIN_SERVICE_ORDER }),
60
+ registerWindowService('presets', presets, { order: BUILTIN_SERVICE_ORDER }),
61
+ registerWindowService('efforts', efforts, { order: BUILTIN_SERVICE_ORDER }),
62
+ registerWindowService('defaults', defaults, { order: BUILTIN_SERVICE_ORDER }),
63
+ ]
64
+ return () => {
65
+ for (const dispose of disposers) dispose()
66
+ }
67
+ }
68
+
69
+ export function registerTodosService(todos: readonly TodoItemLike[]): () => void {
70
+ return registerWindowService('todos', todos, { order: BUILTIN_SERVICE_ORDER })
71
+ }
@@ -0,0 +1,20 @@
1
+ import { useSyncExternalStore } from 'react'
2
+ import { keyedRegistry } from '../kernel/registry.ts'
3
+
4
+ export const windowServices = keyedRegistry<unknown>()
5
+
6
+ export const registerWindowService = windowServices.register.bind(windowServices)
7
+ export const subscribeWindowServices = windowServices.subscribe.bind(windowServices)
8
+
9
+ export function hasWindowServices(required: readonly string[] | undefined): boolean {
10
+ if (required === undefined || required.length === 0) return true
11
+ return required.every(name => windowServices.has(name))
12
+ }
13
+
14
+ export function useWindowService<T>(name: string): T | undefined {
15
+ return useSyncExternalStore(
16
+ subscribeWindowServices,
17
+ () => (windowServices.has(name) ? (windowServices.get(name) as T) : undefined),
18
+ () => undefined,
19
+ )
20
+ }
@@ -0,0 +1,15 @@
1
+ import { ModelsDialog } from '../dialog/models-dialog.tsx'
2
+ import { useWindowService } from '../window-services.ts'
3
+ import type { ModelApi } from '../dialog/models-dialog.tsx'
4
+ import type { WindowProps } from '../windows.ts'
5
+
6
+ export interface ModelsWindowProps extends WindowProps {
7
+ onModelSelected(provider: string, model: string): void
8
+ onAddProvider(): void
9
+ }
10
+
11
+ export function ModelsWindow({ onModelSelected, onAddProvider, handleRef, ...props }: ModelsWindowProps) {
12
+ const api = useWindowService<ModelApi>('models')
13
+ if (api === undefined) return null
14
+ return <ModelsDialog ref={handleRef} api={api} onModelSelected={onModelSelected} onAddProvider={onAddProvider} {...props} />
15
+ }
@@ -0,0 +1,14 @@
1
+ import { ProvidersDialog } from '../dialog/providers-dialog.tsx'
2
+ import { useWindowService } from '../window-services.ts'
3
+ import type { ModelApi } from '../dialog/models-dialog.tsx'
4
+ import type { WindowProps } from '../windows.ts'
5
+
6
+ export interface ProvidersWindowProps extends WindowProps {
7
+ onModelSelected(provider: string, model: string): void
8
+ }
9
+
10
+ export function ProvidersWindow({ onModelSelected, handleRef, ...props }: ProvidersWindowProps) {
11
+ const api = useWindowService<ModelApi>('models')
12
+ if (api === undefined) return null
13
+ return <ProvidersDialog ref={handleRef} api={api} onModelSelected={onModelSelected} {...props} />
14
+ }
@@ -0,0 +1,18 @@
1
+ import type { ComponentType, Ref } from 'react'
2
+ import { useWindowService } from '../window-services.ts'
3
+ import type { DialogHandle } from '../dialog/dialog.tsx'
4
+ import type { WindowProps } from '../windows.ts'
5
+
6
+ export interface ServiceDialogProps<Api> {
7
+ api: Api
8
+ onClose(): void
9
+ ref?: Ref<DialogHandle>
10
+ }
11
+
12
+ export function createServiceWindow<Api>(name: string, Dialog: ComponentType<ServiceDialogProps<Api>>): ComponentType<WindowProps> {
13
+ return function ServiceWindow({ handleRef, onClose }: WindowProps) {
14
+ const api = useWindowService<Api>(name)
15
+ if (api === undefined) return null
16
+ return <Dialog ref={handleRef} api={api} onClose={onClose} />
17
+ }
18
+ }
@@ -0,0 +1,25 @@
1
+ import { SessionsDialog } from '../dialog/sessions-dialog.tsx'
2
+ import { useWindowService } from '../window-services.ts'
3
+ import type { SessionsApi } from '../dialog/sessions-dialog.tsx'
4
+ import type { WindowProps } from '../windows.ts'
5
+
6
+ export interface SessionsWindowProps extends WindowProps {
7
+ onBeforeSessionSelected?(): void
8
+ onSessionSelected(): void
9
+ onNewSession?(): void
10
+ }
11
+
12
+ export function SessionsWindow({ onBeforeSessionSelected, onSessionSelected, onNewSession, handleRef, ...props }: SessionsWindowProps) {
13
+ const api = useWindowService<SessionsApi>('sessions')
14
+ if (api === undefined) return null
15
+ return (
16
+ <SessionsDialog
17
+ ref={handleRef}
18
+ api={api}
19
+ onBeforeSessionSelected={onBeforeSessionSelected}
20
+ onSessionSelected={onSessionSelected}
21
+ onNewSession={onNewSession}
22
+ {...props}
23
+ />
24
+ )
25
+ }
@@ -0,0 +1,94 @@
1
+ import { ModelsWindow } from './windows/models-window.tsx'
2
+ import { ProvidersWindow } from './windows/providers-window.tsx'
3
+ import { SessionsWindow } from './windows/sessions-window.tsx'
4
+ import { PresetsDialog } from './dialog/presets-dialog.tsx'
5
+ import { EffortDialog } from './dialog/effort-dialog.tsx'
6
+ import { DefaultsDialog } from './dialog/defaults-dialog.tsx'
7
+ import { TodoDialog } from './dialog/todo-dialog.tsx'
8
+ import { createServiceWindow } from './windows/service-window.tsx'
9
+ import { registerWindow } from './windows.ts'
10
+
11
+ /**
12
+ * Builtin windows register as high-order fallback layers: a plugin
13
+ * contribution for the same id (default order 100) always wins regardless of
14
+ * registration timing, and disposing the plugin restores the builtin.
15
+ */
16
+ export const BUILTIN_WINDOW_ORDER = 500
17
+
18
+ export interface BuiltinWindowDeps {
19
+ onModelSelected(provider: string, model: string): void
20
+ onAddProvider(): void
21
+ onBeforeSessionSelected(): void
22
+ onSessionSelected(): void
23
+ onNewSession(): void
24
+ }
25
+
26
+ export function registerBuiltinWindows(deps: BuiltinWindowDeps): () => void {
27
+ const disposers = [
28
+ registerWindow({
29
+ id: 'models',
30
+ title: 'models',
31
+ order: BUILTIN_WINDOW_ORDER + 0,
32
+ required: ['models'],
33
+ component: props => <ModelsWindow {...props} onModelSelected={deps.onModelSelected} onAddProvider={deps.onAddProvider} />,
34
+ command: { name: 'models', description: 'Open model selection' },
35
+ }),
36
+ registerWindow({
37
+ id: 'providers',
38
+ title: 'providers',
39
+ order: BUILTIN_WINDOW_ORDER + 5,
40
+ required: ['models'],
41
+ component: props => <ProvidersWindow {...props} onModelSelected={deps.onModelSelected} />,
42
+ command: { name: 'providers', description: 'Open provider selection' },
43
+ }),
44
+ registerWindow({
45
+ id: 'sessions',
46
+ title: 'sessions',
47
+ order: BUILTIN_WINDOW_ORDER + 10,
48
+ required: ['sessions'],
49
+ component: props => (
50
+ <SessionsWindow
51
+ {...props}
52
+ onBeforeSessionSelected={deps.onBeforeSessionSelected}
53
+ onSessionSelected={deps.onSessionSelected}
54
+ onNewSession={deps.onNewSession}
55
+ />
56
+ ),
57
+ command: { name: 'sessions', description: 'Open session picker' },
58
+ }),
59
+ registerWindow({
60
+ id: 'preset',
61
+ title: 'preset',
62
+ order: BUILTIN_WINDOW_ORDER + 20,
63
+ required: ['presets'],
64
+ component: createServiceWindow('presets', PresetsDialog),
65
+ command: { name: 'preset', description: 'Select agent preset' },
66
+ }),
67
+ registerWindow({
68
+ id: 'effort',
69
+ title: 'reasoning effort',
70
+ order: BUILTIN_WINDOW_ORDER + 30,
71
+ required: ['efforts'],
72
+ component: createServiceWindow('efforts', EffortDialog),
73
+ command: { name: 'reasoning-effort', description: "Select the current model's reasoning effort" },
74
+ }),
75
+ registerWindow({
76
+ id: 'defaults',
77
+ title: 'defaults',
78
+ order: BUILTIN_WINDOW_ORDER + 40,
79
+ required: ['defaults'],
80
+ component: createServiceWindow('defaults', DefaultsDialog),
81
+ command: { name: 'defaults', description: 'Set default permission and agent preset' },
82
+ }),
83
+ registerWindow({
84
+ id: 'todo',
85
+ title: 'todo',
86
+ order: BUILTIN_WINDOW_ORDER + 50,
87
+ required: ['todos'],
88
+ component: createServiceWindow('todos', TodoDialog),
89
+ }),
90
+ ]
91
+ return () => {
92
+ for (const dispose of disposers) dispose()
93
+ }
94
+ }
@@ -0,0 +1,18 @@
1
+ import { keyedRegistry } from '../kernel/registry.ts'
2
+ import type { WindowContribution } from '../contract/index.ts'
3
+
4
+ export type { WindowCommandSpec, WindowContribution, WindowProps } from '../contract/index.ts'
5
+
6
+ const inner = keyedRegistry<WindowContribution>()
7
+
8
+ export function registerWindow(contribution: WindowContribution): () => void {
9
+ return inner.register(contribution.id, contribution, { order: contribution.order })
10
+ }
11
+
12
+ export function listWindows(): WindowContribution[] {
13
+ return inner.values()
14
+ }
15
+
16
+ export function subscribeWindows(listener: () => void): () => void {
17
+ return inner.subscribe(listener)
18
+ }
package/src/vendor.ts ADDED
@@ -0,0 +1,12 @@
1
+ import './runtime/prod-react.ts'
2
+ /**
3
+ * Shared react/ink runtime for third-party TUI plugins.
4
+ *
5
+ * Plugin authors must import react and ink from this entry instead of adding
6
+ * them as dependencies: the profile module graph only contains what dshtui
7
+ * itself links, so a separate react install forks the React instance and
8
+ * breaks hooks. Re-exporting keeps module identity identical to dshtui's
9
+ * own copy while the bundler treats both as externals.
10
+ */
11
+ export { default as React, Children, Component, Fragment, Profiler, StrictMode, Suspense, createContext, createElement, createRef, forwardRef, isValidElement, lazy, memo, startTransition, useCallback, useContext, useDebugValue, useDeferredValue, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useOptimistic, useReducer, useRef, useState, useSyncExternalStore, useTransition } from 'react'
12
+ export { Box, Text, Static, useApp, useInput, useStdin, useStdout, useFocus, useFocusManager, measureElement } from 'ink'