header.tsx 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. import { type Accessor, createMemo, Match, Show, Switch } from "solid-js"
  2. import { useRouteData } from "@tui/context/route"
  3. import { useSync } from "@tui/context/sync"
  4. import { useTheme } from "@tui/context/theme"
  5. import { EmptyBorder } from "@tui/component/border"
  6. import type { Session } from "@opencode-ai/sdk/v2"
  7. import { useKeybind } from "../../context/keybind"
  8. import { useTerminalDimensions } from "@opentui/solid"
  9. const Title = (props: { session: Accessor<Session>; truncate?: boolean }) => {
  10. const { theme } = useTheme()
  11. return (
  12. <text fg={theme.text} wrapMode={props.truncate ? "none" : undefined} flexShrink={props.truncate ? 1 : 0}>
  13. <span style={{ bold: true }}>#</span> <span style={{ bold: true }}>{props.session().title}</span>
  14. </text>
  15. )
  16. }
  17. export function Header() {
  18. const route = useRouteData("session")
  19. const sync = useSync()
  20. const session = createMemo(() => sync.session.get(route.sessionID)!)
  21. const shareEnabled = createMemo(() => sync.data.config.share !== "disabled")
  22. const showShare = createMemo(() => shareEnabled() && !session()?.share?.url)
  23. const { theme } = useTheme()
  24. const keybind = useKeybind()
  25. const dimensions = useTerminalDimensions()
  26. const tall = createMemo(() => dimensions().height > 40)
  27. return (
  28. <box flexShrink={0}>
  29. <box
  30. height={1}
  31. border={["left"]}
  32. borderColor={theme.border}
  33. customBorderChars={{
  34. ...EmptyBorder,
  35. vertical: theme.backgroundPanel.a !== 0 ? "╻" : " ",
  36. }}
  37. >
  38. <box
  39. height={1}
  40. border={["top"]}
  41. borderColor={theme.backgroundPanel}
  42. customBorderChars={
  43. theme.backgroundPanel.a !== 0
  44. ? {
  45. ...EmptyBorder,
  46. horizontal: "▄",
  47. }
  48. : {
  49. ...EmptyBorder,
  50. horizontal: " ",
  51. }
  52. }
  53. />
  54. </box>
  55. <box
  56. border={["left"]}
  57. borderColor={theme.border}
  58. customBorderChars={{
  59. ...EmptyBorder,
  60. vertical: "┃",
  61. bottomLeft: "╹",
  62. }}
  63. >
  64. <box
  65. paddingTop={tall() ? 1 : 0}
  66. paddingBottom={tall() ? 1 : 0}
  67. paddingLeft={2}
  68. paddingRight={1}
  69. flexShrink={0}
  70. flexGrow={1}
  71. backgroundColor={theme.backgroundPanel}
  72. >
  73. <Switch>
  74. <Match when={session()?.parentID}>
  75. <box flexDirection="row" gap={2}>
  76. <text fg={theme.text}>
  77. <b>Subagent session</b>
  78. </text>
  79. <text fg={theme.text}>
  80. Parent <span style={{ fg: theme.textMuted }}>{keybind.print("session_parent")}</span>
  81. </text>
  82. <text fg={theme.text}>
  83. Prev <span style={{ fg: theme.textMuted }}>{keybind.print("session_child_cycle_reverse")}</span>
  84. </text>
  85. <text fg={theme.text}>
  86. Next <span style={{ fg: theme.textMuted }}>{keybind.print("session_child_cycle")}</span>
  87. </text>
  88. <box flexGrow={1} flexShrink={1} />
  89. <Show when={showShare()}>
  90. <text fg={theme.textMuted} wrapMode="none" flexShrink={0}>
  91. /share{" "}
  92. </text>
  93. </Show>
  94. </box>
  95. </Match>
  96. <Match when={true}>
  97. <box flexDirection="row" justifyContent="space-between" gap={1}>
  98. <Title session={session} truncate={!tall()} />
  99. <Show when={showShare()}>
  100. <text fg={theme.textMuted} wrapMode="none" flexShrink={0}>
  101. /share{" "}
  102. </text>
  103. </Show>
  104. </box>
  105. </Match>
  106. </Switch>
  107. </box>
  108. </box>
  109. <box
  110. height={1}
  111. border={["left"]}
  112. borderColor={theme.border}
  113. customBorderChars={{
  114. ...EmptyBorder,
  115. vertical: theme.backgroundPanel.a !== 0 ? "╹" : " ",
  116. }}
  117. >
  118. <box
  119. height={1}
  120. border={["bottom"]}
  121. borderColor={theme.backgroundPanel}
  122. customBorderChars={
  123. theme.backgroundPanel.a !== 0
  124. ? {
  125. ...EmptyBorder,
  126. horizontal: "▀",
  127. }
  128. : {
  129. ...EmptyBorder,
  130. horizontal: " ",
  131. }
  132. }
  133. />
  134. </box>
  135. </box>
  136. )
  137. }