@neurocode-ai/tui 1.18.8

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 (241) hide show
  1. package/bunfig.toml +4 -0
  2. package/package.json +72 -0
  3. package/src/app.tsx +1134 -0
  4. package/src/attention.ts +260 -0
  5. package/src/audio.d.ts +9 -0
  6. package/src/audio.ts +53 -0
  7. package/src/clipboard.ts +124 -0
  8. package/src/component/bg-pulse-render.ts +436 -0
  9. package/src/component/bg-pulse.tsx +99 -0
  10. package/src/component/command-palette.tsx +79 -0
  11. package/src/component/dialog-agent.tsx +31 -0
  12. package/src/component/dialog-console-org.tsx +135 -0
  13. package/src/component/dialog-debug.tsx +90 -0
  14. package/src/component/dialog-mcp.tsx +85 -0
  15. package/src/component/dialog-model.tsx +197 -0
  16. package/src/component/dialog-move-session.tsx +353 -0
  17. package/src/component/dialog-provider.tsx +469 -0
  18. package/src/component/dialog-retry-action.tsx +160 -0
  19. package/src/component/dialog-session-delete-failed.tsx +99 -0
  20. package/src/component/dialog-session-list.tsx +364 -0
  21. package/src/component/dialog-session-rename.tsx +31 -0
  22. package/src/component/dialog-skill.tsx +70 -0
  23. package/src/component/dialog-stash.tsx +87 -0
  24. package/src/component/dialog-status.tsx +168 -0
  25. package/src/component/dialog-tag.tsx +47 -0
  26. package/src/component/dialog-theme-list.tsx +50 -0
  27. package/src/component/dialog-variant.tsx +39 -0
  28. package/src/component/dialog-workspace-create.tsx +308 -0
  29. package/src/component/dialog-workspace-file-changes.tsx +144 -0
  30. package/src/component/dialog-workspace-list.tsx +112 -0
  31. package/src/component/dialog-workspace-unavailable.tsx +69 -0
  32. package/src/component/error-component.tsx +240 -0
  33. package/src/component/logo.tsx +61 -0
  34. package/src/component/plugin-route-missing.tsx +14 -0
  35. package/src/component/prompt/autocomplete.tsx +781 -0
  36. package/src/component/prompt/cwd.ts +0 -0
  37. package/src/component/prompt/frecency.tsx +1 -0
  38. package/src/component/prompt/history.tsx +1 -0
  39. package/src/component/prompt/index.tsx +1713 -0
  40. package/src/component/prompt/local-attachment.ts +48 -0
  41. package/src/component/prompt/move.tsx +205 -0
  42. package/src/component/prompt/stash.tsx +1 -0
  43. package/src/component/prompt/workspace.tsx +137 -0
  44. package/src/component/register-spinner.ts +6 -0
  45. package/src/component/spinner.tsx +26 -0
  46. package/src/component/startup-loading.tsx +63 -0
  47. package/src/component/todo-item.tsx +32 -0
  48. package/src/component/use-connected.tsx +12 -0
  49. package/src/component/workspace-label.tsx +19 -0
  50. package/src/config/index.tsx +129 -0
  51. package/src/config/keybind.ts +471 -0
  52. package/src/context/args.tsx +16 -0
  53. package/src/context/clipboard.tsx +18 -0
  54. package/src/context/data.tsx +569 -0
  55. package/src/context/directory.ts +17 -0
  56. package/src/context/editor.ts +408 -0
  57. package/src/context/epilogue.tsx +6 -0
  58. package/src/context/event.ts +36 -0
  59. package/src/context/exit.tsx +8 -0
  60. package/src/context/helper.tsx +26 -0
  61. package/src/context/kv.tsx +66 -0
  62. package/src/context/local.tsx +542 -0
  63. package/src/context/location.tsx +14 -0
  64. package/src/context/path-format.tsx +24 -0
  65. package/src/context/permission.tsx +26 -0
  66. package/src/context/project.tsx +115 -0
  67. package/src/context/prompt.tsx +18 -0
  68. package/src/context/route.tsx +60 -0
  69. package/src/context/runtime.tsx +62 -0
  70. package/src/context/sdk.tsx +151 -0
  71. package/src/context/sync.tsx +666 -0
  72. package/src/context/theme.tsx +332 -0
  73. package/src/context/thinking.ts +67 -0
  74. package/src/editor-zed.ts +286 -0
  75. package/src/editor.ts +101 -0
  76. package/src/feature-plugins/builtins.ts +36 -0
  77. package/src/feature-plugins/home/footer.tsx +100 -0
  78. package/src/feature-plugins/home/tips-view.tsx +287 -0
  79. package/src/feature-plugins/home/tips.tsx +59 -0
  80. package/src/feature-plugins/sidebar/context.tsx +65 -0
  81. package/src/feature-plugins/sidebar/files.tsx +70 -0
  82. package/src/feature-plugins/sidebar/footer.tsx +98 -0
  83. package/src/feature-plugins/sidebar/lsp.tsx +65 -0
  84. package/src/feature-plugins/sidebar/mcp.tsx +97 -0
  85. package/src/feature-plugins/sidebar/todo.tsx +49 -0
  86. package/src/feature-plugins/system/diff-viewer-file-tree-utils.ts +232 -0
  87. package/src/feature-plugins/system/diff-viewer-file-tree.tsx +162 -0
  88. package/src/feature-plugins/system/diff-viewer-ui.tsx +103 -0
  89. package/src/feature-plugins/system/diff-viewer.tsx +1077 -0
  90. package/src/feature-plugins/system/notifications.ts +94 -0
  91. package/src/feature-plugins/system/plugins.tsx +269 -0
  92. package/src/feature-plugins/system/which-key.tsx +608 -0
  93. package/src/index.tsx +1 -0
  94. package/src/keymap.tsx +290 -0
  95. package/src/logo.ts +11 -0
  96. package/src/parsers-config.ts +386 -0
  97. package/src/plugin/adapters.tsx +355 -0
  98. package/src/plugin/api.ts +52 -0
  99. package/src/plugin/command-shim.ts +109 -0
  100. package/src/plugin/runtime.tsx +81 -0
  101. package/src/plugin/slots.tsx +65 -0
  102. package/src/prompt/display.ts +48 -0
  103. package/src/prompt/frecency.tsx +80 -0
  104. package/src/prompt/history.tsx +111 -0
  105. package/src/prompt/part.ts +29 -0
  106. package/src/prompt/stash.tsx +89 -0
  107. package/src/prompt/traits.ts +29 -0
  108. package/src/routes/home/session-destination.tsx +41 -0
  109. package/src/routes/home.tsx +95 -0
  110. package/src/routes/session/dialog-fork-from-timeline.tsx +76 -0
  111. package/src/routes/session/dialog-message.tsx +109 -0
  112. package/src/routes/session/dialog-subagent.tsx +26 -0
  113. package/src/routes/session/dialog-timeline.tsx +47 -0
  114. package/src/routes/session/footer.tsx +91 -0
  115. package/src/routes/session/index.tsx +2710 -0
  116. package/src/routes/session/permission.tsx +718 -0
  117. package/src/routes/session/question.tsx +514 -0
  118. package/src/routes/session/sidebar.tsx +103 -0
  119. package/src/routes/session/subagent-footer.tsx +132 -0
  120. package/src/runtime.tsx +9 -0
  121. package/src/terminal-win32.ts +130 -0
  122. package/src/theme/assets/aura.json +69 -0
  123. package/src/theme/assets/ayu.json +80 -0
  124. package/src/theme/assets/carbonfox.json +248 -0
  125. package/src/theme/assets/catppuccin-frappe.json +230 -0
  126. package/src/theme/assets/catppuccin-macchiato.json +230 -0
  127. package/src/theme/assets/catppuccin.json +112 -0
  128. package/src/theme/assets/cobalt2.json +225 -0
  129. package/src/theme/assets/cursor.json +249 -0
  130. package/src/theme/assets/dracula.json +219 -0
  131. package/src/theme/assets/everforest.json +241 -0
  132. package/src/theme/assets/flexoki.json +237 -0
  133. package/src/theme/assets/github.json +233 -0
  134. package/src/theme/assets/gruvbox.json +242 -0
  135. package/src/theme/assets/kanagawa.json +77 -0
  136. package/src/theme/assets/lucent-orng.json +234 -0
  137. package/src/theme/assets/material.json +235 -0
  138. package/src/theme/assets/matrix.json +77 -0
  139. package/src/theme/assets/mercury.json +252 -0
  140. package/src/theme/assets/monokai.json +221 -0
  141. package/src/theme/assets/nightowl.json +221 -0
  142. package/src/theme/assets/nord.json +223 -0
  143. package/src/theme/assets/one-dark.json +84 -0
  144. package/src/theme/assets/opencode.json +245 -0
  145. package/src/theme/assets/orng.json +249 -0
  146. package/src/theme/assets/osaka-jade.json +93 -0
  147. package/src/theme/assets/palenight.json +222 -0
  148. package/src/theme/assets/rosepine.json +234 -0
  149. package/src/theme/assets/solarized.json +223 -0
  150. package/src/theme/assets/synthwave84.json +226 -0
  151. package/src/theme/assets/tokyonight.json +243 -0
  152. package/src/theme/assets/vercel.json +245 -0
  153. package/src/theme/assets/vesper.json +218 -0
  154. package/src/theme/assets/zenburn.json +223 -0
  155. package/src/theme/index.ts +1089 -0
  156. package/src/ui/border.ts +21 -0
  157. package/src/ui/dialog-alert.tsx +66 -0
  158. package/src/ui/dialog-confirm.tsx +108 -0
  159. package/src/ui/dialog-export-options.tsx +217 -0
  160. package/src/ui/dialog-help.tsx +40 -0
  161. package/src/ui/dialog-prompt.tsx +126 -0
  162. package/src/ui/dialog-select.tsx +790 -0
  163. package/src/ui/dialog.tsx +231 -0
  164. package/src/ui/link.tsx +34 -0
  165. package/src/ui/spinner.ts +368 -0
  166. package/src/ui/toast.tsx +102 -0
  167. package/src/util/collapse-tool-output.ts +19 -0
  168. package/src/util/error.ts +182 -0
  169. package/src/util/filetype.ts +130 -0
  170. package/src/util/format.ts +20 -0
  171. package/src/util/layout.ts +25 -0
  172. package/src/util/locale.ts +86 -0
  173. package/src/util/model.ts +28 -0
  174. package/src/util/path.ts +12 -0
  175. package/src/util/persistence.ts +33 -0
  176. package/src/util/presentation.ts +38 -0
  177. package/src/util/provider-origin.ts +7 -0
  178. package/src/util/record.ts +3 -0
  179. package/src/util/renderer.ts +7 -0
  180. package/src/util/revert-diff.ts +18 -0
  181. package/src/util/scroll.ts +27 -0
  182. package/src/util/selection.ts +79 -0
  183. package/src/util/session.ts +3 -0
  184. package/src/util/signal.ts +51 -0
  185. package/src/util/system.ts +20 -0
  186. package/src/util/tool-display.ts +13 -0
  187. package/src/util/transcript.ts +112 -0
  188. package/sst-env.d.ts +10 -0
  189. package/test/app-lifecycle.test.tsx +128 -0
  190. package/test/cli/cmd/tui/dialog-workspace-create.test.ts +28 -0
  191. package/test/cli/cmd/tui/model-options.test.ts +32 -0
  192. package/test/cli/cmd/tui/notifications.test.ts +267 -0
  193. package/test/cli/cmd/tui/provider-options.test.ts +41 -0
  194. package/test/cli/cmd/tui/sync-fixture.tsx +70 -0
  195. package/test/cli/cmd/tui/sync-live-hydration.test.tsx +262 -0
  196. package/test/cli/cmd/tui/sync-undefined-messages.test.tsx +43 -0
  197. package/test/cli/cmd/tui/sync.test.tsx +65 -0
  198. package/test/cli/tui/__snapshots__/inline-tool-wrap-snapshot.test.tsx.snap +92 -0
  199. package/test/cli/tui/data.test.tsx +486 -0
  200. package/test/cli/tui/dialog-prompt.test.tsx +147 -0
  201. package/test/cli/tui/diff-viewer-file-tree.test.tsx +200 -0
  202. package/test/cli/tui/diff-viewer.test.tsx +268 -0
  203. package/test/cli/tui/inline-tool-wrap-snapshot.test.tsx +351 -0
  204. package/test/cli/tui/prompt-submit-race.test.ts +98 -0
  205. package/test/cli/tui/thinking.test.ts +36 -0
  206. package/test/cli/tui/use-event.test.tsx +148 -0
  207. package/test/clipboard.test.ts +19 -0
  208. package/test/component/dialog-session-list.test.ts +46 -0
  209. package/test/config.test.tsx +127 -0
  210. package/test/context/local.test.ts +22 -0
  211. package/test/editor.test.ts +32 -0
  212. package/test/feature-plugins/diff-viewer-file-tree-utils.test.ts +323 -0
  213. package/test/fixture/fixture.ts +13 -0
  214. package/test/fixture/tui-environment.tsx +32 -0
  215. package/test/fixture/tui-plugin.ts +36 -0
  216. package/test/fixture/tui-runtime.ts +12 -0
  217. package/test/fixture/tui-sdk.ts +109 -0
  218. package/test/index.test.tsx +6 -0
  219. package/test/keymap.test.tsx +141 -0
  220. package/test/plugin/runtime.test.ts +50 -0
  221. package/test/plugin/slots.test.tsx +38 -0
  222. package/test/prompt/display.test.ts +33 -0
  223. package/test/prompt/history.test.ts +39 -0
  224. package/test/prompt/jsonl.test.ts +24 -0
  225. package/test/prompt/local-attachment.test.ts +43 -0
  226. package/test/prompt/part.test.ts +53 -0
  227. package/test/prompt/persistence.test.ts +23 -0
  228. package/test/prompt/traits.test.ts +25 -0
  229. package/test/runtime.test.tsx +37 -0
  230. package/test/theme.test.ts +81 -0
  231. package/test/util/error.test.ts +49 -0
  232. package/test/util/filetype.test.ts +16 -0
  233. package/test/util/format.test.ts +59 -0
  234. package/test/util/model.test.ts +9 -0
  235. package/test/util/presentation.test.ts +8 -0
  236. package/test/util/renderer.test.ts +30 -0
  237. package/test/util/revert-diff.test.ts +35 -0
  238. package/test/util/session.test.ts +10 -0
  239. package/test/util/tool-display.test.ts +40 -0
  240. package/test/util/transcript.test.ts +421 -0
  241. package/tsconfig.json +10 -0
