@neurocode-ai/session-ui 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 (114) hide show
  1. package/package.json +71 -0
  2. package/src/components/apply-patch-file.test.ts +43 -0
  3. package/src/components/apply-patch-file.ts +78 -0
  4. package/src/components/basic-tool.css +358 -0
  5. package/src/components/basic-tool.stories.tsx +133 -0
  6. package/src/components/basic-tool.tsx +343 -0
  7. package/src/components/dock-prompt.stories.tsx +62 -0
  8. package/src/components/dock-prompt.tsx +23 -0
  9. package/src/components/file-media.tsx +291 -0
  10. package/src/components/file-search.tsx +72 -0
  11. package/src/components/file-ssr.tsx +197 -0
  12. package/src/components/file.css +46 -0
  13. package/src/components/file.tsx +1202 -0
  14. package/src/components/line-comment-annotations.tsx +637 -0
  15. package/src/components/line-comment-styles.ts +292 -0
  16. package/src/components/line-comment.stories.tsx +115 -0
  17. package/src/components/line-comment.tsx +439 -0
  18. package/src/components/markdown-cache.tsx +78 -0
  19. package/src/components/markdown-code-state.test.ts +32 -0
  20. package/src/components/markdown-code-state.ts +22 -0
  21. package/src/components/markdown-inline-code-kind.test.ts +46 -0
  22. package/src/components/markdown-inline-code-kind.ts +1915 -0
  23. package/src/components/markdown-preload.test.ts +18 -0
  24. package/src/components/markdown-shiki.worker.ts +104 -0
  25. package/src/components/markdown-stream.test.ts +194 -0
  26. package/src/components/markdown-stream.ts +110 -0
  27. package/src/components/markdown-worker-protocol.test.ts +81 -0
  28. package/src/components/markdown-worker-protocol.ts +48 -0
  29. package/src/components/markdown-worker-queue.test.ts +49 -0
  30. package/src/components/markdown-worker-queue.ts +64 -0
  31. package/src/components/markdown-worker-transport.test.ts +56 -0
  32. package/src/components/markdown-worker-transport.ts +41 -0
  33. package/src/components/markdown-worker.ts +122 -0
  34. package/src/components/markdown.css +377 -0
  35. package/src/components/markdown.stories.tsx +53 -0
  36. package/src/components/markdown.tsx +678 -0
  37. package/src/components/message-file.test.ts +63 -0
  38. package/src/components/message-file.ts +34 -0
  39. package/src/components/message-nav.css +127 -0
  40. package/src/components/message-nav.stories.tsx +7 -0
  41. package/src/components/message-nav.tsx +102 -0
  42. package/src/components/message-part-text.ts +3 -0
  43. package/src/components/message-part.css +1569 -0
  44. package/src/components/message-part.stories.tsx +7 -0
  45. package/src/components/message-part.test.ts +28 -0
  46. package/src/components/message-part.tsx +2662 -0
  47. package/src/components/session-diff.test.ts +135 -0
  48. package/src/components/session-diff.ts +145 -0
  49. package/src/components/session-retry.tsx +74 -0
  50. package/src/components/session-review.css +247 -0
  51. package/src/components/session-review.stories.tsx +7 -0
  52. package/src/components/session-review.tsx +657 -0
  53. package/src/components/session-turn.css +241 -0
  54. package/src/components/session-turn.stories.tsx +7 -0
  55. package/src/components/session-turn.tsx +543 -0
  56. package/src/components/shell-submessage-motion.stories.tsx +346 -0
  57. package/src/components/shell-submessage.css +23 -0
  58. package/src/components/timeline-playground.stories.tsx +2090 -0
  59. package/src/components/tool-count-label.css +57 -0
  60. package/src/components/tool-count-label.tsx +58 -0
  61. package/src/components/tool-count-summary.css +102 -0
  62. package/src/components/tool-count-summary.stories.tsx +238 -0
  63. package/src/components/tool-count-summary.tsx +52 -0
  64. package/src/components/tool-error-card.css +91 -0
  65. package/src/components/tool-error-card.stories.tsx +92 -0
  66. package/src/components/tool-error-card.tsx +157 -0
  67. package/src/components/tool-status-title.css +89 -0
  68. package/src/components/tool-status-title.tsx +133 -0
  69. package/src/context/data.tsx +64 -0
  70. package/src/context/index.ts +1 -0
  71. package/src/pierre/comment-hover.ts +83 -0
  72. package/src/pierre/commented-lines.ts +91 -0
  73. package/src/pierre/diff-selection.ts +71 -0
  74. package/src/pierre/file-find.ts +485 -0
  75. package/src/pierre/file-runtime.ts +114 -0
  76. package/src/pierre/file-selection.ts +85 -0
  77. package/src/pierre/index.ts +186 -0
  78. package/src/pierre/media.ts +110 -0
  79. package/src/pierre/selection-bridge.ts +132 -0
  80. package/src/pierre/virtualizer.ts +100 -0
  81. package/src/pierre/worker.ts +52 -0
  82. package/src/styles/index.css +14 -0
  83. package/src/v2/components/attachment-card-v2.css +70 -0
  84. package/src/v2/components/attachment-card-v2.tsx +33 -0
  85. package/src/v2/components/basic-tool-v2.css +163 -0
  86. package/src/v2/components/basic-tool-v2.stories.tsx +137 -0
  87. package/src/v2/components/basic-tool-v2.tsx +139 -0
  88. package/src/v2/components/comment-card-v2.tsx +64 -0
  89. package/src/v2/components/line-comment-annotations-v2.tsx +220 -0
  90. package/src/v2/components/prompt-input/attachments.ts +266 -0
  91. package/src/v2/components/prompt-input/index.tsx +706 -0
  92. package/src/v2/components/prompt-input/interaction.ts +482 -0
  93. package/src/v2/components/prompt-input/machine.test.ts +164 -0
  94. package/src/v2/components/prompt-input/machine.ts +261 -0
  95. package/src/v2/components/prompt-input/prompt-input.stories.tsx +221 -0
  96. package/src/v2/components/prompt-input/store.test.ts +116 -0
  97. package/src/v2/components/prompt-input/store.ts +152 -0
  98. package/src/v2/components/prompt-input/types.ts +106 -0
  99. package/src/v2/components/session-file-panel-v2.tsx +43 -0
  100. package/src/v2/components/session-progress-indicator-v2.css +875 -0
  101. package/src/v2/components/session-progress-indicator-v2.stories.tsx +66 -0
  102. package/src/v2/components/session-progress-indicator-v2.tsx +32 -0
  103. package/src/v2/components/session-review-empty-changes-v2.tsx +17 -0
  104. package/src/v2/components/session-review-empty-no-git-v2.tsx +28 -0
  105. package/src/v2/components/session-review-file-preview-v2-virtualize.test.ts +13 -0
  106. package/src/v2/components/session-review-file-preview-v2-virtualize.ts +5 -0
  107. package/src/v2/components/session-review-file-preview-v2.tsx +290 -0
  108. package/src/v2/components/session-review-v2.css +458 -0
  109. package/src/v2/components/session-review-v2.tsx +340 -0
  110. package/src/v2/components/tool-error-card-v2.css +200 -0
  111. package/src/v2/components/tool-error-card-v2.stories.tsx +91 -0
  112. package/src/v2/components/tool-error-card-v2.tsx +166 -0
  113. package/sst-env.d.ts +10 -0
  114. package/tsconfig.json +19 -0
