@opencode-ai/ui 1.17.18 → 1.17.20
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/v2/components/icon.d.ts +8 -0
- package/dist/v2/components/line-comment-v2.d.ts +4 -0
- package/dist/v2/components/split-button-v2.d.ts +5 -0
- package/package.json +1 -1
- package/src/components/tabs.css +120 -0
- package/src/v2/components/file-tree-v2.css +18 -1
- package/src/v2/components/icon-button-v2.css +8 -4
- package/src/v2/components/icon.tsx +8 -0
- package/src/v2/components/line-comment-v2.css +54 -0
- package/src/v2/components/line-comment-v2.tsx +151 -2
- package/src/v2/components/project-avatar-v2.tsx +2 -3
- package/src/v2/components/select-v2.css +1 -0
- package/src/v2/components/split-button-v2.css +99 -0
- package/src/v2/components/split-button-v2.tsx +48 -0
- package/src/v2/components/tabs-v2.css +1 -0
|
@@ -88,6 +88,10 @@ declare const icons: {
|
|
|
88
88
|
viewBox: string;
|
|
89
89
|
body: string;
|
|
90
90
|
};
|
|
91
|
+
"outline-xmark": {
|
|
92
|
+
viewBox: string;
|
|
93
|
+
body: string;
|
|
94
|
+
};
|
|
91
95
|
"outline-chevron-down": {
|
|
92
96
|
viewBox: string;
|
|
93
97
|
body: string;
|
|
@@ -128,6 +132,10 @@ declare const icons: {
|
|
|
128
132
|
viewBox: string;
|
|
129
133
|
body: string;
|
|
130
134
|
};
|
|
135
|
+
"outline-share": {
|
|
136
|
+
viewBox: string;
|
|
137
|
+
body: string;
|
|
138
|
+
};
|
|
131
139
|
reset: {
|
|
132
140
|
viewBox: string;
|
|
133
141
|
body: string;
|
|
@@ -11,6 +11,9 @@ export interface LineCommentV2Props extends ComponentProps<"div"> {
|
|
|
11
11
|
actions?: JSX.Element;
|
|
12
12
|
}
|
|
13
13
|
export declare function LineCommentV2(props: LineCommentV2Props): JSX.Element;
|
|
14
|
+
export type LineCommentEditorV2Mention = {
|
|
15
|
+
items: (query: string) => string[] | Promise<string[]>;
|
|
16
|
+
};
|
|
14
17
|
export interface LineCommentEditorV2Props extends Omit<ComponentProps<"div">, "children" | "onInput" | "onSubmit"> {
|
|
15
18
|
/** Visible field label above the textarea (default: “Comment”). */
|
|
16
19
|
heading?: JSX.Element | string;
|
|
@@ -24,5 +27,6 @@ export interface LineCommentEditorV2Props extends Omit<ComponentProps<"div">, "c
|
|
|
24
27
|
cancelLabel?: string;
|
|
25
28
|
submitLabel?: string;
|
|
26
29
|
autofocus?: boolean;
|
|
30
|
+
mention?: LineCommentEditorV2Mention;
|
|
27
31
|
}
|
|
28
32
|
export declare function LineCommentEditorV2(props: LineCommentEditorV2Props): JSX.Element;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type ComponentProps, type ParentProps } from "solid-js";
|
|
2
|
+
import "./split-button-v2.css";
|
|
3
|
+
export declare function SplitButtonV2(props: ParentProps<ComponentProps<"div">>): import("solid-js").JSX.Element;
|
|
4
|
+
export declare function SplitButtonV2Action(props: ComponentProps<"button">): import("solid-js").JSX.Element;
|
|
5
|
+
export declare function SplitButtonV2MenuTrigger(props: ComponentProps<"button">): import("solid-js").JSX.Element;
|
package/package.json
CHANGED
package/src/components/tabs.css
CHANGED
|
@@ -679,3 +679,123 @@
|
|
|
679
679
|
[data-component="tabs-drag-preview"] > * {
|
|
680
680
|
position: relative;
|
|
681
681
|
}
|
|
682
|
+
|
|
683
|
+
body[data-new-layout] #review-panel [data-component="tabs"],
|
|
684
|
+
body[data-new-layout] #terminal-panel [data-component="tabs"],
|
|
685
|
+
body[data-new-layout] #review-panel [data-component="tabs"][data-variant="normal"][data-orientation="horizontal"],
|
|
686
|
+
body[data-new-layout] #terminal-panel [data-component="tabs"][data-variant="normal"][data-orientation="horizontal"] {
|
|
687
|
+
background-color: var(--v2-background-bg-base);
|
|
688
|
+
|
|
689
|
+
[data-slot="tabs-list"] {
|
|
690
|
+
background-color: var(--v2-background-bg-base);
|
|
691
|
+
|
|
692
|
+
> .sticky {
|
|
693
|
+
background-color: var(--v2-background-bg-base);
|
|
694
|
+
|
|
695
|
+
&::before {
|
|
696
|
+
background: linear-gradient(90deg, transparent, var(--v2-background-bg-base));
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
body[data-new-layout] #review-panel [data-component="tabs"][data-variant="normal"][data-orientation="horizontal"] {
|
|
703
|
+
[data-slot="tabs-list"] {
|
|
704
|
+
height: 52px;
|
|
705
|
+
padding-right: 12px;
|
|
706
|
+
gap: 8px;
|
|
707
|
+
--tabs-review-fade: 8px;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
[data-slot="tabs-trigger-wrapper"] {
|
|
711
|
+
height: 28px;
|
|
712
|
+
padding-inline: 10px;
|
|
713
|
+
border: 0;
|
|
714
|
+
background-color: transparent;
|
|
715
|
+
box-shadow: none;
|
|
716
|
+
|
|
717
|
+
&::after {
|
|
718
|
+
display: none;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
[data-slot="tabs-trigger"] {
|
|
722
|
+
padding: 0 !important;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
&:has([data-slot="tabs-trigger-close-button"]) {
|
|
726
|
+
padding-right: 10px;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
&:has([data-selected]) {
|
|
730
|
+
background-color: var(--v2-background-bg-layer-02);
|
|
731
|
+
box-shadow: none;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
[data-slot="tabs-trigger"] {
|
|
736
|
+
font-family: Inter, sans-serif;
|
|
737
|
+
font-style: normal;
|
|
738
|
+
font-weight: 440;
|
|
739
|
+
font-size: 13px;
|
|
740
|
+
line-height: 16px;
|
|
741
|
+
letter-spacing: -0.04px;
|
|
742
|
+
color: var(--v2-text-text-muted);
|
|
743
|
+
font-variation-settings: "slnt" 0;
|
|
744
|
+
font-variant-numeric: tabular-nums;
|
|
745
|
+
|
|
746
|
+
&[data-selected] {
|
|
747
|
+
color: var(--v2-text-text-base);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
.tab-fileicon-color,
|
|
751
|
+
.tab-fileicon-mono,
|
|
752
|
+
[data-slot="icon-svg"] {
|
|
753
|
+
margin-left: -2px;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
/* Preview/temporary tabs: beat trigger + .text-14-medium upright defaults */
|
|
758
|
+
[data-slot="tabs-trigger"] .italic {
|
|
759
|
+
font-style: italic !important;
|
|
760
|
+
font-variation-settings: "slnt" -10 !important;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
body[data-new-layout] #review-panel [data-component="tabs"] .session-review-v2-tabs-bar {
|
|
765
|
+
width: 100%;
|
|
766
|
+
min-width: 0;
|
|
767
|
+
gap: 8px;
|
|
768
|
+
box-sizing: border-box;
|
|
769
|
+
border-bottom: 1px solid var(--border-weaker-base, var(--v2-border-border-weak));
|
|
770
|
+
background-color: var(--v2-background-bg-base);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
body[data-new-layout] #review-panel [data-component="tabs"] .session-review-v2-tabs-bar [data-slot="tabs-list"],
|
|
774
|
+
body[data-new-layout]
|
|
775
|
+
#review-panel
|
|
776
|
+
[data-component="tabs"][data-variant="normal"][data-orientation="horizontal"]
|
|
777
|
+
.session-review-v2-tabs-bar
|
|
778
|
+
[data-slot="tabs-list"] {
|
|
779
|
+
flex: 1 1 0;
|
|
780
|
+
min-width: 0;
|
|
781
|
+
width: auto;
|
|
782
|
+
border-bottom: none;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
body[data-new-layout]
|
|
786
|
+
#review-panel
|
|
787
|
+
[data-component="tabs"]
|
|
788
|
+
.session-review-v2-tabs-bar
|
|
789
|
+
[data-slot="tabs-list"]
|
|
790
|
+
> .sticky {
|
|
791
|
+
padding-right: 0;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
body[data-new-layout] #review-panel [data-component="tabs"] .session-review-v2-open-in-app-slot {
|
|
795
|
+
flex-shrink: 0;
|
|
796
|
+
margin-left: auto;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
body[data-new-layout] #review-panel [data-component="tabs"] .session-review-v2-open-in-app {
|
|
800
|
+
flex-shrink: 0;
|
|
801
|
+
}
|
|
@@ -14,12 +14,14 @@
|
|
|
14
14
|
justify-content: flex-start;
|
|
15
15
|
gap: 6px;
|
|
16
16
|
padding-right: 8px;
|
|
17
|
+
overflow: visible;
|
|
17
18
|
border: none;
|
|
18
19
|
border-radius: 6px;
|
|
19
20
|
background-color: transparent;
|
|
20
21
|
color: var(--v2-text-text-muted);
|
|
21
22
|
text-align: left;
|
|
22
23
|
cursor: pointer;
|
|
24
|
+
scroll-margin-block: 8px;
|
|
23
25
|
transition:
|
|
24
26
|
background-color 120ms ease,
|
|
25
27
|
color 120ms ease;
|
|
@@ -42,6 +44,21 @@
|
|
|
42
44
|
background-color: var(--v2-overlay-simple-overlay-pressed);
|
|
43
45
|
}
|
|
44
46
|
|
|
47
|
+
[data-component="file-tree-v2"] [data-slot="file-tree-v2-guide"] {
|
|
48
|
+
position: absolute;
|
|
49
|
+
top: 0;
|
|
50
|
+
margin-top: -2px;
|
|
51
|
+
height: calc(100% + 2px);
|
|
52
|
+
width: 1px;
|
|
53
|
+
pointer-events: none;
|
|
54
|
+
background-color: var(--v2-border-border-muted);
|
|
55
|
+
opacity: 0;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
[data-component="file-tree-v2"]:hover [data-slot="file-tree-v2-guide"] {
|
|
59
|
+
opacity: 0.5;
|
|
60
|
+
}
|
|
61
|
+
|
|
45
62
|
[data-component="file-tree-v2"] .filetree-icon--mono {
|
|
46
63
|
color: var(--v2-icon-icon-muted);
|
|
47
64
|
}
|
|
@@ -108,7 +125,7 @@
|
|
|
108
125
|
}
|
|
109
126
|
|
|
110
127
|
[data-component="file-tree-v2"] [data-slot="file-tree-v2-change"][data-change="modified"] {
|
|
111
|
-
|
|
128
|
+
color: var(--v2-state-fg-info);
|
|
112
129
|
}
|
|
113
130
|
|
|
114
131
|
[data-component="file-tree-v2"] [data-slot="file-tree-v2-change"][data-change="added"] {
|
|
@@ -112,8 +112,8 @@
|
|
|
112
112
|
color: var(--v2-text-text-base);
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
[data-component="icon-button-v2"][data-variant="ghost"]:is(:hover, [data-state="hover"]
|
|
116
|
-
|
|
115
|
+
[data-component="icon-button-v2"][data-variant="ghost"]:is(:hover, [data-state="hover"]):not(:disabled):not(
|
|
116
|
+
[data-expanded]
|
|
117
117
|
) {
|
|
118
118
|
background-color: var(--v2-overlay-simple-overlay-hover);
|
|
119
119
|
}
|
|
@@ -122,6 +122,10 @@
|
|
|
122
122
|
background-color: var(--v2-overlay-simple-overlay-pressed);
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
[data-component="icon-button-v2"][data-variant="ghost"]:where([data-expanded]):not(:disabled) {
|
|
126
|
+
background-color: var(--v2-overlay-simple-overlay-pressed);
|
|
127
|
+
}
|
|
128
|
+
|
|
125
129
|
[data-component="icon-button-v2"][data-variant="ghost"]:is(:disabled, [data-state="disabled"]) {
|
|
126
130
|
opacity: 0.5;
|
|
127
131
|
cursor: not-allowed;
|
|
@@ -133,8 +137,8 @@
|
|
|
133
137
|
color: var(--v2-icon-icon-muted);
|
|
134
138
|
}
|
|
135
139
|
|
|
136
|
-
[data-component="icon-button-v2"][data-variant="ghost-muted"]:is(:hover, [data-state="hover"]
|
|
137
|
-
|
|
140
|
+
[data-component="icon-button-v2"][data-variant="ghost-muted"]:is(:hover, [data-state="hover"]):not(:disabled):not(
|
|
141
|
+
[data-expanded]
|
|
138
142
|
) {
|
|
139
143
|
background-color: var(--v2-overlay-simple-overlay-hover);
|
|
140
144
|
}
|
|
@@ -89,6 +89,10 @@ const icons = {
|
|
|
89
89
|
viewBox: "0 0 16 16",
|
|
90
90
|
body: `<path d="M4.25 11.75L11.75 4.25M11.75 11.75L4.25 4.25" stroke="currentColor"/>`,
|
|
91
91
|
},
|
|
92
|
+
"outline-xmark": {
|
|
93
|
+
viewBox: "0 0 16 16",
|
|
94
|
+
body: `<path fill-rule="evenodd" clip-rule="evenodd" d="M4.99487 5.70186L7.29297 7.99995L4.99487 10.2981L5.70198 11.0052L8.00008 8.70706L10.2982 11.0052L11.0053 10.2981L8.70718 7.99995L11.0053 5.70186L10.2982 4.99475L8.00008 7.29285L5.70198 4.99475L4.99487 5.70186Z" fill="currentColor"/>`,
|
|
95
|
+
},
|
|
92
96
|
"outline-chevron-down": {
|
|
93
97
|
viewBox: "0 0 16 16",
|
|
94
98
|
body: `<path d="M5 6.5L8 9.5L11 6.5" stroke="currentColor"/>`,
|
|
@@ -129,6 +133,10 @@ const icons = {
|
|
|
129
133
|
viewBox: "0 0 16 16",
|
|
130
134
|
body: `<path d="M13.5555 6.66656V2.44434H9.33326M13.5555 2.44434L7.99993 7.99989M13.5555 9.33324V13.5555C13.5555 13.5555 12.7599 13.5555 11.7777 13.5555H2.44438C2.44438 13.5555 2.44438 12.7599 2.44438 11.7777V4.22213C2.44438 3.2399 2.44434 2.44435 2.44434 2.44435H6.66661" stroke="currentColor"/>`,
|
|
131
135
|
},
|
|
136
|
+
"outline-share": {
|
|
137
|
+
viewBox: "0 0 16 16",
|
|
138
|
+
body: `<path d="M13.5554 10.4445V13.5556C13.5554 13.5556 12.7599 13.5556 11.7777 13.5556H4.22211C3.23989 13.5556 2.44434 13.5556 2.44434 13.5556V10.4445M4.88878 5.55557L7.99989 2.44446L11.111 5.55557M7.99989 2.44446L7.99989 9.11112" stroke="currentColor"/>`,
|
|
139
|
+
},
|
|
132
140
|
reset: {
|
|
133
141
|
viewBox: "0 0 20 20",
|
|
134
142
|
body: `<path d="M5.83333 4.16406L2.5 7.4974L5.83333 10.8307M3.33333 7.4974H17.9167V15.4141H10" stroke="currentColor" stroke-linecap="square"/>`,
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
[data-component="line-comment-v2"] {
|
|
8
8
|
box-sizing: border-box;
|
|
9
|
+
font-family: var(--v2-font-family-sans);
|
|
9
10
|
font-variant-numeric: tabular-nums;
|
|
10
11
|
min-width: 0;
|
|
11
12
|
width: 100%;
|
|
@@ -202,3 +203,56 @@
|
|
|
202
203
|
gap: 8px;
|
|
203
204
|
flex-shrink: 0;
|
|
204
205
|
}
|
|
206
|
+
|
|
207
|
+
[data-component="line-comment-v2"][data-variant="editor"] [data-slot="line-comment-v2-mention-list"] {
|
|
208
|
+
display: flex;
|
|
209
|
+
flex-direction: column;
|
|
210
|
+
gap: 4px;
|
|
211
|
+
max-height: 180px;
|
|
212
|
+
overflow: auto;
|
|
213
|
+
margin-top: 8px;
|
|
214
|
+
padding: 4px;
|
|
215
|
+
border: 0.5px solid var(--v2-border-border-muted);
|
|
216
|
+
border-radius: 6px;
|
|
217
|
+
background: var(--v2-background-bg-layer-02);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
[data-component="line-comment-v2"][data-variant="editor"] [data-slot="line-comment-v2-mention-item"] {
|
|
221
|
+
display: flex;
|
|
222
|
+
align-items: center;
|
|
223
|
+
gap: 8px;
|
|
224
|
+
width: 100%;
|
|
225
|
+
min-width: 0;
|
|
226
|
+
padding: 6px 8px;
|
|
227
|
+
border: 0;
|
|
228
|
+
border-radius: 4px;
|
|
229
|
+
background: transparent;
|
|
230
|
+
color: var(--v2-text-text-base);
|
|
231
|
+
text-align: left;
|
|
232
|
+
cursor: pointer;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
[data-component="line-comment-v2"][data-variant="editor"] [data-slot="line-comment-v2-mention-item"][data-active] {
|
|
236
|
+
background: var(--v2-background-bg-layer-03);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
[data-component="line-comment-v2"][data-variant="editor"] [data-slot="line-comment-v2-mention-path"] {
|
|
240
|
+
display: flex;
|
|
241
|
+
align-items: center;
|
|
242
|
+
min-width: 0;
|
|
243
|
+
font-size: 12px;
|
|
244
|
+
line-height: 16px;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
[data-component="line-comment-v2"][data-variant="editor"] [data-slot="line-comment-v2-mention-dir"] {
|
|
248
|
+
min-width: 0;
|
|
249
|
+
color: var(--v2-text-text-muted);
|
|
250
|
+
white-space: nowrap;
|
|
251
|
+
overflow: hidden;
|
|
252
|
+
text-overflow: ellipsis;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
[data-component="line-comment-v2"][data-variant="editor"] [data-slot="line-comment-v2-mention-file"] {
|
|
256
|
+
color: var(--v2-text-text-base);
|
|
257
|
+
white-space: nowrap;
|
|
258
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { For, Show, createSignal, onMount, splitProps, type ComponentProps, type JSX } from "solid-js"
|
|
2
|
+
import { FileIcon } from "../../components/file-icon"
|
|
3
|
+
import { useFilteredList } from "../../hooks"
|
|
2
4
|
import { ButtonV2 } from "./button-v2"
|
|
3
5
|
import "./line-comment-v2.css"
|
|
4
6
|
|
|
@@ -53,6 +55,10 @@ export function LineCommentV2(props: LineCommentV2Props) {
|
|
|
53
55
|
)
|
|
54
56
|
}
|
|
55
57
|
|
|
58
|
+
export type LineCommentEditorV2Mention = {
|
|
59
|
+
items: (query: string) => string[] | Promise<string[]>
|
|
60
|
+
}
|
|
61
|
+
|
|
56
62
|
export interface LineCommentEditorV2Props extends Omit<ComponentProps<"div">, "children" | "onInput" | "onSubmit"> {
|
|
57
63
|
/** Visible field label above the textarea (default: “Comment”). */
|
|
58
64
|
heading?: JSX.Element | string
|
|
@@ -66,10 +72,22 @@ export interface LineCommentEditorV2Props extends Omit<ComponentProps<"div">, "c
|
|
|
66
72
|
cancelLabel?: string
|
|
67
73
|
submitLabel?: string
|
|
68
74
|
autofocus?: boolean
|
|
75
|
+
mention?: LineCommentEditorV2Mention
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function pathFilename(path: string) {
|
|
79
|
+
const index = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"))
|
|
80
|
+
return index === -1 ? path : path.slice(index + 1)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function pathDirectory(path: string) {
|
|
84
|
+
const index = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"))
|
|
85
|
+
return index === -1 ? "" : path.slice(0, index + 1)
|
|
69
86
|
}
|
|
70
87
|
|
|
71
88
|
export function LineCommentEditorV2(props: LineCommentEditorV2Props) {
|
|
72
89
|
let textareaRef: HTMLTextAreaElement | undefined
|
|
90
|
+
const [mentionOpen, setMentionOpen] = createSignal(false)
|
|
73
91
|
|
|
74
92
|
const [local, rest] = splitProps(props, [
|
|
75
93
|
"heading",
|
|
@@ -83,6 +101,7 @@ export function LineCommentEditorV2(props: LineCommentEditorV2Props) {
|
|
|
83
101
|
"cancelLabel",
|
|
84
102
|
"submitLabel",
|
|
85
103
|
"autofocus",
|
|
104
|
+
"mention",
|
|
86
105
|
"class",
|
|
87
106
|
"classList",
|
|
88
107
|
])
|
|
@@ -90,6 +109,78 @@ export function LineCommentEditorV2(props: LineCommentEditorV2Props) {
|
|
|
90
109
|
const heading = () => local.heading ?? "Comment"
|
|
91
110
|
const canSubmit = () => local.value.trim().length > 0
|
|
92
111
|
|
|
112
|
+
const closeMention = () => {
|
|
113
|
+
setMentionOpen(false)
|
|
114
|
+
mention.clear()
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const currentMention = () => {
|
|
118
|
+
const textarea = textareaRef
|
|
119
|
+
if (!textarea) return
|
|
120
|
+
if (!local.mention) return
|
|
121
|
+
if (textarea.selectionStart !== textarea.selectionEnd) return
|
|
122
|
+
|
|
123
|
+
const end = textarea.selectionStart
|
|
124
|
+
const match = textarea.value.slice(0, end).match(/@(\S*)$/)
|
|
125
|
+
if (!match) return
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
query: match[1] ?? "",
|
|
129
|
+
start: end - match[0].length,
|
|
130
|
+
end,
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function selectMention(item: { path: string } | undefined) {
|
|
135
|
+
if (!item) return
|
|
136
|
+
|
|
137
|
+
const textarea = textareaRef
|
|
138
|
+
const query = currentMention()
|
|
139
|
+
if (!textarea || !query) return
|
|
140
|
+
|
|
141
|
+
const value = `${textarea.value.slice(0, query.start)}@${item.path} ${textarea.value.slice(query.end)}`
|
|
142
|
+
const cursor = query.start + item.path.length + 2
|
|
143
|
+
|
|
144
|
+
local.onInput(value)
|
|
145
|
+
closeMention()
|
|
146
|
+
|
|
147
|
+
requestAnimationFrame(() => {
|
|
148
|
+
textarea.focus()
|
|
149
|
+
textarea.setSelectionRange(cursor, cursor)
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const mention = useFilteredList<{ path: string }>({
|
|
154
|
+
items: async (query) => {
|
|
155
|
+
if (!local.mention) return []
|
|
156
|
+
if (!query.trim()) return []
|
|
157
|
+
const paths = await local.mention.items(query)
|
|
158
|
+
return paths.map((path) => ({ path }))
|
|
159
|
+
},
|
|
160
|
+
key: (item) => item.path,
|
|
161
|
+
filterKeys: ["path"],
|
|
162
|
+
skipFilter: () => true,
|
|
163
|
+
onSelect: selectMention,
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
const syncMention = () => {
|
|
167
|
+
const item = currentMention()
|
|
168
|
+
if (!item) {
|
|
169
|
+
closeMention()
|
|
170
|
+
return
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
setMentionOpen(true)
|
|
174
|
+
mention.onInput(item.query)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const selectActiveMention = () => {
|
|
178
|
+
const items = mention.flat()
|
|
179
|
+
if (items.length === 0) return
|
|
180
|
+
const active = mention.active()
|
|
181
|
+
selectMention(items.find((item) => item.path === active) ?? items[0])
|
|
182
|
+
}
|
|
183
|
+
|
|
93
184
|
const submit = () => {
|
|
94
185
|
const v = local.value.trim()
|
|
95
186
|
if (!v) return
|
|
@@ -122,9 +213,39 @@ export function LineCommentEditorV2(props: LineCommentEditorV2Props) {
|
|
|
122
213
|
rows={local.rows ?? 3}
|
|
123
214
|
placeholder={local.placeholder ?? "Add context for this change"}
|
|
124
215
|
value={local.value}
|
|
125
|
-
onInput={(e) =>
|
|
216
|
+
onInput={(e) => {
|
|
217
|
+
local.onInput(e.currentTarget.value)
|
|
218
|
+
syncMention()
|
|
219
|
+
}}
|
|
220
|
+
onClick={() => syncMention()}
|
|
221
|
+
onSelect={() => syncMention()}
|
|
126
222
|
onKeyDown={(e) => {
|
|
127
223
|
e.stopPropagation()
|
|
224
|
+
if (e.isComposing || e.keyCode === 229) return
|
|
225
|
+
|
|
226
|
+
if (mentionOpen()) {
|
|
227
|
+
if (e.key === "Escape") {
|
|
228
|
+
e.preventDefault()
|
|
229
|
+
closeMention()
|
|
230
|
+
return
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (e.key === "Tab") {
|
|
234
|
+
if (mention.flat().length === 0) return
|
|
235
|
+
e.preventDefault()
|
|
236
|
+
selectActiveMention()
|
|
237
|
+
return
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const nav = e.key === "ArrowUp" || e.key === "ArrowDown" || e.key === "Enter"
|
|
241
|
+
const ctrlNav = e.ctrlKey && !e.metaKey && !e.altKey && !e.shiftKey && (e.key === "n" || e.key === "p")
|
|
242
|
+
if ((nav || ctrlNav) && mention.flat().length > 0) {
|
|
243
|
+
mention.onKeyDown(e)
|
|
244
|
+
e.preventDefault()
|
|
245
|
+
return
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
128
249
|
if (e.key === "Escape") {
|
|
129
250
|
e.preventDefault()
|
|
130
251
|
e.currentTarget.blur()
|
|
@@ -137,6 +258,34 @@ export function LineCommentEditorV2(props: LineCommentEditorV2Props) {
|
|
|
137
258
|
}
|
|
138
259
|
}}
|
|
139
260
|
/>
|
|
261
|
+
<Show when={mentionOpen() && mention.flat().length > 0}>
|
|
262
|
+
<div data-slot="line-comment-v2-mention-list">
|
|
263
|
+
<For each={mention.flat().slice(0, 10)}>
|
|
264
|
+
{(item) => {
|
|
265
|
+
const directory = item.path.endsWith("/") ? item.path : pathDirectory(item.path)
|
|
266
|
+
const name = item.path.endsWith("/") ? "" : pathFilename(item.path)
|
|
267
|
+
return (
|
|
268
|
+
<button
|
|
269
|
+
type="button"
|
|
270
|
+
data-slot="line-comment-v2-mention-item"
|
|
271
|
+
data-active={mention.active() === item.path ? "" : undefined}
|
|
272
|
+
onMouseDown={(event) => event.preventDefault()}
|
|
273
|
+
onMouseEnter={() => mention.setActive(item.path)}
|
|
274
|
+
onClick={() => selectMention(item)}
|
|
275
|
+
>
|
|
276
|
+
<FileIcon node={{ path: item.path, type: "file" }} class="shrink-0 size-4" />
|
|
277
|
+
<div data-slot="line-comment-v2-mention-path">
|
|
278
|
+
<span data-slot="line-comment-v2-mention-dir">{directory}</span>
|
|
279
|
+
<Show when={name}>
|
|
280
|
+
<span data-slot="line-comment-v2-mention-file">{name}</span>
|
|
281
|
+
</Show>
|
|
282
|
+
</div>
|
|
283
|
+
</button>
|
|
284
|
+
)
|
|
285
|
+
}}
|
|
286
|
+
</For>
|
|
287
|
+
</div>
|
|
288
|
+
</Show>
|
|
140
289
|
</div>
|
|
141
290
|
<div data-slot="line-comment-v2-footer">
|
|
142
291
|
<div data-slot="line-comment-v2-footer-meta">{local.selection}</div>
|
|
@@ -38,7 +38,6 @@ export interface ProjectAvatarProps extends ComponentProps<"div"> {
|
|
|
38
38
|
|
|
39
39
|
export function ProjectAvatar(props: ProjectAvatarProps) {
|
|
40
40
|
const [split, rest] = splitProps(props, ["fallback", "src", "variant", "unread", "class", "classList", "style"])
|
|
41
|
-
const src = split.src
|
|
42
41
|
return (
|
|
43
42
|
<div
|
|
44
43
|
{...rest}
|
|
@@ -53,9 +52,9 @@ export function ProjectAvatar(props: ProjectAvatarProps) {
|
|
|
53
52
|
<div
|
|
54
53
|
data-slot="project-avatar-surface"
|
|
55
54
|
data-variant={split.variant ?? "gray"}
|
|
56
|
-
data-has-image={src ? "" : undefined}
|
|
55
|
+
data-has-image={split.src ? "" : undefined}
|
|
57
56
|
>
|
|
58
|
-
<Show when={src} fallback={first(split.fallback)}>
|
|
57
|
+
<Show when={split.src} fallback={first(split.fallback)}>
|
|
59
58
|
{(value) => <img src={value()} draggable={false} data-slot="project-avatar-image" />}
|
|
60
59
|
</Show>
|
|
61
60
|
</div>
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
[data-component="split-button-v2"] {
|
|
2
|
+
display: inline-flex;
|
|
3
|
+
align-items: stretch;
|
|
4
|
+
flex-shrink: 0;
|
|
5
|
+
height: 28px;
|
|
6
|
+
border-radius: 6px;
|
|
7
|
+
background-color: transparent;
|
|
8
|
+
box-shadow: none;
|
|
9
|
+
overflow: hidden;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
[data-component="split-button-v2"]:is(:hover, :has([data-component="split-button-v2-menu-trigger"][data-expanded])) {
|
|
13
|
+
box-shadow: inset 0 0 0 1px var(--v2-border-border-muted);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
[data-component="split-button-v2-action"],
|
|
17
|
+
[data-component="split-button-v2-menu-trigger"] {
|
|
18
|
+
display: inline-flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
justify-content: center;
|
|
21
|
+
height: 28px;
|
|
22
|
+
margin: 0;
|
|
23
|
+
padding: 0;
|
|
24
|
+
border: none;
|
|
25
|
+
background: transparent;
|
|
26
|
+
color: var(--v2-icon-icon-base);
|
|
27
|
+
cursor: default;
|
|
28
|
+
outline: none;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
[data-component="split-button-v2-action"] {
|
|
32
|
+
width: 28px;
|
|
33
|
+
flex-shrink: 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
[data-component="split-button-v2-action"]:hover:not(:disabled) {
|
|
37
|
+
background-color: var(--v2-overlay-simple-overlay-hover);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
[data-component="split-button-v2-action"]:is(:active, [data-pressed]):not(:disabled) {
|
|
41
|
+
background-color: var(--v2-overlay-simple-overlay-pressed);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
[data-component="split-button-v2-action"]:is(:focus-visible, [data-focus]) {
|
|
45
|
+
outline: 2px solid var(--v2-border-border-focus);
|
|
46
|
+
outline-offset: -2px;
|
|
47
|
+
z-index: 1;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
[data-component="split-button-v2-action"]:disabled {
|
|
51
|
+
opacity: 0.5;
|
|
52
|
+
cursor: not-allowed;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
[data-component="split-button-v2-menu-trigger"] {
|
|
56
|
+
width: 20px;
|
|
57
|
+
flex-shrink: 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
[data-component="split-button-v2-menu-trigger"]:is(:hover, [data-expanded]):not(:disabled) {
|
|
61
|
+
background-color: var(--v2-overlay-simple-overlay-hover);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
[data-component="split-button-v2-menu-trigger"]:is(:active, [data-pressed]):not(:disabled) {
|
|
65
|
+
background-color: var(--v2-overlay-simple-overlay-pressed);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
[data-component="split-button-v2-menu-trigger"]:is(:focus-visible, [data-focus]) {
|
|
69
|
+
outline: 2px solid var(--v2-border-border-focus);
|
|
70
|
+
outline-offset: -2px;
|
|
71
|
+
z-index: 1;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
[data-component="split-button-v2-menu-trigger"]:disabled {
|
|
75
|
+
opacity: 0.5;
|
|
76
|
+
cursor: not-allowed;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
[data-component="split-button-v2-action"] [data-component="app-icon"] {
|
|
80
|
+
width: 16px;
|
|
81
|
+
height: 16px;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
[data-component="menu-v2-content"].open-in-app-v2-menu [data-component="app-icon"] {
|
|
85
|
+
width: 16px;
|
|
86
|
+
height: 16px;
|
|
87
|
+
flex-shrink: 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
[data-component="menu-v2-content"].open-in-app-v2-menu [data-component="icon"] {
|
|
91
|
+
width: 16px;
|
|
92
|
+
height: 16px;
|
|
93
|
+
flex-shrink: 0;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
[data-component="menu-v2-content"].open-in-app-v2-menu [data-component="icon"] [data-slot="icon-svg"] {
|
|
97
|
+
width: 16px;
|
|
98
|
+
height: 16px;
|
|
99
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { splitProps, type ComponentProps, type ParentProps } from "solid-js"
|
|
2
|
+
import "./split-button-v2.css"
|
|
3
|
+
|
|
4
|
+
export function SplitButtonV2(props: ParentProps<ComponentProps<"div">>) {
|
|
5
|
+
const [split, rest] = splitProps(props, ["class", "classList", "children"])
|
|
6
|
+
return (
|
|
7
|
+
<div
|
|
8
|
+
data-component="split-button-v2"
|
|
9
|
+
classList={{
|
|
10
|
+
...split.classList,
|
|
11
|
+
[split.class ?? ""]: !!split.class,
|
|
12
|
+
}}
|
|
13
|
+
{...rest}
|
|
14
|
+
>
|
|
15
|
+
{split.children}
|
|
16
|
+
</div>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function SplitButtonV2Action(props: ComponentProps<"button">) {
|
|
21
|
+
const [split, rest] = splitProps(props, ["class", "classList"])
|
|
22
|
+
return (
|
|
23
|
+
<button
|
|
24
|
+
type="button"
|
|
25
|
+
data-component="split-button-v2-action"
|
|
26
|
+
classList={{
|
|
27
|
+
...split.classList,
|
|
28
|
+
[split.class ?? ""]: !!split.class,
|
|
29
|
+
}}
|
|
30
|
+
{...rest}
|
|
31
|
+
/>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function SplitButtonV2MenuTrigger(props: ComponentProps<"button">) {
|
|
36
|
+
const [split, rest] = splitProps(props, ["class", "classList"])
|
|
37
|
+
return (
|
|
38
|
+
<button
|
|
39
|
+
type="button"
|
|
40
|
+
data-component="split-button-v2-menu-trigger"
|
|
41
|
+
classList={{
|
|
42
|
+
...split.classList,
|
|
43
|
+
[split.class ?? ""]: !!split.class,
|
|
44
|
+
}}
|
|
45
|
+
{...rest}
|
|
46
|
+
/>
|
|
47
|
+
)
|
|
48
|
+
}
|