@@ -0,0 +1,162 @@
1
+ /** @jsxImportSource @opentui/solid */
2
+ import type { ColorInput, RGBA, ScrollBoxRenderable } from "@opentui/core"
3
+ import { Locale } from "../../util/locale"
4
+ import { tint } from "../../context/theme"
5
+ import { createEffect, createMemo, For, Match, Switch } from "solid-js"
6
+ import { buildFileTree, flattenFileTree, type FileTreeItem, type FileTreeRow } from "./diff-viewer-file-tree-utils"
7
+ import { Panel } from "./diff-viewer-ui"
8
+
9
+ const FILE_TREE_STATUS_WIDTH = 2
10
+
11
+ export type DiffViewerFileTreeTheme = {
12
+ readonly background: RGBA
13
+ readonly backgroundPanel: ColorInput
14
+ readonly backgroundElement: ColorInput
15
+ readonly primary: ColorInput
16
+ readonly secondary: ColorInput
17
+ readonly selectedListItemText: ColorInput
18
+ readonly text: RGBA
19
+ readonly textMuted: RGBA
20
+ readonly error: ColorInput
21
+ }
22
+
23
+ export type DiffViewerFileTreeProps = {
24
+ readonly width: number
25
+ readonly files: readonly FileTreeItem[]
26
+ readonly loading: boolean
27
+ readonly error: unknown
28
+ readonly theme: DiffViewerFileTreeTheme
29
+ readonly focused?: boolean
30
+ readonly highlightedNode?: number
31
+ readonly selectedFileIndex?: number
32
+ readonly reviewedFileNames?: ReadonlySet<string>
33
+ readonly expandedNodes?: ReadonlySet<number>
34
+ readonly onRowClick?: (row: FileTreeRow) => void
35
+ }
36
+
37
+ export function DiffViewerFileTree(props: DiffViewerFileTreeProps) {
38
+ const tree = createMemo(() => buildFileTree(props.files))
39
+ const rows = createMemo(() => flattenFileTree(tree(), props.expandedNodes))
40
+ let scroll: ScrollBoxRenderable | undefined
41
+
42
+ createEffect(() => {
43
+ const node = props.highlightedNode
44
+ if (node === undefined) return
45
+ const selectedIndex = rows().findIndex((row) => row.id === node)
46
+ if (selectedIndex === -1) return
47
+ const scrollSelectedIntoView = () => scrollFileTreeRowIntoView(scroll, selectedIndex)
48
+ scrollSelectedIntoView()
49
+ requestAnimationFrame(scrollSelectedIntoView)
50
+ })
51
+
52
+ const fadedColor = () => tint(props.theme.text, props.theme.background, 0.75)
53
+
54
+ return (
55
+ <Panel border="both" width={props.width}>
56
+ <scrollbox
57
+ ref={(element: ScrollBoxRenderable) => (scroll = element)}
58
+ verticalScrollbarOptions={{ visible: false }}
59
+ horizontalScrollbarOptions={{ visible: false }}
60
+ >
61
+ <Switch>
62
+ <Match when={props.loading || props.error}>
63
+ <text />
64
+ </Match>
65
+ <Match when={props.files.length === 0}>
66
+ <text fg={props.theme.text}>No files</text>
67
+ </Match>
68
+ <Match when={props.files.length > 0}>
69
+ <For each={rows()}>
70
+ {(row, index) => {
71
+ const highlighted = () => props.focused && props.highlightedNode === row.id
72
+ const selected = () => row.fileIndex !== undefined && props.selectedFileIndex === row.fileIndex
73
+ const reviewed = () => {
74
+ const file = row.fileIndex === undefined ? undefined : props.files[row.fileIndex]?.file
75
+ return file !== undefined && (props.reviewedFileNames?.has(file) ?? false)
76
+ }
77
+ const prefix = () => fileTreeRowPrefix(rows(), index(), row, props.expandedNodes)
78
+ const status = () => fileTreeRowStatus(row, props.files, reviewed())
79
+ const name = () =>
80
+ Locale.truncate(row.name, Math.max(1, props.width - FILE_TREE_STATUS_WIDTH - prefix().length))
81
+ return (
82
+ <box
83
+ flexDirection="row"
84
+ width="100%"
85
+ backgroundColor={highlighted() ? props.theme.primary : undefined}
86
+ onMouseUp={() => props.onRowClick?.(row)}
87
+ >
88
+ <text fg={highlighted() ? props.theme.background : fadedColor()} wrapMode="none" flexShrink={0}>
89
+ {prefix()}
90
+ </text>
91
+ <box flexGrow={1} minWidth={0}>
92
+ <text
93
+ fg={
94
+ highlighted()
95
+ ? props.theme.background
96
+ : selected()
97
+ ? props.theme.primary
98
+ : reviewed() || row.kind === "directory"
99
+ ? props.theme.textMuted
100
+ : props.theme.text
101
+ }
102
+ wrapMode="none"
103
+ >
104
+ {name()}
105
+ </text>
106
+ </box>
107
+ <text
108
+ fg={highlighted() ? props.theme.background : props.theme.textMuted}
109
+ wrapMode="none"
110
+ flexShrink={0}
111
+ >
112
+ {status()}
113
+ </text>
114
+ </box>
115
+ )
116
+ }}
117
+ </For>
118
+ </Match>
119
+ </Switch>
120
+ </scrollbox>
121
+ </Panel>
122
+ )
123
+ }
124
+
125
+ function scrollFileTreeRowIntoView(scroll: ScrollBoxRenderable | undefined, index: number) {
126
+ if (!scroll) return
127
+ if (index < scroll.scrollTop) {
128
+ scroll.scrollTo(index)
129
+ return
130
+ }
131
+ if (index >= scroll.scrollTop + scroll.viewport.height) {
132
+ scroll.scrollTo(index - scroll.viewport.height + 1)
133
+ }
134
+ }
135
+
136
+ function fileTreeRowPrefix(
137
+ rows: readonly FileTreeRow[],
138
+ index: number,
139
+ row: FileTreeRow,
140
+ expandedNodes: ReadonlySet<number> | undefined,
141
+ ) {
142
+ const indentation = Array.from({ length: row.depth }, (_, depth) => {
143
+ if (depth === 0 && !hasLaterSibling(rows, 0, 0)) return " "
144
+ return hasLaterSibling(rows, index, depth) ? "│ " : " "
145
+ }).join("")
146
+ const topRoot = index === 0 && row.depth === 0
147
+ const branch = topRoot ? " " : hasLaterSibling(rows, index, row.depth) ? "├─ " : "└─ "
148
+ const marker = row.kind === "directory" ? (expandedNodes && !expandedNodes.has(row.id) ? "▸ " : "▾ ") : ""
149
+
150
+ return `${indentation}${branch}${marker}`
151
+ }
152
+
153
+ function hasLaterSibling(rows: readonly FileTreeRow[], index: number, depth: number) {
154
+ return rows.slice(index + 1).find((row) => row.depth <= depth)?.depth === depth
155
+ }
156
+
157
+ function fileTreeRowStatus(row: FileTreeRow, files: readonly FileTreeItem[], reviewed: boolean) {
158
+ if (row.fileIndex === undefined) return ""
159
+ const status = files[row.fileIndex]?.status
160
+ const marker = status === "modified" ? "M" : status === "added" ? "A" : status === "deleted" ? "D" : "?"
161
+ return `${reviewed ? "✓" : " "}${marker}`.padStart(FILE_TREE_STATUS_WIDTH)
162
+ }
@@ -0,0 +1,103 @@
1
+ import type { BorderSides, ColorInput } from "@opentui/core"
2
+ import type { JSX } from "@opentui/solid"
3
+ import { useTheme } from "../../context/theme"
4
+ import { createContext, Show, splitProps, useContext } from "solid-js"
5
+
6
+ export type Axis = "x" | "y"
7
+ export type SeparatorEdge = "edge" | "edge-in" | "edge-out"
8
+ export type PanelBorder = "start" | "end" | "both" | "none"
9
+
10
+ const PanelGroupContext = createContext<{ axis: Axis }>()
11
+
12
+ function crossAxis(axis: Axis) {
13
+ return axis === "x" ? "y" : "x"
14
+ }
15
+
16
+ function usePanelGroup() {
17
+ return useContext(PanelGroupContext)
18
+ }
19
+
20
+ export function PanelGroup(props: JSX.IntrinsicElements["box"] & { axis: Axis }) {
21
+ const [local, boxProps] = splitProps(props, ["axis", "children"])
22
+ return (
23
+ <PanelGroupContext.Provider value={{ axis: local.axis }}>
24
+ <box minWidth={0} minHeight={0} padding={0} flexDirection={local.axis === "x" ? "row" : "column"} {...boxProps}>
25
+ {local.children}
26
+ </box>
27
+ </PanelGroupContext.Provider>
28
+ )
29
+ }
30
+
31
+ export function Panel(props: Omit<JSX.IntrinsicElements["box"], "border"> & { border?: PanelBorder }) {
32
+ const group = usePanelGroup()
33
+ const { theme } = useTheme()
34
+ const [local, boxProps] = splitProps(props, ["border"])
35
+ const border = local.border ?? "start"
36
+ const borderProps =
37
+ border === "none"
38
+ ? {}
39
+ : {
40
+ border: panelBorderSides(group?.axis ?? "y", border),
41
+ borderColor: theme.border,
42
+ }
43
+
44
+ return (
45
+ <box
46
+ minWidth={0}
47
+ minHeight={0}
48
+ flexDirection={crossAxis(group?.axis || "y") === "x" ? "row" : "column"}
49
+ {...borderProps}
50
+ {...boxProps}
51
+ />
52
+ )
53
+ }
54
+
55
+ function panelBorderSides(axis: Axis, border: Exclude<PanelBorder, "none">): BorderSides[] {
56
+ if (axis === "x") return border === "both" ? ["top", "bottom"] : [border === "start" ? "top" : "bottom"]
57
+ return border === "both" ? ["left", "right"] : [border === "start" ? "left" : "right"]
58
+ }
59
+
60
+ export function Separator(props: { axis?: Axis; color?: ColorInput; start?: SeparatorEdge; end?: SeparatorEdge }) {
61
+ const group = usePanelGroup()
62
+ const { theme } = useTheme()
63
+ const color = () => props.color ?? theme.border
64
+ const axis = () => props.axis ?? crossAxis(group?.axis ?? "y")
65
+ if (axis() === "y") {
66
+ return (
67
+ <Show
68
+ when={props.start || props.end}
69
+ fallback={<box width={1} flexShrink={0} border={["left"]} borderColor={color()} />}
70
+ >
71
+ <box width={1} flexShrink={0} flexDirection="column">
72
+ <Show when={props.start}>{(edge) => <text fg={color()}>{verticalEdge(edge(), "start")}</text>}</Show>
73
+ <box flexGrow={1} border={["left"]} borderColor={color()} />
74
+ <Show when={props.end}>{(edge) => <text fg={color()}>{verticalEdge(edge(), "end")}</text>}</Show>
75
+ </box>
76
+ </Show>
77
+ )
78
+ }
79
+ return (
80
+ <Show
81
+ when={props.start || props.end}
82
+ fallback={<box height={1} flexShrink={0} border={["top"]} borderColor={color()} />}
83
+ >
84
+ <box height={1} flexShrink={0} flexDirection="row">
85
+ <Show when={props.start}>{(edge) => <text fg={color()}>{horizontalEdge(edge(), "start")}</text>}</Show>
86
+ <box flexGrow={1} border={["top"]} borderColor={color()} />
87
+ <Show when={props.end}>{(edge) => <text fg={color()}>{horizontalEdge(edge(), "end")}</text>}</Show>
88
+ </box>
89
+ </Show>
90
+ )
91
+ }
92
+
93
+ function horizontalEdge(edge: SeparatorEdge, side: "start" | "end") {
94
+ if (edge === "edge") return side === "start" ? "├" : "┤"
95
+ if (edge === "edge-in") return "┴"
96
+ return "┬"
97
+ }
98
+
99
+ function verticalEdge(edge: SeparatorEdge, side: "start" | "end") {
100
+ if (edge === "edge") return side === "start" ? "┬" : "┴"
101
+ if (edge === "edge-in") return "┤"
102
+ return "├"
103
+ }