@@ -0,0 +1,163 @@
1
+ * {
2
+ -webkit-font-smoothing: antialiased;
3
+ -moz-osx-font-smoothing: grayscale;
4
+ text-rendering: geometricPrecision;
5
+ }
6
+
7
+ [data-component="basic-tool-v2"] {
8
+ --bt-title: var(--v2-text-text-base);
9
+ --bt-sep: var(--v2-text-text-muted);
10
+ --bt-subtitle: var(--v2-text-text-muted);
11
+ --bt-args: var(--v2-text-text-muted);
12
+ --bt-chevron: var(--v2-text-text-faint);
13
+ --bt-content: var(--v2-text-text-muted);
14
+
15
+ box-sizing: border-box;
16
+ display: flex;
17
+ flex-direction: column;
18
+ justify-content: center;
19
+ align-items: flex-start;
20
+ padding: 4px 0;
21
+ gap: 8px;
22
+ min-width: 0;
23
+ width: 100%;
24
+ font-variant-numeric: tabular-nums;
25
+
26
+ [data-slot="basic-tool-v2-trigger"] {
27
+ box-sizing: border-box;
28
+ display: flex;
29
+ flex-direction: row;
30
+ align-items: center;
31
+ gap: 6px;
32
+ width: 100%;
33
+ min-width: 0;
34
+ min-height: 20px;
35
+ padding: 0;
36
+ margin: 0;
37
+ text-align: left;
38
+ color: inherit;
39
+ outline: none;
40
+ }
41
+
42
+ [data-slot="basic-tool-v2-trigger"]:focus-visible {
43
+ outline: 2px solid var(--v2-border-border-focus);
44
+ outline-offset: 2px;
45
+ border-radius: 2px;
46
+ }
47
+
48
+ [data-slot="basic-tool-v2-labels"] {
49
+ display: inline-flex;
50
+ flex-direction: row;
51
+ align-items: center;
52
+ gap: 6px;
53
+ min-width: 0;
54
+ max-width: 100%;
55
+ }
56
+
57
+ [data-slot="basic-tool-v2-title"] {
58
+ display: flex;
59
+ align-items: center;
60
+ flex-shrink: 0;
61
+ font-size: 13px;
62
+ font-weight: 440;
63
+ line-height: 1;
64
+ letter-spacing: -0.04px;
65
+ color: var(--bt-title);
66
+ font-variation-settings: "slnt" 0;
67
+ user-select: none;
68
+ }
69
+
70
+ [data-slot="basic-tool-v2-sep"] {
71
+ display: flex;
72
+ align-items: center;
73
+ justify-content: center;
74
+ flex-shrink: 0;
75
+ font-size: 11px;
76
+ font-weight: 530;
77
+ line-height: 12px;
78
+ letter-spacing: 0.05px;
79
+ color: var(--bt-sep);
80
+ font-variation-settings: "slnt" 0;
81
+ user-select: none;
82
+ }
83
+
84
+ [data-slot="basic-tool-v2-subtitle"] {
85
+ display: flex;
86
+ align-items: center;
87
+ min-width: 0;
88
+ flex: 0 1 auto;
89
+ overflow: hidden;
90
+ text-overflow: ellipsis;
91
+ white-space: nowrap;
92
+ font-size: 13px;
93
+ font-weight: 440;
94
+ line-height: 1;
95
+ letter-spacing: -0.04px;
96
+ color: var(--bt-subtitle);
97
+ font-variation-settings: "slnt" 0;
98
+ user-select: none;
99
+ }
100
+
101
+ [data-slot="basic-tool-v2-arg"] {
102
+ display: flex;
103
+ align-items: center;
104
+ flex-shrink: 0;
105
+ font-size: 13px;
106
+ font-weight: 440;
107
+ line-height: 1;
108
+ letter-spacing: -0.04px;
109
+ color: var(--bt-args);
110
+ font-variation-settings: "slnt" 0;
111
+ user-select: none;
112
+ }
113
+
114
+ [data-slot="basic-tool-v2-diff"] {
115
+ flex-shrink: 0;
116
+ }
117
+
118
+ [data-slot="basic-tool-v2-diff"] [data-component="diff-changes"] {
119
+ gap: 4px;
120
+ }
121
+
122
+ [data-slot="basic-tool-v2-chevron-wrap"] {
123
+ display: flex;
124
+ flex-direction: row;
125
+ justify-content: center;
126
+ align-items: center;
127
+ flex-shrink: 0;
128
+ width: 20px;
129
+ height: 20px;
130
+ padding: 2px;
131
+ border-radius: 4px;
132
+ color: var(--bt-chevron);
133
+ user-select: none;
134
+ }
135
+
136
+ [data-slot="basic-tool-v2-chevron"] {
137
+ display: block;
138
+ flex-shrink: 0;
139
+ width: 16px;
140
+ height: 16px;
141
+ transition: transform 0.15s ease-out;
142
+ }
143
+
144
+ &[data-expanded] [data-slot="basic-tool-v2-chevron"] {
145
+ transform: rotate(90deg);
146
+ }
147
+
148
+ [data-slot="basic-tool-v2-content"] {
149
+ box-sizing: border-box;
150
+ min-width: 0;
151
+ width: 100%;
152
+ overflow: hidden;
153
+ }
154
+
155
+ [data-slot="basic-tool-v2-content-inner"] {
156
+ font-size: 15px;
157
+ font-weight: 440;
158
+ line-height: 24px;
159
+ letter-spacing: 0;
160
+ color: var(--bt-content);
161
+ font-variation-settings: "slnt" 0;
162
+ }
163
+ }
@@ -0,0 +1,137 @@
1
+ import { createSignal } from "solid-js"
2
+ import { BasicToolV2 } from "./basic-tool-v2"
3
+
4
+ const docs = `### Overview
5
+ Compact collapsible tool row showing title, subtitle, args, and diff changes, with an expand/collapse chevron.
6
+
7
+ ### API
8
+ - \`BasicToolV2\` wraps Kobalte \`Collapsible\`. Pass \`open\`, \`defaultOpen\`, and \`onOpenChange\` for controlled/uncontrolled disclosure.
9
+ - \`trigger\` accepts either a \`BasicToolV2TriggerTitle\` object (title, subtitle, args, changes) or arbitrary JSX.
10
+ - When \`status\` is \`"pending"\` or \`"running"\`, subtitle/args/chevron hide and the title shows a shimmer animation.
11
+ - Pass \`children\` for expandable detail content.
12
+
13
+ ### Theming
14
+ - Uses \`data-component="basic-tool-v2"\` and slot attributes; colors via \`--bt-*\` CSS variables.
15
+ `
16
+
17
+ export default {
18
+ title: "UI V2/BasicTool",
19
+ id: "components-basic-tool-v2",
20
+ component: BasicToolV2,
21
+ tags: ["autodocs"],
22
+ parameters: {
23
+ frameBackground: "#fff",
24
+ layout: "padded",
25
+ docs: {
26
+ description: {
27
+ component: docs,
28
+ },
29
+ },
30
+ },
31
+ }
32
+
33
+ export const Default = {
34
+ render: () => (
35
+ <BasicToolV2
36
+ trigger={{
37
+ title: "Read",
38
+ subtitle: "src/index.ts",
39
+ args: ["lines=1-50"],
40
+ changes: { additions: 12, deletions: 3 },
41
+ }}
42
+ defaultOpen={false}
43
+ >
44
+ File content appears here.
45
+ </BasicToolV2>
46
+ ),
47
+ }
48
+
49
+ export const Expanded = {
50
+ render: () => (
51
+ <BasicToolV2
52
+ trigger={{
53
+ title: "Read",
54
+ subtitle: "src/index.ts",
55
+ args: ["lines=1-50"],
56
+ changes: { additions: 12, deletions: 3 },
57
+ }}
58
+ defaultOpen={true}
59
+ >
60
+ File content appears here.
61
+ </BasicToolV2>
62
+ ),
63
+ }
64
+
65
+ export const Pending = {
66
+ render: () => (
67
+ <BasicToolV2
68
+ trigger={{
69
+ title: "Read",
70
+ subtitle: "src/index.ts",
71
+ args: ["lines=1-50"],
72
+ changes: { additions: 12, deletions: 3 },
73
+ }}
74
+ status="pending"
75
+ />
76
+ ),
77
+ }
78
+
79
+ export const NoChildren = {
80
+ render: () => (
81
+ <BasicToolV2
82
+ trigger={{
83
+ title: "Grep",
84
+ subtitle: "pattern=TODO",
85
+ args: ["recursive=true"],
86
+ }}
87
+ />
88
+ ),
89
+ }
90
+
91
+ export const CustomTrigger = {
92
+ render: () => (
93
+ <BasicToolV2
94
+ trigger={
95
+ <span style={{ color: "#161616", "font-size": "13px", "font-weight": "440" }}>Custom trigger content</span>
96
+ }
97
+ >
98
+ Expandable detail for custom trigger.
99
+ </BasicToolV2>
100
+ ),
101
+ }
102
+
103
+ export const Controlled = {
104
+ render: () => {
105
+ const [open, setOpen] = createSignal(false)
106
+ return (
107
+ <div style={{ display: "flex", "flex-direction": "column", gap: "16px", "max-width": "420px" }}>
108
+ <button
109
+ type="button"
110
+ onClick={() => setOpen((o) => !o)}
111
+ style={{
112
+ padding: "4px 10px",
113
+ "font-size": "12px",
114
+ "border-radius": "6px",
115
+ border: "1px solid rgba(0,0,0,0.15)",
116
+ background: "#fff",
117
+ color: "#161616",
118
+ cursor: "pointer",
119
+ }}
120
+ >
121
+ Toggle from outside: {open() ? "Open" : "Closed"}
122
+ </button>
123
+ <BasicToolV2
124
+ trigger={{
125
+ title: "Write",
126
+ subtitle: "src/utils.ts",
127
+ changes: { additions: 8, deletions: 2 },
128
+ }}
129
+ open={open()}
130
+ onOpenChange={setOpen}
131
+ >
132
+ Controlled content.
133
+ </BasicToolV2>
134
+ </div>
135
+ )
136
+ },
137
+ }
@@ -0,0 +1,139 @@
1
+ import { Collapsible } from "@kobalte/core/collapsible"
2
+ import { type ComponentProps, type JSX, For, Show, createMemo, splitProps } from "solid-js"
3
+ import { DiffChanges } from "@neurocode-ai/ui/v2/diff-changes-v2"
4
+ import { TextShimmerV2 } from "@neurocode-ai/ui/v2/text-shimmer-v2"
5
+ import "./basic-tool-v2.css"
6
+
7
+ function ChevronIcon() {
8
+ return (
9
+ <svg
10
+ data-slot="basic-tool-v2-chevron"
11
+ width="16"
12
+ height="16"
13
+ viewBox="0 0 16 16"
14
+ fill="none"
15
+ xmlns="http://www.w3.org/2000/svg"
16
+ aria-hidden="true"
17
+ >
18
+ <path
19
+ d="M6.75194 10.6243C6.41861 10.8187 6 10.5783 6 10.1924V5.80837C6 5.42247 6.41861 5.18204 6.75194 5.37648L10.5096 7.56846C10.8404 7.7614 10.8404 8.2393 10.5096 8.43224L6.75194 10.6243Z"
20
+ fill="currentColor"
21
+ />
22
+ </svg>
23
+ )
24
+ }
25
+
26
+ export interface BasicToolV2TriggerTitle {
27
+ title: string
28
+ subtitle?: string
29
+ args?: string[]
30
+ changes?: { additions: number; deletions: number } | { additions: number; deletions: number }[]
31
+ action?: JSX.Element
32
+ }
33
+
34
+ const isTriggerTitle = (val: unknown): val is BasicToolV2TriggerTitle =>
35
+ typeof val === "object" && val !== null && "title" in val && (typeof Node === "undefined" || !(val instanceof Node))
36
+
37
+ export interface BasicToolV2Props extends Omit<ComponentProps<"div">, "children" | "title"> {
38
+ trigger: BasicToolV2TriggerTitle | JSX.Element
39
+ children?: JSX.Element
40
+ status?: string
41
+ open?: boolean
42
+ defaultOpen?: boolean
43
+ onOpenChange?: (open: boolean) => void
44
+ onSubtitleClick?: () => void
45
+ }
46
+
47
+ export function BasicToolV2(props: BasicToolV2Props) {
48
+ const [local, rest] = splitProps(props, [
49
+ "trigger",
50
+ "children",
51
+ "status",
52
+ "open",
53
+ "defaultOpen",
54
+ "onOpenChange",
55
+ "onSubtitleClick",
56
+ "class",
57
+ "classList",
58
+ ])
59
+
60
+ const pending = createMemo(() => local.status === "pending" || local.status === "running")
61
+
62
+ const hasChildren = createMemo(() => {
63
+ const c = local.children
64
+ if (c == null) return false
65
+ return true
66
+ })
67
+
68
+ const canExpand = createMemo(() => hasChildren() && !pending())
69
+
70
+ const handleOpenChange = (value: boolean) => {
71
+ if (pending()) return
72
+ local.onOpenChange?.(value)
73
+ }
74
+
75
+ return (
76
+ <Collapsible
77
+ {...rest}
78
+ data-component="basic-tool-v2"
79
+ open={local.open}
80
+ defaultOpen={local.defaultOpen}
81
+ onOpenChange={handleOpenChange}
82
+ disabled={!canExpand()}
83
+ classList={{
84
+ ...local.classList,
85
+ [local.class ?? ""]: !!local.class,
86
+ }}
87
+ >
88
+ <Collapsible.Trigger as="div" role="button" data-slot="basic-tool-v2-trigger">
89
+ <div data-slot="basic-tool-v2-labels">
90
+ <Show when={isTriggerTitle(local.trigger) && local.trigger} fallback={local.trigger as JSX.Element}>
91
+ {(title) => (
92
+ <>
93
+ <span data-slot="basic-tool-v2-title">
94
+ <TextShimmerV2 text={title().title} active={pending()} />
95
+ </span>
96
+ <Show when={!pending() && title().subtitle}>
97
+ <span data-slot="basic-tool-v2-sep" aria-hidden="true">
98
+ ·
99
+ </span>
100
+ <span
101
+ data-slot="basic-tool-v2-subtitle"
102
+ style={local.onSubtitleClick ? { cursor: "pointer" } : undefined}
103
+ onClick={(e) => {
104
+ if (local.onSubtitleClick) {
105
+ e.stopPropagation()
106
+ local.onSubtitleClick()
107
+ }
108
+ }}
109
+ >
110
+ {title().subtitle}
111
+ </span>
112
+ </Show>
113
+ <Show when={!pending() && title().args?.length}>
114
+ <For each={title().args}>{(arg) => <span data-slot="basic-tool-v2-arg">{arg}</span>}</For>
115
+ </Show>
116
+ <Show when={!pending() && title().changes}>
117
+ <span data-slot="basic-tool-v2-diff">
118
+ <DiffChanges changes={title().changes!} />
119
+ </span>
120
+ </Show>
121
+ <Show when={!pending() && title().action}>{(action) => action()}</Show>
122
+ </>
123
+ )}
124
+ </Show>
125
+ <Show when={canExpand()}>
126
+ <span data-slot="basic-tool-v2-chevron-wrap">
127
+ <ChevronIcon />
128
+ </span>
129
+ </Show>
130
+ </div>
131
+ </Collapsible.Trigger>
132
+ <Show when={canExpand()}>
133
+ <Collapsible.Content data-slot="basic-tool-v2-content">
134
+ <div data-slot="basic-tool-v2-content-inner">{local.children}</div>
135
+ </Collapsible.Content>
136
+ </Show>
137
+ </Collapsible>
138
+ )
139
+ }
@@ -0,0 +1,64 @@
1
+ import { createSignal, onCleanup, onMount, Show } from "solid-js"
2
+ import { FileIcon } from "@neurocode-ai/ui/file-icon"
3
+ import { getFilenameTruncated } from "@neurocode-ai/core/util/path"
4
+ import { TooltipV2 } from "@neurocode-ai/ui/v2/tooltip-v2"
5
+ import { AttachmentCardV2 } from "./attachment-card-v2"
6
+
7
+ export function CommentCardV2(props: {
8
+ comment: string
9
+ path: string
10
+ selection?: { startLine: number; endLine: number }
11
+ active?: boolean
12
+ title?: string
13
+ tooltip?: boolean
14
+ wide?: boolean
15
+ onClick?: () => void
16
+ }) {
17
+ let title: HTMLSpanElement | undefined
18
+ const [truncated, setTruncated] = createSignal(false)
19
+
20
+ onMount(() => {
21
+ const element = title
22
+ if (!element) return
23
+ const sync = () => setTruncated(element.scrollWidth > element.clientWidth)
24
+ const measure = () => requestAnimationFrame(sync)
25
+ const observer = new ResizeObserver(sync)
26
+ observer.observe(element)
27
+ measure()
28
+ void document.fonts?.ready.then(measure)
29
+ onCleanup(() => observer.disconnect())
30
+ })
31
+
32
+ return (
33
+ <TooltipV2
34
+ placement="top"
35
+ openDelay={1000}
36
+ value={props.title ?? props.comment}
37
+ disabled={!props.tooltip || !truncated()}
38
+ class={props.wide ? "w-full" : undefined}
39
+ contentStyle={{ "max-width": "320px", "white-space": "pre-wrap" }}
40
+ >
41
+ <AttachmentCardV2
42
+ title={props.comment}
43
+ active={props.active}
44
+ clickable={!!props.onClick}
45
+ wide={props.wide}
46
+ surface="base"
47
+ titleRef={(element) => {
48
+ title = element
49
+ }}
50
+ onClick={props.onClick}
51
+ >
52
+ <FileIcon node={{ path: props.path, type: "file" }} />
53
+ <span>
54
+ {getFilenameTruncated(props.path, 14)}
55
+ <Show when={props.selection}>
56
+ {(sel) =>
57
+ sel().startLine === sel().endLine ? `:${sel().startLine}` : `:${sel().startLine}-${sel().endLine}`
58
+ }
59
+ </Show>
60
+ </span>
61
+ </AttachmentCardV2>
62
+ </TooltipV2>
63
+ )
64
+ }