@yarvelle/ui 0.0.0-stage → 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.
- package/bin/yarvelle-ui-codegen.js +115 -0
- package/dist/index-DLbrj3Po.d.ts +90 -0
- package/dist/index.d.ts +706 -0
- package/dist/index.js +4418 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/api/index.d.ts +99 -0
- package/dist/lib/api/index.js +113 -0
- package/dist/lib/api/index.js.map +1 -0
- package/dist/lib/auth/index.d.ts +21 -0
- package/dist/lib/auth/index.js +37 -0
- package/dist/lib/auth/index.js.map +1 -0
- package/dist/lib/cn.d.ts +5 -0
- package/dist/lib/cn.js +10 -0
- package/dist/lib/cn.js.map +1 -0
- package/dist/lib/i18n/index.d.ts +7 -0
- package/dist/lib/i18n/index.js +45 -0
- package/dist/lib/i18n/index.js.map +1 -0
- package/dist/lib/index.d.ts +8 -0
- package/dist/lib/index.js +272 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/sse/index.d.ts +11 -0
- package/dist/lib/sse/index.js +43 -0
- package/dist/lib/sse/index.js.map +1 -0
- package/dist/lib/storage-namespace.d.ts +6 -0
- package/dist/lib/storage-namespace.js +34 -0
- package/dist/lib/storage-namespace.js.map +1 -0
- package/dist/slots/index.d.ts +12 -0
- package/dist/slots/index.js +86 -0
- package/dist/slots/index.js.map +1 -0
- package/dist/styles.css +270 -0
- package/dist/testing/index.d.ts +3 -0
- package/dist/testing/index.js +36 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/tokens/index.d.ts +37 -0
- package/dist/tokens/index.js +155 -0
- package/dist/tokens/index.js.map +1 -0
- package/dist/tokens/tailwind-preset.d.ts +5 -0
- package/dist/tokens/tailwind-preset.js +155 -0
- package/dist/tokens/tailwind-preset.js.map +1 -0
- package/dist/types-CP_CDg6G.d.ts +51 -0
- package/package.json +69 -4
- package/README.md +0 -3
package/dist/index.js
ADDED
|
@@ -0,0 +1,4418 @@
|
|
|
1
|
+
// src/tokens/tailwind-preset.ts
|
|
2
|
+
var yarvellePreset = {
|
|
3
|
+
content: [],
|
|
4
|
+
darkMode: ["class", '[data-theme="dark"]'],
|
|
5
|
+
theme: {
|
|
6
|
+
colors: {
|
|
7
|
+
transparent: "transparent",
|
|
8
|
+
current: "currentColor",
|
|
9
|
+
canvas: "var(--ui-canvas)",
|
|
10
|
+
surface: {
|
|
11
|
+
DEFAULT: "var(--ui-surface)",
|
|
12
|
+
2: "var(--ui-surface-2)",
|
|
13
|
+
3: "var(--ui-surface-3)"
|
|
14
|
+
},
|
|
15
|
+
rail: "var(--ui-rail)",
|
|
16
|
+
overlay: "var(--ui-overlay)",
|
|
17
|
+
fg: {
|
|
18
|
+
DEFAULT: "var(--ui-fg)",
|
|
19
|
+
muted: "var(--ui-fg-muted)",
|
|
20
|
+
subtle: "var(--ui-fg-subtle)",
|
|
21
|
+
"on-primary": "var(--ui-fg-on-primary)",
|
|
22
|
+
link: "var(--ui-fg-link)"
|
|
23
|
+
},
|
|
24
|
+
border: {
|
|
25
|
+
DEFAULT: "var(--ui-border)",
|
|
26
|
+
strong: "var(--ui-border-strong)"
|
|
27
|
+
},
|
|
28
|
+
primary: {
|
|
29
|
+
DEFAULT: "var(--ui-primary)",
|
|
30
|
+
hover: "var(--ui-primary-hover)",
|
|
31
|
+
fg: "var(--ui-primary-fg)",
|
|
32
|
+
soft: "var(--ui-primary-soft)",
|
|
33
|
+
"soft-fg": "var(--ui-primary-soft-fg)"
|
|
34
|
+
},
|
|
35
|
+
info: {
|
|
36
|
+
bg: "var(--ui-info-bg)",
|
|
37
|
+
fg: "var(--ui-info-fg)",
|
|
38
|
+
border: "var(--ui-info-border)"
|
|
39
|
+
},
|
|
40
|
+
warn: {
|
|
41
|
+
bg: "var(--ui-warn-bg)",
|
|
42
|
+
fg: "var(--ui-warn-fg)",
|
|
43
|
+
border: "var(--ui-warn-border)"
|
|
44
|
+
},
|
|
45
|
+
danger: {
|
|
46
|
+
bg: "var(--ui-danger-bg)",
|
|
47
|
+
fg: "var(--ui-danger-fg)",
|
|
48
|
+
border: "var(--ui-danger-border)"
|
|
49
|
+
},
|
|
50
|
+
success: {
|
|
51
|
+
bg: "var(--ui-success-bg)",
|
|
52
|
+
fg: "var(--ui-success-fg)",
|
|
53
|
+
border: "var(--ui-success-border)"
|
|
54
|
+
},
|
|
55
|
+
emergency: {
|
|
56
|
+
DEFAULT: "var(--ui-emergency)",
|
|
57
|
+
fg: "var(--ui-emergency-fg)"
|
|
58
|
+
},
|
|
59
|
+
focus: {
|
|
60
|
+
DEFAULT: "var(--ui-focus)",
|
|
61
|
+
offset: "var(--ui-focus-offset)"
|
|
62
|
+
},
|
|
63
|
+
member: {
|
|
64
|
+
1: { bg: "var(--ui-member-1-bg)", fg: "var(--ui-member-1-fg)" },
|
|
65
|
+
2: { bg: "var(--ui-member-2-bg)", fg: "var(--ui-member-2-fg)" },
|
|
66
|
+
3: { bg: "var(--ui-member-3-bg)", fg: "var(--ui-member-3-fg)" },
|
|
67
|
+
4: { bg: "var(--ui-member-4-bg)", fg: "var(--ui-member-4-fg)" },
|
|
68
|
+
5: { bg: "var(--ui-member-5-bg)", fg: "var(--ui-member-5-fg)" }
|
|
69
|
+
},
|
|
70
|
+
scope: {
|
|
71
|
+
health: { bg: "var(--ui-scope-health-bg)", fg: "var(--ui-scope-health-fg)" },
|
|
72
|
+
finance: { bg: "var(--ui-scope-finance-bg)", fg: "var(--ui-scope-finance-fg)" },
|
|
73
|
+
education: { bg: "var(--ui-scope-education-bg)", fg: "var(--ui-scope-education-fg)" },
|
|
74
|
+
household: { bg: "var(--ui-scope-household-bg)", fg: "var(--ui-scope-household-fg)" }
|
|
75
|
+
},
|
|
76
|
+
risk: {
|
|
77
|
+
low: { bg: "var(--ui-risk-low-bg)", fg: "var(--ui-risk-low-fg)", border: "var(--ui-risk-low-border)" },
|
|
78
|
+
medium: { bg: "var(--ui-risk-medium-bg)", fg: "var(--ui-risk-medium-fg)", border: "var(--ui-risk-medium-border)" },
|
|
79
|
+
high: { bg: "var(--ui-risk-high-bg)", fg: "var(--ui-risk-high-fg)", border: "var(--ui-risk-high-border)" },
|
|
80
|
+
critical: { bg: "var(--ui-risk-critical-bg)", fg: "var(--ui-risk-critical-fg)", border: "var(--ui-risk-critical-border)" }
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
spacing: {
|
|
84
|
+
0: "var(--ui-space-0)",
|
|
85
|
+
1: "var(--ui-space-1)",
|
|
86
|
+
2: "var(--ui-space-2)",
|
|
87
|
+
3: "var(--ui-space-3)",
|
|
88
|
+
4: "var(--ui-space-4)",
|
|
89
|
+
5: "var(--ui-space-5)",
|
|
90
|
+
6: "var(--ui-space-6)",
|
|
91
|
+
8: "var(--ui-space-8)",
|
|
92
|
+
10: "var(--ui-space-10)",
|
|
93
|
+
12: "var(--ui-space-12)",
|
|
94
|
+
16: "var(--ui-space-16)"
|
|
95
|
+
},
|
|
96
|
+
fontFamily: {
|
|
97
|
+
sans: ["var(--ui-font-sans)"],
|
|
98
|
+
display: ["var(--ui-font-display)"],
|
|
99
|
+
mono: ["var(--ui-font-mono)"]
|
|
100
|
+
},
|
|
101
|
+
fontSize: {
|
|
102
|
+
xs: ["var(--ui-text-xs)", { lineHeight: "var(--ui-leading-xs)" }],
|
|
103
|
+
sm: ["var(--ui-text-sm)", { lineHeight: "var(--ui-leading-sm)" }],
|
|
104
|
+
base: ["var(--ui-text-md)", { lineHeight: "var(--ui-leading-md)" }],
|
|
105
|
+
md: ["var(--ui-text-md)", { lineHeight: "var(--ui-leading-md)" }],
|
|
106
|
+
lg: ["var(--ui-text-lg)", { lineHeight: "var(--ui-leading-lg)" }],
|
|
107
|
+
xl: ["var(--ui-text-xl)", { lineHeight: "var(--ui-leading-xl)" }],
|
|
108
|
+
"2xl": ["var(--ui-text-2xl)", { lineHeight: "var(--ui-leading-2xl)" }],
|
|
109
|
+
"3xl": ["var(--ui-text-3xl)", { lineHeight: "var(--ui-leading-3xl)" }],
|
|
110
|
+
"4xl": ["var(--ui-text-4xl)", { lineHeight: "var(--ui-leading-4xl)" }]
|
|
111
|
+
},
|
|
112
|
+
borderRadius: {
|
|
113
|
+
none: "0px",
|
|
114
|
+
sm: "var(--ui-radius-sm)",
|
|
115
|
+
md: "var(--ui-radius-md)",
|
|
116
|
+
lg: "var(--ui-radius-lg)",
|
|
117
|
+
xl: "var(--ui-radius-xl)",
|
|
118
|
+
full: "var(--ui-radius-full)"
|
|
119
|
+
},
|
|
120
|
+
boxShadow: {
|
|
121
|
+
0: "var(--ui-shadow-0)",
|
|
122
|
+
1: "var(--ui-shadow-1)",
|
|
123
|
+
2: "var(--ui-shadow-2)",
|
|
124
|
+
3: "var(--ui-shadow-3)"
|
|
125
|
+
},
|
|
126
|
+
minHeight: {
|
|
127
|
+
tap: "var(--ui-tap)"
|
|
128
|
+
},
|
|
129
|
+
minWidth: {
|
|
130
|
+
tap: "var(--ui-tap)"
|
|
131
|
+
},
|
|
132
|
+
transitionTimingFunction: {
|
|
133
|
+
ui: "var(--ui-ease)"
|
|
134
|
+
},
|
|
135
|
+
transitionDuration: {
|
|
136
|
+
fast: "var(--ui-duration-fast)",
|
|
137
|
+
base: "var(--ui-duration-base)",
|
|
138
|
+
slow: "var(--ui-duration-slow)"
|
|
139
|
+
},
|
|
140
|
+
zIndex: {
|
|
141
|
+
rail: "var(--ui-z-rail)",
|
|
142
|
+
sticky: "var(--ui-z-sticky)",
|
|
143
|
+
popover: "var(--ui-z-popover)",
|
|
144
|
+
modal: "var(--ui-z-modal)",
|
|
145
|
+
toast: "var(--ui-z-toast)",
|
|
146
|
+
skip: "var(--ui-z-skip)"
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
// src/primitives/Button.tsx
|
|
152
|
+
import * as React from "react";
|
|
153
|
+
|
|
154
|
+
// src/lib/cn.ts
|
|
155
|
+
import { clsx } from "clsx";
|
|
156
|
+
import { twMerge } from "tailwind-merge";
|
|
157
|
+
function cn(...inputs) {
|
|
158
|
+
return twMerge(clsx(inputs));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// src/primitives/Button.tsx
|
|
162
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
163
|
+
var variantStyles = {
|
|
164
|
+
primary: "bg-primary hover:bg-primary-hover active:bg-primary-hover text-primary-fg border border-transparent shadow-1",
|
|
165
|
+
secondary: "bg-surface hover:bg-surface-2 active:bg-surface-3 text-fg border border-border-strong shadow-1",
|
|
166
|
+
ghost: "bg-transparent hover:bg-surface-2 active:bg-surface-3 text-fg-muted hover:text-fg border border-transparent",
|
|
167
|
+
outline: "bg-transparent hover:bg-surface-2 active:bg-surface-3 text-fg border border-border-strong",
|
|
168
|
+
danger: "bg-danger-bg hover:bg-danger-fg text-danger-fg hover:text-white border border-danger-border"
|
|
169
|
+
};
|
|
170
|
+
var sizeStyles = {
|
|
171
|
+
sm: "h-[34px] px-3 text-sm rounded-md relative before:absolute before:-inset-y-1.5 before:inset-x-0 before:content-['']",
|
|
172
|
+
md: "min-h-[44px] h-10 px-4 text-sm rounded-md",
|
|
173
|
+
lg: "min-h-[48px] h-12 px-5 text-base rounded-md"
|
|
174
|
+
};
|
|
175
|
+
var Button = React.forwardRef(
|
|
176
|
+
({
|
|
177
|
+
variant = "primary",
|
|
178
|
+
size = "md",
|
|
179
|
+
loading = false,
|
|
180
|
+
disabled = false,
|
|
181
|
+
leftIcon,
|
|
182
|
+
rightIcon,
|
|
183
|
+
children,
|
|
184
|
+
className,
|
|
185
|
+
type = "button",
|
|
186
|
+
...props
|
|
187
|
+
}, ref) => {
|
|
188
|
+
const isDisabled = disabled || loading;
|
|
189
|
+
return /* @__PURE__ */ jsxs(
|
|
190
|
+
"button",
|
|
191
|
+
{
|
|
192
|
+
ref,
|
|
193
|
+
type,
|
|
194
|
+
disabled: isDisabled,
|
|
195
|
+
"data-slot": "button",
|
|
196
|
+
"data-variant": variant,
|
|
197
|
+
"data-size": size,
|
|
198
|
+
"data-loading": loading ? "true" : void 0,
|
|
199
|
+
className: cn(
|
|
200
|
+
"yc-Button-root inline-flex items-center justify-center font-medium select-none",
|
|
201
|
+
"transition-colors duration-fast ease-ui",
|
|
202
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-focus focus-visible:ring-offset-2",
|
|
203
|
+
"disabled:opacity-50 disabled:cursor-not-allowed disabled:pointer-events-none",
|
|
204
|
+
variantStyles[variant],
|
|
205
|
+
sizeStyles[size],
|
|
206
|
+
className
|
|
207
|
+
),
|
|
208
|
+
...props,
|
|
209
|
+
children: [
|
|
210
|
+
loading ? /* @__PURE__ */ jsx(
|
|
211
|
+
"span",
|
|
212
|
+
{
|
|
213
|
+
className: "yc-Button-spinner me-2 inline-block h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent",
|
|
214
|
+
"data-slot": "spinner",
|
|
215
|
+
role: "status",
|
|
216
|
+
"aria-label": "Loading"
|
|
217
|
+
}
|
|
218
|
+
) : leftIcon ? /* @__PURE__ */ jsx("span", { className: "yc-Button-icon yc-Button-icon-start me-2 inline-flex shrink-0 items-center", "data-slot": "icon-start", children: leftIcon }) : null,
|
|
219
|
+
/* @__PURE__ */ jsx("span", { className: "yc-Button-label truncate", "data-slot": "label", children }),
|
|
220
|
+
!loading && rightIcon ? /* @__PURE__ */ jsx("span", { className: "yc-Button-icon yc-Button-icon-end ms-2 inline-flex shrink-0 items-center", "data-slot": "icon-end", children: rightIcon }) : null
|
|
221
|
+
]
|
|
222
|
+
}
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
);
|
|
226
|
+
Button.displayName = "Button";
|
|
227
|
+
|
|
228
|
+
// src/primitives/Dialog.tsx
|
|
229
|
+
import * as React2 from "react";
|
|
230
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
231
|
+
var DialogContext = React2.createContext(null);
|
|
232
|
+
function useDialogContext() {
|
|
233
|
+
const context = React2.useContext(DialogContext);
|
|
234
|
+
if (!context) {
|
|
235
|
+
throw new Error("Dialog compound components must be rendered within a <Dialog>");
|
|
236
|
+
}
|
|
237
|
+
return context;
|
|
238
|
+
}
|
|
239
|
+
function Dialog({
|
|
240
|
+
open: controlledOpen,
|
|
241
|
+
defaultOpen = false,
|
|
242
|
+
onOpenChange,
|
|
243
|
+
children
|
|
244
|
+
}) {
|
|
245
|
+
const [uncontrolledOpen, setUncontrolledOpen] = React2.useState(defaultOpen);
|
|
246
|
+
const isControlled = controlledOpen !== void 0;
|
|
247
|
+
const isOpen = isControlled ? controlledOpen : uncontrolledOpen;
|
|
248
|
+
const triggerRef = React2.useRef(null);
|
|
249
|
+
const dialogId = React2.useId();
|
|
250
|
+
const handleOpenChange = React2.useCallback(
|
|
251
|
+
(newOpen) => {
|
|
252
|
+
if (!isControlled) {
|
|
253
|
+
setUncontrolledOpen(newOpen);
|
|
254
|
+
}
|
|
255
|
+
onOpenChange?.(newOpen);
|
|
256
|
+
},
|
|
257
|
+
[isControlled, onOpenChange]
|
|
258
|
+
);
|
|
259
|
+
return /* @__PURE__ */ jsx2(
|
|
260
|
+
DialogContext.Provider,
|
|
261
|
+
{
|
|
262
|
+
value: {
|
|
263
|
+
open: isOpen,
|
|
264
|
+
onOpenChange: handleOpenChange,
|
|
265
|
+
dialogId,
|
|
266
|
+
triggerRef
|
|
267
|
+
},
|
|
268
|
+
children
|
|
269
|
+
}
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
var DialogTrigger = React2.forwardRef(
|
|
273
|
+
({ children, className, onClick, asChild = false, ...props }, ref) => {
|
|
274
|
+
const { open, onOpenChange, dialogId, triggerRef } = useDialogContext();
|
|
275
|
+
const setRef = React2.useCallback(
|
|
276
|
+
(node) => {
|
|
277
|
+
triggerRef.current = node;
|
|
278
|
+
if (typeof ref === "function") {
|
|
279
|
+
ref(node);
|
|
280
|
+
} else if (ref) {
|
|
281
|
+
ref.current = node;
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
[ref, triggerRef]
|
|
285
|
+
);
|
|
286
|
+
return /* @__PURE__ */ jsx2(
|
|
287
|
+
"button",
|
|
288
|
+
{
|
|
289
|
+
ref: setRef,
|
|
290
|
+
type: "button",
|
|
291
|
+
"aria-haspopup": "dialog",
|
|
292
|
+
"aria-expanded": open,
|
|
293
|
+
"aria-controls": dialogId,
|
|
294
|
+
"data-slot": "trigger",
|
|
295
|
+
className: cn("yc-Dialog-trigger", className),
|
|
296
|
+
onClick: (e) => {
|
|
297
|
+
onClick?.(e);
|
|
298
|
+
if (!e.defaultPrevented) {
|
|
299
|
+
onOpenChange(!open);
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
...props,
|
|
303
|
+
children
|
|
304
|
+
}
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
);
|
|
308
|
+
DialogTrigger.displayName = "DialogTrigger";
|
|
309
|
+
var DialogContent = React2.forwardRef(
|
|
310
|
+
({ children, className, ...props }, ref) => {
|
|
311
|
+
const { open, onOpenChange, dialogId, triggerRef } = useDialogContext();
|
|
312
|
+
const contentRef = React2.useRef(null);
|
|
313
|
+
React2.useEffect(() => {
|
|
314
|
+
if (!open) return;
|
|
315
|
+
const handleKeyDown = (e) => {
|
|
316
|
+
if (e.key === "Escape") {
|
|
317
|
+
e.stopPropagation();
|
|
318
|
+
onOpenChange(false);
|
|
319
|
+
triggerRef.current?.focus();
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
323
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
324
|
+
}, [open, onOpenChange, triggerRef]);
|
|
325
|
+
if (!open) return null;
|
|
326
|
+
return /* @__PURE__ */ jsxs2(
|
|
327
|
+
"div",
|
|
328
|
+
{
|
|
329
|
+
className: "yc-Dialog-portal fixed inset-0 z-50 flex items-center justify-center p-4",
|
|
330
|
+
"data-slot": "portal",
|
|
331
|
+
children: [
|
|
332
|
+
/* @__PURE__ */ jsx2(
|
|
333
|
+
"div",
|
|
334
|
+
{
|
|
335
|
+
className: "yc-Dialog-overlay fixed inset-0 bg-overlay transition-opacity",
|
|
336
|
+
"data-slot": "overlay",
|
|
337
|
+
onClick: () => onOpenChange(false),
|
|
338
|
+
"aria-hidden": "true"
|
|
339
|
+
}
|
|
340
|
+
),
|
|
341
|
+
/* @__PURE__ */ jsx2(
|
|
342
|
+
"div",
|
|
343
|
+
{
|
|
344
|
+
ref: (node) => {
|
|
345
|
+
contentRef.current = node;
|
|
346
|
+
if (typeof ref === "function") ref(node);
|
|
347
|
+
else if (ref) ref.current = node;
|
|
348
|
+
},
|
|
349
|
+
id: dialogId,
|
|
350
|
+
role: "dialog",
|
|
351
|
+
"aria-modal": "true",
|
|
352
|
+
"data-slot": "content",
|
|
353
|
+
className: cn(
|
|
354
|
+
"yc-Dialog-content relative z-10 w-full max-w-lg rounded-xl bg-surface p-6 text-fg shadow-3 border border-border",
|
|
355
|
+
"focus:outline-none",
|
|
356
|
+
className
|
|
357
|
+
),
|
|
358
|
+
...props,
|
|
359
|
+
children
|
|
360
|
+
}
|
|
361
|
+
)
|
|
362
|
+
]
|
|
363
|
+
}
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
);
|
|
367
|
+
DialogContent.displayName = "DialogContent";
|
|
368
|
+
var DialogHeader = ({
|
|
369
|
+
className,
|
|
370
|
+
...props
|
|
371
|
+
}) => /* @__PURE__ */ jsx2(
|
|
372
|
+
"div",
|
|
373
|
+
{
|
|
374
|
+
className: cn("yc-Dialog-header flex flex-col space-y-1.5 text-start pb-4", className),
|
|
375
|
+
"data-slot": "header",
|
|
376
|
+
...props
|
|
377
|
+
}
|
|
378
|
+
);
|
|
379
|
+
DialogHeader.displayName = "DialogHeader";
|
|
380
|
+
var DialogFooter = ({
|
|
381
|
+
className,
|
|
382
|
+
...props
|
|
383
|
+
}) => /* @__PURE__ */ jsx2(
|
|
384
|
+
"div",
|
|
385
|
+
{
|
|
386
|
+
className: cn("yc-Dialog-footer flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 pt-4", className),
|
|
387
|
+
"data-slot": "footer",
|
|
388
|
+
...props
|
|
389
|
+
}
|
|
390
|
+
);
|
|
391
|
+
DialogFooter.displayName = "DialogFooter";
|
|
392
|
+
var DialogTitle = ({
|
|
393
|
+
className,
|
|
394
|
+
...props
|
|
395
|
+
}) => /* @__PURE__ */ jsx2(
|
|
396
|
+
"h2",
|
|
397
|
+
{
|
|
398
|
+
className: cn("yc-Dialog-title text-lg font-semibold leading-none tracking-tight text-fg", className),
|
|
399
|
+
"data-slot": "title",
|
|
400
|
+
...props
|
|
401
|
+
}
|
|
402
|
+
);
|
|
403
|
+
DialogTitle.displayName = "DialogTitle";
|
|
404
|
+
var DialogDescription = ({
|
|
405
|
+
className,
|
|
406
|
+
...props
|
|
407
|
+
}) => /* @__PURE__ */ jsx2(
|
|
408
|
+
"p",
|
|
409
|
+
{
|
|
410
|
+
className: cn("yc-Dialog-description text-sm text-fg-muted", className),
|
|
411
|
+
"data-slot": "description",
|
|
412
|
+
...props
|
|
413
|
+
}
|
|
414
|
+
);
|
|
415
|
+
DialogDescription.displayName = "DialogDescription";
|
|
416
|
+
var DialogClose = ({
|
|
417
|
+
className,
|
|
418
|
+
children,
|
|
419
|
+
onClick,
|
|
420
|
+
asChild = false,
|
|
421
|
+
...props
|
|
422
|
+
}) => {
|
|
423
|
+
const { onOpenChange } = useDialogContext();
|
|
424
|
+
return /* @__PURE__ */ jsx2(
|
|
425
|
+
"button",
|
|
426
|
+
{
|
|
427
|
+
type: "button",
|
|
428
|
+
className: cn("yc-Dialog-close", className),
|
|
429
|
+
"data-slot": "close",
|
|
430
|
+
onClick: (e) => {
|
|
431
|
+
onClick?.(e);
|
|
432
|
+
if (!e.defaultPrevented) onOpenChange(false);
|
|
433
|
+
},
|
|
434
|
+
...props,
|
|
435
|
+
children: children || "Close"
|
|
436
|
+
}
|
|
437
|
+
);
|
|
438
|
+
};
|
|
439
|
+
DialogClose.displayName = "DialogClose";
|
|
440
|
+
|
|
441
|
+
// src/primitives/Sheet.tsx
|
|
442
|
+
import * as React3 from "react";
|
|
443
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
444
|
+
var SheetContext = React3.createContext(null);
|
|
445
|
+
function useSheetContext() {
|
|
446
|
+
const context = React3.useContext(SheetContext);
|
|
447
|
+
if (!context) {
|
|
448
|
+
throw new Error("Sheet compound components must be rendered within a <Sheet>");
|
|
449
|
+
}
|
|
450
|
+
return context;
|
|
451
|
+
}
|
|
452
|
+
function Sheet({
|
|
453
|
+
open: controlledOpen,
|
|
454
|
+
defaultOpen = false,
|
|
455
|
+
onOpenChange,
|
|
456
|
+
side = "end",
|
|
457
|
+
children
|
|
458
|
+
}) {
|
|
459
|
+
const [uncontrolledOpen, setUncontrolledOpen] = React3.useState(defaultOpen);
|
|
460
|
+
const isControlled = controlledOpen !== void 0;
|
|
461
|
+
const isOpen = isControlled ? controlledOpen : uncontrolledOpen;
|
|
462
|
+
const triggerRef = React3.useRef(null);
|
|
463
|
+
const sheetId = React3.useId();
|
|
464
|
+
const handleOpenChange = React3.useCallback(
|
|
465
|
+
(newOpen) => {
|
|
466
|
+
if (!isControlled) {
|
|
467
|
+
setUncontrolledOpen(newOpen);
|
|
468
|
+
}
|
|
469
|
+
onOpenChange?.(newOpen);
|
|
470
|
+
},
|
|
471
|
+
[isControlled, onOpenChange]
|
|
472
|
+
);
|
|
473
|
+
return /* @__PURE__ */ jsx3(
|
|
474
|
+
SheetContext.Provider,
|
|
475
|
+
{
|
|
476
|
+
value: {
|
|
477
|
+
open: isOpen,
|
|
478
|
+
onOpenChange: handleOpenChange,
|
|
479
|
+
side,
|
|
480
|
+
sheetId,
|
|
481
|
+
triggerRef
|
|
482
|
+
},
|
|
483
|
+
children
|
|
484
|
+
}
|
|
485
|
+
);
|
|
486
|
+
}
|
|
487
|
+
var SheetTrigger = React3.forwardRef(
|
|
488
|
+
({ children, className, onClick, asChild = false, ...props }, ref) => {
|
|
489
|
+
const { open, onOpenChange, sheetId, triggerRef } = useSheetContext();
|
|
490
|
+
return /* @__PURE__ */ jsx3(
|
|
491
|
+
"button",
|
|
492
|
+
{
|
|
493
|
+
ref: (node) => {
|
|
494
|
+
triggerRef.current = node;
|
|
495
|
+
if (typeof ref === "function") ref(node);
|
|
496
|
+
else if (ref) ref.current = node;
|
|
497
|
+
},
|
|
498
|
+
type: "button",
|
|
499
|
+
"aria-haspopup": "dialog",
|
|
500
|
+
"aria-expanded": open,
|
|
501
|
+
"aria-controls": sheetId,
|
|
502
|
+
"data-slot": "trigger",
|
|
503
|
+
className: cn("yc-Sheet-trigger", className),
|
|
504
|
+
onClick: (e) => {
|
|
505
|
+
onClick?.(e);
|
|
506
|
+
if (!e.defaultPrevented) onOpenChange(!open);
|
|
507
|
+
},
|
|
508
|
+
...props,
|
|
509
|
+
children
|
|
510
|
+
}
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
);
|
|
514
|
+
SheetTrigger.displayName = "SheetTrigger";
|
|
515
|
+
var sideStyles = {
|
|
516
|
+
end: "inset-y-0 end-0 h-full w-3/4 max-w-md border-s border-border",
|
|
517
|
+
start: "inset-y-0 start-0 h-full w-3/4 max-w-md border-e border-border",
|
|
518
|
+
bottom: "inset-x-0 bottom-0 max-h-[85vh] border-t border-border rounded-t-xl"
|
|
519
|
+
};
|
|
520
|
+
var SheetContent = React3.forwardRef(
|
|
521
|
+
({ children, className, side: propSide, ...props }, ref) => {
|
|
522
|
+
const { open, onOpenChange, side: contextSide, sheetId, triggerRef } = useSheetContext();
|
|
523
|
+
const side = propSide || contextSide;
|
|
524
|
+
React3.useEffect(() => {
|
|
525
|
+
if (!open) return;
|
|
526
|
+
const handleKeyDown = (e) => {
|
|
527
|
+
if (e.key === "Escape") {
|
|
528
|
+
e.stopPropagation();
|
|
529
|
+
onOpenChange(false);
|
|
530
|
+
triggerRef.current?.focus();
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
534
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
535
|
+
}, [open, onOpenChange, triggerRef]);
|
|
536
|
+
if (!open) return null;
|
|
537
|
+
return /* @__PURE__ */ jsxs3("div", { className: "yc-Sheet-portal fixed inset-0 z-50 flex", "data-slot": "portal", children: [
|
|
538
|
+
/* @__PURE__ */ jsx3(
|
|
539
|
+
"div",
|
|
540
|
+
{
|
|
541
|
+
className: "yc-Sheet-overlay fixed inset-0 bg-overlay transition-opacity",
|
|
542
|
+
"data-slot": "overlay",
|
|
543
|
+
onClick: () => onOpenChange(false),
|
|
544
|
+
"aria-hidden": "true"
|
|
545
|
+
}
|
|
546
|
+
),
|
|
547
|
+
/* @__PURE__ */ jsx3(
|
|
548
|
+
"div",
|
|
549
|
+
{
|
|
550
|
+
ref,
|
|
551
|
+
id: sheetId,
|
|
552
|
+
role: "dialog",
|
|
553
|
+
"aria-modal": "true",
|
|
554
|
+
"data-slot": "content",
|
|
555
|
+
"data-side": side,
|
|
556
|
+
className: cn(
|
|
557
|
+
"yc-Sheet-content fixed z-10 bg-surface p-6 shadow-3 text-fg transition-transform",
|
|
558
|
+
sideStyles[side],
|
|
559
|
+
className
|
|
560
|
+
),
|
|
561
|
+
...props,
|
|
562
|
+
children
|
|
563
|
+
}
|
|
564
|
+
)
|
|
565
|
+
] });
|
|
566
|
+
}
|
|
567
|
+
);
|
|
568
|
+
SheetContent.displayName = "SheetContent";
|
|
569
|
+
var SheetHeader = ({
|
|
570
|
+
className,
|
|
571
|
+
...props
|
|
572
|
+
}) => /* @__PURE__ */ jsx3(
|
|
573
|
+
"div",
|
|
574
|
+
{
|
|
575
|
+
className: cn("yc-Sheet-header flex flex-col space-y-2 text-start pb-4", className),
|
|
576
|
+
"data-slot": "header",
|
|
577
|
+
...props
|
|
578
|
+
}
|
|
579
|
+
);
|
|
580
|
+
SheetHeader.displayName = "SheetHeader";
|
|
581
|
+
var SheetFooter = ({
|
|
582
|
+
className,
|
|
583
|
+
...props
|
|
584
|
+
}) => /* @__PURE__ */ jsx3(
|
|
585
|
+
"div",
|
|
586
|
+
{
|
|
587
|
+
className: cn("yc-Sheet-footer flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 pt-4", className),
|
|
588
|
+
"data-slot": "footer",
|
|
589
|
+
...props
|
|
590
|
+
}
|
|
591
|
+
);
|
|
592
|
+
SheetFooter.displayName = "SheetFooter";
|
|
593
|
+
var SheetTitle = ({
|
|
594
|
+
className,
|
|
595
|
+
...props
|
|
596
|
+
}) => /* @__PURE__ */ jsx3(
|
|
597
|
+
"h2",
|
|
598
|
+
{
|
|
599
|
+
className: cn("yc-Sheet-title text-lg font-semibold text-fg", className),
|
|
600
|
+
"data-slot": "title",
|
|
601
|
+
...props
|
|
602
|
+
}
|
|
603
|
+
);
|
|
604
|
+
SheetTitle.displayName = "SheetTitle";
|
|
605
|
+
var SheetDescription = ({
|
|
606
|
+
className,
|
|
607
|
+
...props
|
|
608
|
+
}) => /* @__PURE__ */ jsx3(
|
|
609
|
+
"p",
|
|
610
|
+
{
|
|
611
|
+
className: cn("yc-Sheet-description text-sm text-fg-muted", className),
|
|
612
|
+
"data-slot": "description",
|
|
613
|
+
...props
|
|
614
|
+
}
|
|
615
|
+
);
|
|
616
|
+
SheetDescription.displayName = "SheetDescription";
|
|
617
|
+
var SheetClose = ({
|
|
618
|
+
className,
|
|
619
|
+
children,
|
|
620
|
+
onClick,
|
|
621
|
+
...props
|
|
622
|
+
}) => {
|
|
623
|
+
const { onOpenChange } = useSheetContext();
|
|
624
|
+
return /* @__PURE__ */ jsx3(
|
|
625
|
+
"button",
|
|
626
|
+
{
|
|
627
|
+
type: "button",
|
|
628
|
+
className: cn("yc-Sheet-close", className),
|
|
629
|
+
"data-slot": "close",
|
|
630
|
+
onClick: (e) => {
|
|
631
|
+
onClick?.(e);
|
|
632
|
+
if (!e.defaultPrevented) onOpenChange(false);
|
|
633
|
+
},
|
|
634
|
+
...props,
|
|
635
|
+
children: children || "Close"
|
|
636
|
+
}
|
|
637
|
+
);
|
|
638
|
+
};
|
|
639
|
+
SheetClose.displayName = "SheetClose";
|
|
640
|
+
|
|
641
|
+
// src/primitives/Card.tsx
|
|
642
|
+
import * as React4 from "react";
|
|
643
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
644
|
+
var Card = React4.forwardRef(
|
|
645
|
+
({ className, variant = "default", ...props }, ref) => /* @__PURE__ */ jsx4(
|
|
646
|
+
"div",
|
|
647
|
+
{
|
|
648
|
+
ref,
|
|
649
|
+
"data-slot": "card",
|
|
650
|
+
"data-variant": variant,
|
|
651
|
+
className: cn(
|
|
652
|
+
"yc-Card-root rounded-lg border border-border bg-surface text-fg shadow-1",
|
|
653
|
+
variant === "muted" && "bg-surface-2",
|
|
654
|
+
variant === "interactive" && "hover:border-border-strong hover:bg-surface-2 transition-colors duration-fast cursor-pointer",
|
|
655
|
+
className
|
|
656
|
+
),
|
|
657
|
+
...props
|
|
658
|
+
}
|
|
659
|
+
)
|
|
660
|
+
);
|
|
661
|
+
Card.displayName = "Card";
|
|
662
|
+
var CardHeader = React4.forwardRef(
|
|
663
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx4(
|
|
664
|
+
"div",
|
|
665
|
+
{
|
|
666
|
+
ref,
|
|
667
|
+
"data-slot": "header",
|
|
668
|
+
className: cn("yc-Card-header flex flex-col space-y-1.5 p-6 text-start", className),
|
|
669
|
+
...props
|
|
670
|
+
}
|
|
671
|
+
)
|
|
672
|
+
);
|
|
673
|
+
CardHeader.displayName = "CardHeader";
|
|
674
|
+
var CardTitle = React4.forwardRef(
|
|
675
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx4(
|
|
676
|
+
"h3",
|
|
677
|
+
{
|
|
678
|
+
ref,
|
|
679
|
+
"data-slot": "title",
|
|
680
|
+
className: cn("yc-Card-title text-lg font-semibold leading-none tracking-tight text-fg", className),
|
|
681
|
+
...props
|
|
682
|
+
}
|
|
683
|
+
)
|
|
684
|
+
);
|
|
685
|
+
CardTitle.displayName = "CardTitle";
|
|
686
|
+
var CardDescription = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx4(
|
|
687
|
+
"p",
|
|
688
|
+
{
|
|
689
|
+
ref,
|
|
690
|
+
"data-slot": "description",
|
|
691
|
+
className: cn("yc-Card-description text-sm text-fg-muted", className),
|
|
692
|
+
...props
|
|
693
|
+
}
|
|
694
|
+
));
|
|
695
|
+
CardDescription.displayName = "CardDescription";
|
|
696
|
+
var CardContent = React4.forwardRef(
|
|
697
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx4("div", { ref, "data-slot": "content", className: cn("yc-Card-content p-6 pt-0 text-start", className), ...props })
|
|
698
|
+
);
|
|
699
|
+
CardContent.displayName = "CardContent";
|
|
700
|
+
var CardFooter = React4.forwardRef(
|
|
701
|
+
({ className, ...props }, ref) => /* @__PURE__ */ jsx4(
|
|
702
|
+
"div",
|
|
703
|
+
{
|
|
704
|
+
ref,
|
|
705
|
+
"data-slot": "footer",
|
|
706
|
+
className: cn("yc-Card-footer flex items-center p-6 pt-0", className),
|
|
707
|
+
...props
|
|
708
|
+
}
|
|
709
|
+
)
|
|
710
|
+
);
|
|
711
|
+
CardFooter.displayName = "CardFooter";
|
|
712
|
+
|
|
713
|
+
// src/primitives/Badge.tsx
|
|
714
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
715
|
+
var variantStyles2 = {
|
|
716
|
+
default: "bg-primary-soft text-primary-soft-fg border-transparent",
|
|
717
|
+
primary: "bg-primary text-primary-fg border-transparent",
|
|
718
|
+
neutral: "bg-surface-2 text-fg-muted border-border",
|
|
719
|
+
info: "bg-info-bg text-info-fg border-info-border",
|
|
720
|
+
warn: "bg-warn-bg text-warn-fg border-warn-border",
|
|
721
|
+
danger: "bg-danger-bg text-danger-fg border-danger-border",
|
|
722
|
+
success: "bg-success-bg text-success-fg border-success-border",
|
|
723
|
+
outline: "bg-transparent text-fg border-border-strong"
|
|
724
|
+
};
|
|
725
|
+
var Badge = ({
|
|
726
|
+
className,
|
|
727
|
+
variant = "default",
|
|
728
|
+
children,
|
|
729
|
+
...props
|
|
730
|
+
}) => {
|
|
731
|
+
return /* @__PURE__ */ jsx5(
|
|
732
|
+
"span",
|
|
733
|
+
{
|
|
734
|
+
"data-slot": "badge",
|
|
735
|
+
"data-variant": variant,
|
|
736
|
+
className: cn(
|
|
737
|
+
"yc-Badge-root inline-flex items-center rounded-sm border px-2.5 py-0.5 text-xs font-medium transition-colors",
|
|
738
|
+
variantStyles2[variant],
|
|
739
|
+
className
|
|
740
|
+
),
|
|
741
|
+
...props,
|
|
742
|
+
children
|
|
743
|
+
}
|
|
744
|
+
);
|
|
745
|
+
};
|
|
746
|
+
Badge.displayName = "Badge";
|
|
747
|
+
|
|
748
|
+
// src/primitives/Avatar.tsx
|
|
749
|
+
import * as React5 from "react";
|
|
750
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
751
|
+
var sizeStyles2 = {
|
|
752
|
+
sm: "h-8 w-8 text-xs",
|
|
753
|
+
md: "h-10 w-10 text-sm",
|
|
754
|
+
lg: "h-12 w-12 text-base",
|
|
755
|
+
xl: "h-16 w-16 text-lg"
|
|
756
|
+
};
|
|
757
|
+
var memberColorStyles = {
|
|
758
|
+
"1": "bg-member-1-bg text-member-1-fg",
|
|
759
|
+
"2": "bg-member-2-bg text-member-2-fg",
|
|
760
|
+
"3": "bg-member-3-bg text-member-3-fg",
|
|
761
|
+
"4": "bg-member-4-bg text-member-4-fg",
|
|
762
|
+
"5": "bg-member-5-bg text-member-5-fg"
|
|
763
|
+
};
|
|
764
|
+
var Avatar = ({
|
|
765
|
+
src,
|
|
766
|
+
alt = "",
|
|
767
|
+
name,
|
|
768
|
+
size = "md",
|
|
769
|
+
colorSlot = "1",
|
|
770
|
+
children,
|
|
771
|
+
className,
|
|
772
|
+
...props
|
|
773
|
+
}) => {
|
|
774
|
+
const [hasError, setHasError] = React5.useState(false);
|
|
775
|
+
const initials = React5.useMemo(() => {
|
|
776
|
+
if (!name) return "?";
|
|
777
|
+
const parts = name.trim().split(/\s+/);
|
|
778
|
+
if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
|
|
779
|
+
return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
|
|
780
|
+
}, [name]);
|
|
781
|
+
const showImage = src && !hasError;
|
|
782
|
+
return /* @__PURE__ */ jsx6(
|
|
783
|
+
"div",
|
|
784
|
+
{
|
|
785
|
+
"data-slot": "avatar",
|
|
786
|
+
"data-size": size,
|
|
787
|
+
"data-color-slot": colorSlot,
|
|
788
|
+
className: cn(
|
|
789
|
+
"yc-Avatar-root relative inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full font-medium select-none border border-border",
|
|
790
|
+
sizeStyles2[size],
|
|
791
|
+
memberColorStyles[colorSlot],
|
|
792
|
+
className
|
|
793
|
+
),
|
|
794
|
+
...props,
|
|
795
|
+
children: children ? children : showImage ? /* @__PURE__ */ jsx6(
|
|
796
|
+
"img",
|
|
797
|
+
{
|
|
798
|
+
src,
|
|
799
|
+
alt: alt || name || "Avatar",
|
|
800
|
+
onError: () => setHasError(true),
|
|
801
|
+
"data-slot": "avatar-image",
|
|
802
|
+
className: "yc-Avatar-image h-full w-full object-cover"
|
|
803
|
+
}
|
|
804
|
+
) : /* @__PURE__ */ jsx6("span", { "data-slot": "avatar-fallback", className: "yc-Avatar-fallback leading-none", children: initials })
|
|
805
|
+
}
|
|
806
|
+
);
|
|
807
|
+
};
|
|
808
|
+
Avatar.displayName = "Avatar";
|
|
809
|
+
var AvatarImage = ({ className, alt = "", ...props }) => /* @__PURE__ */ jsx6(
|
|
810
|
+
"img",
|
|
811
|
+
{
|
|
812
|
+
"data-slot": "avatar-image",
|
|
813
|
+
alt,
|
|
814
|
+
className: cn("yc-Avatar-image h-full w-full object-cover", className),
|
|
815
|
+
...props
|
|
816
|
+
}
|
|
817
|
+
);
|
|
818
|
+
AvatarImage.displayName = "AvatarImage";
|
|
819
|
+
var AvatarFallback = ({ className, children, ...props }) => /* @__PURE__ */ jsx6(
|
|
820
|
+
"span",
|
|
821
|
+
{
|
|
822
|
+
"data-slot": "avatar-fallback",
|
|
823
|
+
className: cn("yc-Avatar-fallback leading-none font-medium", className),
|
|
824
|
+
...props,
|
|
825
|
+
children
|
|
826
|
+
}
|
|
827
|
+
);
|
|
828
|
+
AvatarFallback.displayName = "AvatarFallback";
|
|
829
|
+
|
|
830
|
+
// src/primitives/Chip.tsx
|
|
831
|
+
import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
832
|
+
var scopeStyles = {
|
|
833
|
+
default: {
|
|
834
|
+
default: "bg-surface text-fg border-border hover:bg-surface-2",
|
|
835
|
+
selected: "bg-primary text-primary-fg border-primary"
|
|
836
|
+
},
|
|
837
|
+
health: {
|
|
838
|
+
default: "bg-scope-health-bg text-scope-health-fg border-transparent hover:opacity-90",
|
|
839
|
+
selected: "bg-primary text-primary-fg border-primary"
|
|
840
|
+
},
|
|
841
|
+
finance: {
|
|
842
|
+
default: "bg-scope-finance-bg text-scope-finance-fg border-transparent hover:opacity-90",
|
|
843
|
+
selected: "bg-primary text-primary-fg border-primary"
|
|
844
|
+
},
|
|
845
|
+
education: {
|
|
846
|
+
default: "bg-scope-education-bg text-scope-education-fg border-transparent hover:opacity-90",
|
|
847
|
+
selected: "bg-primary text-primary-fg border-primary"
|
|
848
|
+
},
|
|
849
|
+
household: {
|
|
850
|
+
default: "bg-scope-household-bg text-scope-household-fg border-transparent hover:opacity-90",
|
|
851
|
+
selected: "bg-primary text-primary-fg border-primary"
|
|
852
|
+
}
|
|
853
|
+
};
|
|
854
|
+
var Chip = ({
|
|
855
|
+
children,
|
|
856
|
+
selected = false,
|
|
857
|
+
scope = "default",
|
|
858
|
+
onRemove,
|
|
859
|
+
startIcon,
|
|
860
|
+
className,
|
|
861
|
+
onClick,
|
|
862
|
+
disabled = false,
|
|
863
|
+
...props
|
|
864
|
+
}) => {
|
|
865
|
+
return /* @__PURE__ */ jsxs4(
|
|
866
|
+
"button",
|
|
867
|
+
{
|
|
868
|
+
type: "button",
|
|
869
|
+
"data-slot": "chip",
|
|
870
|
+
"data-selected": selected ? "true" : void 0,
|
|
871
|
+
"data-scope": scope,
|
|
872
|
+
disabled,
|
|
873
|
+
onClick,
|
|
874
|
+
className: cn(
|
|
875
|
+
"yc-Chip-root inline-flex items-center rounded-sm border px-3 py-1 text-xs font-medium transition-colors select-none",
|
|
876
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-focus",
|
|
877
|
+
"disabled:opacity-50 disabled:cursor-not-allowed",
|
|
878
|
+
selected ? scopeStyles[scope].selected : scopeStyles[scope].default,
|
|
879
|
+
className
|
|
880
|
+
),
|
|
881
|
+
...props,
|
|
882
|
+
children: [
|
|
883
|
+
startIcon && /* @__PURE__ */ jsx7("span", { className: "yc-Chip-icon me-1.5 inline-flex shrink-0 items-center", "data-slot": "icon", children: startIcon }),
|
|
884
|
+
/* @__PURE__ */ jsx7("span", { className: "yc-Chip-label truncate", "data-slot": "label", children }),
|
|
885
|
+
onRemove && /* @__PURE__ */ jsx7(
|
|
886
|
+
"span",
|
|
887
|
+
{
|
|
888
|
+
role: "button",
|
|
889
|
+
tabIndex: 0,
|
|
890
|
+
"aria-label": "Remove",
|
|
891
|
+
"data-slot": "remove",
|
|
892
|
+
onClick: (e) => {
|
|
893
|
+
e.stopPropagation();
|
|
894
|
+
onRemove();
|
|
895
|
+
},
|
|
896
|
+
className: "yc-Chip-remove ms-1.5 inline-flex items-center justify-center rounded-full p-0.5 hover:bg-black/10 focus:outline-none",
|
|
897
|
+
children: /* @__PURE__ */ jsx7("svg", { className: "h-3 w-3", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx7("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
|
|
898
|
+
}
|
|
899
|
+
)
|
|
900
|
+
]
|
|
901
|
+
}
|
|
902
|
+
);
|
|
903
|
+
};
|
|
904
|
+
Chip.displayName = "Chip";
|
|
905
|
+
|
|
906
|
+
// src/primitives/Menu.tsx
|
|
907
|
+
import * as React6 from "react";
|
|
908
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
909
|
+
var MenuContext = React6.createContext(null);
|
|
910
|
+
function useMenuContext() {
|
|
911
|
+
const ctx = React6.useContext(MenuContext);
|
|
912
|
+
if (!ctx) throw new Error("Menu compound components must be rendered inside <Menu>");
|
|
913
|
+
return ctx;
|
|
914
|
+
}
|
|
915
|
+
function Menu({ open: controlledOpen, defaultOpen = false, onOpenChange, children }) {
|
|
916
|
+
const [uncontrolledOpen, setUncontrolledOpen] = React6.useState(defaultOpen);
|
|
917
|
+
const isControlled = controlledOpen !== void 0;
|
|
918
|
+
const isOpen = isControlled ? controlledOpen : uncontrolledOpen;
|
|
919
|
+
const triggerRef = React6.useRef(null);
|
|
920
|
+
const menuId = React6.useId();
|
|
921
|
+
const setOpen = React6.useCallback(
|
|
922
|
+
(next) => {
|
|
923
|
+
if (!isControlled) setUncontrolledOpen(next);
|
|
924
|
+
onOpenChange?.(next);
|
|
925
|
+
},
|
|
926
|
+
[isControlled, onOpenChange]
|
|
927
|
+
);
|
|
928
|
+
return /* @__PURE__ */ jsx8(MenuContext.Provider, { value: { open: isOpen, setOpen, triggerRef, menuId }, children: /* @__PURE__ */ jsx8("div", { className: "yc-Menu-root relative inline-block text-start", "data-slot": "root", children }) });
|
|
929
|
+
}
|
|
930
|
+
var MenuTrigger = React6.forwardRef(
|
|
931
|
+
({ children, className, onClick, ...props }, ref) => {
|
|
932
|
+
const { open, setOpen, menuId, triggerRef } = useMenuContext();
|
|
933
|
+
return /* @__PURE__ */ jsx8(
|
|
934
|
+
"button",
|
|
935
|
+
{
|
|
936
|
+
ref: (node) => {
|
|
937
|
+
triggerRef.current = node;
|
|
938
|
+
if (typeof ref === "function") ref(node);
|
|
939
|
+
else if (ref) ref.current = node;
|
|
940
|
+
},
|
|
941
|
+
type: "button",
|
|
942
|
+
"aria-haspopup": "menu",
|
|
943
|
+
"aria-expanded": open,
|
|
944
|
+
"aria-controls": menuId,
|
|
945
|
+
"data-slot": "trigger",
|
|
946
|
+
className: cn("yc-Menu-trigger inline-flex items-center justify-center", className),
|
|
947
|
+
onClick: (e) => {
|
|
948
|
+
onClick?.(e);
|
|
949
|
+
if (!e.defaultPrevented) setOpen(!open);
|
|
950
|
+
},
|
|
951
|
+
...props,
|
|
952
|
+
children
|
|
953
|
+
}
|
|
954
|
+
);
|
|
955
|
+
}
|
|
956
|
+
);
|
|
957
|
+
MenuTrigger.displayName = "MenuTrigger";
|
|
958
|
+
var MenuContent = React6.forwardRef(
|
|
959
|
+
({ children, className, align = "start", ...props }, ref) => {
|
|
960
|
+
const { open, setOpen, menuId, triggerRef } = useMenuContext();
|
|
961
|
+
const contentRef = React6.useRef(null);
|
|
962
|
+
React6.useEffect(() => {
|
|
963
|
+
if (!open) return;
|
|
964
|
+
const handleClickOutside = (e) => {
|
|
965
|
+
if (contentRef.current && !contentRef.current.contains(e.target) && triggerRef.current && !triggerRef.current.contains(e.target)) {
|
|
966
|
+
setOpen(false);
|
|
967
|
+
}
|
|
968
|
+
};
|
|
969
|
+
const handleKeyDown = (e) => {
|
|
970
|
+
if (e.key === "Escape") {
|
|
971
|
+
e.stopPropagation();
|
|
972
|
+
setOpen(false);
|
|
973
|
+
triggerRef.current?.focus();
|
|
974
|
+
}
|
|
975
|
+
};
|
|
976
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
977
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
978
|
+
return () => {
|
|
979
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
980
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
981
|
+
};
|
|
982
|
+
}, [open, setOpen, triggerRef]);
|
|
983
|
+
if (!open) return null;
|
|
984
|
+
const alignClass = align === "end" ? "end-0" : align === "center" ? "start-1/2 -translate-x-1/2" : "start-0";
|
|
985
|
+
return /* @__PURE__ */ jsx8(
|
|
986
|
+
"div",
|
|
987
|
+
{
|
|
988
|
+
ref: (node) => {
|
|
989
|
+
contentRef.current = node;
|
|
990
|
+
if (typeof ref === "function") ref(node);
|
|
991
|
+
else if (ref) ref.current = node;
|
|
992
|
+
},
|
|
993
|
+
id: menuId,
|
|
994
|
+
role: "menu",
|
|
995
|
+
"aria-orientation": "vertical",
|
|
996
|
+
tabIndex: -1,
|
|
997
|
+
"data-slot": "content",
|
|
998
|
+
className: cn(
|
|
999
|
+
"yc-Menu-content absolute z-50 mt-1 min-w-[12rem] rounded-md bg-surface p-1 text-fg shadow-2 border border-border focus:outline-none",
|
|
1000
|
+
alignClass,
|
|
1001
|
+
className
|
|
1002
|
+
),
|
|
1003
|
+
...props,
|
|
1004
|
+
children
|
|
1005
|
+
}
|
|
1006
|
+
);
|
|
1007
|
+
}
|
|
1008
|
+
);
|
|
1009
|
+
MenuContent.displayName = "MenuContent";
|
|
1010
|
+
var MenuItem = React6.forwardRef(
|
|
1011
|
+
({ children, className, destructive = false, onClick, disabled = false, ...props }, ref) => {
|
|
1012
|
+
const { setOpen } = useMenuContext();
|
|
1013
|
+
return /* @__PURE__ */ jsx8(
|
|
1014
|
+
"button",
|
|
1015
|
+
{
|
|
1016
|
+
ref,
|
|
1017
|
+
type: "button",
|
|
1018
|
+
role: "menuitem",
|
|
1019
|
+
tabIndex: -1,
|
|
1020
|
+
disabled,
|
|
1021
|
+
"data-slot": "item",
|
|
1022
|
+
"data-destructive": destructive ? "true" : void 0,
|
|
1023
|
+
className: cn(
|
|
1024
|
+
"yc-Menu-item group relative flex w-full cursor-pointer select-none items-center rounded-sm px-2.5 py-1.5 text-sm outline-none transition-colors duration-fast",
|
|
1025
|
+
"hover:bg-surface-2 focus:bg-surface-2",
|
|
1026
|
+
destructive ? "text-danger-fg hover:bg-danger-bg focus:bg-danger-bg" : "text-fg hover:text-fg",
|
|
1027
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
1028
|
+
className
|
|
1029
|
+
),
|
|
1030
|
+
onClick: (e) => {
|
|
1031
|
+
onClick?.(e);
|
|
1032
|
+
if (!e.defaultPrevented) setOpen(false);
|
|
1033
|
+
},
|
|
1034
|
+
...props,
|
|
1035
|
+
children
|
|
1036
|
+
}
|
|
1037
|
+
);
|
|
1038
|
+
}
|
|
1039
|
+
);
|
|
1040
|
+
MenuItem.displayName = "MenuItem";
|
|
1041
|
+
var MenuSeparator = ({ className, ...props }) => /* @__PURE__ */ jsx8(
|
|
1042
|
+
"div",
|
|
1043
|
+
{
|
|
1044
|
+
role: "separator",
|
|
1045
|
+
"data-slot": "separator",
|
|
1046
|
+
className: cn("yc-Menu-separator my-1 h-px bg-border", className),
|
|
1047
|
+
...props
|
|
1048
|
+
}
|
|
1049
|
+
);
|
|
1050
|
+
MenuSeparator.displayName = "MenuSeparator";
|
|
1051
|
+
|
|
1052
|
+
// src/primitives/Popover.tsx
|
|
1053
|
+
import * as React7 from "react";
|
|
1054
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
1055
|
+
var PopoverContext = React7.createContext(null);
|
|
1056
|
+
function usePopoverContext() {
|
|
1057
|
+
const ctx = React7.useContext(PopoverContext);
|
|
1058
|
+
if (!ctx) throw new Error("Popover compound components must be rendered inside <Popover>");
|
|
1059
|
+
return ctx;
|
|
1060
|
+
}
|
|
1061
|
+
function Popover({ open: controlledOpen, defaultOpen = false, onOpenChange, children }) {
|
|
1062
|
+
const [uncontrolledOpen, setUncontrolledOpen] = React7.useState(defaultOpen);
|
|
1063
|
+
const isControlled = controlledOpen !== void 0;
|
|
1064
|
+
const isOpen = isControlled ? controlledOpen : uncontrolledOpen;
|
|
1065
|
+
const triggerRef = React7.useRef(null);
|
|
1066
|
+
const popoverId = React7.useId();
|
|
1067
|
+
const setOpen = React7.useCallback(
|
|
1068
|
+
(next) => {
|
|
1069
|
+
if (!isControlled) setUncontrolledOpen(next);
|
|
1070
|
+
onOpenChange?.(next);
|
|
1071
|
+
},
|
|
1072
|
+
[isControlled, onOpenChange]
|
|
1073
|
+
);
|
|
1074
|
+
return /* @__PURE__ */ jsx9(PopoverContext.Provider, { value: { open: isOpen, setOpen, triggerRef, popoverId }, children: /* @__PURE__ */ jsx9("div", { className: "yc-Popover-root relative inline-block text-start", "data-slot": "root", children }) });
|
|
1075
|
+
}
|
|
1076
|
+
var PopoverTrigger = React7.forwardRef(
|
|
1077
|
+
({ children, className, onClick, ...props }, ref) => {
|
|
1078
|
+
const { open, setOpen, popoverId, triggerRef } = usePopoverContext();
|
|
1079
|
+
return /* @__PURE__ */ jsx9(
|
|
1080
|
+
"button",
|
|
1081
|
+
{
|
|
1082
|
+
ref: (node) => {
|
|
1083
|
+
triggerRef.current = node;
|
|
1084
|
+
if (typeof ref === "function") ref(node);
|
|
1085
|
+
else if (ref) ref.current = node;
|
|
1086
|
+
},
|
|
1087
|
+
type: "button",
|
|
1088
|
+
"aria-haspopup": "dialog",
|
|
1089
|
+
"aria-expanded": open,
|
|
1090
|
+
"aria-controls": popoverId,
|
|
1091
|
+
"data-slot": "trigger",
|
|
1092
|
+
className: cn("yc-Popover-trigger inline-flex items-center justify-center", className),
|
|
1093
|
+
onClick: (e) => {
|
|
1094
|
+
onClick?.(e);
|
|
1095
|
+
if (!e.defaultPrevented) setOpen(!open);
|
|
1096
|
+
},
|
|
1097
|
+
...props,
|
|
1098
|
+
children
|
|
1099
|
+
}
|
|
1100
|
+
);
|
|
1101
|
+
}
|
|
1102
|
+
);
|
|
1103
|
+
PopoverTrigger.displayName = "PopoverTrigger";
|
|
1104
|
+
var PopoverContent = React7.forwardRef(
|
|
1105
|
+
({ children, className, align = "start", ...props }, ref) => {
|
|
1106
|
+
const { open, setOpen, popoverId, triggerRef } = usePopoverContext();
|
|
1107
|
+
const contentRef = React7.useRef(null);
|
|
1108
|
+
React7.useEffect(() => {
|
|
1109
|
+
if (!open) return;
|
|
1110
|
+
const handleClickOutside = (e) => {
|
|
1111
|
+
if (contentRef.current && !contentRef.current.contains(e.target) && triggerRef.current && !triggerRef.current.contains(e.target)) {
|
|
1112
|
+
setOpen(false);
|
|
1113
|
+
}
|
|
1114
|
+
};
|
|
1115
|
+
const handleKeyDown = (e) => {
|
|
1116
|
+
if (e.key === "Escape") {
|
|
1117
|
+
e.stopPropagation();
|
|
1118
|
+
setOpen(false);
|
|
1119
|
+
triggerRef.current?.focus();
|
|
1120
|
+
}
|
|
1121
|
+
};
|
|
1122
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
1123
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
1124
|
+
return () => {
|
|
1125
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
1126
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
1127
|
+
};
|
|
1128
|
+
}, [open, setOpen, triggerRef]);
|
|
1129
|
+
if (!open) return null;
|
|
1130
|
+
const alignClass = align === "end" ? "end-0" : align === "center" ? "start-1/2 -translate-x-1/2" : "start-0";
|
|
1131
|
+
return /* @__PURE__ */ jsx9(
|
|
1132
|
+
"div",
|
|
1133
|
+
{
|
|
1134
|
+
ref: (node) => {
|
|
1135
|
+
contentRef.current = node;
|
|
1136
|
+
if (typeof ref === "function") ref(node);
|
|
1137
|
+
else if (ref) ref.current = node;
|
|
1138
|
+
},
|
|
1139
|
+
id: popoverId,
|
|
1140
|
+
role: "dialog",
|
|
1141
|
+
"data-slot": "content",
|
|
1142
|
+
className: cn(
|
|
1143
|
+
"yc-Popover-content absolute z-50 mt-1 min-w-[14rem] rounded-lg bg-surface p-4 text-fg shadow-2 border border-border focus:outline-none",
|
|
1144
|
+
alignClass,
|
|
1145
|
+
className
|
|
1146
|
+
),
|
|
1147
|
+
...props,
|
|
1148
|
+
children
|
|
1149
|
+
}
|
|
1150
|
+
);
|
|
1151
|
+
}
|
|
1152
|
+
);
|
|
1153
|
+
PopoverContent.displayName = "PopoverContent";
|
|
1154
|
+
|
|
1155
|
+
// src/primitives/Tabs.tsx
|
|
1156
|
+
import * as React8 from "react";
|
|
1157
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
1158
|
+
var TabsContext = React8.createContext(null);
|
|
1159
|
+
function useTabsContext() {
|
|
1160
|
+
const ctx = React8.useContext(TabsContext);
|
|
1161
|
+
if (!ctx) throw new Error("Tabs compound components must be rendered inside <Tabs>");
|
|
1162
|
+
return ctx;
|
|
1163
|
+
}
|
|
1164
|
+
function Tabs({
|
|
1165
|
+
value: controlledValue,
|
|
1166
|
+
defaultValue = "",
|
|
1167
|
+
onValueChange,
|
|
1168
|
+
children,
|
|
1169
|
+
className,
|
|
1170
|
+
...props
|
|
1171
|
+
}) {
|
|
1172
|
+
const [uncontrolledValue, setUncontrolledValue] = React8.useState(defaultValue);
|
|
1173
|
+
const isControlled = controlledValue !== void 0;
|
|
1174
|
+
const activeValue = isControlled ? controlledValue : uncontrolledValue;
|
|
1175
|
+
const baseId = React8.useId();
|
|
1176
|
+
const handleValueChange = React8.useCallback(
|
|
1177
|
+
(newVal) => {
|
|
1178
|
+
if (!isControlled) setUncontrolledValue(newVal);
|
|
1179
|
+
onValueChange?.(newVal);
|
|
1180
|
+
},
|
|
1181
|
+
[isControlled, onValueChange]
|
|
1182
|
+
);
|
|
1183
|
+
return /* @__PURE__ */ jsx10(TabsContext.Provider, { value: { value: activeValue, onValueChange: handleValueChange, baseId }, children: /* @__PURE__ */ jsx10("div", { className: cn("yc-Tabs-root flex flex-col", className), "data-slot": "root", ...props, children }) });
|
|
1184
|
+
}
|
|
1185
|
+
var TabsList = React8.forwardRef(
|
|
1186
|
+
({ children, className, ...props }, ref) => {
|
|
1187
|
+
return /* @__PURE__ */ jsx10(
|
|
1188
|
+
"div",
|
|
1189
|
+
{
|
|
1190
|
+
ref,
|
|
1191
|
+
role: "tablist",
|
|
1192
|
+
"data-slot": "list",
|
|
1193
|
+
className: cn(
|
|
1194
|
+
"yc-Tabs-list inline-flex h-10 items-center justify-start rounded-lg bg-surface-2 p-1 text-fg-muted",
|
|
1195
|
+
className
|
|
1196
|
+
),
|
|
1197
|
+
...props,
|
|
1198
|
+
children
|
|
1199
|
+
}
|
|
1200
|
+
);
|
|
1201
|
+
}
|
|
1202
|
+
);
|
|
1203
|
+
TabsList.displayName = "TabsList";
|
|
1204
|
+
var TabsTrigger = React8.forwardRef(
|
|
1205
|
+
({ value, children, className, disabled = false, ...props }, ref) => {
|
|
1206
|
+
const { value: activeValue, onValueChange, baseId } = useTabsContext();
|
|
1207
|
+
const isSelected = activeValue === value;
|
|
1208
|
+
const tabId = `${baseId}-tab-${value}`;
|
|
1209
|
+
const panelId = `${baseId}-panel-${value}`;
|
|
1210
|
+
return /* @__PURE__ */ jsx10(
|
|
1211
|
+
"button",
|
|
1212
|
+
{
|
|
1213
|
+
ref,
|
|
1214
|
+
id: tabId,
|
|
1215
|
+
type: "button",
|
|
1216
|
+
role: "tab",
|
|
1217
|
+
"aria-selected": isSelected,
|
|
1218
|
+
"aria-controls": panelId,
|
|
1219
|
+
tabIndex: isSelected ? 0 : -1,
|
|
1220
|
+
disabled,
|
|
1221
|
+
"data-slot": "trigger",
|
|
1222
|
+
"data-state": isSelected ? "active" : "inactive",
|
|
1223
|
+
className: cn(
|
|
1224
|
+
"yc-Tabs-trigger inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1.5 text-sm font-medium transition-all duration-fast",
|
|
1225
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-focus focus-visible:ring-offset-2",
|
|
1226
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
1227
|
+
isSelected ? "bg-surface text-fg shadow-1" : "text-fg-muted hover:text-fg hover:bg-surface-3",
|
|
1228
|
+
className
|
|
1229
|
+
),
|
|
1230
|
+
onClick: () => onValueChange(value),
|
|
1231
|
+
...props,
|
|
1232
|
+
children
|
|
1233
|
+
}
|
|
1234
|
+
);
|
|
1235
|
+
}
|
|
1236
|
+
);
|
|
1237
|
+
TabsTrigger.displayName = "TabsTrigger";
|
|
1238
|
+
var TabsContent = React8.forwardRef(
|
|
1239
|
+
({ value, children, className, ...props }, ref) => {
|
|
1240
|
+
const { value: activeValue, baseId } = useTabsContext();
|
|
1241
|
+
const isSelected = activeValue === value;
|
|
1242
|
+
const tabId = `${baseId}-tab-${value}`;
|
|
1243
|
+
const panelId = `${baseId}-panel-${value}`;
|
|
1244
|
+
if (!isSelected) return null;
|
|
1245
|
+
return /* @__PURE__ */ jsx10(
|
|
1246
|
+
"div",
|
|
1247
|
+
{
|
|
1248
|
+
ref,
|
|
1249
|
+
id: panelId,
|
|
1250
|
+
role: "tabpanel",
|
|
1251
|
+
"aria-labelledby": tabId,
|
|
1252
|
+
tabIndex: 0,
|
|
1253
|
+
"data-slot": "content",
|
|
1254
|
+
className: cn(
|
|
1255
|
+
"yc-Tabs-content mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-focus focus-visible:ring-offset-2",
|
|
1256
|
+
className
|
|
1257
|
+
),
|
|
1258
|
+
...props,
|
|
1259
|
+
children
|
|
1260
|
+
}
|
|
1261
|
+
);
|
|
1262
|
+
}
|
|
1263
|
+
);
|
|
1264
|
+
TabsContent.displayName = "TabsContent";
|
|
1265
|
+
|
|
1266
|
+
// src/primitives/Segmented.tsx
|
|
1267
|
+
import * as React9 from "react";
|
|
1268
|
+
import { jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1269
|
+
function SegmentedControl({
|
|
1270
|
+
options,
|
|
1271
|
+
value: controlledValue,
|
|
1272
|
+
defaultValue,
|
|
1273
|
+
onChange,
|
|
1274
|
+
className,
|
|
1275
|
+
size = "md"
|
|
1276
|
+
}) {
|
|
1277
|
+
const [uncontrolledValue, setUncontrolledValue] = React9.useState(
|
|
1278
|
+
defaultValue || (options.length > 0 ? options[0].value : "")
|
|
1279
|
+
);
|
|
1280
|
+
const isControlled = controlledValue !== void 0;
|
|
1281
|
+
const activeValue = isControlled ? controlledValue : uncontrolledValue;
|
|
1282
|
+
const handleClick = (optValue) => {
|
|
1283
|
+
if (!isControlled) {
|
|
1284
|
+
setUncontrolledValue(optValue);
|
|
1285
|
+
}
|
|
1286
|
+
onChange?.(optValue);
|
|
1287
|
+
};
|
|
1288
|
+
const sizeClasses = size === "sm" ? "h-8 p-0.5 text-xs" : "min-h-[44px] h-10 p-1 text-sm";
|
|
1289
|
+
return /* @__PURE__ */ jsx11(
|
|
1290
|
+
"div",
|
|
1291
|
+
{
|
|
1292
|
+
role: "radiogroup",
|
|
1293
|
+
"data-slot": "segmented-control",
|
|
1294
|
+
"data-size": size,
|
|
1295
|
+
className: cn(
|
|
1296
|
+
"yc-Segmented-root inline-flex items-center rounded-lg bg-surface-2 text-fg-muted border border-border select-none",
|
|
1297
|
+
sizeClasses,
|
|
1298
|
+
className
|
|
1299
|
+
),
|
|
1300
|
+
children: options.map((opt) => {
|
|
1301
|
+
const isSelected = opt.value === activeValue;
|
|
1302
|
+
return /* @__PURE__ */ jsxs5(
|
|
1303
|
+
"button",
|
|
1304
|
+
{
|
|
1305
|
+
type: "button",
|
|
1306
|
+
role: "radio",
|
|
1307
|
+
"aria-checked": isSelected,
|
|
1308
|
+
disabled: opt.disabled,
|
|
1309
|
+
"data-slot": "item",
|
|
1310
|
+
"data-state": isSelected ? "checked" : "unchecked",
|
|
1311
|
+
onClick: () => handleClick(opt.value),
|
|
1312
|
+
className: cn(
|
|
1313
|
+
"yc-Segmented-item inline-flex h-full items-center justify-center gap-1.5 rounded-md px-3 font-medium transition-all duration-fast",
|
|
1314
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-focus",
|
|
1315
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
1316
|
+
isSelected ? "bg-surface text-fg shadow-1" : "text-fg-muted hover:text-fg hover:bg-surface-3"
|
|
1317
|
+
),
|
|
1318
|
+
children: [
|
|
1319
|
+
opt.icon && /* @__PURE__ */ jsx11("span", { className: "yc-Segmented-icon inline-flex shrink-0 items-center", "data-slot": "icon", children: opt.icon }),
|
|
1320
|
+
/* @__PURE__ */ jsx11("span", { className: "yc-Segmented-label truncate", "data-slot": "label", children: opt.label })
|
|
1321
|
+
]
|
|
1322
|
+
},
|
|
1323
|
+
opt.value
|
|
1324
|
+
);
|
|
1325
|
+
})
|
|
1326
|
+
}
|
|
1327
|
+
);
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
// src/primitives/EmptyState.tsx
|
|
1331
|
+
import * as React10 from "react";
|
|
1332
|
+
import { jsx as jsx12, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1333
|
+
var EmptyState = React10.forwardRef(
|
|
1334
|
+
({ icon, title, description, action, className, ...props }, ref) => {
|
|
1335
|
+
return /* @__PURE__ */ jsxs6(
|
|
1336
|
+
"div",
|
|
1337
|
+
{
|
|
1338
|
+
ref,
|
|
1339
|
+
"data-slot": "empty-state",
|
|
1340
|
+
className: cn(
|
|
1341
|
+
"yc-EmptyState-root flex flex-col items-center justify-center p-8 text-center",
|
|
1342
|
+
className
|
|
1343
|
+
),
|
|
1344
|
+
...props,
|
|
1345
|
+
children: [
|
|
1346
|
+
icon && /* @__PURE__ */ jsx12(
|
|
1347
|
+
"div",
|
|
1348
|
+
{
|
|
1349
|
+
className: "yc-EmptyState-icon mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-surface-2 text-fg-muted",
|
|
1350
|
+
"data-slot": "icon",
|
|
1351
|
+
children: icon
|
|
1352
|
+
}
|
|
1353
|
+
),
|
|
1354
|
+
/* @__PURE__ */ jsx12("h3", { className: "yc-EmptyState-title text-base font-medium text-fg", "data-slot": "title", children: title }),
|
|
1355
|
+
description && /* @__PURE__ */ jsx12(
|
|
1356
|
+
"p",
|
|
1357
|
+
{
|
|
1358
|
+
className: "yc-EmptyState-description mt-1.5 max-w-sm text-sm text-fg-muted",
|
|
1359
|
+
"data-slot": "description",
|
|
1360
|
+
children: description
|
|
1361
|
+
}
|
|
1362
|
+
),
|
|
1363
|
+
action && /* @__PURE__ */ jsx12("div", { className: "yc-EmptyState-action mt-5", "data-slot": "action", children: action })
|
|
1364
|
+
]
|
|
1365
|
+
}
|
|
1366
|
+
);
|
|
1367
|
+
}
|
|
1368
|
+
);
|
|
1369
|
+
EmptyState.displayName = "EmptyState";
|
|
1370
|
+
|
|
1371
|
+
// src/primitives/Skeleton.tsx
|
|
1372
|
+
import * as React11 from "react";
|
|
1373
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
1374
|
+
var Skeleton = React11.forwardRef(
|
|
1375
|
+
({ className, ...props }, ref) => {
|
|
1376
|
+
return /* @__PURE__ */ jsx13(
|
|
1377
|
+
"div",
|
|
1378
|
+
{
|
|
1379
|
+
ref,
|
|
1380
|
+
"data-slot": "skeleton",
|
|
1381
|
+
className: cn("yc-Skeleton-root animate-pulse rounded-md bg-surface-3", className),
|
|
1382
|
+
...props
|
|
1383
|
+
}
|
|
1384
|
+
);
|
|
1385
|
+
}
|
|
1386
|
+
);
|
|
1387
|
+
Skeleton.displayName = "Skeleton";
|
|
1388
|
+
|
|
1389
|
+
// src/primitives/InlineError.tsx
|
|
1390
|
+
import * as React12 from "react";
|
|
1391
|
+
import { jsx as jsx14, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1392
|
+
var InlineError = React12.forwardRef(
|
|
1393
|
+
({ message, icon, children, className, ...props }, ref) => {
|
|
1394
|
+
const content = message || children;
|
|
1395
|
+
if (!content) return null;
|
|
1396
|
+
return /* @__PURE__ */ jsxs7(
|
|
1397
|
+
"div",
|
|
1398
|
+
{
|
|
1399
|
+
ref,
|
|
1400
|
+
role: "alert",
|
|
1401
|
+
"data-slot": "inline-error",
|
|
1402
|
+
className: cn(
|
|
1403
|
+
"yc-InlineError-root flex items-center gap-1.5 text-xs font-medium text-danger-fg",
|
|
1404
|
+
className
|
|
1405
|
+
),
|
|
1406
|
+
...props,
|
|
1407
|
+
children: [
|
|
1408
|
+
icon && /* @__PURE__ */ jsx14("span", { className: "yc-InlineError-icon inline-flex shrink-0 items-center", "data-slot": "icon", children: icon }),
|
|
1409
|
+
/* @__PURE__ */ jsx14("span", { className: "yc-InlineError-message", "data-slot": "message", children: content })
|
|
1410
|
+
]
|
|
1411
|
+
}
|
|
1412
|
+
);
|
|
1413
|
+
}
|
|
1414
|
+
);
|
|
1415
|
+
InlineError.displayName = "InlineError";
|
|
1416
|
+
|
|
1417
|
+
// src/primitives/Toaster.tsx
|
|
1418
|
+
import * as React13 from "react";
|
|
1419
|
+
import { jsx as jsx15, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1420
|
+
var ToastContext = React13.createContext(null);
|
|
1421
|
+
function useToast() {
|
|
1422
|
+
const ctx = React13.useContext(ToastContext);
|
|
1423
|
+
if (!ctx) {
|
|
1424
|
+
throw new Error("useToast must be used within a ToastProvider or Toaster");
|
|
1425
|
+
}
|
|
1426
|
+
return ctx;
|
|
1427
|
+
}
|
|
1428
|
+
function ToastProvider({ children }) {
|
|
1429
|
+
const [toasts, setToasts] = React13.useState([]);
|
|
1430
|
+
const removeToast = React13.useCallback((id) => {
|
|
1431
|
+
setToasts((prev) => prev.filter((t) => t.id !== id));
|
|
1432
|
+
}, []);
|
|
1433
|
+
const addToast = React13.useCallback(
|
|
1434
|
+
(toast) => {
|
|
1435
|
+
const id = Math.random().toString(36).substring(2, 9);
|
|
1436
|
+
const newToast = { ...toast, id };
|
|
1437
|
+
setToasts((prev) => [...prev, newToast]);
|
|
1438
|
+
const duration = toast.duration ?? 5e3;
|
|
1439
|
+
if (duration > 0) {
|
|
1440
|
+
setTimeout(() => {
|
|
1441
|
+
removeToast(id);
|
|
1442
|
+
}, duration);
|
|
1443
|
+
}
|
|
1444
|
+
return id;
|
|
1445
|
+
},
|
|
1446
|
+
[removeToast]
|
|
1447
|
+
);
|
|
1448
|
+
return /* @__PURE__ */ jsx15(ToastContext.Provider, { value: { toasts, addToast, removeToast }, children });
|
|
1449
|
+
}
|
|
1450
|
+
var variantStyles3 = {
|
|
1451
|
+
info: "border-info-border bg-surface text-fg",
|
|
1452
|
+
success: "border-success-border bg-surface text-fg",
|
|
1453
|
+
warn: "border-warn-border bg-surface text-fg",
|
|
1454
|
+
danger: "border-danger-border bg-surface text-fg"
|
|
1455
|
+
};
|
|
1456
|
+
function Toaster({ className, position = "bottom-end" }) {
|
|
1457
|
+
const ctx = React13.useContext(ToastContext);
|
|
1458
|
+
const toasts = ctx ? ctx.toasts : [];
|
|
1459
|
+
const removeToast = ctx ? ctx.removeToast : () => {
|
|
1460
|
+
};
|
|
1461
|
+
const positionClass = position === "bottom-end" ? "bottom-4 end-4" : position === "bottom-start" ? "bottom-4 start-4" : position === "top-end" ? "top-4 end-4" : "top-4 start-4";
|
|
1462
|
+
return /* @__PURE__ */ jsx15(
|
|
1463
|
+
"div",
|
|
1464
|
+
{
|
|
1465
|
+
"data-slot": "toaster",
|
|
1466
|
+
role: "region",
|
|
1467
|
+
"aria-live": "polite",
|
|
1468
|
+
"aria-label": "Notifications",
|
|
1469
|
+
className: cn("yc-Toaster-root fixed z-50 flex flex-col gap-2 max-w-sm w-full pointer-events-none", positionClass, className),
|
|
1470
|
+
children: toasts.map((toast) => /* @__PURE__ */ jsxs8(
|
|
1471
|
+
"div",
|
|
1472
|
+
{
|
|
1473
|
+
role: "status",
|
|
1474
|
+
"data-slot": "toast",
|
|
1475
|
+
"data-variant": toast.variant || "info",
|
|
1476
|
+
className: cn(
|
|
1477
|
+
"yc-Toaster-toast pointer-events-auto flex w-full items-start justify-between rounded-lg p-3 shadow-2 border transition-all duration-normal",
|
|
1478
|
+
variantStyles3[toast.variant || "info"]
|
|
1479
|
+
),
|
|
1480
|
+
children: [
|
|
1481
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex-1 pe-2", children: [
|
|
1482
|
+
/* @__PURE__ */ jsx15("h4", { className: "yc-Toaster-title text-sm font-medium", "data-slot": "title", children: toast.title }),
|
|
1483
|
+
toast.description && /* @__PURE__ */ jsx15("p", { className: "yc-Toaster-description mt-0.5 text-xs text-fg-muted", "data-slot": "description", children: toast.description })
|
|
1484
|
+
] }),
|
|
1485
|
+
/* @__PURE__ */ jsx15(
|
|
1486
|
+
"button",
|
|
1487
|
+
{
|
|
1488
|
+
type: "button",
|
|
1489
|
+
"data-slot": "close",
|
|
1490
|
+
className: "yc-Toaster-close text-fg-muted hover:text-fg text-xs p-1",
|
|
1491
|
+
onClick: () => removeToast(toast.id),
|
|
1492
|
+
"aria-label": "Dismiss",
|
|
1493
|
+
children: "\u2715"
|
|
1494
|
+
}
|
|
1495
|
+
)
|
|
1496
|
+
]
|
|
1497
|
+
},
|
|
1498
|
+
toast.id
|
|
1499
|
+
))
|
|
1500
|
+
}
|
|
1501
|
+
);
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
// src/chat/Chat.tsx
|
|
1505
|
+
import * as React21 from "react";
|
|
1506
|
+
|
|
1507
|
+
// src/chat/ChatMarkdown.tsx
|
|
1508
|
+
import * as React16 from "react";
|
|
1509
|
+
|
|
1510
|
+
// src/chat/CodeBlock.tsx
|
|
1511
|
+
import * as React14 from "react";
|
|
1512
|
+
import { jsx as jsx16, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1513
|
+
function CodeBlock({ code, language = "text", className }) {
|
|
1514
|
+
const [copied, setCopied] = React14.useState(false);
|
|
1515
|
+
const handleCopy = async () => {
|
|
1516
|
+
try {
|
|
1517
|
+
await navigator.clipboard.writeText(code);
|
|
1518
|
+
setCopied(true);
|
|
1519
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
1520
|
+
} catch {
|
|
1521
|
+
}
|
|
1522
|
+
};
|
|
1523
|
+
return /* @__PURE__ */ jsxs9(
|
|
1524
|
+
"div",
|
|
1525
|
+
{
|
|
1526
|
+
"data-slot": "code-block",
|
|
1527
|
+
className: cn(
|
|
1528
|
+
"yc-CodeBlock-root my-2 overflow-hidden rounded-lg border border-border bg-surface-2 text-xs font-mono shadow-1",
|
|
1529
|
+
className
|
|
1530
|
+
),
|
|
1531
|
+
children: [
|
|
1532
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex items-center justify-between border-b border-border bg-surface-3 px-3 py-1.5 text-fg-muted", children: [
|
|
1533
|
+
/* @__PURE__ */ jsx16("span", { className: "font-semibold uppercase tracking-wider text-[11px]", children: language }),
|
|
1534
|
+
/* @__PURE__ */ jsx16(
|
|
1535
|
+
"button",
|
|
1536
|
+
{
|
|
1537
|
+
type: "button",
|
|
1538
|
+
onClick: handleCopy,
|
|
1539
|
+
className: "rounded px-2 py-0.5 text-[11px] transition-colors hover:bg-surface hover:text-fg",
|
|
1540
|
+
"aria-label": "Copy code to clipboard",
|
|
1541
|
+
children: copied ? "Copied!" : "Copy"
|
|
1542
|
+
}
|
|
1543
|
+
)
|
|
1544
|
+
] }),
|
|
1545
|
+
/* @__PURE__ */ jsx16("pre", { className: "overflow-x-auto p-3 text-fg leading-relaxed", children: /* @__PURE__ */ jsx16("code", { children: code }) })
|
|
1546
|
+
]
|
|
1547
|
+
}
|
|
1548
|
+
);
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
// src/chat/MermaidDiagram.tsx
|
|
1552
|
+
import * as React15 from "react";
|
|
1553
|
+
import { jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1554
|
+
function MermaidDiagram({ chart, className }) {
|
|
1555
|
+
const [showSource, setShowSource] = React15.useState(false);
|
|
1556
|
+
return /* @__PURE__ */ jsxs10(
|
|
1557
|
+
"div",
|
|
1558
|
+
{
|
|
1559
|
+
"data-slot": "mermaid-diagram",
|
|
1560
|
+
className: cn(
|
|
1561
|
+
"yc-MermaidDiagram-root my-3 rounded-lg border border-border bg-surface p-3 text-xs shadow-1",
|
|
1562
|
+
className
|
|
1563
|
+
),
|
|
1564
|
+
children: [
|
|
1565
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex items-center justify-between border-b border-border pb-2 mb-2 text-fg-muted font-mono", children: [
|
|
1566
|
+
/* @__PURE__ */ jsx17("span", { className: "font-semibold text-fg", children: "Diagram" }),
|
|
1567
|
+
/* @__PURE__ */ jsx17(
|
|
1568
|
+
"button",
|
|
1569
|
+
{
|
|
1570
|
+
type: "button",
|
|
1571
|
+
onClick: () => setShowSource(!showSource),
|
|
1572
|
+
className: "text-[11px] underline hover:text-fg",
|
|
1573
|
+
children: showSource ? "View rendering" : "View syntax"
|
|
1574
|
+
}
|
|
1575
|
+
)
|
|
1576
|
+
] }),
|
|
1577
|
+
showSource ? /* @__PURE__ */ jsx17("pre", { className: "overflow-x-auto rounded bg-surface-2 p-3 font-mono text-fg", children: /* @__PURE__ */ jsx17("code", { children: chart }) }) : /* @__PURE__ */ jsx17(
|
|
1578
|
+
"div",
|
|
1579
|
+
{
|
|
1580
|
+
"data-slot": "diagram-viewport",
|
|
1581
|
+
className: "yc-MermaidDiagram-viewport flex items-center justify-center p-4 bg-surface-2 rounded overflow-x-auto",
|
|
1582
|
+
children: /* @__PURE__ */ jsx17("pre", { className: "font-mono text-xs text-fg leading-relaxed", children: chart })
|
|
1583
|
+
}
|
|
1584
|
+
)
|
|
1585
|
+
]
|
|
1586
|
+
}
|
|
1587
|
+
);
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
// src/chat/ChatMarkdown.tsx
|
|
1591
|
+
import { jsx as jsx18 } from "react/jsx-runtime";
|
|
1592
|
+
function ChatMarkdown({ content, className }) {
|
|
1593
|
+
const blocks = React16.useMemo(() => {
|
|
1594
|
+
const lines = content.split("\n");
|
|
1595
|
+
const result = [];
|
|
1596
|
+
let currentCode = [];
|
|
1597
|
+
let currentLang = "";
|
|
1598
|
+
let inCode = false;
|
|
1599
|
+
let currentText = [];
|
|
1600
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1601
|
+
const line = lines[i];
|
|
1602
|
+
if (line.startsWith("```")) {
|
|
1603
|
+
if (inCode) {
|
|
1604
|
+
const codeString = currentCode.join("\n");
|
|
1605
|
+
if (currentLang === "mermaid") {
|
|
1606
|
+
result.push({ type: "mermaid", content: codeString });
|
|
1607
|
+
} else {
|
|
1608
|
+
result.push({ type: "code", content: codeString, lang: currentLang });
|
|
1609
|
+
}
|
|
1610
|
+
currentCode = [];
|
|
1611
|
+
currentLang = "";
|
|
1612
|
+
inCode = false;
|
|
1613
|
+
} else {
|
|
1614
|
+
if (currentText.length > 0) {
|
|
1615
|
+
result.push({ type: "text", content: currentText.join("\n") });
|
|
1616
|
+
currentText = [];
|
|
1617
|
+
}
|
|
1618
|
+
inCode = true;
|
|
1619
|
+
currentLang = line.replace("```", "").trim();
|
|
1620
|
+
}
|
|
1621
|
+
} else if (inCode) {
|
|
1622
|
+
currentCode.push(line);
|
|
1623
|
+
} else {
|
|
1624
|
+
currentText.push(line);
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
if (inCode && currentCode.length > 0) {
|
|
1628
|
+
result.push({ type: "code", content: currentCode.join("\n"), lang: currentLang });
|
|
1629
|
+
} else if (currentText.length > 0) {
|
|
1630
|
+
result.push({ type: "text", content: currentText.join("\n") });
|
|
1631
|
+
}
|
|
1632
|
+
return result;
|
|
1633
|
+
}, [content]);
|
|
1634
|
+
return /* @__PURE__ */ jsx18(
|
|
1635
|
+
"div",
|
|
1636
|
+
{
|
|
1637
|
+
"data-slot": "chat-markdown",
|
|
1638
|
+
className: cn("yc-ChatMarkdown-root text-sm text-fg space-y-2 leading-relaxed", className),
|
|
1639
|
+
children: blocks.map((b, idx) => {
|
|
1640
|
+
if (b.type === "code") {
|
|
1641
|
+
return /* @__PURE__ */ jsx18(CodeBlock, { code: b.content, language: b.lang }, idx);
|
|
1642
|
+
}
|
|
1643
|
+
if (b.type === "mermaid") {
|
|
1644
|
+
return /* @__PURE__ */ jsx18(MermaidDiagram, { chart: b.content }, idx);
|
|
1645
|
+
}
|
|
1646
|
+
return /* @__PURE__ */ jsx18("div", { className: "whitespace-pre-wrap", children: b.content }, idx);
|
|
1647
|
+
})
|
|
1648
|
+
}
|
|
1649
|
+
);
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
// src/chat/MessageToolbar.tsx
|
|
1653
|
+
import * as React17 from "react";
|
|
1654
|
+
import { Fragment, jsx as jsx19, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1655
|
+
function MessageToolbar({
|
|
1656
|
+
content,
|
|
1657
|
+
onRetry,
|
|
1658
|
+
onFeedback,
|
|
1659
|
+
className
|
|
1660
|
+
}) {
|
|
1661
|
+
const [copied, setCopied] = React17.useState(false);
|
|
1662
|
+
const [rated, setRated] = React17.useState(null);
|
|
1663
|
+
const handleCopy = async () => {
|
|
1664
|
+
try {
|
|
1665
|
+
await navigator.clipboard.writeText(content);
|
|
1666
|
+
setCopied(true);
|
|
1667
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
1668
|
+
} catch {
|
|
1669
|
+
}
|
|
1670
|
+
};
|
|
1671
|
+
const handleRate = (rating) => {
|
|
1672
|
+
setRated(rating);
|
|
1673
|
+
onFeedback?.(rating);
|
|
1674
|
+
};
|
|
1675
|
+
return /* @__PURE__ */ jsxs11(
|
|
1676
|
+
"div",
|
|
1677
|
+
{
|
|
1678
|
+
"data-slot": "message-toolbar",
|
|
1679
|
+
className: cn(
|
|
1680
|
+
"yc-MessageToolbar-root inline-flex items-center gap-1 rounded-md bg-surface-2 p-0.5 text-xs text-fg-muted border border-border shadow-1",
|
|
1681
|
+
className
|
|
1682
|
+
),
|
|
1683
|
+
children: [
|
|
1684
|
+
/* @__PURE__ */ jsx19(
|
|
1685
|
+
"button",
|
|
1686
|
+
{
|
|
1687
|
+
type: "button",
|
|
1688
|
+
onClick: handleCopy,
|
|
1689
|
+
className: "rounded px-1.5 py-0.5 hover:bg-surface hover:text-fg",
|
|
1690
|
+
"aria-label": "Copy message text",
|
|
1691
|
+
children: copied ? "Copied" : "Copy"
|
|
1692
|
+
}
|
|
1693
|
+
),
|
|
1694
|
+
onRetry && /* @__PURE__ */ jsx19(
|
|
1695
|
+
"button",
|
|
1696
|
+
{
|
|
1697
|
+
type: "button",
|
|
1698
|
+
onClick: onRetry,
|
|
1699
|
+
className: "rounded px-1.5 py-0.5 hover:bg-surface hover:text-fg",
|
|
1700
|
+
"aria-label": "Retry message generation",
|
|
1701
|
+
children: "Retry"
|
|
1702
|
+
}
|
|
1703
|
+
),
|
|
1704
|
+
onFeedback && /* @__PURE__ */ jsxs11(Fragment, { children: [
|
|
1705
|
+
/* @__PURE__ */ jsx19(
|
|
1706
|
+
"button",
|
|
1707
|
+
{
|
|
1708
|
+
type: "button",
|
|
1709
|
+
onClick: () => handleRate("positive"),
|
|
1710
|
+
className: cn(
|
|
1711
|
+
"rounded px-1.5 py-0.5 hover:bg-surface hover:text-fg",
|
|
1712
|
+
rated === "positive" && "text-primary font-bold"
|
|
1713
|
+
),
|
|
1714
|
+
"aria-label": "Helpful",
|
|
1715
|
+
children: "\u{1F44D}"
|
|
1716
|
+
}
|
|
1717
|
+
),
|
|
1718
|
+
/* @__PURE__ */ jsx19(
|
|
1719
|
+
"button",
|
|
1720
|
+
{
|
|
1721
|
+
type: "button",
|
|
1722
|
+
onClick: () => handleRate("negative"),
|
|
1723
|
+
className: cn(
|
|
1724
|
+
"rounded px-1.5 py-0.5 hover:bg-surface hover:text-fg",
|
|
1725
|
+
rated === "negative" && "text-danger-fg font-bold"
|
|
1726
|
+
),
|
|
1727
|
+
"aria-label": "Not helpful",
|
|
1728
|
+
children: "\u{1F44E}"
|
|
1729
|
+
}
|
|
1730
|
+
)
|
|
1731
|
+
] })
|
|
1732
|
+
]
|
|
1733
|
+
}
|
|
1734
|
+
);
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
// src/chat/ThinkingIndicator.tsx
|
|
1738
|
+
import * as React18 from "react";
|
|
1739
|
+
import { jsx as jsx20, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1740
|
+
function ThinkingIndicator({
|
|
1741
|
+
label = "Thinking...",
|
|
1742
|
+
reasoning,
|
|
1743
|
+
className
|
|
1744
|
+
}) {
|
|
1745
|
+
const [open, setOpen] = React18.useState(false);
|
|
1746
|
+
return /* @__PURE__ */ jsxs12(
|
|
1747
|
+
"div",
|
|
1748
|
+
{
|
|
1749
|
+
"data-slot": "thinking-indicator",
|
|
1750
|
+
className: cn(
|
|
1751
|
+
"yc-ThinkingIndicator-root inline-flex flex-col gap-1 text-xs text-fg-muted",
|
|
1752
|
+
className
|
|
1753
|
+
),
|
|
1754
|
+
children: [
|
|
1755
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2", children: [
|
|
1756
|
+
/* @__PURE__ */ jsxs12("span", { className: "flex gap-1 items-center", children: [
|
|
1757
|
+
/* @__PURE__ */ jsx20("span", { className: "h-1.5 w-1.5 animate-bounce rounded-full bg-fg-muted" }),
|
|
1758
|
+
/* @__PURE__ */ jsx20("span", { className: "h-1.5 w-1.5 animate-bounce [animation-delay:0.2s] rounded-full bg-fg-muted" }),
|
|
1759
|
+
/* @__PURE__ */ jsx20("span", { className: "h-1.5 w-1.5 animate-bounce [animation-delay:0.4s] rounded-full bg-fg-muted" })
|
|
1760
|
+
] }),
|
|
1761
|
+
/* @__PURE__ */ jsx20("span", { className: "font-medium", children: label }),
|
|
1762
|
+
reasoning && /* @__PURE__ */ jsx20(
|
|
1763
|
+
"button",
|
|
1764
|
+
{
|
|
1765
|
+
type: "button",
|
|
1766
|
+
onClick: () => setOpen(!open),
|
|
1767
|
+
className: "text-[11px] underline hover:text-fg",
|
|
1768
|
+
children: open ? "Hide details" : "Show details"
|
|
1769
|
+
}
|
|
1770
|
+
)
|
|
1771
|
+
] }),
|
|
1772
|
+
open && reasoning && /* @__PURE__ */ jsx20("div", { className: "mt-1 rounded border border-border bg-surface-2 p-2 font-mono text-[11px] text-fg whitespace-pre-wrap", children: reasoning })
|
|
1773
|
+
]
|
|
1774
|
+
}
|
|
1775
|
+
);
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
// src/chat/ApprovalCard.tsx
|
|
1779
|
+
import * as React19 from "react";
|
|
1780
|
+
import { jsx as jsx21, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1781
|
+
var levelBadgeVariant = {
|
|
1782
|
+
info: "info",
|
|
1783
|
+
warn: "warn",
|
|
1784
|
+
danger: "danger"
|
|
1785
|
+
};
|
|
1786
|
+
var levelBorderStyles = {
|
|
1787
|
+
info: "border-info-border bg-info-bg/30",
|
|
1788
|
+
warn: "border-warn-border bg-warn-bg/30",
|
|
1789
|
+
danger: "border-danger-border bg-danger-bg/30"
|
|
1790
|
+
};
|
|
1791
|
+
function ApprovalCard({
|
|
1792
|
+
id,
|
|
1793
|
+
level,
|
|
1794
|
+
label,
|
|
1795
|
+
details,
|
|
1796
|
+
onDecide,
|
|
1797
|
+
status = "pending",
|
|
1798
|
+
className
|
|
1799
|
+
}) {
|
|
1800
|
+
const [loading, setLoading] = React19.useState(false);
|
|
1801
|
+
const [currentStatus, setCurrentStatus] = React19.useState(status);
|
|
1802
|
+
const handleDecision = async (decision) => {
|
|
1803
|
+
try {
|
|
1804
|
+
setLoading(true);
|
|
1805
|
+
if (onDecide) {
|
|
1806
|
+
await onDecide(id, decision);
|
|
1807
|
+
} else {
|
|
1808
|
+
await fetch(`/api/v1/approvals/${encodeURIComponent(id)}/decide`, {
|
|
1809
|
+
method: "POST",
|
|
1810
|
+
headers: { "Content-Type": "application/json" },
|
|
1811
|
+
body: JSON.stringify({ decision })
|
|
1812
|
+
});
|
|
1813
|
+
}
|
|
1814
|
+
setCurrentStatus(decision);
|
|
1815
|
+
} catch (err) {
|
|
1816
|
+
console.error("Failed to submit approval decision:", err);
|
|
1817
|
+
} finally {
|
|
1818
|
+
setLoading(false);
|
|
1819
|
+
}
|
|
1820
|
+
};
|
|
1821
|
+
return /* @__PURE__ */ jsxs13(
|
|
1822
|
+
"div",
|
|
1823
|
+
{
|
|
1824
|
+
"data-slot": "approval-card",
|
|
1825
|
+
"data-risk-level": level,
|
|
1826
|
+
className: cn(
|
|
1827
|
+
"yc-ApprovalCard-root rounded-lg border p-4 shadow-1 transition-colors duration-fast",
|
|
1828
|
+
levelBorderStyles[level],
|
|
1829
|
+
className
|
|
1830
|
+
),
|
|
1831
|
+
children: [
|
|
1832
|
+
/* @__PURE__ */ jsxs13("div", { className: "flex items-center justify-between gap-2 pb-2", children: [
|
|
1833
|
+
/* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-2", children: [
|
|
1834
|
+
/* @__PURE__ */ jsx21(Badge, { variant: levelBadgeVariant[level], children: level.toUpperCase() }),
|
|
1835
|
+
/* @__PURE__ */ jsx21("span", { className: "yc-ApprovalCard-title text-sm font-semibold text-fg", "data-slot": "title", children: label })
|
|
1836
|
+
] }),
|
|
1837
|
+
currentStatus !== "pending" && /* @__PURE__ */ jsx21(Badge, { variant: currentStatus === "approved" ? "success" : "danger", children: currentStatus === "approved" ? "Approved" : "Rejected" })
|
|
1838
|
+
] }),
|
|
1839
|
+
details && Object.keys(details).length > 0 && /* @__PURE__ */ jsx21("div", { className: "yc-ApprovalCard-details mt-2 rounded bg-surface/80 p-2.5 text-xs text-fg-muted font-mono", "data-slot": "details", children: /* @__PURE__ */ jsx21("pre", { className: "overflow-x-auto whitespace-pre-wrap", children: JSON.stringify(details, null, 2) }) }),
|
|
1840
|
+
currentStatus === "pending" && /* @__PURE__ */ jsxs13("div", { className: "yc-ApprovalCard-actions mt-3 flex items-center justify-end gap-2", "data-slot": "actions", children: [
|
|
1841
|
+
/* @__PURE__ */ jsx21(
|
|
1842
|
+
Button,
|
|
1843
|
+
{
|
|
1844
|
+
size: "sm",
|
|
1845
|
+
variant: "ghost",
|
|
1846
|
+
disabled: loading,
|
|
1847
|
+
onClick: () => handleDecision("rejected"),
|
|
1848
|
+
children: "Reject"
|
|
1849
|
+
}
|
|
1850
|
+
),
|
|
1851
|
+
/* @__PURE__ */ jsx21(
|
|
1852
|
+
Button,
|
|
1853
|
+
{
|
|
1854
|
+
size: "sm",
|
|
1855
|
+
variant: level === "danger" ? "danger" : "primary",
|
|
1856
|
+
loading,
|
|
1857
|
+
onClick: () => handleDecision("approved"),
|
|
1858
|
+
children: "Approve & Continue"
|
|
1859
|
+
}
|
|
1860
|
+
)
|
|
1861
|
+
] })
|
|
1862
|
+
]
|
|
1863
|
+
}
|
|
1864
|
+
);
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
// src/chat/ToolTraceCard.tsx
|
|
1868
|
+
import * as React20 from "react";
|
|
1869
|
+
import { jsx as jsx22, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1870
|
+
function ToolTraceCard({
|
|
1871
|
+
trace,
|
|
1872
|
+
className,
|
|
1873
|
+
defaultExpanded = false
|
|
1874
|
+
}) {
|
|
1875
|
+
const [expanded, setExpanded] = React20.useState(defaultExpanded);
|
|
1876
|
+
const statusVariant = {
|
|
1877
|
+
running: "info",
|
|
1878
|
+
success: "success",
|
|
1879
|
+
error: "danger"
|
|
1880
|
+
};
|
|
1881
|
+
return /* @__PURE__ */ jsxs14(
|
|
1882
|
+
"div",
|
|
1883
|
+
{
|
|
1884
|
+
"data-slot": "tool-trace-card",
|
|
1885
|
+
className: cn(
|
|
1886
|
+
"yc-ToolTraceCard-root my-1.5 rounded-lg border border-border bg-surface text-fg shadow-1 text-xs",
|
|
1887
|
+
className
|
|
1888
|
+
),
|
|
1889
|
+
children: [
|
|
1890
|
+
/* @__PURE__ */ jsxs14(
|
|
1891
|
+
"button",
|
|
1892
|
+
{
|
|
1893
|
+
type: "button",
|
|
1894
|
+
onClick: () => setExpanded(!expanded),
|
|
1895
|
+
"data-slot": "header",
|
|
1896
|
+
className: "yc-ToolTraceCard-header flex w-full items-center justify-between p-2.5 text-start font-mono transition-colors hover:bg-surface-2",
|
|
1897
|
+
children: [
|
|
1898
|
+
/* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-2", children: [
|
|
1899
|
+
/* @__PURE__ */ jsx22("span", { className: "font-semibold text-fg", children: trace.toolName }),
|
|
1900
|
+
/* @__PURE__ */ jsx22(Badge, { variant: statusVariant[trace.status], children: trace.status }),
|
|
1901
|
+
trace.durationMs !== void 0 && /* @__PURE__ */ jsxs14("span", { className: "text-fg-muted", children: [
|
|
1902
|
+
"(",
|
|
1903
|
+
trace.durationMs,
|
|
1904
|
+
"ms)"
|
|
1905
|
+
] })
|
|
1906
|
+
] }),
|
|
1907
|
+
/* @__PURE__ */ jsx22("span", { className: "text-fg-muted", children: expanded ? "\u25B2" : "\u25BC" })
|
|
1908
|
+
]
|
|
1909
|
+
}
|
|
1910
|
+
),
|
|
1911
|
+
expanded && /* @__PURE__ */ jsxs14("div", { "data-slot": "details", className: "yc-ToolTraceCard-details border-t border-border p-2.5 font-mono space-y-2", children: [
|
|
1912
|
+
trace.input !== void 0 && /* @__PURE__ */ jsxs14("div", { children: [
|
|
1913
|
+
/* @__PURE__ */ jsx22("div", { className: "text-fg-muted text-[10px] uppercase font-semibold", children: "Input" }),
|
|
1914
|
+
/* @__PURE__ */ jsx22("pre", { className: "mt-1 max-h-40 overflow-auto rounded bg-surface-2 p-2 text-fg", children: typeof trace.input === "string" ? trace.input : JSON.stringify(trace.input, null, 2) })
|
|
1915
|
+
] }),
|
|
1916
|
+
trace.output !== void 0 && /* @__PURE__ */ jsxs14("div", { children: [
|
|
1917
|
+
/* @__PURE__ */ jsx22("div", { className: "text-fg-muted text-[10px] uppercase font-semibold", children: "Output" }),
|
|
1918
|
+
/* @__PURE__ */ jsx22("pre", { className: "mt-1 max-h-40 overflow-auto rounded bg-surface-2 p-2 text-fg", children: typeof trace.output === "string" ? trace.output : JSON.stringify(trace.output, null, 2) })
|
|
1919
|
+
] })
|
|
1920
|
+
] })
|
|
1921
|
+
]
|
|
1922
|
+
}
|
|
1923
|
+
);
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1926
|
+
// src/chat/ExecutionPlanCard.tsx
|
|
1927
|
+
import { jsx as jsx23, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1928
|
+
function ExecutionPlanCard({
|
|
1929
|
+
steps,
|
|
1930
|
+
className,
|
|
1931
|
+
title = "Execution Plan"
|
|
1932
|
+
}) {
|
|
1933
|
+
if (!steps || steps.length === 0) return null;
|
|
1934
|
+
return /* @__PURE__ */ jsxs15(
|
|
1935
|
+
"div",
|
|
1936
|
+
{
|
|
1937
|
+
"data-slot": "execution-plan-card",
|
|
1938
|
+
className: cn(
|
|
1939
|
+
"yc-ExecutionPlanCard-root my-2 rounded-lg border border-border bg-surface p-3 text-xs shadow-1",
|
|
1940
|
+
className
|
|
1941
|
+
),
|
|
1942
|
+
children: [
|
|
1943
|
+
/* @__PURE__ */ jsxs15("div", { className: "font-semibold text-fg pb-2 flex items-center justify-between", children: [
|
|
1944
|
+
/* @__PURE__ */ jsx23("span", { children: title }),
|
|
1945
|
+
/* @__PURE__ */ jsxs15("span", { className: "text-fg-muted", children: [
|
|
1946
|
+
steps.filter((s) => s.status === "completed").length,
|
|
1947
|
+
"/",
|
|
1948
|
+
steps.length
|
|
1949
|
+
] })
|
|
1950
|
+
] }),
|
|
1951
|
+
/* @__PURE__ */ jsx23("div", { className: "space-y-1.5", children: steps.map((step, idx) => {
|
|
1952
|
+
const isDone = step.status === "completed";
|
|
1953
|
+
const isRunning = step.status === "running";
|
|
1954
|
+
const isFailed = step.status === "failed";
|
|
1955
|
+
return /* @__PURE__ */ jsxs15(
|
|
1956
|
+
"div",
|
|
1957
|
+
{
|
|
1958
|
+
"data-slot": "step",
|
|
1959
|
+
"data-status": step.status,
|
|
1960
|
+
className: "yc-ExecutionPlanCard-step flex items-start gap-2",
|
|
1961
|
+
children: [
|
|
1962
|
+
/* @__PURE__ */ jsx23(
|
|
1963
|
+
"span",
|
|
1964
|
+
{
|
|
1965
|
+
className: cn(
|
|
1966
|
+
"mt-0.5 inline-flex h-4 w-4 shrink-0 items-center justify-center rounded-full text-[10px] font-bold",
|
|
1967
|
+
isDone ? "bg-success-fg text-white" : isRunning ? "animate-spin border-2 border-primary border-t-transparent" : isFailed ? "bg-danger-fg text-white" : "bg-surface-3 text-fg-muted"
|
|
1968
|
+
),
|
|
1969
|
+
children: isDone ? "\u2713" : isFailed ? "\u2715" : isRunning ? "" : idx + 1
|
|
1970
|
+
}
|
|
1971
|
+
),
|
|
1972
|
+
/* @__PURE__ */ jsx23(
|
|
1973
|
+
"span",
|
|
1974
|
+
{
|
|
1975
|
+
className: cn(
|
|
1976
|
+
"flex-1 text-fg",
|
|
1977
|
+
isDone && "line-through text-fg-muted",
|
|
1978
|
+
isRunning && "font-medium text-primary"
|
|
1979
|
+
),
|
|
1980
|
+
children: step.description
|
|
1981
|
+
}
|
|
1982
|
+
)
|
|
1983
|
+
]
|
|
1984
|
+
},
|
|
1985
|
+
step.id || idx
|
|
1986
|
+
);
|
|
1987
|
+
}) })
|
|
1988
|
+
]
|
|
1989
|
+
}
|
|
1990
|
+
);
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
// src/chat/Chat.tsx
|
|
1994
|
+
import { Fragment as Fragment2, jsx as jsx24, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1995
|
+
var ChatContext = React21.createContext(null);
|
|
1996
|
+
function useChatContext() {
|
|
1997
|
+
const ctx = React21.useContext(ChatContext);
|
|
1998
|
+
if (!ctx) throw new Error("Chat compound components must be rendered inside <Chat.Root>");
|
|
1999
|
+
return ctx;
|
|
2000
|
+
}
|
|
2001
|
+
function ChatRoot({
|
|
2002
|
+
messages,
|
|
2003
|
+
onSendMessage,
|
|
2004
|
+
loading = false,
|
|
2005
|
+
slotHooks,
|
|
2006
|
+
children,
|
|
2007
|
+
className
|
|
2008
|
+
}) {
|
|
2009
|
+
const [activeAttachments, setActiveAttachments] = React21.useState([]);
|
|
2010
|
+
return /* @__PURE__ */ jsx24(
|
|
2011
|
+
ChatContext.Provider,
|
|
2012
|
+
{
|
|
2013
|
+
value: {
|
|
2014
|
+
messages,
|
|
2015
|
+
onSendMessage,
|
|
2016
|
+
loading,
|
|
2017
|
+
activeAttachments,
|
|
2018
|
+
setActiveAttachments,
|
|
2019
|
+
slotHooks
|
|
2020
|
+
},
|
|
2021
|
+
children: /* @__PURE__ */ jsx24(
|
|
2022
|
+
"div",
|
|
2023
|
+
{
|
|
2024
|
+
"data-slot": "chat-root",
|
|
2025
|
+
className: cn("yc-Chat-root flex flex-col h-full w-full bg-surface text-fg", className),
|
|
2026
|
+
children
|
|
2027
|
+
}
|
|
2028
|
+
)
|
|
2029
|
+
}
|
|
2030
|
+
);
|
|
2031
|
+
}
|
|
2032
|
+
function ChatThread({
|
|
2033
|
+
className,
|
|
2034
|
+
emptyTitle = "How can I assist you today?",
|
|
2035
|
+
emptyDescription = "Start a conversation by typing a message below."
|
|
2036
|
+
}) {
|
|
2037
|
+
const { messages } = useChatContext();
|
|
2038
|
+
const threadEndRef = React21.useRef(null);
|
|
2039
|
+
React21.useEffect(() => {
|
|
2040
|
+
threadEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
2041
|
+
}, [messages]);
|
|
2042
|
+
return /* @__PURE__ */ jsxs16(
|
|
2043
|
+
"div",
|
|
2044
|
+
{
|
|
2045
|
+
"data-slot": "chat-thread",
|
|
2046
|
+
className: cn("yc-Chat-thread flex-1 overflow-y-auto p-4 space-y-4", className),
|
|
2047
|
+
children: [
|
|
2048
|
+
messages.length === 0 ? /* @__PURE__ */ jsx24(EmptyState, { title: emptyTitle, description: emptyDescription, className: "h-full" }) : messages.map((msg) => /* @__PURE__ */ jsx24(ChatMessageItem, { message: msg }, msg.id)),
|
|
2049
|
+
/* @__PURE__ */ jsx24("div", { ref: threadEndRef })
|
|
2050
|
+
]
|
|
2051
|
+
}
|
|
2052
|
+
);
|
|
2053
|
+
}
|
|
2054
|
+
function ChatMessageItem({ message, className }) {
|
|
2055
|
+
const { slotHooks } = useChatContext();
|
|
2056
|
+
const isUser = message.role === "user";
|
|
2057
|
+
const isSystem = message.role === "system";
|
|
2058
|
+
if (slotHooks?.messageRenderer) {
|
|
2059
|
+
const custom = slotHooks.messageRenderer(message);
|
|
2060
|
+
if (custom) return /* @__PURE__ */ jsx24(Fragment2, { children: custom });
|
|
2061
|
+
}
|
|
2062
|
+
return /* @__PURE__ */ jsxs16(
|
|
2063
|
+
"div",
|
|
2064
|
+
{
|
|
2065
|
+
"data-slot": "chat-message",
|
|
2066
|
+
"data-role": message.role,
|
|
2067
|
+
className: cn(
|
|
2068
|
+
"yc-Chat-message group flex w-full gap-3",
|
|
2069
|
+
isUser ? "justify-end" : "justify-start",
|
|
2070
|
+
className
|
|
2071
|
+
),
|
|
2072
|
+
children: [
|
|
2073
|
+
!isUser && /* @__PURE__ */ jsx24(Avatar, { size: "sm", className: "mt-1", children: message.avatarUrl ? /* @__PURE__ */ jsx24(AvatarImage, { src: message.avatarUrl, alt: message.authorName || "Assistant" }) : /* @__PURE__ */ jsx24(AvatarFallback, { children: (message.authorName || "AI").substring(0, 2).toUpperCase() }) }),
|
|
2074
|
+
/* @__PURE__ */ jsxs16(
|
|
2075
|
+
"div",
|
|
2076
|
+
{
|
|
2077
|
+
className: cn(
|
|
2078
|
+
"flex flex-col max-w-[80%] gap-1.5",
|
|
2079
|
+
isUser ? "items-end" : "items-start"
|
|
2080
|
+
),
|
|
2081
|
+
children: [
|
|
2082
|
+
/* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-2 text-xs text-fg-muted", children: [
|
|
2083
|
+
/* @__PURE__ */ jsx24("span", { className: "font-semibold", children: message.authorName || (isUser ? "You" : "Assistant") }),
|
|
2084
|
+
/* @__PURE__ */ jsx24("span", { children: message.createdAt })
|
|
2085
|
+
] }),
|
|
2086
|
+
/* @__PURE__ */ jsxs16(
|
|
2087
|
+
"div",
|
|
2088
|
+
{
|
|
2089
|
+
"data-slot": "message-bubble",
|
|
2090
|
+
className: cn(
|
|
2091
|
+
"yc-Chat-bubble rounded-2xl px-4 py-2.5 shadow-1 text-sm leading-relaxed",
|
|
2092
|
+
isUser ? "bg-primary text-primary-fg rounded-be-sm" : isSystem ? "bg-surface-2 text-fg-muted border border-border" : "bg-surface-2 text-fg rounded-bs-sm border border-border"
|
|
2093
|
+
),
|
|
2094
|
+
children: [
|
|
2095
|
+
message.reasoning && /* @__PURE__ */ jsx24("div", { className: "mb-2", children: /* @__PURE__ */ jsx24(ThinkingIndicator, { reasoning: message.reasoning }) }),
|
|
2096
|
+
isUser ? /* @__PURE__ */ jsx24("div", { className: "whitespace-pre-wrap", children: message.content }) : /* @__PURE__ */ jsx24(ChatMarkdown, { content: message.content }),
|
|
2097
|
+
message.executionPlan && message.executionPlan.length > 0 && /* @__PURE__ */ jsx24(ExecutionPlanCard, { steps: message.executionPlan }),
|
|
2098
|
+
message.toolTraces && message.toolTraces.length > 0 && /* @__PURE__ */ jsx24("div", { className: "mt-2 space-y-1", children: message.toolTraces.map((trace) => /* @__PURE__ */ jsx24(ToolTraceCard, { trace }, trace.id)) }),
|
|
2099
|
+
message.approval && /* @__PURE__ */ jsx24("div", { className: "mt-2", children: /* @__PURE__ */ jsx24(
|
|
2100
|
+
ApprovalCard,
|
|
2101
|
+
{
|
|
2102
|
+
id: message.approval.id,
|
|
2103
|
+
level: message.approval.level,
|
|
2104
|
+
label: message.approval.label,
|
|
2105
|
+
details: message.approval.details,
|
|
2106
|
+
status: message.approval.status
|
|
2107
|
+
}
|
|
2108
|
+
) })
|
|
2109
|
+
]
|
|
2110
|
+
}
|
|
2111
|
+
),
|
|
2112
|
+
!isUser && /* @__PURE__ */ jsx24("div", { className: "opacity-0 group-hover:opacity-100 transition-opacity duration-fast", children: /* @__PURE__ */ jsx24(MessageToolbar, { content: message.content }) })
|
|
2113
|
+
]
|
|
2114
|
+
}
|
|
2115
|
+
),
|
|
2116
|
+
isUser && /* @__PURE__ */ jsx24(Avatar, { size: "sm", className: "mt-1", children: message.avatarUrl ? /* @__PURE__ */ jsx24(AvatarImage, { src: message.avatarUrl, alt: "User" }) : /* @__PURE__ */ jsx24(AvatarFallback, { children: "ME" }) })
|
|
2117
|
+
]
|
|
2118
|
+
}
|
|
2119
|
+
);
|
|
2120
|
+
}
|
|
2121
|
+
function ChatAttachments({ className }) {
|
|
2122
|
+
const { activeAttachments, setActiveAttachments } = useChatContext();
|
|
2123
|
+
if (activeAttachments.length === 0) return null;
|
|
2124
|
+
return /* @__PURE__ */ jsx24(
|
|
2125
|
+
"div",
|
|
2126
|
+
{
|
|
2127
|
+
"data-slot": "chat-attachments",
|
|
2128
|
+
className: cn("yc-Chat-attachments flex flex-wrap gap-2 px-4 py-2 bg-surface-2 border-t border-border", className),
|
|
2129
|
+
children: activeAttachments.map((att) => /* @__PURE__ */ jsxs16(
|
|
2130
|
+
"div",
|
|
2131
|
+
{
|
|
2132
|
+
"data-slot": "attachment-item",
|
|
2133
|
+
className: "yc-Chat-attachment flex items-center gap-1.5 rounded-md bg-surface px-2.5 py-1 text-xs border border-border shadow-1",
|
|
2134
|
+
children: [
|
|
2135
|
+
/* @__PURE__ */ jsx24("span", { className: "truncate max-w-[150px]", children: att.name }),
|
|
2136
|
+
/* @__PURE__ */ jsx24(
|
|
2137
|
+
"button",
|
|
2138
|
+
{
|
|
2139
|
+
type: "button",
|
|
2140
|
+
className: "text-fg-muted hover:text-danger-fg",
|
|
2141
|
+
onClick: () => setActiveAttachments((prev) => prev.filter((a) => a.id !== att.id)),
|
|
2142
|
+
"aria-label": `Remove ${att.name}`,
|
|
2143
|
+
children: "\u2715"
|
|
2144
|
+
}
|
|
2145
|
+
)
|
|
2146
|
+
]
|
|
2147
|
+
},
|
|
2148
|
+
att.id
|
|
2149
|
+
))
|
|
2150
|
+
}
|
|
2151
|
+
);
|
|
2152
|
+
}
|
|
2153
|
+
function ChatComposer({
|
|
2154
|
+
placeholder = "Type a message...",
|
|
2155
|
+
className
|
|
2156
|
+
}) {
|
|
2157
|
+
const { onSendMessage, loading, activeAttachments, setActiveAttachments, slotHooks } = useChatContext();
|
|
2158
|
+
const [text, setText] = React21.useState("");
|
|
2159
|
+
const fileInputRef = React21.useRef(null);
|
|
2160
|
+
const handleSubmit = async (e) => {
|
|
2161
|
+
e?.preventDefault();
|
|
2162
|
+
const trimmed = text.trim();
|
|
2163
|
+
if (!trimmed && activeAttachments.length === 0) return;
|
|
2164
|
+
if (loading) return;
|
|
2165
|
+
if (slotHooks?.beforeSend) {
|
|
2166
|
+
const result = await slotHooks.beforeSend(trimmed, activeAttachments);
|
|
2167
|
+
if (result === false) return;
|
|
2168
|
+
if (typeof result === "string") {
|
|
2169
|
+
await onSendMessage(result, activeAttachments);
|
|
2170
|
+
setText("");
|
|
2171
|
+
setActiveAttachments([]);
|
|
2172
|
+
return;
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
await onSendMessage(trimmed, activeAttachments);
|
|
2176
|
+
setText("");
|
|
2177
|
+
setActiveAttachments([]);
|
|
2178
|
+
};
|
|
2179
|
+
const handleKeyDown = (e) => {
|
|
2180
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
2181
|
+
e.preventDefault();
|
|
2182
|
+
handleSubmit();
|
|
2183
|
+
}
|
|
2184
|
+
};
|
|
2185
|
+
const handleFileChange = (e) => {
|
|
2186
|
+
const files = e.target.files;
|
|
2187
|
+
if (!files) return;
|
|
2188
|
+
const newAttachments = Array.from(files).map((f) => ({
|
|
2189
|
+
id: Math.random().toString(36).substring(2, 9),
|
|
2190
|
+
name: f.name,
|
|
2191
|
+
mimeType: f.type,
|
|
2192
|
+
sizeBytes: f.size
|
|
2193
|
+
}));
|
|
2194
|
+
setActiveAttachments((prev) => [...prev, ...newAttachments]);
|
|
2195
|
+
};
|
|
2196
|
+
return /* @__PURE__ */ jsxs16(
|
|
2197
|
+
"form",
|
|
2198
|
+
{
|
|
2199
|
+
"data-slot": "chat-composer",
|
|
2200
|
+
onSubmit: handleSubmit,
|
|
2201
|
+
className: cn(
|
|
2202
|
+
"yc-Chat-composer flex flex-col gap-2 p-3 border-t border-border bg-surface",
|
|
2203
|
+
className
|
|
2204
|
+
),
|
|
2205
|
+
children: [
|
|
2206
|
+
/* @__PURE__ */ jsx24(ChatAttachments, {}),
|
|
2207
|
+
/* @__PURE__ */ jsxs16("div", { className: "flex items-end gap-2", children: [
|
|
2208
|
+
/* @__PURE__ */ jsx24(
|
|
2209
|
+
"input",
|
|
2210
|
+
{
|
|
2211
|
+
ref: fileInputRef,
|
|
2212
|
+
type: "file",
|
|
2213
|
+
multiple: true,
|
|
2214
|
+
className: "hidden",
|
|
2215
|
+
onChange: handleFileChange
|
|
2216
|
+
}
|
|
2217
|
+
),
|
|
2218
|
+
/* @__PURE__ */ jsx24(
|
|
2219
|
+
Button,
|
|
2220
|
+
{
|
|
2221
|
+
type: "button",
|
|
2222
|
+
variant: "ghost",
|
|
2223
|
+
size: "sm",
|
|
2224
|
+
onClick: () => fileInputRef.current?.click(),
|
|
2225
|
+
"aria-label": "Attach file",
|
|
2226
|
+
className: "h-10 px-2.5 text-fg-muted",
|
|
2227
|
+
children: "\u{1F4CE}"
|
|
2228
|
+
}
|
|
2229
|
+
),
|
|
2230
|
+
slotHooks?.composerAction,
|
|
2231
|
+
/* @__PURE__ */ jsx24(
|
|
2232
|
+
"textarea",
|
|
2233
|
+
{
|
|
2234
|
+
value: text,
|
|
2235
|
+
onChange: (e) => setText(e.target.value),
|
|
2236
|
+
onKeyDown: handleKeyDown,
|
|
2237
|
+
placeholder,
|
|
2238
|
+
rows: 1,
|
|
2239
|
+
className: "flex-1 max-h-32 min-h-[44px] resize-none rounded-lg border border-border bg-surface-2 p-2.5 text-sm text-fg placeholder:text-fg-muted focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary",
|
|
2240
|
+
"data-slot": "input"
|
|
2241
|
+
}
|
|
2242
|
+
),
|
|
2243
|
+
/* @__PURE__ */ jsx24(
|
|
2244
|
+
Button,
|
|
2245
|
+
{
|
|
2246
|
+
type: "submit",
|
|
2247
|
+
variant: "primary",
|
|
2248
|
+
size: "sm",
|
|
2249
|
+
disabled: loading || !text.trim() && activeAttachments.length === 0,
|
|
2250
|
+
loading,
|
|
2251
|
+
className: "h-10 px-4",
|
|
2252
|
+
children: "Send"
|
|
2253
|
+
}
|
|
2254
|
+
)
|
|
2255
|
+
] })
|
|
2256
|
+
]
|
|
2257
|
+
}
|
|
2258
|
+
);
|
|
2259
|
+
}
|
|
2260
|
+
var Chat = {
|
|
2261
|
+
Root: ChatRoot,
|
|
2262
|
+
Thread: ChatThread,
|
|
2263
|
+
Message: ChatMessageItem,
|
|
2264
|
+
Composer: ChatComposer,
|
|
2265
|
+
Attachments: ChatAttachments
|
|
2266
|
+
};
|
|
2267
|
+
|
|
2268
|
+
// src/chat/ChatConnected.tsx
|
|
2269
|
+
import * as React22 from "react";
|
|
2270
|
+
import { jsx as jsx25, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2271
|
+
function ChatConnected({
|
|
2272
|
+
threadId,
|
|
2273
|
+
initialMessages = [],
|
|
2274
|
+
slotHooks,
|
|
2275
|
+
className,
|
|
2276
|
+
onMessageSent
|
|
2277
|
+
}) {
|
|
2278
|
+
const [messages, setMessages] = React22.useState(initialMessages);
|
|
2279
|
+
const [loading, setLoading] = React22.useState(false);
|
|
2280
|
+
const handleSendMessage = async (content, attachments) => {
|
|
2281
|
+
const userMessage = {
|
|
2282
|
+
id: Math.random().toString(36).substring(2, 9),
|
|
2283
|
+
role: "user",
|
|
2284
|
+
content,
|
|
2285
|
+
createdAt: (/* @__PURE__ */ new Date()).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }),
|
|
2286
|
+
attachments
|
|
2287
|
+
};
|
|
2288
|
+
setMessages((prev) => [...prev, userMessage]);
|
|
2289
|
+
onMessageSent?.(userMessage);
|
|
2290
|
+
setLoading(true);
|
|
2291
|
+
try {
|
|
2292
|
+
const res = await fetch(`/api/v1/threads/${encodeURIComponent(threadId)}/messages`, {
|
|
2293
|
+
method: "POST",
|
|
2294
|
+
headers: { "Content-Type": "application/json" },
|
|
2295
|
+
body: JSON.stringify({ content, attachments })
|
|
2296
|
+
});
|
|
2297
|
+
if (!res.ok) {
|
|
2298
|
+
throw new Error(`Server returned status ${res.status}`);
|
|
2299
|
+
}
|
|
2300
|
+
const data = await res.json();
|
|
2301
|
+
const assistantMessage = {
|
|
2302
|
+
id: data.id || Math.random().toString(36).substring(2, 9),
|
|
2303
|
+
role: "assistant",
|
|
2304
|
+
content: data.content || data.reply || "",
|
|
2305
|
+
createdAt: (/* @__PURE__ */ new Date()).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }),
|
|
2306
|
+
toolTraces: data.toolTraces,
|
|
2307
|
+
executionPlan: data.executionPlan,
|
|
2308
|
+
approval: data.approval
|
|
2309
|
+
};
|
|
2310
|
+
setMessages((prev) => [...prev, assistantMessage]);
|
|
2311
|
+
} catch (err) {
|
|
2312
|
+
console.error("Failed to send message:", err);
|
|
2313
|
+
const errorMessage = {
|
|
2314
|
+
id: Math.random().toString(36).substring(2, 9),
|
|
2315
|
+
role: "system",
|
|
2316
|
+
content: "Unable to connect to the assistant. Please try again.",
|
|
2317
|
+
createdAt: (/* @__PURE__ */ new Date()).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }),
|
|
2318
|
+
status: "error"
|
|
2319
|
+
};
|
|
2320
|
+
setMessages((prev) => [...prev, errorMessage]);
|
|
2321
|
+
} finally {
|
|
2322
|
+
setLoading(false);
|
|
2323
|
+
}
|
|
2324
|
+
};
|
|
2325
|
+
return /* @__PURE__ */ jsxs17(
|
|
2326
|
+
ChatRoot,
|
|
2327
|
+
{
|
|
2328
|
+
messages,
|
|
2329
|
+
onSendMessage: handleSendMessage,
|
|
2330
|
+
loading,
|
|
2331
|
+
slotHooks,
|
|
2332
|
+
className,
|
|
2333
|
+
children: [
|
|
2334
|
+
/* @__PURE__ */ jsx25(ChatThread, {}),
|
|
2335
|
+
/* @__PURE__ */ jsx25(ChatComposer, {})
|
|
2336
|
+
]
|
|
2337
|
+
}
|
|
2338
|
+
);
|
|
2339
|
+
}
|
|
2340
|
+
|
|
2341
|
+
// src/chat/ActivityLogDrawer.tsx
|
|
2342
|
+
import { jsx as jsx26, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2343
|
+
function ActivityLogDrawer({
|
|
2344
|
+
traces,
|
|
2345
|
+
open,
|
|
2346
|
+
onOpenChange,
|
|
2347
|
+
trigger
|
|
2348
|
+
}) {
|
|
2349
|
+
return /* @__PURE__ */ jsxs18(Sheet, { open, onOpenChange, children: [
|
|
2350
|
+
trigger && /* @__PURE__ */ jsx26(SheetTrigger, { asChild: true, children: trigger }),
|
|
2351
|
+
/* @__PURE__ */ jsxs18(SheetContent, { side: "end", className: "yc-ActivityLogDrawer-content flex flex-col gap-4", children: [
|
|
2352
|
+
/* @__PURE__ */ jsxs18(SheetHeader, { children: [
|
|
2353
|
+
/* @__PURE__ */ jsx26(SheetTitle, { children: "Activity Log" }),
|
|
2354
|
+
/* @__PURE__ */ jsx26(SheetDescription, { children: "Audit log of agent execution traces and tool calls." })
|
|
2355
|
+
] }),
|
|
2356
|
+
/* @__PURE__ */ jsx26(
|
|
2357
|
+
"div",
|
|
2358
|
+
{
|
|
2359
|
+
"data-slot": "activity-log-drawer",
|
|
2360
|
+
className: "yc-ActivityLogDrawer-root flex-1 overflow-y-auto space-y-2 pe-1",
|
|
2361
|
+
children: traces.length === 0 ? /* @__PURE__ */ jsx26("div", { className: "text-center text-xs text-fg-muted py-8", children: "No activity recorded for this session." }) : traces.map((trace) => /* @__PURE__ */ jsx26(ToolTraceCard, { trace, defaultExpanded: false }, trace.id))
|
|
2362
|
+
}
|
|
2363
|
+
)
|
|
2364
|
+
] })
|
|
2365
|
+
] });
|
|
2366
|
+
}
|
|
2367
|
+
|
|
2368
|
+
// src/chat/ModelSelector.tsx
|
|
2369
|
+
import { jsx as jsx27, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
2370
|
+
function ModelSelector({
|
|
2371
|
+
models,
|
|
2372
|
+
selectedModelId,
|
|
2373
|
+
onSelectModel,
|
|
2374
|
+
className
|
|
2375
|
+
}) {
|
|
2376
|
+
const currentModel = models.find((m) => m.id === selectedModelId) || models[0];
|
|
2377
|
+
return /* @__PURE__ */ jsx27(
|
|
2378
|
+
"div",
|
|
2379
|
+
{
|
|
2380
|
+
"data-slot": "model-selector",
|
|
2381
|
+
className: cn("yc-ModelSelector-root inline-block", className),
|
|
2382
|
+
children: /* @__PURE__ */ jsxs19(Menu, { children: [
|
|
2383
|
+
/* @__PURE__ */ jsxs19(MenuTrigger, { className: "rounded-md border border-border bg-surface px-2.5 py-1 text-xs font-medium text-fg shadow-1 hover:bg-surface-2", children: [
|
|
2384
|
+
/* @__PURE__ */ jsx27("span", { className: "me-1 text-fg-muted font-normal", children: "Model:" }),
|
|
2385
|
+
/* @__PURE__ */ jsx27("span", { children: currentModel ? currentModel.name : "Select model" }),
|
|
2386
|
+
/* @__PURE__ */ jsx27("span", { className: "ms-1.5 text-fg-muted", children: "\u25BE" })
|
|
2387
|
+
] }),
|
|
2388
|
+
/* @__PURE__ */ jsx27(MenuContent, { align: "start", children: models.map((m) => /* @__PURE__ */ jsx27(
|
|
2389
|
+
MenuItem,
|
|
2390
|
+
{
|
|
2391
|
+
onClick: () => onSelectModel?.(m.id),
|
|
2392
|
+
className: cn(m.id === currentModel?.id && "font-semibold text-primary"),
|
|
2393
|
+
children: /* @__PURE__ */ jsxs19("div", { className: "flex flex-col text-start", children: [
|
|
2394
|
+
/* @__PURE__ */ jsx27("span", { children: m.name }),
|
|
2395
|
+
m.provider && /* @__PURE__ */ jsx27("span", { className: "text-[10px] text-fg-muted font-normal", children: m.provider })
|
|
2396
|
+
] })
|
|
2397
|
+
},
|
|
2398
|
+
m.id
|
|
2399
|
+
)) })
|
|
2400
|
+
] })
|
|
2401
|
+
}
|
|
2402
|
+
);
|
|
2403
|
+
}
|
|
2404
|
+
|
|
2405
|
+
// src/notes/RecordSearch.tsx
|
|
2406
|
+
import { jsx as jsx28, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
2407
|
+
function RecordSearch({
|
|
2408
|
+
searchQuery,
|
|
2409
|
+
onSearchChange,
|
|
2410
|
+
selectedFilter,
|
|
2411
|
+
onFilterChange,
|
|
2412
|
+
filterOptions = [
|
|
2413
|
+
{ id: "all", label: "All Records" },
|
|
2414
|
+
{ id: "pinned", label: "Pinned" },
|
|
2415
|
+
{ id: "recent", label: "Recent" }
|
|
2416
|
+
],
|
|
2417
|
+
placeholder = "Search records...",
|
|
2418
|
+
className
|
|
2419
|
+
}) {
|
|
2420
|
+
return /* @__PURE__ */ jsxs20(
|
|
2421
|
+
"div",
|
|
2422
|
+
{
|
|
2423
|
+
"data-slot": "record-search",
|
|
2424
|
+
className: cn("yc-RecordSearch-root flex flex-col gap-2.5", className),
|
|
2425
|
+
children: [
|
|
2426
|
+
/* @__PURE__ */ jsxs20("div", { className: "relative flex items-center", children: [
|
|
2427
|
+
/* @__PURE__ */ jsx28("span", { className: "absolute start-3 text-fg-muted text-sm", children: "\u{1F50D}" }),
|
|
2428
|
+
/* @__PURE__ */ jsx28(
|
|
2429
|
+
"input",
|
|
2430
|
+
{
|
|
2431
|
+
type: "text",
|
|
2432
|
+
value: searchQuery,
|
|
2433
|
+
onChange: (e) => onSearchChange(e.target.value),
|
|
2434
|
+
placeholder,
|
|
2435
|
+
className: "w-full rounded-lg border border-border bg-surface-2 py-2 ps-9 pe-3 text-sm text-fg placeholder:text-fg-muted focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary",
|
|
2436
|
+
"data-slot": "search-input"
|
|
2437
|
+
}
|
|
2438
|
+
),
|
|
2439
|
+
searchQuery && /* @__PURE__ */ jsx28(
|
|
2440
|
+
"button",
|
|
2441
|
+
{
|
|
2442
|
+
type: "button",
|
|
2443
|
+
onClick: () => onSearchChange(""),
|
|
2444
|
+
className: "absolute end-3 text-fg-muted hover:text-fg text-xs",
|
|
2445
|
+
"aria-label": "Clear search",
|
|
2446
|
+
children: "\u2715"
|
|
2447
|
+
}
|
|
2448
|
+
)
|
|
2449
|
+
] }),
|
|
2450
|
+
/* @__PURE__ */ jsx28(
|
|
2451
|
+
"div",
|
|
2452
|
+
{
|
|
2453
|
+
"data-slot": "filter-chips",
|
|
2454
|
+
className: "yc-RecordSearch-chips flex flex-wrap gap-1.5",
|
|
2455
|
+
children: filterOptions.map((opt) => {
|
|
2456
|
+
const isSelected = opt.id === selectedFilter;
|
|
2457
|
+
return /* @__PURE__ */ jsxs20(
|
|
2458
|
+
Chip,
|
|
2459
|
+
{
|
|
2460
|
+
selected: isSelected,
|
|
2461
|
+
onClick: () => onFilterChange(opt.id),
|
|
2462
|
+
children: [
|
|
2463
|
+
opt.label,
|
|
2464
|
+
opt.count !== void 0 && /* @__PURE__ */ jsxs20("span", { className: "ms-1.5 text-xs text-fg-muted font-normal", children: [
|
|
2465
|
+
"(",
|
|
2466
|
+
opt.count,
|
|
2467
|
+
")"
|
|
2468
|
+
] })
|
|
2469
|
+
]
|
|
2470
|
+
},
|
|
2471
|
+
opt.id
|
|
2472
|
+
);
|
|
2473
|
+
})
|
|
2474
|
+
}
|
|
2475
|
+
)
|
|
2476
|
+
]
|
|
2477
|
+
}
|
|
2478
|
+
);
|
|
2479
|
+
}
|
|
2480
|
+
|
|
2481
|
+
// src/notes/NotesList.tsx
|
|
2482
|
+
import { jsx as jsx29, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
2483
|
+
function NotesList({
|
|
2484
|
+
notes,
|
|
2485
|
+
selectedNoteId,
|
|
2486
|
+
onSelectNote,
|
|
2487
|
+
className,
|
|
2488
|
+
emptyTitle = "No notes found",
|
|
2489
|
+
emptyDescription = "Create a new note or adjust your search filter."
|
|
2490
|
+
}) {
|
|
2491
|
+
if (notes.length === 0) {
|
|
2492
|
+
return /* @__PURE__ */ jsx29(EmptyState, { title: emptyTitle, description: emptyDescription, className });
|
|
2493
|
+
}
|
|
2494
|
+
return /* @__PURE__ */ jsx29(
|
|
2495
|
+
"div",
|
|
2496
|
+
{
|
|
2497
|
+
"data-slot": "notes-list",
|
|
2498
|
+
className: cn("yc-NotesList-root flex flex-col gap-2 overflow-y-auto", className),
|
|
2499
|
+
children: notes.map((note) => {
|
|
2500
|
+
const isSelected = note.id === selectedNoteId;
|
|
2501
|
+
return /* @__PURE__ */ jsxs21(
|
|
2502
|
+
"button",
|
|
2503
|
+
{
|
|
2504
|
+
type: "button",
|
|
2505
|
+
onClick: () => onSelectNote(note),
|
|
2506
|
+
"data-slot": "note-item",
|
|
2507
|
+
"data-selected": isSelected ? "true" : void 0,
|
|
2508
|
+
className: cn(
|
|
2509
|
+
"yc-NotesList-item flex flex-col text-start rounded-lg border p-3 transition-colors duration-fast shadow-1",
|
|
2510
|
+
isSelected ? "border-primary bg-primary-subtle/20" : "border-border bg-surface hover:bg-surface-2"
|
|
2511
|
+
),
|
|
2512
|
+
children: [
|
|
2513
|
+
/* @__PURE__ */ jsxs21("div", { className: "flex items-center justify-between gap-2 pb-1", children: [
|
|
2514
|
+
/* @__PURE__ */ jsxs21("span", { className: "font-semibold text-sm text-fg truncate", children: [
|
|
2515
|
+
note.pinned && /* @__PURE__ */ jsx29("span", { className: "me-1 text-primary", children: "\u{1F4CC}" }),
|
|
2516
|
+
note.title || "Untitled Note"
|
|
2517
|
+
] }),
|
|
2518
|
+
/* @__PURE__ */ jsx29("span", { className: "text-[11px] text-fg-muted shrink-0", children: note.updatedAt })
|
|
2519
|
+
] }),
|
|
2520
|
+
/* @__PURE__ */ jsx29("p", { className: "text-xs text-fg-muted line-clamp-2 leading-relaxed", children: note.content || "No content" }),
|
|
2521
|
+
note.tags && note.tags.length > 0 && /* @__PURE__ */ jsx29("div", { className: "mt-2 flex flex-wrap gap-1", children: note.tags.map((tag) => /* @__PURE__ */ jsx29(Badge, { variant: "neutral", children: tag }, tag)) })
|
|
2522
|
+
]
|
|
2523
|
+
},
|
|
2524
|
+
note.id
|
|
2525
|
+
);
|
|
2526
|
+
})
|
|
2527
|
+
}
|
|
2528
|
+
);
|
|
2529
|
+
}
|
|
2530
|
+
|
|
2531
|
+
// src/notes/NoteView.tsx
|
|
2532
|
+
import { jsx as jsx30, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
2533
|
+
function NoteView({
|
|
2534
|
+
note,
|
|
2535
|
+
onEdit,
|
|
2536
|
+
onDelete,
|
|
2537
|
+
onTogglePin,
|
|
2538
|
+
className
|
|
2539
|
+
}) {
|
|
2540
|
+
return /* @__PURE__ */ jsxs22(
|
|
2541
|
+
"div",
|
|
2542
|
+
{
|
|
2543
|
+
"data-slot": "note-view",
|
|
2544
|
+
className: cn(
|
|
2545
|
+
"yc-NoteView-root flex flex-col h-full rounded-lg border border-border bg-surface p-6 shadow-1",
|
|
2546
|
+
className
|
|
2547
|
+
),
|
|
2548
|
+
children: [
|
|
2549
|
+
/* @__PURE__ */ jsxs22("div", { className: "flex items-start justify-between gap-4 border-b border-border pb-4", children: [
|
|
2550
|
+
/* @__PURE__ */ jsxs22("div", { children: [
|
|
2551
|
+
/* @__PURE__ */ jsxs22("h2", { className: "yc-NoteView-title text-xl font-bold text-fg flex items-center gap-2", children: [
|
|
2552
|
+
note.pinned && /* @__PURE__ */ jsx30("span", { className: "text-primary text-base", children: "\u{1F4CC}" }),
|
|
2553
|
+
note.title || "Untitled Note"
|
|
2554
|
+
] }),
|
|
2555
|
+
/* @__PURE__ */ jsxs22("div", { className: "mt-1 flex items-center gap-3 text-xs text-fg-muted", children: [
|
|
2556
|
+
/* @__PURE__ */ jsxs22("span", { children: [
|
|
2557
|
+
"Updated ",
|
|
2558
|
+
note.updatedAt
|
|
2559
|
+
] }),
|
|
2560
|
+
/* @__PURE__ */ jsxs22("span", { children: [
|
|
2561
|
+
"Created ",
|
|
2562
|
+
note.createdAt
|
|
2563
|
+
] })
|
|
2564
|
+
] })
|
|
2565
|
+
] }),
|
|
2566
|
+
/* @__PURE__ */ jsxs22("div", { className: "flex items-center gap-2", children: [
|
|
2567
|
+
onTogglePin && /* @__PURE__ */ jsx30(
|
|
2568
|
+
Button,
|
|
2569
|
+
{
|
|
2570
|
+
size: "sm",
|
|
2571
|
+
variant: "ghost",
|
|
2572
|
+
onClick: () => onTogglePin(note.id),
|
|
2573
|
+
"aria-label": note.pinned ? "Unpin note" : "Pin note",
|
|
2574
|
+
children: note.pinned ? "Unpin" : "Pin"
|
|
2575
|
+
}
|
|
2576
|
+
),
|
|
2577
|
+
onEdit && /* @__PURE__ */ jsx30(Button, { size: "sm", variant: "secondary", onClick: () => onEdit(note), children: "Edit" }),
|
|
2578
|
+
onDelete && /* @__PURE__ */ jsx30(Button, { size: "sm", variant: "danger", onClick: () => onDelete(note.id), children: "Delete" })
|
|
2579
|
+
] })
|
|
2580
|
+
] }),
|
|
2581
|
+
note.tags && note.tags.length > 0 && /* @__PURE__ */ jsx30("div", { className: "mt-3 flex flex-wrap gap-1.5 pb-2", children: note.tags.map((tag) => /* @__PURE__ */ jsx30(Badge, { variant: "neutral", children: tag }, tag)) }),
|
|
2582
|
+
/* @__PURE__ */ jsx30(
|
|
2583
|
+
"div",
|
|
2584
|
+
{
|
|
2585
|
+
"data-slot": "note-content",
|
|
2586
|
+
className: "yc-NoteView-content mt-4 flex-1 overflow-y-auto text-sm text-fg leading-relaxed whitespace-pre-wrap",
|
|
2587
|
+
children: note.content
|
|
2588
|
+
}
|
|
2589
|
+
)
|
|
2590
|
+
]
|
|
2591
|
+
}
|
|
2592
|
+
);
|
|
2593
|
+
}
|
|
2594
|
+
|
|
2595
|
+
// src/calendar/Calendar.tsx
|
|
2596
|
+
import * as React23 from "react";
|
|
2597
|
+
import { jsx as jsx31, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
2598
|
+
function Calendar({
|
|
2599
|
+
events,
|
|
2600
|
+
selectedDate = /* @__PURE__ */ new Date(),
|
|
2601
|
+
onSelectDate,
|
|
2602
|
+
onSelectEvent,
|
|
2603
|
+
viewMode = "month",
|
|
2604
|
+
onViewModeChange,
|
|
2605
|
+
className
|
|
2606
|
+
}) {
|
|
2607
|
+
const [currentDate, setCurrentDate] = React23.useState(selectedDate);
|
|
2608
|
+
const [currentView, setCurrentView] = React23.useState(viewMode);
|
|
2609
|
+
const activeView = onViewModeChange ? viewMode : currentView;
|
|
2610
|
+
const setView = (v) => {
|
|
2611
|
+
setCurrentView(v);
|
|
2612
|
+
onViewModeChange?.(v);
|
|
2613
|
+
};
|
|
2614
|
+
const handlePrev = () => {
|
|
2615
|
+
const next = new Date(currentDate);
|
|
2616
|
+
if (activeView === "month") {
|
|
2617
|
+
next.setMonth(next.getMonth() - 1);
|
|
2618
|
+
} else if (activeView === "week") {
|
|
2619
|
+
next.setDate(next.getDate() - 7);
|
|
2620
|
+
} else {
|
|
2621
|
+
next.setDate(next.getDate() - 1);
|
|
2622
|
+
}
|
|
2623
|
+
setCurrentDate(next);
|
|
2624
|
+
onSelectDate?.(next);
|
|
2625
|
+
};
|
|
2626
|
+
const handleNext = () => {
|
|
2627
|
+
const next = new Date(currentDate);
|
|
2628
|
+
if (activeView === "month") {
|
|
2629
|
+
next.setMonth(next.getMonth() + 1);
|
|
2630
|
+
} else if (activeView === "week") {
|
|
2631
|
+
next.setDate(next.getDate() + 7);
|
|
2632
|
+
} else {
|
|
2633
|
+
next.setDate(next.getDate() + 1);
|
|
2634
|
+
}
|
|
2635
|
+
setCurrentDate(next);
|
|
2636
|
+
onSelectDate?.(next);
|
|
2637
|
+
};
|
|
2638
|
+
const handleToday = () => {
|
|
2639
|
+
const today = /* @__PURE__ */ new Date();
|
|
2640
|
+
setCurrentDate(today);
|
|
2641
|
+
onSelectDate?.(today);
|
|
2642
|
+
};
|
|
2643
|
+
const monthName = currentDate.toLocaleString("default", { month: "long", year: "numeric" });
|
|
2644
|
+
const monthDays = React23.useMemo(() => {
|
|
2645
|
+
const year = currentDate.getFullYear();
|
|
2646
|
+
const month = currentDate.getMonth();
|
|
2647
|
+
const firstDayIndex = new Date(year, month, 1).getDay();
|
|
2648
|
+
const daysCount = new Date(year, month + 1, 0).getDate();
|
|
2649
|
+
const days = [];
|
|
2650
|
+
const prevMonthDaysCount = new Date(year, month, 0).getDate();
|
|
2651
|
+
for (let i = firstDayIndex - 1; i >= 0; i--) {
|
|
2652
|
+
days.push({
|
|
2653
|
+
dayNumber: prevMonthDaysCount - i,
|
|
2654
|
+
date: new Date(year, month - 1, prevMonthDaysCount - i),
|
|
2655
|
+
isCurrentMonth: false
|
|
2656
|
+
});
|
|
2657
|
+
}
|
|
2658
|
+
for (let i = 1; i <= daysCount; i++) {
|
|
2659
|
+
days.push({
|
|
2660
|
+
dayNumber: i,
|
|
2661
|
+
date: new Date(year, month, i),
|
|
2662
|
+
isCurrentMonth: true
|
|
2663
|
+
});
|
|
2664
|
+
}
|
|
2665
|
+
return days;
|
|
2666
|
+
}, [currentDate]);
|
|
2667
|
+
return /* @__PURE__ */ jsxs23(
|
|
2668
|
+
"div",
|
|
2669
|
+
{
|
|
2670
|
+
"data-slot": "calendar",
|
|
2671
|
+
"data-view": activeView,
|
|
2672
|
+
className: cn(
|
|
2673
|
+
"yc-Calendar-root flex flex-col rounded-xl border border-border bg-surface p-4 shadow-1",
|
|
2674
|
+
className
|
|
2675
|
+
),
|
|
2676
|
+
children: [
|
|
2677
|
+
/* @__PURE__ */ jsxs23("div", { className: "flex flex-wrap items-center justify-between gap-3 pb-4 border-b border-border", children: [
|
|
2678
|
+
/* @__PURE__ */ jsxs23("div", { className: "flex items-center gap-2", children: [
|
|
2679
|
+
/* @__PURE__ */ jsx31("h2", { className: "text-lg font-bold text-fg", children: monthName }),
|
|
2680
|
+
/* @__PURE__ */ jsx31(Button, { size: "sm", variant: "outline", onClick: handleToday, children: "Today" }),
|
|
2681
|
+
/* @__PURE__ */ jsxs23("div", { className: "flex items-center gap-1", children: [
|
|
2682
|
+
/* @__PURE__ */ jsx31(Button, { size: "sm", variant: "ghost", onClick: handlePrev, "aria-label": "Previous", children: "\u2039" }),
|
|
2683
|
+
/* @__PURE__ */ jsx31(Button, { size: "sm", variant: "ghost", onClick: handleNext, "aria-label": "Next", children: "\u203A" })
|
|
2684
|
+
] })
|
|
2685
|
+
] }),
|
|
2686
|
+
/* @__PURE__ */ jsx31(
|
|
2687
|
+
SegmentedControl,
|
|
2688
|
+
{
|
|
2689
|
+
size: "sm",
|
|
2690
|
+
value: activeView,
|
|
2691
|
+
onChange: (v) => setView(v),
|
|
2692
|
+
options: [
|
|
2693
|
+
{ value: "month", label: "Month" },
|
|
2694
|
+
{ value: "week", label: "Week" },
|
|
2695
|
+
{ value: "agenda", label: "Agenda" }
|
|
2696
|
+
]
|
|
2697
|
+
}
|
|
2698
|
+
)
|
|
2699
|
+
] }),
|
|
2700
|
+
activeView === "month" && /* @__PURE__ */ jsxs23("div", { "data-slot": "month-view", className: "mt-4 flex flex-col", children: [
|
|
2701
|
+
/* @__PURE__ */ jsx31("div", { className: "grid grid-cols-7 text-center text-xs font-semibold text-fg-muted pb-2", children: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].map((d) => /* @__PURE__ */ jsx31("div", { className: "py-1", children: d }, d)) }),
|
|
2702
|
+
/* @__PURE__ */ jsx31("div", { className: "grid grid-cols-7 gap-px bg-border rounded-lg overflow-hidden border border-border", children: monthDays.map((d, idx) => {
|
|
2703
|
+
const dateStr = d.date.toISOString().split("T")[0];
|
|
2704
|
+
const dayEvents = events.filter((e) => {
|
|
2705
|
+
const s = typeof e.start === "string" ? e.start : e.start.toISOString();
|
|
2706
|
+
return s.startsWith(dateStr);
|
|
2707
|
+
});
|
|
2708
|
+
const hasConflict = dayEvents.some((e) => e.conflict);
|
|
2709
|
+
return /* @__PURE__ */ jsxs23(
|
|
2710
|
+
"div",
|
|
2711
|
+
{
|
|
2712
|
+
onClick: () => onSelectDate?.(d.date),
|
|
2713
|
+
className: cn(
|
|
2714
|
+
"min-h-[80px] p-1.5 flex flex-col justify-between cursor-pointer transition-colors",
|
|
2715
|
+
d.isCurrentMonth ? "bg-surface hover:bg-surface-2" : "bg-surface-2/50 text-fg-muted"
|
|
2716
|
+
),
|
|
2717
|
+
children: [
|
|
2718
|
+
/* @__PURE__ */ jsxs23("div", { className: "flex items-center justify-between", children: [
|
|
2719
|
+
/* @__PURE__ */ jsx31("span", { className: "text-xs font-medium", children: d.dayNumber }),
|
|
2720
|
+
hasConflict && /* @__PURE__ */ jsx31(Badge, { variant: "danger", className: "text-[10px] px-1 py-0", children: "Conflict" })
|
|
2721
|
+
] }),
|
|
2722
|
+
/* @__PURE__ */ jsxs23("div", { className: "flex flex-col gap-1 mt-1", children: [
|
|
2723
|
+
dayEvents.slice(0, 2).map((ev) => /* @__PURE__ */ jsx31(
|
|
2724
|
+
"button",
|
|
2725
|
+
{
|
|
2726
|
+
type: "button",
|
|
2727
|
+
onClick: (e) => {
|
|
2728
|
+
e.stopPropagation();
|
|
2729
|
+
onSelectEvent?.(ev);
|
|
2730
|
+
},
|
|
2731
|
+
className: cn(
|
|
2732
|
+
"truncate rounded px-1 text-[11px] text-start font-medium",
|
|
2733
|
+
ev.conflict ? "bg-danger-bg text-danger-fg" : "bg-primary-subtle text-primary"
|
|
2734
|
+
),
|
|
2735
|
+
children: ev.title
|
|
2736
|
+
},
|
|
2737
|
+
ev.id
|
|
2738
|
+
)),
|
|
2739
|
+
dayEvents.length > 2 && /* @__PURE__ */ jsxs23("span", { className: "text-[10px] text-fg-muted ps-1", children: [
|
|
2740
|
+
"+",
|
|
2741
|
+
dayEvents.length - 2,
|
|
2742
|
+
" more"
|
|
2743
|
+
] })
|
|
2744
|
+
] })
|
|
2745
|
+
]
|
|
2746
|
+
},
|
|
2747
|
+
idx
|
|
2748
|
+
);
|
|
2749
|
+
}) })
|
|
2750
|
+
] }),
|
|
2751
|
+
activeView === "week" && /* @__PURE__ */ jsx31("div", { "data-slot": "week-view", className: "mt-4 p-4 text-center text-sm text-fg-muted", children: /* @__PURE__ */ jsx31("div", { className: "grid grid-cols-7 gap-2", children: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].map((dayName, idx) => /* @__PURE__ */ jsxs23("div", { className: "rounded-lg border border-border p-2 bg-surface-2", children: [
|
|
2752
|
+
/* @__PURE__ */ jsx31("div", { className: "font-semibold text-fg text-xs pb-1", children: dayName }),
|
|
2753
|
+
/* @__PURE__ */ jsx31("div", { className: "space-y-1 mt-2", children: events.slice(0, 2).map((ev) => /* @__PURE__ */ jsx31(
|
|
2754
|
+
"div",
|
|
2755
|
+
{
|
|
2756
|
+
onClick: () => onSelectEvent?.(ev),
|
|
2757
|
+
className: cn(
|
|
2758
|
+
"cursor-pointer rounded p-1 text-[11px] truncate text-start",
|
|
2759
|
+
ev.conflict ? "bg-danger-bg text-danger-fg" : "bg-surface text-fg shadow-1"
|
|
2760
|
+
),
|
|
2761
|
+
children: ev.title
|
|
2762
|
+
},
|
|
2763
|
+
ev.id
|
|
2764
|
+
)) })
|
|
2765
|
+
] }, idx)) }) }),
|
|
2766
|
+
activeView === "agenda" && /* @__PURE__ */ jsx31("div", { "data-slot": "agenda-view", className: "mt-4 flex flex-col gap-2", children: events.length === 0 ? /* @__PURE__ */ jsx31("div", { className: "py-8 text-center text-sm text-fg-muted", children: "No scheduled events." }) : events.map((ev) => /* @__PURE__ */ jsxs23(
|
|
2767
|
+
"div",
|
|
2768
|
+
{
|
|
2769
|
+
onClick: () => onSelectEvent?.(ev),
|
|
2770
|
+
className: cn(
|
|
2771
|
+
"flex items-center justify-between rounded-lg border p-3 cursor-pointer hover:bg-surface-2 transition-colors",
|
|
2772
|
+
ev.conflict ? "border-danger-border bg-danger-bg/20" : "border-border bg-surface"
|
|
2773
|
+
),
|
|
2774
|
+
children: [
|
|
2775
|
+
/* @__PURE__ */ jsxs23("div", { children: [
|
|
2776
|
+
/* @__PURE__ */ jsx31("h4", { className: "text-sm font-semibold text-fg", children: ev.title }),
|
|
2777
|
+
/* @__PURE__ */ jsxs23("p", { className: "text-xs text-fg-muted", children: [
|
|
2778
|
+
String(ev.start),
|
|
2779
|
+
" ",
|
|
2780
|
+
ev.location && `\u2022 ${ev.location}`
|
|
2781
|
+
] })
|
|
2782
|
+
] }),
|
|
2783
|
+
ev.conflict && /* @__PURE__ */ jsx31(Badge, { variant: "danger", children: "Conflict" })
|
|
2784
|
+
]
|
|
2785
|
+
},
|
|
2786
|
+
ev.id
|
|
2787
|
+
)) })
|
|
2788
|
+
]
|
|
2789
|
+
}
|
|
2790
|
+
);
|
|
2791
|
+
}
|
|
2792
|
+
|
|
2793
|
+
// src/calendar/ScheduleAgenda.tsx
|
|
2794
|
+
import { jsx as jsx32, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
2795
|
+
function ScheduleAgenda({
|
|
2796
|
+
events,
|
|
2797
|
+
onSelectEvent,
|
|
2798
|
+
className
|
|
2799
|
+
}) {
|
|
2800
|
+
return /* @__PURE__ */ jsx32(
|
|
2801
|
+
"div",
|
|
2802
|
+
{
|
|
2803
|
+
"data-slot": "schedule-agenda",
|
|
2804
|
+
className: cn("yc-ScheduleAgenda-root flex flex-col gap-3", className),
|
|
2805
|
+
children: events.length === 0 ? /* @__PURE__ */ jsx32("div", { className: "py-6 text-center text-sm text-fg-muted", children: "No scheduled agenda items." }) : events.map((event) => /* @__PURE__ */ jsxs24(
|
|
2806
|
+
"div",
|
|
2807
|
+
{
|
|
2808
|
+
onClick: () => onSelectEvent?.(event),
|
|
2809
|
+
"data-slot": "agenda-item",
|
|
2810
|
+
className: cn(
|
|
2811
|
+
"yc-ScheduleAgenda-item flex items-center justify-between rounded-lg border p-3 shadow-1 cursor-pointer transition-colors",
|
|
2812
|
+
event.conflict ? "border-danger-border bg-danger-bg/20" : "border-border bg-surface hover:bg-surface-2"
|
|
2813
|
+
),
|
|
2814
|
+
children: [
|
|
2815
|
+
/* @__PURE__ */ jsxs24("div", { className: "flex flex-col gap-0.5", children: [
|
|
2816
|
+
/* @__PURE__ */ jsx32("span", { className: "text-sm font-semibold text-fg", children: event.title }),
|
|
2817
|
+
/* @__PURE__ */ jsxs24("span", { className: "text-xs text-fg-muted", children: [
|
|
2818
|
+
String(event.start),
|
|
2819
|
+
" ",
|
|
2820
|
+
event.location && `\u2022 ${event.location}`
|
|
2821
|
+
] })
|
|
2822
|
+
] }),
|
|
2823
|
+
event.conflict && /* @__PURE__ */ jsx32(Badge, { variant: "danger", children: "Conflict" })
|
|
2824
|
+
]
|
|
2825
|
+
},
|
|
2826
|
+
event.id
|
|
2827
|
+
))
|
|
2828
|
+
}
|
|
2829
|
+
);
|
|
2830
|
+
}
|
|
2831
|
+
|
|
2832
|
+
// src/calendar/Reminders.tsx
|
|
2833
|
+
import { jsx as jsx33, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
2834
|
+
function Reminders({
|
|
2835
|
+
reminders,
|
|
2836
|
+
onComplete,
|
|
2837
|
+
onSnooze,
|
|
2838
|
+
className
|
|
2839
|
+
}) {
|
|
2840
|
+
return /* @__PURE__ */ jsx33(
|
|
2841
|
+
"div",
|
|
2842
|
+
{
|
|
2843
|
+
"data-slot": "reminders",
|
|
2844
|
+
className: cn("yc-Reminders-root flex flex-col gap-2", className),
|
|
2845
|
+
children: reminders.length === 0 ? /* @__PURE__ */ jsx33("div", { className: "py-6 text-center text-sm text-fg-muted", children: "No reminders pending." }) : reminders.map((reminder) => {
|
|
2846
|
+
const isDone = reminder.completed;
|
|
2847
|
+
return /* @__PURE__ */ jsxs25(
|
|
2848
|
+
"div",
|
|
2849
|
+
{
|
|
2850
|
+
"data-slot": "reminder-item",
|
|
2851
|
+
"data-completed": isDone ? "true" : void 0,
|
|
2852
|
+
className: cn(
|
|
2853
|
+
"yc-Reminders-item flex items-center justify-between rounded-lg border border-border bg-surface p-3 shadow-1 transition-opacity",
|
|
2854
|
+
isDone && "opacity-60"
|
|
2855
|
+
),
|
|
2856
|
+
children: [
|
|
2857
|
+
/* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-3", children: [
|
|
2858
|
+
/* @__PURE__ */ jsx33(
|
|
2859
|
+
"input",
|
|
2860
|
+
{
|
|
2861
|
+
type: "checkbox",
|
|
2862
|
+
checked: isDone,
|
|
2863
|
+
onChange: (e) => onComplete?.(reminder.id, e.target.checked),
|
|
2864
|
+
className: "h-4 w-4 rounded border-border text-primary focus:ring-primary",
|
|
2865
|
+
"aria-label": `Mark ${reminder.title} as completed`
|
|
2866
|
+
}
|
|
2867
|
+
),
|
|
2868
|
+
/* @__PURE__ */ jsxs25("div", { className: "flex flex-col", children: [
|
|
2869
|
+
/* @__PURE__ */ jsx33(
|
|
2870
|
+
"span",
|
|
2871
|
+
{
|
|
2872
|
+
className: cn(
|
|
2873
|
+
"text-sm font-medium text-fg",
|
|
2874
|
+
isDone && "line-through text-fg-muted"
|
|
2875
|
+
),
|
|
2876
|
+
children: reminder.title
|
|
2877
|
+
}
|
|
2878
|
+
),
|
|
2879
|
+
/* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-2 text-xs text-fg-muted", children: [
|
|
2880
|
+
/* @__PURE__ */ jsxs25("span", { children: [
|
|
2881
|
+
"Due: ",
|
|
2882
|
+
reminder.dueAt
|
|
2883
|
+
] }),
|
|
2884
|
+
reminder.rrule && /* @__PURE__ */ jsxs25(Badge, { variant: "neutral", className: "text-[10px] px-1 py-0", children: [
|
|
2885
|
+
"\u{1F504} ",
|
|
2886
|
+
reminder.rrule
|
|
2887
|
+
] }),
|
|
2888
|
+
reminder.snoozedUntil && /* @__PURE__ */ jsxs25("span", { className: "text-warn-fg", children: [
|
|
2889
|
+
"Snoozed until ",
|
|
2890
|
+
reminder.snoozedUntil
|
|
2891
|
+
] })
|
|
2892
|
+
] })
|
|
2893
|
+
] })
|
|
2894
|
+
] }),
|
|
2895
|
+
!isDone && onSnooze && /* @__PURE__ */ jsx33(
|
|
2896
|
+
Button,
|
|
2897
|
+
{
|
|
2898
|
+
size: "sm",
|
|
2899
|
+
variant: "ghost",
|
|
2900
|
+
onClick: () => onSnooze(reminder.id, 15),
|
|
2901
|
+
className: "text-xs",
|
|
2902
|
+
children: "Snooze 15m"
|
|
2903
|
+
}
|
|
2904
|
+
)
|
|
2905
|
+
]
|
|
2906
|
+
},
|
|
2907
|
+
reminder.id
|
|
2908
|
+
);
|
|
2909
|
+
})
|
|
2910
|
+
}
|
|
2911
|
+
);
|
|
2912
|
+
}
|
|
2913
|
+
|
|
2914
|
+
// src/calendar/Routines.tsx
|
|
2915
|
+
import { jsx as jsx34, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
2916
|
+
function Routines({
|
|
2917
|
+
routines,
|
|
2918
|
+
onToggleActive,
|
|
2919
|
+
className
|
|
2920
|
+
}) {
|
|
2921
|
+
return /* @__PURE__ */ jsx34(
|
|
2922
|
+
"div",
|
|
2923
|
+
{
|
|
2924
|
+
"data-slot": "routines",
|
|
2925
|
+
className: cn("yc-Routines-root flex flex-col gap-2.5", className),
|
|
2926
|
+
children: routines.length === 0 ? /* @__PURE__ */ jsx34("div", { className: "py-6 text-center text-sm text-fg-muted", children: "No active routines configured." }) : routines.map((routine) => /* @__PURE__ */ jsxs26(
|
|
2927
|
+
"div",
|
|
2928
|
+
{
|
|
2929
|
+
"data-slot": "routine-item",
|
|
2930
|
+
className: "yc-Routines-item flex items-center justify-between rounded-lg border border-border bg-surface p-3 shadow-1",
|
|
2931
|
+
children: [
|
|
2932
|
+
/* @__PURE__ */ jsxs26("div", { className: "flex flex-col gap-1", children: [
|
|
2933
|
+
/* @__PURE__ */ jsx34("span", { className: "text-sm font-semibold text-fg", children: routine.title }),
|
|
2934
|
+
/* @__PURE__ */ jsxs26("div", { className: "flex items-center gap-2 text-xs text-fg-muted", children: [
|
|
2935
|
+
/* @__PURE__ */ jsx34(Badge, { variant: "neutral", children: routine.frequency }),
|
|
2936
|
+
/* @__PURE__ */ jsx34("span", { children: routine.timeOfDay }),
|
|
2937
|
+
routine.durationMinutes && /* @__PURE__ */ jsxs26("span", { children: [
|
|
2938
|
+
"(",
|
|
2939
|
+
routine.durationMinutes,
|
|
2940
|
+
" mins)"
|
|
2941
|
+
] })
|
|
2942
|
+
] })
|
|
2943
|
+
] }),
|
|
2944
|
+
/* @__PURE__ */ jsxs26("label", { className: "relative inline-flex cursor-pointer items-center", children: [
|
|
2945
|
+
/* @__PURE__ */ jsx34(
|
|
2946
|
+
"input",
|
|
2947
|
+
{
|
|
2948
|
+
type: "checkbox",
|
|
2949
|
+
checked: routine.active,
|
|
2950
|
+
onChange: (e) => onToggleActive?.(routine.id, e.target.checked),
|
|
2951
|
+
className: "sr-only peer",
|
|
2952
|
+
"aria-label": `Toggle ${routine.title} routine`
|
|
2953
|
+
}
|
|
2954
|
+
),
|
|
2955
|
+
/* @__PURE__ */ jsx34("div", { className: "h-6 w-11 rounded-full bg-surface-3 peer peer-checked:bg-primary peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-border after:border after:rounded-full after:h-5 after:w-5 after:transition-all" })
|
|
2956
|
+
] })
|
|
2957
|
+
]
|
|
2958
|
+
},
|
|
2959
|
+
routine.id
|
|
2960
|
+
))
|
|
2961
|
+
}
|
|
2962
|
+
);
|
|
2963
|
+
}
|
|
2964
|
+
|
|
2965
|
+
// src/calendar/ActivityTimeline.tsx
|
|
2966
|
+
import { jsx as jsx35, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
2967
|
+
var statusColors = {
|
|
2968
|
+
info: "bg-info-fg",
|
|
2969
|
+
success: "bg-success-fg",
|
|
2970
|
+
warn: "bg-warn-fg",
|
|
2971
|
+
danger: "bg-danger-fg"
|
|
2972
|
+
};
|
|
2973
|
+
function ActivityTimeline({ events, className }) {
|
|
2974
|
+
return /* @__PURE__ */ jsx35(
|
|
2975
|
+
"div",
|
|
2976
|
+
{
|
|
2977
|
+
"data-slot": "activity-timeline",
|
|
2978
|
+
className: cn("yc-ActivityTimeline-root flex flex-col", className),
|
|
2979
|
+
children: events.length === 0 ? /* @__PURE__ */ jsx35("div", { className: "py-6 text-center text-sm text-fg-muted", children: "No timeline activities." }) : /* @__PURE__ */ jsx35("div", { className: "relative border-s border-border ms-3.5 space-y-6 my-2", children: events.map((ev) => /* @__PURE__ */ jsxs27(
|
|
2980
|
+
"div",
|
|
2981
|
+
{
|
|
2982
|
+
"data-slot": "timeline-item",
|
|
2983
|
+
className: "yc-ActivityTimeline-item relative ps-6",
|
|
2984
|
+
children: [
|
|
2985
|
+
/* @__PURE__ */ jsx35(
|
|
2986
|
+
"span",
|
|
2987
|
+
{
|
|
2988
|
+
className: cn(
|
|
2989
|
+
"absolute -start-1.5 top-1 h-3 w-3 rounded-full border-2 border-surface",
|
|
2990
|
+
statusColors[ev.status || "info"]
|
|
2991
|
+
)
|
|
2992
|
+
}
|
|
2993
|
+
),
|
|
2994
|
+
/* @__PURE__ */ jsxs27("div", { className: "flex flex-col", children: [
|
|
2995
|
+
/* @__PURE__ */ jsxs27("div", { className: "flex items-center justify-between gap-2", children: [
|
|
2996
|
+
/* @__PURE__ */ jsx35("span", { className: "text-sm font-semibold text-fg", children: ev.title }),
|
|
2997
|
+
/* @__PURE__ */ jsx35("span", { className: "text-xs text-fg-muted", children: ev.timestamp })
|
|
2998
|
+
] }),
|
|
2999
|
+
ev.description && /* @__PURE__ */ jsx35("p", { className: "mt-1 text-xs text-fg-muted leading-relaxed", children: ev.description })
|
|
3000
|
+
] })
|
|
3001
|
+
]
|
|
3002
|
+
},
|
|
3003
|
+
ev.id
|
|
3004
|
+
)) })
|
|
3005
|
+
}
|
|
3006
|
+
);
|
|
3007
|
+
}
|
|
3008
|
+
|
|
3009
|
+
// src/profiles/ProfileSwitcher.tsx
|
|
3010
|
+
import { jsx as jsx36, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
3011
|
+
function ProfileSwitcher({
|
|
3012
|
+
members,
|
|
3013
|
+
activeMemberId,
|
|
3014
|
+
onSelectMember,
|
|
3015
|
+
className
|
|
3016
|
+
}) {
|
|
3017
|
+
const activeMember = members.find((m) => m.id === activeMemberId) || members[0];
|
|
3018
|
+
return /* @__PURE__ */ jsx36(
|
|
3019
|
+
"div",
|
|
3020
|
+
{
|
|
3021
|
+
"data-slot": "profile-switcher",
|
|
3022
|
+
className: cn("yc-ProfileSwitcher-root inline-block", className),
|
|
3023
|
+
children: /* @__PURE__ */ jsxs28(Menu, { children: [
|
|
3024
|
+
/* @__PURE__ */ jsxs28(MenuTrigger, { className: "flex items-center gap-2 rounded-lg border border-border bg-surface px-2.5 py-1.5 shadow-1 hover:bg-surface-2 transition-colors", children: [
|
|
3025
|
+
/* @__PURE__ */ jsx36(Avatar, { size: "sm", children: activeMember?.avatarUrl ? /* @__PURE__ */ jsx36(AvatarImage, { src: activeMember.avatarUrl, alt: activeMember.displayName }) : /* @__PURE__ */ jsx36(AvatarFallback, { children: activeMember ? activeMember.displayName.substring(0, 2).toUpperCase() : "ME" }) }),
|
|
3026
|
+
/* @__PURE__ */ jsxs28("div", { className: "flex flex-col text-start", children: [
|
|
3027
|
+
/* @__PURE__ */ jsx36("span", { className: "text-xs font-semibold text-fg", children: activeMember ? activeMember.displayName : "Select Profile" }),
|
|
3028
|
+
activeMember?.relationship && /* @__PURE__ */ jsx36("span", { className: "text-[10px] text-fg-muted", children: activeMember.relationship })
|
|
3029
|
+
] }),
|
|
3030
|
+
/* @__PURE__ */ jsx36("span", { className: "ms-1 text-xs text-fg-muted", children: "\u25BE" })
|
|
3031
|
+
] }),
|
|
3032
|
+
/* @__PURE__ */ jsx36(MenuContent, { align: "start", children: members.map((member) => /* @__PURE__ */ jsxs28(
|
|
3033
|
+
MenuItem,
|
|
3034
|
+
{
|
|
3035
|
+
onClick: () => onSelectMember(member.id),
|
|
3036
|
+
className: cn(
|
|
3037
|
+
"flex items-center gap-2.5 py-2",
|
|
3038
|
+
member.id === activeMemberId && "bg-surface-2 font-semibold"
|
|
3039
|
+
),
|
|
3040
|
+
children: [
|
|
3041
|
+
/* @__PURE__ */ jsx36(Avatar, { size: "sm", children: member.avatarUrl ? /* @__PURE__ */ jsx36(AvatarImage, { src: member.avatarUrl, alt: member.displayName }) : /* @__PURE__ */ jsx36(AvatarFallback, { children: member.displayName.substring(0, 2).toUpperCase() }) }),
|
|
3042
|
+
/* @__PURE__ */ jsxs28("div", { className: "flex flex-col text-start", children: [
|
|
3043
|
+
/* @__PURE__ */ jsx36("span", { className: "text-xs text-fg", children: member.displayName }),
|
|
3044
|
+
/* @__PURE__ */ jsx36("span", { className: "text-[10px] text-fg-muted", children: member.relationship || member.role })
|
|
3045
|
+
] })
|
|
3046
|
+
]
|
|
3047
|
+
},
|
|
3048
|
+
member.id
|
|
3049
|
+
)) })
|
|
3050
|
+
] })
|
|
3051
|
+
}
|
|
3052
|
+
);
|
|
3053
|
+
}
|
|
3054
|
+
|
|
3055
|
+
// src/profiles/MemberCard.tsx
|
|
3056
|
+
import { jsx as jsx37, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
3057
|
+
var roleBadgeVariant = {
|
|
3058
|
+
owner: "primary",
|
|
3059
|
+
steward: "info",
|
|
3060
|
+
member: "neutral",
|
|
3061
|
+
viewer: "warn"
|
|
3062
|
+
};
|
|
3063
|
+
function MemberCard({ member, onEdit, className }) {
|
|
3064
|
+
return /* @__PURE__ */ jsxs29(
|
|
3065
|
+
"div",
|
|
3066
|
+
{
|
|
3067
|
+
"data-slot": "member-card",
|
|
3068
|
+
className: cn(
|
|
3069
|
+
"yc-MemberCard-root flex items-center justify-between rounded-xl border border-border bg-surface p-4 shadow-1",
|
|
3070
|
+
className
|
|
3071
|
+
),
|
|
3072
|
+
children: [
|
|
3073
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex items-center gap-3", children: [
|
|
3074
|
+
/* @__PURE__ */ jsx37(Avatar, { size: "md", children: member.avatarUrl ? /* @__PURE__ */ jsx37(AvatarImage, { src: member.avatarUrl, alt: member.displayName }) : /* @__PURE__ */ jsx37(AvatarFallback, { children: member.displayName.substring(0, 2).toUpperCase() }) }),
|
|
3075
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex flex-col", children: [
|
|
3076
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex items-center gap-2", children: [
|
|
3077
|
+
/* @__PURE__ */ jsx37("span", { className: "text-sm font-bold text-fg", children: member.displayName }),
|
|
3078
|
+
/* @__PURE__ */ jsx37(Badge, { variant: roleBadgeVariant[member.role], children: member.role })
|
|
3079
|
+
] }),
|
|
3080
|
+
member.relationship && /* @__PURE__ */ jsx37("span", { className: "text-xs text-fg-muted", children: member.relationship }),
|
|
3081
|
+
member.scopes && member.scopes.length > 0 && /* @__PURE__ */ jsx37("div", { className: "mt-1 flex flex-wrap gap-1", children: member.scopes.map((scope) => /* @__PURE__ */ jsx37(
|
|
3082
|
+
"span",
|
|
3083
|
+
{
|
|
3084
|
+
className: "rounded bg-surface-2 px-1.5 py-0.5 text-[10px] text-fg-muted",
|
|
3085
|
+
children: scope
|
|
3086
|
+
},
|
|
3087
|
+
scope
|
|
3088
|
+
)) })
|
|
3089
|
+
] })
|
|
3090
|
+
] }),
|
|
3091
|
+
onEdit && /* @__PURE__ */ jsx37(Button, { size: "sm", variant: "ghost", onClick: () => onEdit(member), children: "Edit" })
|
|
3092
|
+
]
|
|
3093
|
+
}
|
|
3094
|
+
);
|
|
3095
|
+
}
|
|
3096
|
+
|
|
3097
|
+
// src/profiles/ProfileEditModal.tsx
|
|
3098
|
+
import * as React24 from "react";
|
|
3099
|
+
import { jsx as jsx38, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
3100
|
+
function ProfileEditModal({
|
|
3101
|
+
member,
|
|
3102
|
+
open,
|
|
3103
|
+
onOpenChange,
|
|
3104
|
+
onSave
|
|
3105
|
+
}) {
|
|
3106
|
+
const [displayName, setDisplayName] = React24.useState(member?.displayName || "");
|
|
3107
|
+
const [relationship, setRelationship] = React24.useState(member?.relationship || "");
|
|
3108
|
+
const [role, setRole] = React24.useState(member?.role || "member");
|
|
3109
|
+
React24.useEffect(() => {
|
|
3110
|
+
if (member) {
|
|
3111
|
+
setDisplayName(member.displayName);
|
|
3112
|
+
setRelationship(member.relationship || "");
|
|
3113
|
+
setRole(member.role);
|
|
3114
|
+
}
|
|
3115
|
+
}, [member]);
|
|
3116
|
+
const handleSubmit = (e) => {
|
|
3117
|
+
e.preventDefault();
|
|
3118
|
+
if (!member) return;
|
|
3119
|
+
onSave({
|
|
3120
|
+
...member,
|
|
3121
|
+
displayName,
|
|
3122
|
+
relationship,
|
|
3123
|
+
role
|
|
3124
|
+
});
|
|
3125
|
+
onOpenChange(false);
|
|
3126
|
+
};
|
|
3127
|
+
return /* @__PURE__ */ jsx38(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsx38(DialogContent, { className: "yc-ProfileEditModal-content max-w-md", children: /* @__PURE__ */ jsxs30("form", { onSubmit: handleSubmit, "data-slot": "profile-edit-modal", children: [
|
|
3128
|
+
/* @__PURE__ */ jsx38(DialogHeader, { children: /* @__PURE__ */ jsx38(DialogTitle, { children: "Edit Profile" }) }),
|
|
3129
|
+
/* @__PURE__ */ jsxs30("div", { className: "space-y-4 py-4 text-xs", children: [
|
|
3130
|
+
/* @__PURE__ */ jsxs30("div", { children: [
|
|
3131
|
+
/* @__PURE__ */ jsx38("label", { className: "block font-medium text-fg mb-1", children: "Display Name" }),
|
|
3132
|
+
/* @__PURE__ */ jsx38(
|
|
3133
|
+
"input",
|
|
3134
|
+
{
|
|
3135
|
+
type: "text",
|
|
3136
|
+
required: true,
|
|
3137
|
+
value: displayName,
|
|
3138
|
+
onChange: (e) => setDisplayName(e.target.value),
|
|
3139
|
+
className: "w-full rounded-md border border-border bg-surface-2 p-2 text-sm text-fg focus:border-primary focus:outline-none"
|
|
3140
|
+
}
|
|
3141
|
+
)
|
|
3142
|
+
] }),
|
|
3143
|
+
/* @__PURE__ */ jsxs30("div", { children: [
|
|
3144
|
+
/* @__PURE__ */ jsx38("label", { className: "block font-medium text-fg mb-1", children: "Relationship" }),
|
|
3145
|
+
/* @__PURE__ */ jsx38(
|
|
3146
|
+
"input",
|
|
3147
|
+
{
|
|
3148
|
+
type: "text",
|
|
3149
|
+
value: relationship,
|
|
3150
|
+
onChange: (e) => setRelationship(e.target.value),
|
|
3151
|
+
placeholder: "e.g. Self, Spouse, Child, Ward",
|
|
3152
|
+
className: "w-full rounded-md border border-border bg-surface-2 p-2 text-sm text-fg focus:border-primary focus:outline-none"
|
|
3153
|
+
}
|
|
3154
|
+
)
|
|
3155
|
+
] }),
|
|
3156
|
+
/* @__PURE__ */ jsxs30("div", { children: [
|
|
3157
|
+
/* @__PURE__ */ jsx38("label", { className: "block font-medium text-fg mb-1", children: "Role" }),
|
|
3158
|
+
/* @__PURE__ */ jsxs30(
|
|
3159
|
+
"select",
|
|
3160
|
+
{
|
|
3161
|
+
value: role,
|
|
3162
|
+
onChange: (e) => setRole(e.target.value),
|
|
3163
|
+
className: "w-full rounded-md border border-border bg-surface-2 p-2 text-sm text-fg focus:border-primary focus:outline-none",
|
|
3164
|
+
children: [
|
|
3165
|
+
/* @__PURE__ */ jsx38("option", { value: "owner", children: "Owner" }),
|
|
3166
|
+
/* @__PURE__ */ jsx38("option", { value: "steward", children: "Steward" }),
|
|
3167
|
+
/* @__PURE__ */ jsx38("option", { value: "member", children: "Member" }),
|
|
3168
|
+
/* @__PURE__ */ jsx38("option", { value: "viewer", children: "Viewer" })
|
|
3169
|
+
]
|
|
3170
|
+
}
|
|
3171
|
+
)
|
|
3172
|
+
] })
|
|
3173
|
+
] }),
|
|
3174
|
+
/* @__PURE__ */ jsxs30(DialogFooter, { className: "gap-2", children: [
|
|
3175
|
+
/* @__PURE__ */ jsx38(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx38(Button, { type: "button", variant: "ghost", size: "sm", children: "Cancel" }) }),
|
|
3176
|
+
/* @__PURE__ */ jsx38(Button, { type: "submit", variant: "primary", size: "sm", children: "Save Changes" })
|
|
3177
|
+
] })
|
|
3178
|
+
] }) }) });
|
|
3179
|
+
}
|
|
3180
|
+
|
|
3181
|
+
// src/profiles/RoleMatrix.tsx
|
|
3182
|
+
import { jsx as jsx39, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
3183
|
+
function RoleMatrix({
|
|
3184
|
+
permissions,
|
|
3185
|
+
onChange,
|
|
3186
|
+
readOnly = false,
|
|
3187
|
+
className
|
|
3188
|
+
}) {
|
|
3189
|
+
const handleToggle = (domainIndex, field, val) => {
|
|
3190
|
+
if (readOnly || !onChange) return;
|
|
3191
|
+
const next = [...permissions];
|
|
3192
|
+
next[domainIndex] = {
|
|
3193
|
+
...next[domainIndex],
|
|
3194
|
+
[field]: val
|
|
3195
|
+
};
|
|
3196
|
+
onChange(next);
|
|
3197
|
+
};
|
|
3198
|
+
return /* @__PURE__ */ jsx39(
|
|
3199
|
+
"div",
|
|
3200
|
+
{
|
|
3201
|
+
"data-slot": "role-matrix",
|
|
3202
|
+
className: cn(
|
|
3203
|
+
"yc-RoleMatrix-root overflow-x-auto rounded-xl border border-border bg-surface text-fg shadow-1",
|
|
3204
|
+
className
|
|
3205
|
+
),
|
|
3206
|
+
children: /* @__PURE__ */ jsxs31("table", { className: "w-full text-start text-xs", children: [
|
|
3207
|
+
/* @__PURE__ */ jsx39("thead", { className: "border-b border-border bg-surface-2 text-fg-muted font-semibold", children: /* @__PURE__ */ jsxs31("tr", { children: [
|
|
3208
|
+
/* @__PURE__ */ jsx39("th", { className: "p-3 text-start", children: "Domain Scope" }),
|
|
3209
|
+
/* @__PURE__ */ jsx39("th", { className: "p-3 text-center", children: "Read" }),
|
|
3210
|
+
/* @__PURE__ */ jsx39("th", { className: "p-3 text-center", children: "Write" }),
|
|
3211
|
+
/* @__PURE__ */ jsx39("th", { className: "p-3 text-center", children: "Approve" })
|
|
3212
|
+
] }) }),
|
|
3213
|
+
/* @__PURE__ */ jsx39("tbody", { className: "divide-y divide-border", children: permissions.map((perm, idx) => /* @__PURE__ */ jsxs31("tr", { className: "hover:bg-surface-2/50 transition-colors", children: [
|
|
3214
|
+
/* @__PURE__ */ jsx39("td", { className: "p-3 font-medium text-fg capitalize", children: perm.domain }),
|
|
3215
|
+
/* @__PURE__ */ jsx39("td", { className: "p-3 text-center", children: /* @__PURE__ */ jsx39(
|
|
3216
|
+
"input",
|
|
3217
|
+
{
|
|
3218
|
+
type: "checkbox",
|
|
3219
|
+
checked: perm.read,
|
|
3220
|
+
disabled: readOnly,
|
|
3221
|
+
onChange: (e) => handleToggle(idx, "read", e.target.checked),
|
|
3222
|
+
className: "h-4 w-4 rounded border-border text-primary focus:ring-primary disabled:opacity-50",
|
|
3223
|
+
"aria-label": `Read permission for ${perm.domain}`
|
|
3224
|
+
}
|
|
3225
|
+
) }),
|
|
3226
|
+
/* @__PURE__ */ jsx39("td", { className: "p-3 text-center", children: /* @__PURE__ */ jsx39(
|
|
3227
|
+
"input",
|
|
3228
|
+
{
|
|
3229
|
+
type: "checkbox",
|
|
3230
|
+
checked: perm.write,
|
|
3231
|
+
disabled: readOnly,
|
|
3232
|
+
onChange: (e) => handleToggle(idx, "write", e.target.checked),
|
|
3233
|
+
className: "h-4 w-4 rounded border-border text-primary focus:ring-primary disabled:opacity-50",
|
|
3234
|
+
"aria-label": `Write permission for ${perm.domain}`
|
|
3235
|
+
}
|
|
3236
|
+
) }),
|
|
3237
|
+
/* @__PURE__ */ jsx39("td", { className: "p-3 text-center", children: /* @__PURE__ */ jsx39(
|
|
3238
|
+
"input",
|
|
3239
|
+
{
|
|
3240
|
+
type: "checkbox",
|
|
3241
|
+
checked: perm.approve,
|
|
3242
|
+
disabled: readOnly,
|
|
3243
|
+
onChange: (e) => handleToggle(idx, "approve", e.target.checked),
|
|
3244
|
+
className: "h-4 w-4 rounded border-border text-primary focus:ring-primary disabled:opacity-50",
|
|
3245
|
+
"aria-label": `Approve permission for ${perm.domain}`
|
|
3246
|
+
}
|
|
3247
|
+
) })
|
|
3248
|
+
] }, perm.domain)) })
|
|
3249
|
+
] })
|
|
3250
|
+
}
|
|
3251
|
+
);
|
|
3252
|
+
}
|
|
3253
|
+
|
|
3254
|
+
// src/profiles/ViewerInvite.tsx
|
|
3255
|
+
import * as React25 from "react";
|
|
3256
|
+
import { jsx as jsx40, jsxs as jsxs32 } from "react/jsx-runtime";
|
|
3257
|
+
function ViewerInvite({
|
|
3258
|
+
availableScopes = ["general", "notes", "calendar", "finances", "records"],
|
|
3259
|
+
onSendInvite,
|
|
3260
|
+
className
|
|
3261
|
+
}) {
|
|
3262
|
+
const [email, setEmail] = React25.useState("");
|
|
3263
|
+
const [selectedScopes, setSelectedScopes] = React25.useState(["general"]);
|
|
3264
|
+
const [expiresInDays, setExpiresInDays] = React25.useState(7);
|
|
3265
|
+
const [sent, setSent] = React25.useState(false);
|
|
3266
|
+
const [loading, setLoading] = React25.useState(false);
|
|
3267
|
+
const toggleScope = (scope) => {
|
|
3268
|
+
setSelectedScopes(
|
|
3269
|
+
(prev) => prev.includes(scope) ? prev.filter((s) => s !== scope) : [...prev, scope]
|
|
3270
|
+
);
|
|
3271
|
+
};
|
|
3272
|
+
const handleSubmit = async (e) => {
|
|
3273
|
+
e.preventDefault();
|
|
3274
|
+
if (!email) return;
|
|
3275
|
+
setLoading(true);
|
|
3276
|
+
try {
|
|
3277
|
+
await onSendInvite?.({
|
|
3278
|
+
email,
|
|
3279
|
+
role: "viewer",
|
|
3280
|
+
scopes: selectedScopes,
|
|
3281
|
+
expiresInDays
|
|
3282
|
+
});
|
|
3283
|
+
setSent(true);
|
|
3284
|
+
} finally {
|
|
3285
|
+
setLoading(false);
|
|
3286
|
+
}
|
|
3287
|
+
};
|
|
3288
|
+
return /* @__PURE__ */ jsxs32(
|
|
3289
|
+
"div",
|
|
3290
|
+
{
|
|
3291
|
+
"data-slot": "viewer-invite",
|
|
3292
|
+
className: cn(
|
|
3293
|
+
"yc-ViewerInvite-root rounded-xl border border-border bg-surface p-6 shadow-1 text-xs",
|
|
3294
|
+
className
|
|
3295
|
+
),
|
|
3296
|
+
children: [
|
|
3297
|
+
/* @__PURE__ */ jsx40("h3", { className: "text-base font-bold text-fg", children: "Invite Viewer" }),
|
|
3298
|
+
/* @__PURE__ */ jsx40("p", { className: "mt-1 text-fg-muted leading-relaxed", children: "Grant time-limited, read-only access to selected record scopes." }),
|
|
3299
|
+
sent ? /* @__PURE__ */ jsxs32("div", { className: "mt-4 rounded-lg bg-success-bg/30 border border-success-border p-4 text-center", children: [
|
|
3300
|
+
/* @__PURE__ */ jsx40("span", { className: "font-semibold text-success-fg", children: "Invitation Sent!" }),
|
|
3301
|
+
/* @__PURE__ */ jsxs32("p", { className: "mt-1 text-fg-muted", children: [
|
|
3302
|
+
"An invitation link has been dispatched to ",
|
|
3303
|
+
email,
|
|
3304
|
+
"."
|
|
3305
|
+
] }),
|
|
3306
|
+
/* @__PURE__ */ jsx40(
|
|
3307
|
+
Button,
|
|
3308
|
+
{
|
|
3309
|
+
size: "sm",
|
|
3310
|
+
variant: "ghost",
|
|
3311
|
+
className: "mt-3",
|
|
3312
|
+
onClick: () => {
|
|
3313
|
+
setSent(false);
|
|
3314
|
+
setEmail("");
|
|
3315
|
+
},
|
|
3316
|
+
children: "Invite Another"
|
|
3317
|
+
}
|
|
3318
|
+
)
|
|
3319
|
+
] }) : /* @__PURE__ */ jsxs32("form", { onSubmit: handleSubmit, className: "mt-4 space-y-4", children: [
|
|
3320
|
+
/* @__PURE__ */ jsxs32("div", { children: [
|
|
3321
|
+
/* @__PURE__ */ jsx40("label", { className: "block font-medium text-fg mb-1", children: "Viewer Email Address" }),
|
|
3322
|
+
/* @__PURE__ */ jsx40(
|
|
3323
|
+
"input",
|
|
3324
|
+
{
|
|
3325
|
+
type: "email",
|
|
3326
|
+
required: true,
|
|
3327
|
+
value: email,
|
|
3328
|
+
onChange: (e) => setEmail(e.target.value),
|
|
3329
|
+
placeholder: "e.g. viewer@example.com",
|
|
3330
|
+
className: "w-full rounded-md border border-border bg-surface-2 p-2 text-sm text-fg focus:border-primary focus:outline-none"
|
|
3331
|
+
}
|
|
3332
|
+
)
|
|
3333
|
+
] }),
|
|
3334
|
+
/* @__PURE__ */ jsxs32("div", { children: [
|
|
3335
|
+
/* @__PURE__ */ jsx40("label", { className: "block font-medium text-fg mb-1", children: "Allowed Scopes" }),
|
|
3336
|
+
/* @__PURE__ */ jsx40("div", { className: "flex flex-wrap gap-1.5 mt-1", children: availableScopes.map((scope) => /* @__PURE__ */ jsx40(
|
|
3337
|
+
Chip,
|
|
3338
|
+
{
|
|
3339
|
+
selected: selectedScopes.includes(scope),
|
|
3340
|
+
onClick: () => toggleScope(scope),
|
|
3341
|
+
children: scope
|
|
3342
|
+
},
|
|
3343
|
+
scope
|
|
3344
|
+
)) })
|
|
3345
|
+
] }),
|
|
3346
|
+
/* @__PURE__ */ jsxs32("div", { children: [
|
|
3347
|
+
/* @__PURE__ */ jsx40("label", { className: "block font-medium text-fg mb-1", children: "Access Expiration" }),
|
|
3348
|
+
/* @__PURE__ */ jsxs32(
|
|
3349
|
+
"select",
|
|
3350
|
+
{
|
|
3351
|
+
value: expiresInDays,
|
|
3352
|
+
onChange: (e) => setExpiresInDays(Number(e.target.value)),
|
|
3353
|
+
className: "w-full rounded-md border border-border bg-surface-2 p-2 text-sm text-fg focus:border-primary focus:outline-none",
|
|
3354
|
+
children: [
|
|
3355
|
+
/* @__PURE__ */ jsx40("option", { value: 1, children: "24 hours" }),
|
|
3356
|
+
/* @__PURE__ */ jsx40("option", { value: 7, children: "7 days" }),
|
|
3357
|
+
/* @__PURE__ */ jsx40("option", { value: 30, children: "30 days" }),
|
|
3358
|
+
/* @__PURE__ */ jsx40("option", { value: 90, children: "90 days" })
|
|
3359
|
+
]
|
|
3360
|
+
}
|
|
3361
|
+
)
|
|
3362
|
+
] }),
|
|
3363
|
+
/* @__PURE__ */ jsx40(Button, { type: "submit", variant: "primary", size: "sm", loading, className: "w-full", children: "Send Secure Invitation" })
|
|
3364
|
+
] })
|
|
3365
|
+
]
|
|
3366
|
+
}
|
|
3367
|
+
);
|
|
3368
|
+
}
|
|
3369
|
+
|
|
3370
|
+
// src/profiles/ShareBundle.tsx
|
|
3371
|
+
import * as React26 from "react";
|
|
3372
|
+
import { jsx as jsx41, jsxs as jsxs33 } from "react/jsx-runtime";
|
|
3373
|
+
function ShareBundle({
|
|
3374
|
+
availableScopes = ["general", "notes", "calendar", "finances", "records"],
|
|
3375
|
+
onCreateBundle,
|
|
3376
|
+
className
|
|
3377
|
+
}) {
|
|
3378
|
+
const [name, setName] = React26.useState("");
|
|
3379
|
+
const [selectedScopes, setSelectedScopes] = React26.useState(["general"]);
|
|
3380
|
+
const [requiresPasscode, setRequiresPasscode] = React26.useState(false);
|
|
3381
|
+
const [bundleLink, setBundleLink] = React26.useState(null);
|
|
3382
|
+
const [loading, setLoading] = React26.useState(false);
|
|
3383
|
+
const toggleScope = (scope) => {
|
|
3384
|
+
setSelectedScopes(
|
|
3385
|
+
(prev) => prev.includes(scope) ? prev.filter((s) => s !== scope) : [...prev, scope]
|
|
3386
|
+
);
|
|
3387
|
+
};
|
|
3388
|
+
const handleCreate = async (e) => {
|
|
3389
|
+
e.preventDefault();
|
|
3390
|
+
if (!name) return;
|
|
3391
|
+
setLoading(true);
|
|
3392
|
+
try {
|
|
3393
|
+
const link = await onCreateBundle?.({
|
|
3394
|
+
name,
|
|
3395
|
+
scopes: selectedScopes,
|
|
3396
|
+
requiresPasscode
|
|
3397
|
+
});
|
|
3398
|
+
setBundleLink(typeof link === "string" ? link : "https://yarvelle.local/bundles/b7a9c3");
|
|
3399
|
+
} finally {
|
|
3400
|
+
setLoading(false);
|
|
3401
|
+
}
|
|
3402
|
+
};
|
|
3403
|
+
return /* @__PURE__ */ jsxs33(
|
|
3404
|
+
"div",
|
|
3405
|
+
{
|
|
3406
|
+
"data-slot": "share-bundle",
|
|
3407
|
+
className: cn(
|
|
3408
|
+
"yc-ShareBundle-root rounded-xl border border-border bg-surface p-6 shadow-1 text-xs",
|
|
3409
|
+
className
|
|
3410
|
+
),
|
|
3411
|
+
children: [
|
|
3412
|
+
/* @__PURE__ */ jsx41("h3", { className: "text-base font-bold text-fg", children: "Create Share Bundle" }),
|
|
3413
|
+
/* @__PURE__ */ jsx41("p", { className: "mt-1 text-fg-muted leading-relaxed", children: "Package records into an encrypted export bundle." }),
|
|
3414
|
+
bundleLink ? /* @__PURE__ */ jsxs33("div", { className: "mt-4 rounded-lg bg-surface-2 border border-border p-4 text-center", children: [
|
|
3415
|
+
/* @__PURE__ */ jsx41("span", { className: "font-semibold text-fg", children: "Bundle Created!" }),
|
|
3416
|
+
/* @__PURE__ */ jsx41("p", { className: "mt-1 text-fg-muted", children: "Shareable bundle link:" }),
|
|
3417
|
+
/* @__PURE__ */ jsx41("div", { className: "mt-2 rounded bg-surface p-2 font-mono text-xs text-primary border border-border", children: bundleLink }),
|
|
3418
|
+
/* @__PURE__ */ jsx41(
|
|
3419
|
+
Button,
|
|
3420
|
+
{
|
|
3421
|
+
size: "sm",
|
|
3422
|
+
variant: "ghost",
|
|
3423
|
+
className: "mt-3",
|
|
3424
|
+
onClick: () => {
|
|
3425
|
+
setBundleLink(null);
|
|
3426
|
+
setName("");
|
|
3427
|
+
},
|
|
3428
|
+
children: "Create Another"
|
|
3429
|
+
}
|
|
3430
|
+
)
|
|
3431
|
+
] }) : /* @__PURE__ */ jsxs33("form", { onSubmit: handleCreate, className: "mt-4 space-y-4", children: [
|
|
3432
|
+
/* @__PURE__ */ jsxs33("div", { children: [
|
|
3433
|
+
/* @__PURE__ */ jsx41("label", { className: "block font-medium text-fg mb-1", children: "Bundle Name" }),
|
|
3434
|
+
/* @__PURE__ */ jsx41(
|
|
3435
|
+
"input",
|
|
3436
|
+
{
|
|
3437
|
+
type: "text",
|
|
3438
|
+
required: true,
|
|
3439
|
+
value: name,
|
|
3440
|
+
onChange: (e) => setName(e.target.value),
|
|
3441
|
+
placeholder: "e.g. Annual Record Dossier",
|
|
3442
|
+
className: "w-full rounded-md border border-border bg-surface-2 p-2 text-sm text-fg focus:border-primary focus:outline-none"
|
|
3443
|
+
}
|
|
3444
|
+
)
|
|
3445
|
+
] }),
|
|
3446
|
+
/* @__PURE__ */ jsxs33("div", { children: [
|
|
3447
|
+
/* @__PURE__ */ jsx41("label", { className: "block font-medium text-fg mb-1", children: "Included Scopes" }),
|
|
3448
|
+
/* @__PURE__ */ jsx41("div", { className: "flex flex-wrap gap-1.5 mt-1", children: availableScopes.map((scope) => /* @__PURE__ */ jsx41(
|
|
3449
|
+
Chip,
|
|
3450
|
+
{
|
|
3451
|
+
selected: selectedScopes.includes(scope),
|
|
3452
|
+
onClick: () => toggleScope(scope),
|
|
3453
|
+
children: scope
|
|
3454
|
+
},
|
|
3455
|
+
scope
|
|
3456
|
+
)) })
|
|
3457
|
+
] }),
|
|
3458
|
+
/* @__PURE__ */ jsxs33("div", { className: "flex items-center gap-2", children: [
|
|
3459
|
+
/* @__PURE__ */ jsx41(
|
|
3460
|
+
"input",
|
|
3461
|
+
{
|
|
3462
|
+
id: "passcode-opt",
|
|
3463
|
+
type: "checkbox",
|
|
3464
|
+
checked: requiresPasscode,
|
|
3465
|
+
onChange: (e) => setRequiresPasscode(e.target.checked),
|
|
3466
|
+
className: "h-4 w-4 rounded border-border text-primary focus:ring-primary"
|
|
3467
|
+
}
|
|
3468
|
+
),
|
|
3469
|
+
/* @__PURE__ */ jsx41("label", { htmlFor: "passcode-opt", className: "font-medium text-fg cursor-pointer", children: "Protect bundle with passkey / PIN" })
|
|
3470
|
+
] }),
|
|
3471
|
+
/* @__PURE__ */ jsx41(Button, { type: "submit", variant: "primary", size: "sm", loading, className: "w-full", children: "Generate Secure Bundle" })
|
|
3472
|
+
] })
|
|
3473
|
+
]
|
|
3474
|
+
}
|
|
3475
|
+
);
|
|
3476
|
+
}
|
|
3477
|
+
|
|
3478
|
+
// src/profiles/TeenHandoverBanner.tsx
|
|
3479
|
+
import { jsx as jsx42, jsxs as jsxs34 } from "react/jsx-runtime";
|
|
3480
|
+
function TeenHandoverBanner({
|
|
3481
|
+
memberName,
|
|
3482
|
+
handoverDate,
|
|
3483
|
+
onInitiateHandover,
|
|
3484
|
+
onDismiss,
|
|
3485
|
+
className
|
|
3486
|
+
}) {
|
|
3487
|
+
return /* @__PURE__ */ jsxs34(
|
|
3488
|
+
"div",
|
|
3489
|
+
{
|
|
3490
|
+
"data-slot": "teen-handover-banner",
|
|
3491
|
+
className: cn(
|
|
3492
|
+
"yc-TeenHandoverBanner-root flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 rounded-xl border border-warn-border bg-warn-bg/30 p-4 text-xs text-fg shadow-1",
|
|
3493
|
+
className
|
|
3494
|
+
),
|
|
3495
|
+
children: [
|
|
3496
|
+
/* @__PURE__ */ jsxs34("div", { className: "flex items-start gap-3", children: [
|
|
3497
|
+
/* @__PURE__ */ jsx42("span", { className: "text-warn-fg text-lg leading-none", children: "\u26A0\uFE0F" }),
|
|
3498
|
+
/* @__PURE__ */ jsxs34("div", { children: [
|
|
3499
|
+
/* @__PURE__ */ jsx42("h4", { className: "font-semibold text-fg", children: "Privacy & Age-of-Majority Transition" }),
|
|
3500
|
+
/* @__PURE__ */ jsxs34("p", { className: "mt-0.5 text-fg-muted", children: [
|
|
3501
|
+
/* @__PURE__ */ jsx42("span", { className: "font-medium text-fg", children: memberName }),
|
|
3502
|
+
" reaches transition age on",
|
|
3503
|
+
" ",
|
|
3504
|
+
/* @__PURE__ */ jsx42("span", { className: "font-medium text-fg", children: handoverDate }),
|
|
3505
|
+
". Account stewardship and record privacy settings should be reviewed."
|
|
3506
|
+
] })
|
|
3507
|
+
] })
|
|
3508
|
+
] }),
|
|
3509
|
+
/* @__PURE__ */ jsxs34("div", { className: "flex items-center gap-2 shrink-0", children: [
|
|
3510
|
+
onDismiss && /* @__PURE__ */ jsx42(Button, { size: "sm", variant: "ghost", onClick: onDismiss, children: "Dismiss" }),
|
|
3511
|
+
onInitiateHandover && /* @__PURE__ */ jsx42(Button, { size: "sm", variant: "outline", onClick: onInitiateHandover, children: "Review Privacy" })
|
|
3512
|
+
] })
|
|
3513
|
+
]
|
|
3514
|
+
}
|
|
3515
|
+
);
|
|
3516
|
+
}
|
|
3517
|
+
|
|
3518
|
+
// src/tasks/TaskItem.tsx
|
|
3519
|
+
import { jsx as jsx43, jsxs as jsxs35 } from "react/jsx-runtime";
|
|
3520
|
+
var priorityBadgeVariant = {
|
|
3521
|
+
low: "neutral",
|
|
3522
|
+
medium: "warn",
|
|
3523
|
+
high: "danger"
|
|
3524
|
+
};
|
|
3525
|
+
function TaskItem({
|
|
3526
|
+
task,
|
|
3527
|
+
onToggleComplete,
|
|
3528
|
+
onDelete,
|
|
3529
|
+
className
|
|
3530
|
+
}) {
|
|
3531
|
+
const isDone = task.completed;
|
|
3532
|
+
return /* @__PURE__ */ jsxs35(
|
|
3533
|
+
"div",
|
|
3534
|
+
{
|
|
3535
|
+
"data-slot": "task-item",
|
|
3536
|
+
"data-completed": isDone ? "true" : void 0,
|
|
3537
|
+
className: cn(
|
|
3538
|
+
"yc-TaskItem-root flex items-center justify-between rounded-lg border border-border bg-surface p-3 shadow-1 transition-all",
|
|
3539
|
+
isDone && "opacity-60",
|
|
3540
|
+
className
|
|
3541
|
+
),
|
|
3542
|
+
children: [
|
|
3543
|
+
/* @__PURE__ */ jsxs35("div", { className: "flex items-center gap-3", children: [
|
|
3544
|
+
/* @__PURE__ */ jsx43(
|
|
3545
|
+
"input",
|
|
3546
|
+
{
|
|
3547
|
+
type: "checkbox",
|
|
3548
|
+
checked: isDone,
|
|
3549
|
+
onChange: (e) => onToggleComplete?.(task.id, e.target.checked),
|
|
3550
|
+
className: "h-4 w-4 rounded border-border text-primary focus:ring-primary",
|
|
3551
|
+
"aria-label": `Mark task ${task.title} as ${isDone ? "incomplete" : "complete"}`
|
|
3552
|
+
}
|
|
3553
|
+
),
|
|
3554
|
+
/* @__PURE__ */ jsxs35("div", { className: "flex flex-col", children: [
|
|
3555
|
+
/* @__PURE__ */ jsxs35("div", { className: "flex items-center gap-2", children: [
|
|
3556
|
+
/* @__PURE__ */ jsx43(
|
|
3557
|
+
"span",
|
|
3558
|
+
{
|
|
3559
|
+
className: cn(
|
|
3560
|
+
"text-sm font-medium text-fg",
|
|
3561
|
+
isDone && "line-through text-fg-muted"
|
|
3562
|
+
),
|
|
3563
|
+
children: task.title
|
|
3564
|
+
}
|
|
3565
|
+
),
|
|
3566
|
+
task.priority && /* @__PURE__ */ jsx43(Badge, { variant: priorityBadgeVariant[task.priority], className: "text-[10px] px-1 py-0", children: task.priority })
|
|
3567
|
+
] }),
|
|
3568
|
+
(task.description || task.dueAt) && /* @__PURE__ */ jsxs35("div", { className: "mt-0.5 flex items-center gap-2 text-xs text-fg-muted", children: [
|
|
3569
|
+
task.description && /* @__PURE__ */ jsx43("span", { children: task.description }),
|
|
3570
|
+
task.dueAt && /* @__PURE__ */ jsxs35("span", { children: [
|
|
3571
|
+
"Due: ",
|
|
3572
|
+
task.dueAt
|
|
3573
|
+
] })
|
|
3574
|
+
] })
|
|
3575
|
+
] })
|
|
3576
|
+
] }),
|
|
3577
|
+
onDelete && /* @__PURE__ */ jsx43(
|
|
3578
|
+
"button",
|
|
3579
|
+
{
|
|
3580
|
+
type: "button",
|
|
3581
|
+
onClick: () => onDelete(task.id),
|
|
3582
|
+
className: "text-fg-muted hover:text-danger-fg text-xs p-1",
|
|
3583
|
+
"aria-label": `Delete task ${task.title}`,
|
|
3584
|
+
children: "\u2715"
|
|
3585
|
+
}
|
|
3586
|
+
)
|
|
3587
|
+
]
|
|
3588
|
+
}
|
|
3589
|
+
);
|
|
3590
|
+
}
|
|
3591
|
+
|
|
3592
|
+
// src/tasks/TaskList.tsx
|
|
3593
|
+
import * as React27 from "react";
|
|
3594
|
+
import { jsx as jsx44, jsxs as jsxs36 } from "react/jsx-runtime";
|
|
3595
|
+
function TaskList({
|
|
3596
|
+
tasks,
|
|
3597
|
+
onToggleComplete,
|
|
3598
|
+
onAddTask,
|
|
3599
|
+
onDeleteTask,
|
|
3600
|
+
className,
|
|
3601
|
+
emptyTitle = "No tasks",
|
|
3602
|
+
emptyDescription = "You have no tasks in this view."
|
|
3603
|
+
}) {
|
|
3604
|
+
const [filter, setFilter] = React27.useState("all");
|
|
3605
|
+
const [newTitle, setNewTitle] = React27.useState("");
|
|
3606
|
+
const filtered = tasks.filter((t) => {
|
|
3607
|
+
if (filter === "active") return !t.completed;
|
|
3608
|
+
if (filter === "completed") return t.completed;
|
|
3609
|
+
return true;
|
|
3610
|
+
});
|
|
3611
|
+
const handleAdd = (e) => {
|
|
3612
|
+
e.preventDefault();
|
|
3613
|
+
const trimmed = newTitle.trim();
|
|
3614
|
+
if (!trimmed) return;
|
|
3615
|
+
onAddTask?.(trimmed);
|
|
3616
|
+
setNewTitle("");
|
|
3617
|
+
};
|
|
3618
|
+
return /* @__PURE__ */ jsxs36(
|
|
3619
|
+
"div",
|
|
3620
|
+
{
|
|
3621
|
+
"data-slot": "task-list",
|
|
3622
|
+
className: cn("yc-TaskList-root flex flex-col gap-3", className),
|
|
3623
|
+
children: [
|
|
3624
|
+
/* @__PURE__ */ jsxs36("div", { className: "flex items-center justify-between gap-3", children: [
|
|
3625
|
+
/* @__PURE__ */ jsx44(
|
|
3626
|
+
SegmentedControl,
|
|
3627
|
+
{
|
|
3628
|
+
size: "sm",
|
|
3629
|
+
value: filter,
|
|
3630
|
+
onChange: (v) => setFilter(v),
|
|
3631
|
+
options: [
|
|
3632
|
+
{ value: "all", label: "All" },
|
|
3633
|
+
{ value: "active", label: "Active" },
|
|
3634
|
+
{ value: "completed", label: "Completed" }
|
|
3635
|
+
]
|
|
3636
|
+
}
|
|
3637
|
+
),
|
|
3638
|
+
/* @__PURE__ */ jsxs36("span", { className: "text-xs text-fg-muted", children: [
|
|
3639
|
+
tasks.filter((t) => !t.completed).length,
|
|
3640
|
+
" remaining"
|
|
3641
|
+
] })
|
|
3642
|
+
] }),
|
|
3643
|
+
onAddTask && /* @__PURE__ */ jsxs36("form", { onSubmit: handleAdd, className: "flex gap-2", children: [
|
|
3644
|
+
/* @__PURE__ */ jsx44(
|
|
3645
|
+
"input",
|
|
3646
|
+
{
|
|
3647
|
+
type: "text",
|
|
3648
|
+
value: newTitle,
|
|
3649
|
+
onChange: (e) => setNewTitle(e.target.value),
|
|
3650
|
+
placeholder: "Add a new task...",
|
|
3651
|
+
className: "flex-1 rounded-lg border border-border bg-surface-2 p-2 text-sm text-fg placeholder:text-fg-muted focus:border-primary focus:outline-none"
|
|
3652
|
+
}
|
|
3653
|
+
),
|
|
3654
|
+
/* @__PURE__ */ jsx44(Button, { type: "submit", variant: "primary", size: "sm", children: "Add" })
|
|
3655
|
+
] }),
|
|
3656
|
+
filtered.length === 0 ? /* @__PURE__ */ jsx44(EmptyState, { title: emptyTitle, description: emptyDescription }) : /* @__PURE__ */ jsx44("div", { className: "flex flex-col gap-2", children: filtered.map((task) => /* @__PURE__ */ jsx44(
|
|
3657
|
+
TaskItem,
|
|
3658
|
+
{
|
|
3659
|
+
task,
|
|
3660
|
+
onToggleComplete,
|
|
3661
|
+
onDelete: onDeleteTask
|
|
3662
|
+
},
|
|
3663
|
+
task.id
|
|
3664
|
+
)) })
|
|
3665
|
+
]
|
|
3666
|
+
}
|
|
3667
|
+
);
|
|
3668
|
+
}
|
|
3669
|
+
|
|
3670
|
+
// src/notifications/NotificationCenter.tsx
|
|
3671
|
+
import { jsx as jsx45, jsxs as jsxs37 } from "react/jsx-runtime";
|
|
3672
|
+
var levelBadgeVariant2 = {
|
|
3673
|
+
info: "info",
|
|
3674
|
+
warn: "warn",
|
|
3675
|
+
danger: "danger",
|
|
3676
|
+
success: "success"
|
|
3677
|
+
};
|
|
3678
|
+
function NotificationCenter({
|
|
3679
|
+
notifications,
|
|
3680
|
+
onMarkAllRead,
|
|
3681
|
+
onDismiss,
|
|
3682
|
+
onSelectNotification,
|
|
3683
|
+
className
|
|
3684
|
+
}) {
|
|
3685
|
+
const unreadCount = notifications.filter((n) => !n.read).length;
|
|
3686
|
+
return /* @__PURE__ */ jsx45(
|
|
3687
|
+
"div",
|
|
3688
|
+
{
|
|
3689
|
+
"data-slot": "notification-center",
|
|
3690
|
+
className: cn("yc-NotificationCenter-root inline-block", className),
|
|
3691
|
+
children: /* @__PURE__ */ jsxs37(Popover, { children: [
|
|
3692
|
+
/* @__PURE__ */ jsxs37(PopoverTrigger, { className: "relative flex items-center justify-center p-2 rounded-lg border border-border bg-surface hover:bg-surface-2 transition-colors", children: [
|
|
3693
|
+
/* @__PURE__ */ jsx45("span", { className: "text-base", role: "img", "aria-label": "Notifications", children: "\u{1F514}" }),
|
|
3694
|
+
unreadCount > 0 && /* @__PURE__ */ jsx45("span", { className: "absolute -top-1 -end-1 flex h-4 w-4 items-center justify-center rounded-full bg-danger-fg text-[10px] font-bold text-white", children: unreadCount > 9 ? "9+" : unreadCount })
|
|
3695
|
+
] }),
|
|
3696
|
+
/* @__PURE__ */ jsxs37(PopoverContent, { align: "end", className: "w-80 p-0 shadow-3", children: [
|
|
3697
|
+
/* @__PURE__ */ jsxs37("div", { className: "flex items-center justify-between border-b border-border p-3", children: [
|
|
3698
|
+
/* @__PURE__ */ jsxs37("div", { className: "flex items-center gap-2", children: [
|
|
3699
|
+
/* @__PURE__ */ jsx45("span", { className: "font-semibold text-sm text-fg", children: "Notifications" }),
|
|
3700
|
+
unreadCount > 0 && /* @__PURE__ */ jsxs37(Badge, { variant: "primary", children: [
|
|
3701
|
+
unreadCount,
|
|
3702
|
+
" new"
|
|
3703
|
+
] })
|
|
3704
|
+
] }),
|
|
3705
|
+
unreadCount > 0 && onMarkAllRead && /* @__PURE__ */ jsx45(
|
|
3706
|
+
"button",
|
|
3707
|
+
{
|
|
3708
|
+
type: "button",
|
|
3709
|
+
onClick: onMarkAllRead,
|
|
3710
|
+
className: "text-xs text-primary hover:underline font-medium",
|
|
3711
|
+
children: "Mark all read"
|
|
3712
|
+
}
|
|
3713
|
+
)
|
|
3714
|
+
] }),
|
|
3715
|
+
/* @__PURE__ */ jsx45("div", { className: "max-h-80 overflow-y-auto divide-y divide-border", children: notifications.length === 0 ? /* @__PURE__ */ jsx45("div", { className: "p-6 text-center text-xs text-fg-muted", children: "No notifications right now." }) : notifications.map((n) => /* @__PURE__ */ jsxs37(
|
|
3716
|
+
"div",
|
|
3717
|
+
{
|
|
3718
|
+
onClick: () => onSelectNotification?.(n),
|
|
3719
|
+
"data-slot": "notification-item",
|
|
3720
|
+
"data-read": n.read ? "true" : void 0,
|
|
3721
|
+
className: cn(
|
|
3722
|
+
"flex items-start justify-between p-3 transition-colors text-start cursor-pointer",
|
|
3723
|
+
n.read ? "bg-surface opacity-75" : "bg-surface-2/60"
|
|
3724
|
+
),
|
|
3725
|
+
children: [
|
|
3726
|
+
/* @__PURE__ */ jsxs37("div", { className: "flex flex-col gap-0.5 flex-1 pe-2", children: [
|
|
3727
|
+
/* @__PURE__ */ jsxs37("div", { className: "flex items-center gap-1.5", children: [
|
|
3728
|
+
n.level && /* @__PURE__ */ jsx45(Badge, { variant: levelBadgeVariant2[n.level], className: "text-[9px] px-1 py-0", children: n.level }),
|
|
3729
|
+
/* @__PURE__ */ jsx45("span", { className: "font-medium text-xs text-fg", children: n.title })
|
|
3730
|
+
] }),
|
|
3731
|
+
n.description && /* @__PURE__ */ jsx45("p", { className: "text-[11px] text-fg-muted line-clamp-2", children: n.description }),
|
|
3732
|
+
/* @__PURE__ */ jsx45("span", { className: "text-[10px] text-fg-muted mt-1", children: n.timestamp })
|
|
3733
|
+
] }),
|
|
3734
|
+
onDismiss && /* @__PURE__ */ jsx45(
|
|
3735
|
+
"button",
|
|
3736
|
+
{
|
|
3737
|
+
type: "button",
|
|
3738
|
+
onClick: (e) => {
|
|
3739
|
+
e.stopPropagation();
|
|
3740
|
+
onDismiss(n.id);
|
|
3741
|
+
},
|
|
3742
|
+
className: "text-fg-muted hover:text-fg text-xs p-1",
|
|
3743
|
+
"aria-label": "Dismiss notification",
|
|
3744
|
+
children: "\u2715"
|
|
3745
|
+
}
|
|
3746
|
+
)
|
|
3747
|
+
]
|
|
3748
|
+
},
|
|
3749
|
+
n.id
|
|
3750
|
+
)) })
|
|
3751
|
+
] })
|
|
3752
|
+
] })
|
|
3753
|
+
}
|
|
3754
|
+
);
|
|
3755
|
+
}
|
|
3756
|
+
|
|
3757
|
+
// src/settings/SettingsLayout.tsx
|
|
3758
|
+
import * as React29 from "react";
|
|
3759
|
+
|
|
3760
|
+
// src/slots/SlotRenderer.tsx
|
|
3761
|
+
import { useEffect as useEffect7, useState as useState22 } from "react";
|
|
3762
|
+
import { Fragment as Fragment3, jsx as jsx46, jsxs as jsxs38 } from "react/jsx-runtime";
|
|
3763
|
+
var SlotRenderer = ({
|
|
3764
|
+
contribution,
|
|
3765
|
+
slotProps,
|
|
3766
|
+
fallback = null
|
|
3767
|
+
}) => {
|
|
3768
|
+
const [LoadedComp, setLoadedComp] = useState22(() => contribution.component ?? null);
|
|
3769
|
+
const [error, setError] = useState22(null);
|
|
3770
|
+
useEffect7(() => {
|
|
3771
|
+
let cancelled = false;
|
|
3772
|
+
if (contribution.component) {
|
|
3773
|
+
setLoadedComp(() => contribution.component ?? null);
|
|
3774
|
+
return;
|
|
3775
|
+
}
|
|
3776
|
+
if (contribution.load) {
|
|
3777
|
+
contribution.load().then((mod) => {
|
|
3778
|
+
if (!cancelled) {
|
|
3779
|
+
setLoadedComp(() => mod.default);
|
|
3780
|
+
}
|
|
3781
|
+
}).catch((err) => {
|
|
3782
|
+
if (!cancelled) {
|
|
3783
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
3784
|
+
}
|
|
3785
|
+
});
|
|
3786
|
+
}
|
|
3787
|
+
return () => {
|
|
3788
|
+
cancelled = true;
|
|
3789
|
+
};
|
|
3790
|
+
}, [contribution]);
|
|
3791
|
+
if (error) {
|
|
3792
|
+
return /* @__PURE__ */ jsxs38("div", { className: "yc-SlotRenderer-error text-danger text-sm p-4", "data-slot": "error", children: [
|
|
3793
|
+
"Failed to load slot: ",
|
|
3794
|
+
contribution.id
|
|
3795
|
+
] });
|
|
3796
|
+
}
|
|
3797
|
+
if (!LoadedComp) {
|
|
3798
|
+
return /* @__PURE__ */ jsx46(Fragment3, { children: fallback });
|
|
3799
|
+
}
|
|
3800
|
+
return /* @__PURE__ */ jsx46("div", { className: "yc-SlotRenderer-root", "data-slot": "root", "data-pack": contribution.pack, "data-contribution": contribution.id, children: /* @__PURE__ */ jsx46(LoadedComp, { ...slotProps }) });
|
|
3801
|
+
};
|
|
3802
|
+
|
|
3803
|
+
// src/settings/SettingsLayout.tsx
|
|
3804
|
+
import { jsx as jsx47, jsxs as jsxs39 } from "react/jsx-runtime";
|
|
3805
|
+
function SettingsLayout({
|
|
3806
|
+
sections,
|
|
3807
|
+
activeSectionId,
|
|
3808
|
+
onSelectSection,
|
|
3809
|
+
contributions = [],
|
|
3810
|
+
slotProps,
|
|
3811
|
+
children,
|
|
3812
|
+
className
|
|
3813
|
+
}) {
|
|
3814
|
+
const mergedSections = React29.useMemo(() => {
|
|
3815
|
+
const list = [...sections];
|
|
3816
|
+
const slotSections = contributions.filter((c) => c.slot === "settings.section").map((c) => ({
|
|
3817
|
+
id: c.id,
|
|
3818
|
+
title: c.title,
|
|
3819
|
+
order: c.order ?? 50,
|
|
3820
|
+
slotContribution: c
|
|
3821
|
+
}));
|
|
3822
|
+
list.push(...slotSections);
|
|
3823
|
+
return list.sort((a, b) => (a.order ?? 50) - (b.order ?? 50));
|
|
3824
|
+
}, [sections, contributions]);
|
|
3825
|
+
const [selectedId, setSelectedId] = React29.useState(
|
|
3826
|
+
activeSectionId || (mergedSections[0]?.id ?? "")
|
|
3827
|
+
);
|
|
3828
|
+
const currentId = activeSectionId !== void 0 ? activeSectionId : selectedId;
|
|
3829
|
+
const currentSection = mergedSections.find((s) => s.id === currentId) || mergedSections[0];
|
|
3830
|
+
const handleSelect = (id) => {
|
|
3831
|
+
setSelectedId(id);
|
|
3832
|
+
onSelectSection?.(id);
|
|
3833
|
+
};
|
|
3834
|
+
return /* @__PURE__ */ jsxs39(
|
|
3835
|
+
"div",
|
|
3836
|
+
{
|
|
3837
|
+
"data-slot": "settings-layout",
|
|
3838
|
+
className: cn(
|
|
3839
|
+
"yc-SettingsLayout-root flex flex-col md:flex-row gap-6 w-full max-w-6xl mx-auto p-4 md:p-6",
|
|
3840
|
+
className
|
|
3841
|
+
),
|
|
3842
|
+
children: [
|
|
3843
|
+
/* @__PURE__ */ jsxs39(
|
|
3844
|
+
"nav",
|
|
3845
|
+
{
|
|
3846
|
+
"data-slot": "nav",
|
|
3847
|
+
className: "yc-SettingsLayout-nav w-full md:w-64 shrink-0 flex flex-col gap-1",
|
|
3848
|
+
"aria-label": "Settings navigation",
|
|
3849
|
+
children: [
|
|
3850
|
+
/* @__PURE__ */ jsx47("h2", { className: "text-lg font-bold text-fg px-3 pb-3", children: "Settings" }),
|
|
3851
|
+
mergedSections.map((sec) => {
|
|
3852
|
+
const isActive = sec.id === currentId;
|
|
3853
|
+
return /* @__PURE__ */ jsxs39(
|
|
3854
|
+
"button",
|
|
3855
|
+
{
|
|
3856
|
+
type: "button",
|
|
3857
|
+
onClick: () => handleSelect(sec.id),
|
|
3858
|
+
"data-slot": "nav-item",
|
|
3859
|
+
"data-active": isActive ? "true" : void 0,
|
|
3860
|
+
className: cn(
|
|
3861
|
+
"flex items-center gap-2.5 rounded-lg px-3 py-2 text-sm font-medium transition-colors text-start",
|
|
3862
|
+
isActive ? "bg-surface-2 text-fg font-semibold shadow-1" : "text-fg-muted hover:bg-surface-2/60 hover:text-fg"
|
|
3863
|
+
),
|
|
3864
|
+
children: [
|
|
3865
|
+
sec.icon && /* @__PURE__ */ jsx47("span", { className: "inline-flex shrink-0", children: sec.icon }),
|
|
3866
|
+
/* @__PURE__ */ jsx47("span", { className: "truncate", children: sec.title })
|
|
3867
|
+
]
|
|
3868
|
+
},
|
|
3869
|
+
sec.id
|
|
3870
|
+
);
|
|
3871
|
+
})
|
|
3872
|
+
]
|
|
3873
|
+
}
|
|
3874
|
+
),
|
|
3875
|
+
/* @__PURE__ */ jsx47(
|
|
3876
|
+
"main",
|
|
3877
|
+
{
|
|
3878
|
+
"data-slot": "content",
|
|
3879
|
+
className: "yc-SettingsLayout-content flex-1 rounded-xl border border-border bg-surface p-6 shadow-1",
|
|
3880
|
+
children: currentSection && /* @__PURE__ */ jsxs39("div", { children: [
|
|
3881
|
+
/* @__PURE__ */ jsxs39("div", { className: "border-b border-border pb-4 mb-6", children: [
|
|
3882
|
+
/* @__PURE__ */ jsx47("h3", { className: "text-xl font-bold text-fg", children: currentSection.title }),
|
|
3883
|
+
currentSection.description && /* @__PURE__ */ jsx47("p", { className: "mt-1 text-sm text-fg-muted", children: currentSection.description })
|
|
3884
|
+
] }),
|
|
3885
|
+
currentSection.slotContribution && slotProps ? /* @__PURE__ */ jsx47(
|
|
3886
|
+
SlotRenderer,
|
|
3887
|
+
{
|
|
3888
|
+
contribution: currentSection.slotContribution,
|
|
3889
|
+
slotProps
|
|
3890
|
+
}
|
|
3891
|
+
) : currentSection.component ? /* @__PURE__ */ jsx47(currentSection.component, {}) : children
|
|
3892
|
+
] })
|
|
3893
|
+
}
|
|
3894
|
+
)
|
|
3895
|
+
]
|
|
3896
|
+
}
|
|
3897
|
+
);
|
|
3898
|
+
}
|
|
3899
|
+
|
|
3900
|
+
// src/settings/SettingsSectionSlotRenderer.tsx
|
|
3901
|
+
import { jsx as jsx48, jsxs as jsxs40 } from "react/jsx-runtime";
|
|
3902
|
+
function SettingsSectionSlotRenderer({
|
|
3903
|
+
contributions,
|
|
3904
|
+
slotProps,
|
|
3905
|
+
className
|
|
3906
|
+
}) {
|
|
3907
|
+
const sectionSlots = contributions.filter((c) => c.slot === "settings.section");
|
|
3908
|
+
return /* @__PURE__ */ jsx48(
|
|
3909
|
+
"div",
|
|
3910
|
+
{
|
|
3911
|
+
"data-slot": "settings-section-slot-renderer",
|
|
3912
|
+
className: cn("yc-SettingsSectionSlotRenderer-root space-y-6", className),
|
|
3913
|
+
children: sectionSlots.map((c) => /* @__PURE__ */ jsxs40(
|
|
3914
|
+
"div",
|
|
3915
|
+
{
|
|
3916
|
+
className: "rounded-xl border border-border bg-surface p-6 shadow-1",
|
|
3917
|
+
"data-slot": "slot-section",
|
|
3918
|
+
children: [
|
|
3919
|
+
/* @__PURE__ */ jsx48("div", { className: "border-b border-border pb-3 mb-4", children: /* @__PURE__ */ jsx48("h3", { className: "text-base font-semibold text-fg", children: c.title }) }),
|
|
3920
|
+
/* @__PURE__ */ jsx48(SlotRenderer, { contribution: c, slotProps })
|
|
3921
|
+
]
|
|
3922
|
+
},
|
|
3923
|
+
c.id
|
|
3924
|
+
))
|
|
3925
|
+
}
|
|
3926
|
+
);
|
|
3927
|
+
}
|
|
3928
|
+
|
|
3929
|
+
// src/shell/AppRail.tsx
|
|
3930
|
+
import * as React30 from "react";
|
|
3931
|
+
import { jsx as jsx49, jsxs as jsxs41 } from "react/jsx-runtime";
|
|
3932
|
+
function AppRail({
|
|
3933
|
+
items,
|
|
3934
|
+
contributions = [],
|
|
3935
|
+
activeItemId,
|
|
3936
|
+
onSelectItem,
|
|
3937
|
+
brand,
|
|
3938
|
+
footer,
|
|
3939
|
+
collapsed = false,
|
|
3940
|
+
className
|
|
3941
|
+
}) {
|
|
3942
|
+
const allItems = React30.useMemo(() => {
|
|
3943
|
+
const list = [...items];
|
|
3944
|
+
const slotItems = contributions.filter((c) => c.slot === "sidebar.item").map((c) => ({
|
|
3945
|
+
id: c.id,
|
|
3946
|
+
label: c.title,
|
|
3947
|
+
route: c.route,
|
|
3948
|
+
slotContribution: c
|
|
3949
|
+
}));
|
|
3950
|
+
list.push(...slotItems);
|
|
3951
|
+
return list;
|
|
3952
|
+
}, [items, contributions]);
|
|
3953
|
+
return /* @__PURE__ */ jsxs41(
|
|
3954
|
+
"aside",
|
|
3955
|
+
{
|
|
3956
|
+
"data-slot": "app-rail",
|
|
3957
|
+
"data-collapsed": collapsed ? "true" : void 0,
|
|
3958
|
+
className: cn(
|
|
3959
|
+
"yc-AppRail-root flex flex-col justify-between border-e border-border bg-surface text-fg transition-all duration-normal",
|
|
3960
|
+
collapsed ? "w-16" : "w-64",
|
|
3961
|
+
className
|
|
3962
|
+
),
|
|
3963
|
+
children: [
|
|
3964
|
+
/* @__PURE__ */ jsxs41("div", { className: "flex flex-col", children: [
|
|
3965
|
+
brand && /* @__PURE__ */ jsx49("div", { className: "flex items-center gap-3 border-b border-border p-4", "data-slot": "brand", children: brand }),
|
|
3966
|
+
/* @__PURE__ */ jsx49("nav", { className: "flex flex-col gap-1 p-2", "aria-label": "Main Navigation", children: allItems.map((item) => {
|
|
3967
|
+
const isActive = item.active || item.id === activeItemId;
|
|
3968
|
+
return /* @__PURE__ */ jsxs41(
|
|
3969
|
+
"button",
|
|
3970
|
+
{
|
|
3971
|
+
type: "button",
|
|
3972
|
+
onClick: () => {
|
|
3973
|
+
item.onClick?.();
|
|
3974
|
+
onSelectItem?.(item);
|
|
3975
|
+
},
|
|
3976
|
+
"data-slot": "rail-item",
|
|
3977
|
+
"data-active": isActive ? "true" : void 0,
|
|
3978
|
+
className: cn(
|
|
3979
|
+
"flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors text-start",
|
|
3980
|
+
collapsed && "justify-center px-2",
|
|
3981
|
+
isActive ? "bg-surface-2 text-primary font-semibold shadow-1" : "text-fg-muted hover:bg-surface-2/60 hover:text-fg"
|
|
3982
|
+
),
|
|
3983
|
+
title: collapsed ? item.label : void 0,
|
|
3984
|
+
children: [
|
|
3985
|
+
item.icon && /* @__PURE__ */ jsx49("span", { className: "inline-flex shrink-0", children: item.icon }),
|
|
3986
|
+
!collapsed && /* @__PURE__ */ jsx49("span", { className: "truncate flex-1", children: item.label }),
|
|
3987
|
+
!collapsed && item.badge !== void 0 && /* @__PURE__ */ jsx49("span", { className: "rounded-full bg-primary-subtle px-1.5 py-0.5 text-[10px] font-bold text-primary", children: item.badge })
|
|
3988
|
+
]
|
|
3989
|
+
},
|
|
3990
|
+
item.id
|
|
3991
|
+
);
|
|
3992
|
+
}) })
|
|
3993
|
+
] }),
|
|
3994
|
+
footer && /* @__PURE__ */ jsx49("div", { className: "border-t border-border p-3", "data-slot": "footer", children: footer })
|
|
3995
|
+
]
|
|
3996
|
+
}
|
|
3997
|
+
);
|
|
3998
|
+
}
|
|
3999
|
+
|
|
4000
|
+
// src/shell/MobileTopBar.tsx
|
|
4001
|
+
import { jsx as jsx50, jsxs as jsxs42 } from "react/jsx-runtime";
|
|
4002
|
+
function MobileTopBar({
|
|
4003
|
+
title,
|
|
4004
|
+
startSlot,
|
|
4005
|
+
endSlot,
|
|
4006
|
+
leftSlot,
|
|
4007
|
+
rightSlot,
|
|
4008
|
+
className
|
|
4009
|
+
}) {
|
|
4010
|
+
const leading = startSlot || leftSlot;
|
|
4011
|
+
const trailing = endSlot || rightSlot;
|
|
4012
|
+
return /* @__PURE__ */ jsxs42(
|
|
4013
|
+
"header",
|
|
4014
|
+
{
|
|
4015
|
+
"data-slot": "mobile-top-bar",
|
|
4016
|
+
className: cn(
|
|
4017
|
+
"yc-MobileTopBar-root flex items-center justify-between border-b border-border bg-surface px-4 py-2.5 text-fg md:hidden z-30 sticky top-0",
|
|
4018
|
+
className
|
|
4019
|
+
),
|
|
4020
|
+
children: [
|
|
4021
|
+
/* @__PURE__ */ jsx50("div", { className: "flex items-center gap-2", "data-slot": "start-slot", children: leading }),
|
|
4022
|
+
title && /* @__PURE__ */ jsx50("h1", { className: "text-base font-bold text-fg truncate text-center flex-1 px-2", "data-slot": "title", children: title }),
|
|
4023
|
+
/* @__PURE__ */ jsx50("div", { className: "flex items-center gap-2", "data-slot": "end-slot", children: trailing })
|
|
4024
|
+
]
|
|
4025
|
+
}
|
|
4026
|
+
);
|
|
4027
|
+
}
|
|
4028
|
+
|
|
4029
|
+
// src/shell/MobileTabBar.tsx
|
|
4030
|
+
import { jsx as jsx51, jsxs as jsxs43 } from "react/jsx-runtime";
|
|
4031
|
+
function MobileTabBar({
|
|
4032
|
+
items,
|
|
4033
|
+
activeItemId,
|
|
4034
|
+
onSelectItem,
|
|
4035
|
+
className
|
|
4036
|
+
}) {
|
|
4037
|
+
return /* @__PURE__ */ jsx51(
|
|
4038
|
+
"nav",
|
|
4039
|
+
{
|
|
4040
|
+
"data-slot": "mobile-tab-bar",
|
|
4041
|
+
className: cn(
|
|
4042
|
+
"yc-MobileTabBar-root fixed bottom-0 inset-x-0 z-30 flex items-center justify-around border-t border-border bg-surface px-2 py-1 md:hidden",
|
|
4043
|
+
className
|
|
4044
|
+
),
|
|
4045
|
+
"aria-label": "Mobile Navigation",
|
|
4046
|
+
children: items.map((item) => {
|
|
4047
|
+
const isActive = item.active || item.id === activeItemId;
|
|
4048
|
+
return /* @__PURE__ */ jsxs43(
|
|
4049
|
+
"button",
|
|
4050
|
+
{
|
|
4051
|
+
type: "button",
|
|
4052
|
+
onClick: () => {
|
|
4053
|
+
item.onClick?.();
|
|
4054
|
+
onSelectItem?.(item);
|
|
4055
|
+
},
|
|
4056
|
+
"data-slot": "tab-item",
|
|
4057
|
+
"data-active": isActive ? "true" : void 0,
|
|
4058
|
+
className: cn(
|
|
4059
|
+
"flex flex-col items-center justify-center flex-1 py-1.5 px-1 text-[11px] font-medium transition-colors min-h-[44px]",
|
|
4060
|
+
isActive ? "text-primary font-bold" : "text-fg-muted hover:text-fg"
|
|
4061
|
+
),
|
|
4062
|
+
children: [
|
|
4063
|
+
item.icon && /* @__PURE__ */ jsx51("span", { className: "text-lg leading-none mb-0.5", children: item.icon }),
|
|
4064
|
+
/* @__PURE__ */ jsx51("span", { className: "truncate max-w-[64px]", children: item.label })
|
|
4065
|
+
]
|
|
4066
|
+
},
|
|
4067
|
+
item.id
|
|
4068
|
+
);
|
|
4069
|
+
})
|
|
4070
|
+
}
|
|
4071
|
+
);
|
|
4072
|
+
}
|
|
4073
|
+
|
|
4074
|
+
// src/shell/AppLayout.tsx
|
|
4075
|
+
import { jsx as jsx52, jsxs as jsxs44 } from "react/jsx-runtime";
|
|
4076
|
+
function AppLayout({
|
|
4077
|
+
items,
|
|
4078
|
+
contributions = [],
|
|
4079
|
+
activeItemId,
|
|
4080
|
+
onSelectItem,
|
|
4081
|
+
brand,
|
|
4082
|
+
railFooter,
|
|
4083
|
+
mobileTopLeft,
|
|
4084
|
+
mobileTopRight,
|
|
4085
|
+
children,
|
|
4086
|
+
className
|
|
4087
|
+
}) {
|
|
4088
|
+
const activeItem = items.find((i) => i.id === activeItemId) || items[0];
|
|
4089
|
+
return /* @__PURE__ */ jsxs44(
|
|
4090
|
+
"div",
|
|
4091
|
+
{
|
|
4092
|
+
"data-slot": "app-layout",
|
|
4093
|
+
className: cn(
|
|
4094
|
+
"yc-AppLayout-root flex h-screen w-screen overflow-hidden bg-bg text-fg",
|
|
4095
|
+
className
|
|
4096
|
+
),
|
|
4097
|
+
children: [
|
|
4098
|
+
/* @__PURE__ */ jsx52("div", { className: "hidden md:flex h-full shrink-0", children: /* @__PURE__ */ jsx52(
|
|
4099
|
+
AppRail,
|
|
4100
|
+
{
|
|
4101
|
+
items,
|
|
4102
|
+
contributions,
|
|
4103
|
+
activeItemId,
|
|
4104
|
+
onSelectItem,
|
|
4105
|
+
brand,
|
|
4106
|
+
footer: railFooter
|
|
4107
|
+
}
|
|
4108
|
+
) }),
|
|
4109
|
+
/* @__PURE__ */ jsxs44("div", { className: "flex flex-1 flex-col h-full overflow-hidden", children: [
|
|
4110
|
+
/* @__PURE__ */ jsx52(
|
|
4111
|
+
MobileTopBar,
|
|
4112
|
+
{
|
|
4113
|
+
title: activeItem?.label,
|
|
4114
|
+
leftSlot: mobileTopLeft,
|
|
4115
|
+
rightSlot: mobileTopRight
|
|
4116
|
+
}
|
|
4117
|
+
),
|
|
4118
|
+
/* @__PURE__ */ jsx52(
|
|
4119
|
+
"main",
|
|
4120
|
+
{
|
|
4121
|
+
"data-slot": "main-content",
|
|
4122
|
+
className: "yc-AppLayout-content flex-1 overflow-y-auto pb-16 md:pb-0",
|
|
4123
|
+
children
|
|
4124
|
+
}
|
|
4125
|
+
),
|
|
4126
|
+
/* @__PURE__ */ jsx52(
|
|
4127
|
+
MobileTabBar,
|
|
4128
|
+
{
|
|
4129
|
+
items: items.slice(0, 5),
|
|
4130
|
+
activeItemId,
|
|
4131
|
+
onSelectItem
|
|
4132
|
+
}
|
|
4133
|
+
)
|
|
4134
|
+
] })
|
|
4135
|
+
]
|
|
4136
|
+
}
|
|
4137
|
+
);
|
|
4138
|
+
}
|
|
4139
|
+
|
|
4140
|
+
// src/slots/types.ts
|
|
4141
|
+
function definePackUi(def) {
|
|
4142
|
+
return {
|
|
4143
|
+
pack: def.pack,
|
|
4144
|
+
contributions: def.contributions,
|
|
4145
|
+
messages: def.messages
|
|
4146
|
+
};
|
|
4147
|
+
}
|
|
4148
|
+
function matchesScopePattern(pattern, target) {
|
|
4149
|
+
if (pattern === "*" || pattern === target) return true;
|
|
4150
|
+
if (pattern.endsWith(".*")) {
|
|
4151
|
+
const prefix = pattern.slice(0, -2);
|
|
4152
|
+
return target === prefix || target.startsWith(`${prefix}.`);
|
|
4153
|
+
}
|
|
4154
|
+
return false;
|
|
4155
|
+
}
|
|
4156
|
+
function isScopeSatisfied(requiredScope, activeScopes) {
|
|
4157
|
+
return activeScopes.some((active) => matchesScopePattern(active, requiredScope));
|
|
4158
|
+
}
|
|
4159
|
+
function filterSlotContributions(contributions, options = {}) {
|
|
4160
|
+
const { slot, enabledPacks, activeScopes, hiddenPanels = [], audience } = options;
|
|
4161
|
+
return contributions.filter((c) => {
|
|
4162
|
+
if (slot && c.slot !== slot) return false;
|
|
4163
|
+
if (enabledPacks && !enabledPacks.includes(c.pack)) return false;
|
|
4164
|
+
if (hiddenPanels.includes(c.id)) return false;
|
|
4165
|
+
if (audience && c.audiences && c.audiences.length > 0 && !c.audiences.includes(audience)) {
|
|
4166
|
+
return false;
|
|
4167
|
+
}
|
|
4168
|
+
if (activeScopes && c.requiredScopes && c.requiredScopes.length > 0) {
|
|
4169
|
+
const allSatisfied = c.requiredScopes.every((req) => isScopeSatisfied(req, activeScopes));
|
|
4170
|
+
if (!allSatisfied) return false;
|
|
4171
|
+
}
|
|
4172
|
+
return true;
|
|
4173
|
+
}).sort((a, b) => (a.order ?? 100) - (b.order ?? 100));
|
|
4174
|
+
}
|
|
4175
|
+
|
|
4176
|
+
// src/provider/UiProvider.tsx
|
|
4177
|
+
import { createContext as createContext8, useContext as useContext8, useEffect as useEffect8, useState as useState24 } from "react";
|
|
4178
|
+
import { jsx as jsx53 } from "react/jsx-runtime";
|
|
4179
|
+
var defaultI18n = {
|
|
4180
|
+
locale: "en-US",
|
|
4181
|
+
t: (_id, defaultText) => defaultText || _id
|
|
4182
|
+
};
|
|
4183
|
+
var UiContext = createContext8({
|
|
4184
|
+
theme: "system",
|
|
4185
|
+
resolvedTheme: "light",
|
|
4186
|
+
setTheme: () => {
|
|
4187
|
+
},
|
|
4188
|
+
product: { name: "Yarvelle" },
|
|
4189
|
+
storageNamespace: "yarvelle",
|
|
4190
|
+
i18n: defaultI18n
|
|
4191
|
+
});
|
|
4192
|
+
var UiProvider = ({
|
|
4193
|
+
children,
|
|
4194
|
+
defaultTheme = "system",
|
|
4195
|
+
product = { name: "Yarvelle" },
|
|
4196
|
+
storageNamespace = "yarvelle",
|
|
4197
|
+
locale = "en-US",
|
|
4198
|
+
messages = {}
|
|
4199
|
+
}) => {
|
|
4200
|
+
const [theme, setThemeState] = useState24(defaultTheme);
|
|
4201
|
+
const [resolvedTheme, setResolvedTheme] = useState24("light");
|
|
4202
|
+
useEffect8(() => {
|
|
4203
|
+
const root = document.documentElement;
|
|
4204
|
+
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
4205
|
+
const updateTheme = () => {
|
|
4206
|
+
let isDark = false;
|
|
4207
|
+
if (theme === "dark") {
|
|
4208
|
+
isDark = true;
|
|
4209
|
+
} else if (theme === "system") {
|
|
4210
|
+
isDark = mediaQuery.matches;
|
|
4211
|
+
}
|
|
4212
|
+
setResolvedTheme(isDark ? "dark" : "light");
|
|
4213
|
+
root.setAttribute("data-theme", isDark ? "dark" : "light");
|
|
4214
|
+
if (isDark) {
|
|
4215
|
+
root.classList.add("dark");
|
|
4216
|
+
} else {
|
|
4217
|
+
root.classList.remove("dark");
|
|
4218
|
+
}
|
|
4219
|
+
};
|
|
4220
|
+
updateTheme();
|
|
4221
|
+
mediaQuery.addEventListener("change", updateTheme);
|
|
4222
|
+
return () => mediaQuery.removeEventListener("change", updateTheme);
|
|
4223
|
+
}, [theme]);
|
|
4224
|
+
const t = (id, defaultText) => {
|
|
4225
|
+
if (messages[id]) {
|
|
4226
|
+
return messages[id];
|
|
4227
|
+
}
|
|
4228
|
+
return defaultText || id;
|
|
4229
|
+
};
|
|
4230
|
+
const value = {
|
|
4231
|
+
theme,
|
|
4232
|
+
resolvedTheme,
|
|
4233
|
+
setTheme: setThemeState,
|
|
4234
|
+
product,
|
|
4235
|
+
storageNamespace,
|
|
4236
|
+
i18n: { locale, t }
|
|
4237
|
+
};
|
|
4238
|
+
return /* @__PURE__ */ jsx53(UiContext.Provider, { value, children });
|
|
4239
|
+
};
|
|
4240
|
+
var useUi = () => useContext8(UiContext);
|
|
4241
|
+
var useTheme = () => {
|
|
4242
|
+
const { theme, resolvedTheme, setTheme } = useUi();
|
|
4243
|
+
return { theme, resolvedTheme, setTheme };
|
|
4244
|
+
};
|
|
4245
|
+
var useProduct = () => useUi().product;
|
|
4246
|
+
var useStorageNamespace = () => useUi().storageNamespace;
|
|
4247
|
+
var useI18n = () => useUi().i18n;
|
|
4248
|
+
|
|
4249
|
+
// src/testing/index.ts
|
|
4250
|
+
function createMockSlotContribution(overrides) {
|
|
4251
|
+
return {
|
|
4252
|
+
id: "test-slot-1",
|
|
4253
|
+
pack: "test-pack",
|
|
4254
|
+
slot: "dashboard.card",
|
|
4255
|
+
title: "Test Card",
|
|
4256
|
+
requiredScopes: ["general"],
|
|
4257
|
+
order: 10,
|
|
4258
|
+
...overrides
|
|
4259
|
+
};
|
|
4260
|
+
}
|
|
4261
|
+
function createMockProfile(overrides) {
|
|
4262
|
+
return {
|
|
4263
|
+
id: "profile-1",
|
|
4264
|
+
displayName: "Primary User",
|
|
4265
|
+
role: "owner",
|
|
4266
|
+
scopes: ["general", "notes", "calendar"],
|
|
4267
|
+
...overrides
|
|
4268
|
+
};
|
|
4269
|
+
}
|
|
4270
|
+
function createMockChatMessage(overrides) {
|
|
4271
|
+
return {
|
|
4272
|
+
id: "msg-1",
|
|
4273
|
+
role: "user",
|
|
4274
|
+
content: "Hello, world!",
|
|
4275
|
+
createdAt: "12:00 PM",
|
|
4276
|
+
...overrides
|
|
4277
|
+
};
|
|
4278
|
+
}
|
|
4279
|
+
|
|
4280
|
+
// src/lib/storage-namespace.ts
|
|
4281
|
+
function getStorageKey(namespace, key, suffix) {
|
|
4282
|
+
const base = `${namespace}_${key}`;
|
|
4283
|
+
return suffix ? `${base}_${suffix}` : base;
|
|
4284
|
+
}
|
|
4285
|
+
function getScopedItem(namespace, key, suffix) {
|
|
4286
|
+
if (typeof window === "undefined") return null;
|
|
4287
|
+
try {
|
|
4288
|
+
return localStorage.getItem(getStorageKey(namespace, key, suffix));
|
|
4289
|
+
} catch {
|
|
4290
|
+
return null;
|
|
4291
|
+
}
|
|
4292
|
+
}
|
|
4293
|
+
function setScopedItem(namespace, key, value, suffix) {
|
|
4294
|
+
if (typeof window === "undefined") return;
|
|
4295
|
+
try {
|
|
4296
|
+
localStorage.setItem(getStorageKey(namespace, key, suffix), value);
|
|
4297
|
+
} catch {
|
|
4298
|
+
}
|
|
4299
|
+
}
|
|
4300
|
+
function removeScopedItem(namespace, key, suffix) {
|
|
4301
|
+
if (typeof window === "undefined") return;
|
|
4302
|
+
try {
|
|
4303
|
+
localStorage.removeItem(getStorageKey(namespace, key, suffix));
|
|
4304
|
+
} catch {
|
|
4305
|
+
}
|
|
4306
|
+
}
|
|
4307
|
+
export {
|
|
4308
|
+
ActivityLogDrawer,
|
|
4309
|
+
ActivityTimeline,
|
|
4310
|
+
AppLayout,
|
|
4311
|
+
AppRail,
|
|
4312
|
+
ApprovalCard,
|
|
4313
|
+
Avatar,
|
|
4314
|
+
AvatarFallback,
|
|
4315
|
+
AvatarImage,
|
|
4316
|
+
Badge,
|
|
4317
|
+
Button,
|
|
4318
|
+
Calendar,
|
|
4319
|
+
Card,
|
|
4320
|
+
CardContent,
|
|
4321
|
+
CardDescription,
|
|
4322
|
+
CardFooter,
|
|
4323
|
+
CardHeader,
|
|
4324
|
+
CardTitle,
|
|
4325
|
+
Chat,
|
|
4326
|
+
ChatAttachments,
|
|
4327
|
+
ChatComposer,
|
|
4328
|
+
ChatConnected,
|
|
4329
|
+
ChatMarkdown,
|
|
4330
|
+
ChatMessageItem,
|
|
4331
|
+
ChatRoot,
|
|
4332
|
+
ChatThread,
|
|
4333
|
+
Chip,
|
|
4334
|
+
CodeBlock,
|
|
4335
|
+
Dialog,
|
|
4336
|
+
DialogClose,
|
|
4337
|
+
DialogContent,
|
|
4338
|
+
DialogDescription,
|
|
4339
|
+
DialogFooter,
|
|
4340
|
+
DialogHeader,
|
|
4341
|
+
DialogTitle,
|
|
4342
|
+
DialogTrigger,
|
|
4343
|
+
EmptyState,
|
|
4344
|
+
ExecutionPlanCard,
|
|
4345
|
+
InlineError,
|
|
4346
|
+
MemberCard,
|
|
4347
|
+
Menu,
|
|
4348
|
+
MenuContent,
|
|
4349
|
+
MenuItem,
|
|
4350
|
+
MenuSeparator,
|
|
4351
|
+
MenuTrigger,
|
|
4352
|
+
MermaidDiagram,
|
|
4353
|
+
MessageToolbar,
|
|
4354
|
+
MobileTabBar,
|
|
4355
|
+
MobileTopBar,
|
|
4356
|
+
ModelSelector,
|
|
4357
|
+
NoteView,
|
|
4358
|
+
NotesList,
|
|
4359
|
+
NotificationCenter,
|
|
4360
|
+
Popover,
|
|
4361
|
+
PopoverContent,
|
|
4362
|
+
PopoverTrigger,
|
|
4363
|
+
ProfileEditModal,
|
|
4364
|
+
ProfileSwitcher,
|
|
4365
|
+
RecordSearch,
|
|
4366
|
+
Reminders,
|
|
4367
|
+
RoleMatrix,
|
|
4368
|
+
Routines,
|
|
4369
|
+
ScheduleAgenda,
|
|
4370
|
+
SegmentedControl,
|
|
4371
|
+
SettingsLayout,
|
|
4372
|
+
SettingsSectionSlotRenderer,
|
|
4373
|
+
ShareBundle,
|
|
4374
|
+
Sheet,
|
|
4375
|
+
SheetClose,
|
|
4376
|
+
SheetContent,
|
|
4377
|
+
SheetDescription,
|
|
4378
|
+
SheetFooter,
|
|
4379
|
+
SheetHeader,
|
|
4380
|
+
SheetTitle,
|
|
4381
|
+
SheetTrigger,
|
|
4382
|
+
Skeleton,
|
|
4383
|
+
SlotRenderer,
|
|
4384
|
+
Tabs,
|
|
4385
|
+
TabsContent,
|
|
4386
|
+
TabsList,
|
|
4387
|
+
TabsTrigger,
|
|
4388
|
+
TaskItem,
|
|
4389
|
+
TaskList,
|
|
4390
|
+
TeenHandoverBanner,
|
|
4391
|
+
ThinkingIndicator,
|
|
4392
|
+
ToastProvider,
|
|
4393
|
+
Toaster,
|
|
4394
|
+
ToolTraceCard,
|
|
4395
|
+
UiProvider,
|
|
4396
|
+
ViewerInvite,
|
|
4397
|
+
cn,
|
|
4398
|
+
createMockChatMessage,
|
|
4399
|
+
createMockProfile,
|
|
4400
|
+
createMockSlotContribution,
|
|
4401
|
+
definePackUi,
|
|
4402
|
+
filterSlotContributions,
|
|
4403
|
+
getScopedItem,
|
|
4404
|
+
getStorageKey,
|
|
4405
|
+
isScopeSatisfied,
|
|
4406
|
+
matchesScopePattern,
|
|
4407
|
+
removeScopedItem,
|
|
4408
|
+
setScopedItem,
|
|
4409
|
+
useChatContext,
|
|
4410
|
+
useI18n,
|
|
4411
|
+
useProduct,
|
|
4412
|
+
useStorageNamespace,
|
|
4413
|
+
useTheme,
|
|
4414
|
+
useToast,
|
|
4415
|
+
useUi,
|
|
4416
|
+
yarvellePreset
|
|
4417
|
+
};
|
|
4418
|
+
//# sourceMappingURL=index.js.map
|