@ultrst-armada/floks 1.2.4 → 1.2.5
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/README.md +10 -0
- package/package.json +9 -3
- package/packages/canvas-ui/dist/index.js +16 -16
- package/packages/contracts/dist/index.js +2 -2
- package/packages/ui/dist/{chunk-KYFSUQVT.js → chunk-TORWRS3V.js} +124 -110
- package/packages/ui/dist/chunk-X4B3EBMY.js +22 -0
- package/packages/ui/dist/chunk-YDOKOBRJ.js +1217 -0
- package/packages/ui/dist/components/composite/index.js +1 -1
- package/packages/ui/dist/components/layout/index.js +2 -2
- package/packages/ui/dist/components/settings/index.js +2 -2
- package/packages/ui/dist/frnl/index.css +183 -0
- package/packages/ui/dist/frnl/index.d.ts +555 -0
- package/packages/ui/dist/frnl/index.js +46 -0
- package/packages/ui/dist/frnl/store.d.ts +29 -0
- package/packages/ui/dist/frnl/store.js +12 -0
- package/packages/ui/dist/hierarchy/index.d.ts +15 -51
- package/packages/ui/dist/hierarchy/index.js +6 -19
- package/packages/ui/dist/index.d.ts +4 -2
- package/packages/ui/dist/index.js +80 -54
- package/packages/ui/package.json +9 -2
- package/packages/ui/dist/{chunk-GWW3BGOB.js → chunk-MYXPGPI3.js} +3 -3
- package/packages/ui/dist/{chunk-ROILFUZW.js → chunk-NPIMIWVO.js} +5 -5
|
@@ -0,0 +1,1217 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useFloksFrnlStore
|
|
3
|
+
} from "./chunk-X4B3EBMY.js";
|
|
4
|
+
import {
|
|
5
|
+
BottomStripProvider,
|
|
6
|
+
useBottomStrip
|
|
7
|
+
} from "./chunk-62ITRDJX.js";
|
|
8
|
+
|
|
9
|
+
// src/frnl/frnlButton.tsx
|
|
10
|
+
import { useId, useMemo } from "react";
|
|
11
|
+
import { defineFrnlComponent } from "@ultrst-armada/frnlts";
|
|
12
|
+
import { useFrnlComponent } from "@ultrst-armada/frnlts/react";
|
|
13
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
14
|
+
var frnlButtonDefinition = defineFrnlComponent({
|
|
15
|
+
name: "frnlButton",
|
|
16
|
+
states: {
|
|
17
|
+
idle: {
|
|
18
|
+
name: "idle",
|
|
19
|
+
events: { click: { functionId: "frnlButton.idle.click" } },
|
|
20
|
+
visualParameters: { opacity: 1 },
|
|
21
|
+
phases: {
|
|
22
|
+
attackPhase: { visualDefinition: { parameters: {} } },
|
|
23
|
+
sustainPhase: { visualDefinition: { parameters: {} } },
|
|
24
|
+
releasePhase: { visualDefinition: { parameters: {} } }
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
hover: {
|
|
28
|
+
name: "hover",
|
|
29
|
+
events: { click: { functionId: "frnlButton.hover.click" } },
|
|
30
|
+
visualParameters: { opacity: 1 },
|
|
31
|
+
phases: {
|
|
32
|
+
attackPhase: { visualDefinition: { parameters: {} } },
|
|
33
|
+
sustainPhase: { visualDefinition: { parameters: {} } },
|
|
34
|
+
releasePhase: { visualDefinition: { parameters: {} } }
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
active: {
|
|
38
|
+
name: "active",
|
|
39
|
+
events: { click: { functionId: "frnlButton.active.click" } },
|
|
40
|
+
visualParameters: { opacity: 1 },
|
|
41
|
+
phases: {
|
|
42
|
+
attackPhase: { visualDefinition: { parameters: {} } },
|
|
43
|
+
sustainPhase: { visualDefinition: { parameters: {} } },
|
|
44
|
+
releasePhase: { visualDefinition: { parameters: {} } }
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
disabled: {
|
|
48
|
+
name: "disabled",
|
|
49
|
+
events: { click: { functionId: "frnlButton.disabled.click" } },
|
|
50
|
+
visualParameters: { opacity: 0.4 },
|
|
51
|
+
phases: {
|
|
52
|
+
attackPhase: { visualDefinition: { parameters: {} } },
|
|
53
|
+
sustainPhase: { visualDefinition: { parameters: {} } },
|
|
54
|
+
releasePhase: { visualDefinition: { parameters: {} } }
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
loading: {
|
|
58
|
+
name: "loading",
|
|
59
|
+
events: { click: { functionId: "frnlButton.loading.click" } },
|
|
60
|
+
visualParameters: { opacity: 0.6 },
|
|
61
|
+
phases: {
|
|
62
|
+
attackPhase: { visualDefinition: { parameters: {} } },
|
|
63
|
+
sustainPhase: { visualDefinition: { parameters: {} } },
|
|
64
|
+
releasePhase: { visualDefinition: { parameters: {} } }
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
var SIZE_CLASSES = {
|
|
70
|
+
xs: "px-1.5 py-0.5 text-[10px] gap-1 h-6",
|
|
71
|
+
sm: "px-2.5 py-1 text-xs gap-1.5 h-7",
|
|
72
|
+
md: "px-3.5 py-1.5 text-xs gap-2 h-8",
|
|
73
|
+
lg: "px-4 py-2 text-sm gap-2.5 h-10",
|
|
74
|
+
xl: "px-5 py-2.5 text-base gap-3 h-12"
|
|
75
|
+
};
|
|
76
|
+
var VARIANT_STYLES = {
|
|
77
|
+
primary: {
|
|
78
|
+
backgroundColor: "var(--skipo-bg-surface, #1e293b)",
|
|
79
|
+
borderColor: "var(--skipo-border-subtle, #334155)",
|
|
80
|
+
color: "var(--skipo-text-primary, #f8fafc)"
|
|
81
|
+
},
|
|
82
|
+
secondary: {
|
|
83
|
+
backgroundColor: "var(--skipo-bg-panel, #0f172a)",
|
|
84
|
+
borderColor: "var(--skipo-border-subtle, #334155)",
|
|
85
|
+
color: "var(--skipo-text-muted, #94a3b8)"
|
|
86
|
+
},
|
|
87
|
+
accent: {
|
|
88
|
+
backgroundColor: "var(--skipo-accent, #38bdf8)",
|
|
89
|
+
borderColor: "var(--skipo-accent-contrast, #0284c7)",
|
|
90
|
+
color: "#000000",
|
|
91
|
+
fontWeight: 700
|
|
92
|
+
},
|
|
93
|
+
dock: {
|
|
94
|
+
backgroundColor: "var(--skipo-bg-subtle, #1e293b)",
|
|
95
|
+
borderColor: "var(--skipo-border-color, #334155)",
|
|
96
|
+
color: "var(--skipo-text-primary, #f8fafc)"
|
|
97
|
+
},
|
|
98
|
+
danger: {
|
|
99
|
+
backgroundColor: "rgba(239, 68, 68, 0.15)",
|
|
100
|
+
borderColor: "rgba(239, 68, 68, 0.4)",
|
|
101
|
+
color: "#f87171"
|
|
102
|
+
},
|
|
103
|
+
success: {
|
|
104
|
+
backgroundColor: "rgba(16, 185, 129, 0.15)",
|
|
105
|
+
borderColor: "rgba(16, 185, 129, 0.4)",
|
|
106
|
+
color: "#34d399"
|
|
107
|
+
},
|
|
108
|
+
ghost: {
|
|
109
|
+
backgroundColor: "transparent",
|
|
110
|
+
borderColor: "transparent",
|
|
111
|
+
color: "var(--skipo-text-muted, #94a3b8)"
|
|
112
|
+
},
|
|
113
|
+
outline: {
|
|
114
|
+
backgroundColor: "transparent",
|
|
115
|
+
borderColor: "var(--skipo-border-subtle, #334155)",
|
|
116
|
+
color: "var(--skipo-text-primary, #f8fafc)"
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
var frnlButton = ({
|
|
120
|
+
id,
|
|
121
|
+
variant = "secondary",
|
|
122
|
+
size = "md",
|
|
123
|
+
label,
|
|
124
|
+
tooltip,
|
|
125
|
+
description,
|
|
126
|
+
isActive = false,
|
|
127
|
+
loading = false,
|
|
128
|
+
icon,
|
|
129
|
+
disabled = false,
|
|
130
|
+
children,
|
|
131
|
+
onClick,
|
|
132
|
+
onMouseEnter,
|
|
133
|
+
onMouseLeave,
|
|
134
|
+
onFocus,
|
|
135
|
+
onBlur,
|
|
136
|
+
className = "",
|
|
137
|
+
style,
|
|
138
|
+
...rest
|
|
139
|
+
}) => {
|
|
140
|
+
const store = useFloksFrnlStore();
|
|
141
|
+
const { setInspectionText } = useBottomStrip();
|
|
142
|
+
const generatedId = useId().replace(/[:/]/g, "");
|
|
143
|
+
const componentId = id ?? `frnlButton.${label ? label.toLowerCase().replace(/[^a-z0-9]/g, "-") : generatedId}`;
|
|
144
|
+
const resolvedState = disabled ? "disabled" : loading ? "loading" : isActive ? "active" : "idle";
|
|
145
|
+
const handlers = useMemo(
|
|
146
|
+
() => ({
|
|
147
|
+
"frnlButton.idle.click": (payload) => onClick?.(payload),
|
|
148
|
+
"frnlButton.hover.click": (payload) => onClick?.(payload),
|
|
149
|
+
"frnlButton.active.click": (payload) => onClick?.(payload),
|
|
150
|
+
"frnlButton.disabled.click": () => {
|
|
151
|
+
},
|
|
152
|
+
"frnlButton.loading.click": () => {
|
|
153
|
+
}
|
|
154
|
+
}),
|
|
155
|
+
[onClick]
|
|
156
|
+
);
|
|
157
|
+
const frnl = useFrnlComponent({
|
|
158
|
+
id: componentId,
|
|
159
|
+
definition: frnlButtonDefinition,
|
|
160
|
+
store,
|
|
161
|
+
state: resolvedState,
|
|
162
|
+
properties: { variant, size, label, isActive, disabled, loading },
|
|
163
|
+
handlers
|
|
164
|
+
});
|
|
165
|
+
const handleHoverStart = (e) => {
|
|
166
|
+
onMouseEnter?.(e);
|
|
167
|
+
const effLabel = label || tooltip || (typeof children === "string" ? children : void 0);
|
|
168
|
+
if (effLabel || description) {
|
|
169
|
+
setInspectionText(effLabel || null, description || null);
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
const handleHoverEnd = (e) => {
|
|
173
|
+
onMouseLeave?.(e);
|
|
174
|
+
setInspectionText(null);
|
|
175
|
+
};
|
|
176
|
+
return /* @__PURE__ */ jsxs(
|
|
177
|
+
"button",
|
|
178
|
+
{
|
|
179
|
+
...rest,
|
|
180
|
+
id: componentId,
|
|
181
|
+
type: "button",
|
|
182
|
+
disabled: disabled || loading,
|
|
183
|
+
"data-frnl-component": "frnlButton",
|
|
184
|
+
"data-frnl-state": frnl.state,
|
|
185
|
+
onClick: (e) => {
|
|
186
|
+
if (disabled || loading) return;
|
|
187
|
+
frnl.events.click(e);
|
|
188
|
+
},
|
|
189
|
+
onMouseEnter: handleHoverStart,
|
|
190
|
+
onMouseLeave: handleHoverEnd,
|
|
191
|
+
onFocus: (e) => {
|
|
192
|
+
onFocus?.(e);
|
|
193
|
+
const effLabel = label || tooltip || (typeof children === "string" ? children : void 0);
|
|
194
|
+
if (effLabel || description) setInspectionText(effLabel || null, description || null);
|
|
195
|
+
},
|
|
196
|
+
onBlur: (e) => {
|
|
197
|
+
onBlur?.(e);
|
|
198
|
+
setInspectionText(null);
|
|
199
|
+
},
|
|
200
|
+
className: `inline-flex items-center justify-center font-mono font-bold uppercase tracking-wider border rounded transition-all duration-150 select-none cursor-pointer disabled:opacity-40 disabled:cursor-not-allowed active:scale-[0.98] ${SIZE_CLASSES[size]} ${className}`,
|
|
201
|
+
style: {
|
|
202
|
+
...VARIANT_STYLES[variant],
|
|
203
|
+
...isActive ? { borderColor: "var(--skipo-accent, #38bdf8)", boxShadow: "0 0 10px rgba(56, 189, 248, 0.25)" } : {},
|
|
204
|
+
...style
|
|
205
|
+
},
|
|
206
|
+
children: [
|
|
207
|
+
loading ? /* @__PURE__ */ jsx("span", { className: "w-3.5 h-3.5 border-2 border-current border-t-transparent rounded-full animate-spin shrink-0" }) : icon ? /* @__PURE__ */ jsx("span", { className: "shrink-0 flex items-center justify-center", children: icon }) : null,
|
|
208
|
+
children ? /* @__PURE__ */ jsx("span", { children }) : label ? /* @__PURE__ */ jsx("span", { children: label }) : null
|
|
209
|
+
]
|
|
210
|
+
}
|
|
211
|
+
);
|
|
212
|
+
};
|
|
213
|
+
frnlButton.displayName = "frnlButton";
|
|
214
|
+
|
|
215
|
+
// src/frnl/frnlTextInput.tsx
|
|
216
|
+
import { useId as useId2, useMemo as useMemo2 } from "react";
|
|
217
|
+
import { defineFrnlComponent as defineFrnlComponent2 } from "@ultrst-armada/frnlts";
|
|
218
|
+
import { useFrnlComponent as useFrnlComponent2 } from "@ultrst-armada/frnlts/react";
|
|
219
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
220
|
+
var frnlTextInputDefinition = defineFrnlComponent2({
|
|
221
|
+
name: "frnlTextInput",
|
|
222
|
+
states: {
|
|
223
|
+
idle: {
|
|
224
|
+
name: "idle",
|
|
225
|
+
events: {
|
|
226
|
+
change: { functionId: "frnlTextInput.idle.change" },
|
|
227
|
+
focus: { functionId: "frnlTextInput.idle.focus" }
|
|
228
|
+
},
|
|
229
|
+
visualParameters: { borderColor: "var(--skipo-border-subtle, #334155)" },
|
|
230
|
+
phases: {
|
|
231
|
+
attackPhase: { visualDefinition: { parameters: {} } },
|
|
232
|
+
sustainPhase: { visualDefinition: { parameters: {} } },
|
|
233
|
+
releasePhase: { visualDefinition: { parameters: {} } }
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
focused: {
|
|
237
|
+
name: "focused",
|
|
238
|
+
events: {
|
|
239
|
+
change: { functionId: "frnlTextInput.focused.change" },
|
|
240
|
+
blur: { functionId: "frnlTextInput.focused.blur" }
|
|
241
|
+
},
|
|
242
|
+
visualParameters: { borderColor: "var(--skipo-accent, #38bdf8)" },
|
|
243
|
+
phases: {
|
|
244
|
+
attackPhase: { visualDefinition: { parameters: {} } },
|
|
245
|
+
sustainPhase: { visualDefinition: { parameters: {} } },
|
|
246
|
+
releasePhase: { visualDefinition: { parameters: {} } }
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
error: {
|
|
250
|
+
name: "error",
|
|
251
|
+
events: {
|
|
252
|
+
change: { functionId: "frnlTextInput.error.change" }
|
|
253
|
+
},
|
|
254
|
+
visualParameters: { borderColor: "#ef4444" },
|
|
255
|
+
phases: {
|
|
256
|
+
attackPhase: { visualDefinition: { parameters: {} } },
|
|
257
|
+
sustainPhase: { visualDefinition: { parameters: {} } },
|
|
258
|
+
releasePhase: { visualDefinition: { parameters: {} } }
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
disabled: {
|
|
262
|
+
name: "disabled",
|
|
263
|
+
events: {},
|
|
264
|
+
visualParameters: { opacity: 0.5 },
|
|
265
|
+
phases: {
|
|
266
|
+
attackPhase: { visualDefinition: { parameters: {} } },
|
|
267
|
+
sustainPhase: { visualDefinition: { parameters: {} } },
|
|
268
|
+
releasePhase: { visualDefinition: { parameters: {} } }
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
var frnlTextInput = ({
|
|
274
|
+
id,
|
|
275
|
+
label,
|
|
276
|
+
description,
|
|
277
|
+
error,
|
|
278
|
+
size = "md",
|
|
279
|
+
leadingIcon,
|
|
280
|
+
trailingIcon,
|
|
281
|
+
onClear,
|
|
282
|
+
disabled = false,
|
|
283
|
+
value,
|
|
284
|
+
onChange,
|
|
285
|
+
onFocus,
|
|
286
|
+
onBlur,
|
|
287
|
+
onMouseEnter,
|
|
288
|
+
onMouseLeave,
|
|
289
|
+
className = "",
|
|
290
|
+
style,
|
|
291
|
+
...rest
|
|
292
|
+
}) => {
|
|
293
|
+
const store = useFloksFrnlStore();
|
|
294
|
+
const { setInspectionText } = useBottomStrip();
|
|
295
|
+
const generatedId = useId2().replace(/[:/]/g, "");
|
|
296
|
+
const componentId = id ?? `frnlTextInput.${label ? label.toLowerCase().replace(/[^a-z0-9]/g, "-") : generatedId}`;
|
|
297
|
+
const resolvedState = disabled ? "disabled" : error ? "error" : "idle";
|
|
298
|
+
const handlers = useMemo2(
|
|
299
|
+
() => ({
|
|
300
|
+
"frnlTextInput.idle.change": (payload) => onChange?.(payload),
|
|
301
|
+
"frnlTextInput.idle.focus": (payload) => onFocus?.(payload),
|
|
302
|
+
"frnlTextInput.focused.change": (payload) => onChange?.(payload),
|
|
303
|
+
"frnlTextInput.focused.blur": (payload) => onBlur?.(payload),
|
|
304
|
+
"frnlTextInput.error.change": (payload) => onChange?.(payload)
|
|
305
|
+
}),
|
|
306
|
+
[onChange, onFocus, onBlur]
|
|
307
|
+
);
|
|
308
|
+
const frnl = useFrnlComponent2({
|
|
309
|
+
id: componentId,
|
|
310
|
+
definition: frnlTextInputDefinition,
|
|
311
|
+
store,
|
|
312
|
+
state: resolvedState,
|
|
313
|
+
properties: { label, error: Boolean(error), disabled },
|
|
314
|
+
handlers
|
|
315
|
+
});
|
|
316
|
+
const handleHover = (e) => {
|
|
317
|
+
onMouseEnter?.(e);
|
|
318
|
+
if (label || description) {
|
|
319
|
+
setInspectionText(label || null, description || null);
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
const handleLeave = (e) => {
|
|
323
|
+
onMouseLeave?.(e);
|
|
324
|
+
setInspectionText(null);
|
|
325
|
+
};
|
|
326
|
+
const heightClass = size === "sm" ? "h-7 text-xs" : size === "lg" ? "h-10 text-sm" : "h-8 text-xs";
|
|
327
|
+
return /* @__PURE__ */ jsxs2("div", { className: `flex flex-col gap-1 font-mono select-none ${className}`, children: [
|
|
328
|
+
label && /* @__PURE__ */ jsx2(
|
|
329
|
+
"label",
|
|
330
|
+
{
|
|
331
|
+
htmlFor: componentId,
|
|
332
|
+
className: "text-[10px] font-bold uppercase tracking-wider text-[var(--skipo-text-muted,#94a3b8)]",
|
|
333
|
+
children: label
|
|
334
|
+
}
|
|
335
|
+
),
|
|
336
|
+
/* @__PURE__ */ jsxs2(
|
|
337
|
+
"div",
|
|
338
|
+
{
|
|
339
|
+
className: `relative flex items-center border rounded transition-colors bg-[var(--skipo-bg-panel,#0f172a)] ${error ? "border-rose-500" : "border-[var(--skipo-border-subtle,#334155)] focus-within:border-[var(--skipo-accent,#38bdf8)]"}`,
|
|
340
|
+
style,
|
|
341
|
+
children: [
|
|
342
|
+
leadingIcon && /* @__PURE__ */ jsx2("span", { className: "pl-2.5 text-[var(--skipo-text-muted,#94a3b8)] flex items-center justify-center shrink-0", children: leadingIcon }),
|
|
343
|
+
/* @__PURE__ */ jsx2(
|
|
344
|
+
"input",
|
|
345
|
+
{
|
|
346
|
+
...rest,
|
|
347
|
+
id: componentId,
|
|
348
|
+
value,
|
|
349
|
+
disabled,
|
|
350
|
+
"data-frnl-component": "frnlTextInput",
|
|
351
|
+
"data-frnl-state": frnl.state,
|
|
352
|
+
onChange: (e) => {
|
|
353
|
+
onChange?.(e);
|
|
354
|
+
frnl.events.change?.(e);
|
|
355
|
+
},
|
|
356
|
+
onFocus: (e) => {
|
|
357
|
+
onFocus?.(e);
|
|
358
|
+
frnl.events.focus?.(e);
|
|
359
|
+
if (label || description) setInspectionText(label || null, description || null);
|
|
360
|
+
},
|
|
361
|
+
onBlur: (e) => {
|
|
362
|
+
onBlur?.(e);
|
|
363
|
+
frnl.events.blur?.(e);
|
|
364
|
+
setInspectionText(null);
|
|
365
|
+
},
|
|
366
|
+
onMouseEnter: handleHover,
|
|
367
|
+
onMouseLeave: handleLeave,
|
|
368
|
+
className: `w-full bg-transparent px-3 py-1 text-[var(--skipo-text-primary,#f8fafc)] focus:outline-none placeholder-[var(--skipo-text-muted,#64748b)] disabled:opacity-40 disabled:cursor-not-allowed ${heightClass}`
|
|
369
|
+
}
|
|
370
|
+
),
|
|
371
|
+
trailingIcon && /* @__PURE__ */ jsx2("span", { className: "pr-2.5 text-[var(--skipo-text-muted,#94a3b8)] flex items-center justify-center shrink-0", children: trailingIcon }),
|
|
372
|
+
onClear && value && !disabled && /* @__PURE__ */ jsx2(
|
|
373
|
+
"button",
|
|
374
|
+
{
|
|
375
|
+
type: "button",
|
|
376
|
+
onClick: onClear,
|
|
377
|
+
className: "pr-2.5 text-[var(--skipo-text-muted,#94a3b8)] hover:text-white cursor-pointer",
|
|
378
|
+
"aria-label": "Clear Input",
|
|
379
|
+
children: "\xD7"
|
|
380
|
+
}
|
|
381
|
+
)
|
|
382
|
+
]
|
|
383
|
+
}
|
|
384
|
+
),
|
|
385
|
+
typeof error === "string" && /* @__PURE__ */ jsx2("span", { className: "text-[10px] text-rose-400 font-semibold", children: error })
|
|
386
|
+
] });
|
|
387
|
+
};
|
|
388
|
+
frnlTextInput.displayName = "frnlTextInput";
|
|
389
|
+
|
|
390
|
+
// src/frnl/frnlControls.tsx
|
|
391
|
+
import { useId as useId3, useMemo as useMemo3 } from "react";
|
|
392
|
+
import { defineFrnlComponent as defineFrnlComponent3 } from "@ultrst-armada/frnlts";
|
|
393
|
+
import { useFrnlComponent as useFrnlComponent3 } from "@ultrst-armada/frnlts/react";
|
|
394
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
395
|
+
var frnlSelectDefinition = defineFrnlComponent3({
|
|
396
|
+
name: "frnlSelect",
|
|
397
|
+
states: {
|
|
398
|
+
idle: {
|
|
399
|
+
name: "idle",
|
|
400
|
+
events: { change: { functionId: "frnlSelect.idle.change" } },
|
|
401
|
+
visualParameters: {},
|
|
402
|
+
phases: {
|
|
403
|
+
attackPhase: { visualDefinition: { parameters: {} } },
|
|
404
|
+
sustainPhase: { visualDefinition: { parameters: {} } },
|
|
405
|
+
releasePhase: { visualDefinition: { parameters: {} } }
|
|
406
|
+
}
|
|
407
|
+
},
|
|
408
|
+
disabled: {
|
|
409
|
+
name: "disabled",
|
|
410
|
+
events: {},
|
|
411
|
+
visualParameters: { opacity: 0.5 },
|
|
412
|
+
phases: {
|
|
413
|
+
attackPhase: { visualDefinition: { parameters: {} } },
|
|
414
|
+
sustainPhase: { visualDefinition: { parameters: {} } },
|
|
415
|
+
releasePhase: { visualDefinition: { parameters: {} } }
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
var frnlSelect = ({
|
|
421
|
+
id,
|
|
422
|
+
label,
|
|
423
|
+
description,
|
|
424
|
+
options,
|
|
425
|
+
size = "md",
|
|
426
|
+
disabled = false,
|
|
427
|
+
value,
|
|
428
|
+
onChange,
|
|
429
|
+
onMouseEnter,
|
|
430
|
+
onMouseLeave,
|
|
431
|
+
className = "",
|
|
432
|
+
style,
|
|
433
|
+
...rest
|
|
434
|
+
}) => {
|
|
435
|
+
const store = useFloksFrnlStore();
|
|
436
|
+
const { setInspectionText } = useBottomStrip();
|
|
437
|
+
const generatedId = useId3().replace(/[:/]/g, "");
|
|
438
|
+
const componentId = id ?? `frnlSelect.${label ? label.toLowerCase().replace(/[^a-z0-9]/g, "-") : generatedId}`;
|
|
439
|
+
const handlers = useMemo3(
|
|
440
|
+
() => ({
|
|
441
|
+
"frnlSelect.idle.change": (payload) => onChange?.(payload)
|
|
442
|
+
}),
|
|
443
|
+
[onChange]
|
|
444
|
+
);
|
|
445
|
+
const frnl = useFrnlComponent3({
|
|
446
|
+
id: componentId,
|
|
447
|
+
definition: frnlSelectDefinition,
|
|
448
|
+
store,
|
|
449
|
+
state: disabled ? "disabled" : "idle",
|
|
450
|
+
properties: { label, disabled },
|
|
451
|
+
handlers
|
|
452
|
+
});
|
|
453
|
+
const heightClass = size === "sm" ? "h-7 text-xs" : size === "lg" ? "h-10 text-sm" : "h-8 text-xs";
|
|
454
|
+
return /* @__PURE__ */ jsxs3("div", { className: `flex flex-col gap-1 font-mono select-none ${className}`, children: [
|
|
455
|
+
label && /* @__PURE__ */ jsx3(
|
|
456
|
+
"label",
|
|
457
|
+
{
|
|
458
|
+
htmlFor: componentId,
|
|
459
|
+
className: "text-[10px] font-bold uppercase tracking-wider text-[var(--skipo-text-muted,#94a3b8)]",
|
|
460
|
+
children: label
|
|
461
|
+
}
|
|
462
|
+
),
|
|
463
|
+
/* @__PURE__ */ jsx3(
|
|
464
|
+
"select",
|
|
465
|
+
{
|
|
466
|
+
...rest,
|
|
467
|
+
id: componentId,
|
|
468
|
+
value,
|
|
469
|
+
disabled,
|
|
470
|
+
"data-frnl-component": "frnlSelect",
|
|
471
|
+
"data-frnl-state": frnl.state,
|
|
472
|
+
onChange: (e) => {
|
|
473
|
+
onChange?.(e);
|
|
474
|
+
frnl.events.change?.(e);
|
|
475
|
+
},
|
|
476
|
+
onMouseEnter: (e) => {
|
|
477
|
+
onMouseEnter?.(e);
|
|
478
|
+
if (label || description) setInspectionText(label || null, description || null);
|
|
479
|
+
},
|
|
480
|
+
onMouseLeave: (e) => {
|
|
481
|
+
onMouseLeave?.(e);
|
|
482
|
+
setInspectionText(null);
|
|
483
|
+
},
|
|
484
|
+
className: `w-full bg-[var(--skipo-bg-panel,#0f172a)] text-[var(--skipo-text-primary,#f8fafc)] border border-[var(--skipo-border-subtle,#334155)] rounded px-2.5 py-1 focus:outline-none focus:border-[var(--skipo-accent,#38bdf8)] disabled:opacity-40 disabled:cursor-not-allowed ${heightClass}`,
|
|
485
|
+
style,
|
|
486
|
+
children: options.map((opt) => /* @__PURE__ */ jsx3("option", { value: opt.value, disabled: opt.disabled, className: "bg-[#0f172a] text-white", children: opt.label }, opt.value))
|
|
487
|
+
}
|
|
488
|
+
)
|
|
489
|
+
] });
|
|
490
|
+
};
|
|
491
|
+
frnlSelect.displayName = "frnlSelect";
|
|
492
|
+
var frnlToggleDefinition = defineFrnlComponent3({
|
|
493
|
+
name: "frnlToggle",
|
|
494
|
+
states: {
|
|
495
|
+
off: {
|
|
496
|
+
name: "off",
|
|
497
|
+
events: { toggle: { functionId: "frnlToggle.off.toggle" } },
|
|
498
|
+
visualParameters: { checked: false },
|
|
499
|
+
phases: {
|
|
500
|
+
attackPhase: { visualDefinition: { parameters: {} } },
|
|
501
|
+
sustainPhase: { visualDefinition: { parameters: {} } },
|
|
502
|
+
releasePhase: { visualDefinition: { parameters: {} } }
|
|
503
|
+
}
|
|
504
|
+
},
|
|
505
|
+
on: {
|
|
506
|
+
name: "on",
|
|
507
|
+
events: { toggle: { functionId: "frnlToggle.on.toggle" } },
|
|
508
|
+
visualParameters: { checked: true },
|
|
509
|
+
phases: {
|
|
510
|
+
attackPhase: { visualDefinition: { parameters: {} } },
|
|
511
|
+
sustainPhase: { visualDefinition: { parameters: {} } },
|
|
512
|
+
releasePhase: { visualDefinition: { parameters: {} } }
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
});
|
|
517
|
+
var frnlToggle = ({
|
|
518
|
+
id,
|
|
519
|
+
checked,
|
|
520
|
+
onChange,
|
|
521
|
+
label,
|
|
522
|
+
description,
|
|
523
|
+
disabled = false,
|
|
524
|
+
className = ""
|
|
525
|
+
}) => {
|
|
526
|
+
const store = useFloksFrnlStore();
|
|
527
|
+
const { setInspectionText } = useBottomStrip();
|
|
528
|
+
const generatedId = useId3().replace(/[:/]/g, "");
|
|
529
|
+
const componentId = id ?? `frnlToggle.${label ? label.toLowerCase().replace(/[^a-z0-9]/g, "-") : generatedId}`;
|
|
530
|
+
const handlers = useMemo3(
|
|
531
|
+
() => ({
|
|
532
|
+
"frnlToggle.off.toggle": () => onChange(true),
|
|
533
|
+
"frnlToggle.on.toggle": () => onChange(false)
|
|
534
|
+
}),
|
|
535
|
+
[onChange]
|
|
536
|
+
);
|
|
537
|
+
const frnl = useFrnlComponent3({
|
|
538
|
+
id: componentId,
|
|
539
|
+
definition: frnlToggleDefinition,
|
|
540
|
+
store,
|
|
541
|
+
state: checked ? "on" : "off",
|
|
542
|
+
properties: { label, checked, disabled },
|
|
543
|
+
handlers
|
|
544
|
+
});
|
|
545
|
+
return /* @__PURE__ */ jsxs3(
|
|
546
|
+
"button",
|
|
547
|
+
{
|
|
548
|
+
type: "button",
|
|
549
|
+
role: "switch",
|
|
550
|
+
id: componentId,
|
|
551
|
+
"aria-checked": checked,
|
|
552
|
+
disabled,
|
|
553
|
+
"data-frnl-component": "frnlToggle",
|
|
554
|
+
"data-frnl-state": frnl.state,
|
|
555
|
+
onClick: () => {
|
|
556
|
+
if (disabled) return;
|
|
557
|
+
frnl.events.toggle?.(void 0);
|
|
558
|
+
},
|
|
559
|
+
onMouseEnter: () => {
|
|
560
|
+
if (label || description) setInspectionText(label || null, description || null);
|
|
561
|
+
},
|
|
562
|
+
onMouseLeave: () => setInspectionText(null),
|
|
563
|
+
className: `inline-flex items-center gap-2 select-none font-mono text-xs cursor-pointer disabled:opacity-40 disabled:cursor-not-allowed ${className}`,
|
|
564
|
+
children: [
|
|
565
|
+
/* @__PURE__ */ jsx3(
|
|
566
|
+
"div",
|
|
567
|
+
{
|
|
568
|
+
className: `w-8 h-4 rounded-full transition-colors relative flex items-center p-0.5 ${checked ? "bg-[var(--skipo-accent,#38bdf8)]" : "bg-[var(--skipo-border-subtle,#334155)]"}`,
|
|
569
|
+
children: /* @__PURE__ */ jsx3(
|
|
570
|
+
"div",
|
|
571
|
+
{
|
|
572
|
+
className: `w-3 h-3 rounded-full bg-black transition-transform ${checked ? "translate-x-4" : "translate-x-0"}`
|
|
573
|
+
}
|
|
574
|
+
)
|
|
575
|
+
}
|
|
576
|
+
),
|
|
577
|
+
label && /* @__PURE__ */ jsx3("span", { className: "text-[var(--skipo-text-primary,#f8fafc)] uppercase text-[11px] font-semibold", children: label })
|
|
578
|
+
]
|
|
579
|
+
}
|
|
580
|
+
);
|
|
581
|
+
};
|
|
582
|
+
frnlToggle.displayName = "frnlToggle";
|
|
583
|
+
var frnlSlider = ({
|
|
584
|
+
id,
|
|
585
|
+
value,
|
|
586
|
+
onChange,
|
|
587
|
+
min = 0,
|
|
588
|
+
max = 100,
|
|
589
|
+
step = 1,
|
|
590
|
+
label,
|
|
591
|
+
unit = "",
|
|
592
|
+
description,
|
|
593
|
+
disabled = false,
|
|
594
|
+
className = ""
|
|
595
|
+
}) => {
|
|
596
|
+
const { setInspectionText } = useBottomStrip();
|
|
597
|
+
const generatedId = useId3().replace(/[:/]/g, "");
|
|
598
|
+
const componentId = id ?? `frnlSlider.${label ? label.toLowerCase().replace(/[^a-z0-9]/g, "-") : generatedId}`;
|
|
599
|
+
return /* @__PURE__ */ jsxs3(
|
|
600
|
+
"div",
|
|
601
|
+
{
|
|
602
|
+
id: componentId,
|
|
603
|
+
"data-frnl-component": "frnlSlider",
|
|
604
|
+
onMouseEnter: () => {
|
|
605
|
+
if (label || description) setInspectionText(label || null, description || null);
|
|
606
|
+
},
|
|
607
|
+
onMouseLeave: () => setInspectionText(null),
|
|
608
|
+
className: `flex flex-col gap-1 font-mono select-none ${className}`,
|
|
609
|
+
children: [
|
|
610
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex justify-between items-center text-[10px] uppercase font-bold tracking-wider", children: [
|
|
611
|
+
label && /* @__PURE__ */ jsx3("span", { className: "text-[var(--skipo-text-muted,#94a3b8)]", children: label }),
|
|
612
|
+
/* @__PURE__ */ jsxs3("span", { className: "text-[var(--skipo-accent,#38bdf8)]", children: [
|
|
613
|
+
value,
|
|
614
|
+
unit
|
|
615
|
+
] })
|
|
616
|
+
] }),
|
|
617
|
+
/* @__PURE__ */ jsx3(
|
|
618
|
+
"input",
|
|
619
|
+
{
|
|
620
|
+
type: "range",
|
|
621
|
+
min,
|
|
622
|
+
max,
|
|
623
|
+
step,
|
|
624
|
+
value,
|
|
625
|
+
disabled,
|
|
626
|
+
onChange: (e) => onChange(parseFloat(e.target.value)),
|
|
627
|
+
className: "w-full h-1.5 bg-[var(--skipo-border-subtle,#334155)] accent-[var(--skipo-accent,#38bdf8)] cursor-pointer disabled:opacity-40"
|
|
628
|
+
}
|
|
629
|
+
)
|
|
630
|
+
]
|
|
631
|
+
}
|
|
632
|
+
);
|
|
633
|
+
};
|
|
634
|
+
frnlSlider.displayName = "frnlSlider";
|
|
635
|
+
var BADGE_STYLES = {
|
|
636
|
+
neutral: "bg-[var(--skipo-bg-subtle,#1e293b)] border-[var(--skipo-border-subtle,#334155)] text-[var(--skipo-text-muted,#94a3b8)]",
|
|
637
|
+
accent: "bg-cyan-500/10 border-cyan-500/30 text-cyan-400",
|
|
638
|
+
success: "bg-emerald-500/10 border-emerald-500/30 text-emerald-400",
|
|
639
|
+
danger: "bg-rose-500/10 border-rose-500/30 text-rose-400",
|
|
640
|
+
warning: "bg-amber-500/10 border-amber-500/30 text-amber-400",
|
|
641
|
+
info: "bg-blue-500/10 border-blue-500/30 text-blue-400"
|
|
642
|
+
};
|
|
643
|
+
var frnlBadge = ({
|
|
644
|
+
variant = "neutral",
|
|
645
|
+
size = "md",
|
|
646
|
+
dot = false,
|
|
647
|
+
children,
|
|
648
|
+
className = "",
|
|
649
|
+
...rest
|
|
650
|
+
}) => {
|
|
651
|
+
const sizeClasses = size === "sm" ? "px-1.5 py-0.2 text-[9px]" : "px-2 py-0.5 text-[10px]";
|
|
652
|
+
return /* @__PURE__ */ jsxs3(
|
|
653
|
+
"span",
|
|
654
|
+
{
|
|
655
|
+
...rest,
|
|
656
|
+
"data-frnl-component": "frnlBadge",
|
|
657
|
+
className: `inline-flex items-center gap-1.5 font-mono font-bold uppercase tracking-wider border rounded select-none ${BADGE_STYLES[variant]} ${sizeClasses} ${className}`,
|
|
658
|
+
children: [
|
|
659
|
+
dot && /* @__PURE__ */ jsx3("span", { className: "w-1.5 h-1.5 rounded-full bg-current animate-pulse" }),
|
|
660
|
+
children
|
|
661
|
+
]
|
|
662
|
+
}
|
|
663
|
+
);
|
|
664
|
+
};
|
|
665
|
+
frnlBadge.displayName = "frnlBadge";
|
|
666
|
+
|
|
667
|
+
// src/frnl/frnlTabWindow.tsx
|
|
668
|
+
import { useState } from "react";
|
|
669
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
670
|
+
var frnlTabWindow = ({
|
|
671
|
+
id = "frnl-tab-window",
|
|
672
|
+
tabs,
|
|
673
|
+
activeTabId,
|
|
674
|
+
onTabChange,
|
|
675
|
+
className = "",
|
|
676
|
+
children
|
|
677
|
+
}) => {
|
|
678
|
+
const [internalActiveTab, setInternalActiveTab] = useState(tabs[0]?.id);
|
|
679
|
+
const activeId = activeTabId ?? internalActiveTab;
|
|
680
|
+
const { setInspectionText } = useBottomStrip();
|
|
681
|
+
const handleSelect = (tab) => {
|
|
682
|
+
if (tab.disabled) return;
|
|
683
|
+
if (onTabChange) onTabChange(tab.id);
|
|
684
|
+
else setInternalActiveTab(tab.id);
|
|
685
|
+
};
|
|
686
|
+
const currentTab = tabs.find((t) => t.id === activeId);
|
|
687
|
+
return /* @__PURE__ */ jsxs4(
|
|
688
|
+
"div",
|
|
689
|
+
{
|
|
690
|
+
id,
|
|
691
|
+
"data-frnl-component": "frnlTabWindow",
|
|
692
|
+
className: `flex flex-col w-full h-full min-h-0 bg-[var(--skipo-bg,#0a0c10)] text-[var(--skipo-text-primary,#f8fafc)] font-mono ${className}`,
|
|
693
|
+
children: [
|
|
694
|
+
/* @__PURE__ */ jsx4("div", { className: "h-10 border-b border-[var(--skipo-border-color,#334155)] bg-[var(--skipo-surface,#14171f)] px-3 flex items-center gap-1 shrink-0 select-none overflow-x-auto", children: tabs.map((tab) => {
|
|
695
|
+
const isActive = tab.id === activeId;
|
|
696
|
+
return /* @__PURE__ */ jsxs4(
|
|
697
|
+
"button",
|
|
698
|
+
{
|
|
699
|
+
type: "button",
|
|
700
|
+
role: "tab",
|
|
701
|
+
"aria-selected": isActive,
|
|
702
|
+
disabled: tab.disabled,
|
|
703
|
+
onClick: () => handleSelect(tab),
|
|
704
|
+
onMouseEnter: () => {
|
|
705
|
+
if (tab.label || tab.description) {
|
|
706
|
+
setInspectionText(
|
|
707
|
+
`TAB: ${tab.label.toUpperCase()}`,
|
|
708
|
+
tab.description || `Switch to ${tab.label} tab view`
|
|
709
|
+
);
|
|
710
|
+
}
|
|
711
|
+
},
|
|
712
|
+
onMouseLeave: () => setInspectionText(null),
|
|
713
|
+
className: `flex items-center gap-2 px-3 py-1.5 text-xs font-bold uppercase tracking-wider rounded transition-all cursor-pointer disabled:opacity-40 disabled:cursor-not-allowed ${isActive ? "bg-[var(--skipo-bg-panel,#0f172a)] text-[var(--skipo-accent,#38bdf8)] border border-[var(--skipo-border-subtle,#334155)]" : "text-[var(--skipo-text-muted,#94a3b8)] hover:text-white hover:bg-[var(--skipo-bg-subtle,#1e293b)] border border-transparent"}`,
|
|
714
|
+
children: [
|
|
715
|
+
tab.icon && /* @__PURE__ */ jsx4("span", { className: "w-3.5 h-3.5 shrink-0 flex items-center", children: tab.icon }),
|
|
716
|
+
/* @__PURE__ */ jsx4("span", { children: tab.label })
|
|
717
|
+
]
|
|
718
|
+
},
|
|
719
|
+
tab.id
|
|
720
|
+
);
|
|
721
|
+
}) }),
|
|
722
|
+
/* @__PURE__ */ jsx4("div", { className: "flex-1 overflow-auto p-4 min-h-0 relative", children: currentTab?.content ?? children })
|
|
723
|
+
]
|
|
724
|
+
}
|
|
725
|
+
);
|
|
726
|
+
};
|
|
727
|
+
frnlTabWindow.displayName = "frnlTabWindow";
|
|
728
|
+
|
|
729
|
+
// src/frnl/frnlTopBar.tsx
|
|
730
|
+
import React5, { useState as useState2, useRef, useEffect } from "react";
|
|
731
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
732
|
+
var frnlTopBar = ({
|
|
733
|
+
id = "frnl-top-bar",
|
|
734
|
+
breadcrumbs = [],
|
|
735
|
+
appLogo,
|
|
736
|
+
appName,
|
|
737
|
+
tabs = [],
|
|
738
|
+
activeTabId,
|
|
739
|
+
onTabChange,
|
|
740
|
+
user,
|
|
741
|
+
onLogout,
|
|
742
|
+
onOpenSettings,
|
|
743
|
+
actions,
|
|
744
|
+
className = ""
|
|
745
|
+
}) => {
|
|
746
|
+
const { setInspectionText } = useBottomStrip();
|
|
747
|
+
const [profileOpen, setProfileOpen] = useState2(false);
|
|
748
|
+
const profileMenuRef = useRef(null);
|
|
749
|
+
useEffect(() => {
|
|
750
|
+
const handleClickOutside = (e) => {
|
|
751
|
+
if (profileMenuRef.current && !profileMenuRef.current.contains(e.target)) {
|
|
752
|
+
setProfileOpen(false);
|
|
753
|
+
}
|
|
754
|
+
};
|
|
755
|
+
if (profileOpen) {
|
|
756
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
757
|
+
}
|
|
758
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
759
|
+
}, [profileOpen]);
|
|
760
|
+
return /* @__PURE__ */ jsxs5(
|
|
761
|
+
"header",
|
|
762
|
+
{
|
|
763
|
+
id,
|
|
764
|
+
"data-frnl-component": "frnlTopBar",
|
|
765
|
+
className: `h-11 border-b border-[var(--skipo-border-color,#334155)] bg-[var(--skipo-surface,#14171f)] px-3 flex items-center justify-between select-none shrink-0 font-mono text-xs z-40 relative ${className}`,
|
|
766
|
+
children: [
|
|
767
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2 min-w-0", children: [
|
|
768
|
+
appLogo ? /* @__PURE__ */ jsx5("div", { className: "shrink-0 flex items-center justify-center", children: appLogo }) : appName ? /* @__PURE__ */ jsx5(
|
|
769
|
+
"div",
|
|
770
|
+
{
|
|
771
|
+
className: "w-6 h-6 rounded bg-[var(--skipo-accent,#38bdf8)] text-black font-black flex items-center justify-center text-xs shrink-0 cursor-default",
|
|
772
|
+
onMouseEnter: () => setInspectionText(`APPLICATION: ${appName.toUpperCase()}`),
|
|
773
|
+
onMouseLeave: () => setInspectionText(null),
|
|
774
|
+
children: appName.slice(0, 2).toUpperCase()
|
|
775
|
+
}
|
|
776
|
+
) : null,
|
|
777
|
+
breadcrumbs.length > 0 && /* @__PURE__ */ jsx5("nav", { "aria-label": "Breadcrumb", className: "flex items-center gap-1.5 text-xs text-[var(--skipo-text-muted,#94a3b8)]", children: breadcrumbs.map((crumb, idx) => /* @__PURE__ */ jsxs5(React5.Fragment, { children: [
|
|
778
|
+
crumb.onClick ? /* @__PURE__ */ jsx5(
|
|
779
|
+
"button",
|
|
780
|
+
{
|
|
781
|
+
type: "button",
|
|
782
|
+
onClick: crumb.onClick,
|
|
783
|
+
onMouseEnter: () => setInspectionText(`NAVIGATE: ${crumb.label.toUpperCase()}`),
|
|
784
|
+
onMouseLeave: () => setInspectionText(null),
|
|
785
|
+
className: "hover:text-white transition-colors cursor-pointer truncate max-w-[140px]",
|
|
786
|
+
children: crumb.label
|
|
787
|
+
}
|
|
788
|
+
) : crumb.href ? /* @__PURE__ */ jsx5(
|
|
789
|
+
"a",
|
|
790
|
+
{
|
|
791
|
+
href: crumb.href,
|
|
792
|
+
onMouseEnter: () => setInspectionText(`NAVIGATE: ${crumb.label.toUpperCase()}`),
|
|
793
|
+
onMouseLeave: () => setInspectionText(null),
|
|
794
|
+
className: "hover:text-white transition-colors truncate max-w-[140px]",
|
|
795
|
+
children: crumb.label
|
|
796
|
+
}
|
|
797
|
+
) : /* @__PURE__ */ jsx5("span", { className: "text-[var(--skipo-text-primary,#f8fafc)] font-semibold truncate max-w-[140px]", children: crumb.label }),
|
|
798
|
+
idx < breadcrumbs.length - 1 && /* @__PURE__ */ jsx5("span", { className: "opacity-40", children: "/" })
|
|
799
|
+
] }, idx)) })
|
|
800
|
+
] }),
|
|
801
|
+
tabs.length > 0 && /* @__PURE__ */ jsx5(
|
|
802
|
+
"nav",
|
|
803
|
+
{
|
|
804
|
+
role: "tablist",
|
|
805
|
+
className: "flex items-center gap-1 bg-[var(--skipo-bg-panel,#0f172a)] p-0.5 rounded border border-[var(--skipo-border-subtle,#334155)]",
|
|
806
|
+
children: tabs.map((tab) => {
|
|
807
|
+
const isActive = tab.id === activeTabId;
|
|
808
|
+
return /* @__PURE__ */ jsx5(
|
|
809
|
+
"button",
|
|
810
|
+
{
|
|
811
|
+
type: "button",
|
|
812
|
+
role: "tab",
|
|
813
|
+
"aria-selected": isActive,
|
|
814
|
+
disabled: tab.disabled,
|
|
815
|
+
onClick: () => onTabChange?.(tab.id),
|
|
816
|
+
onMouseEnter: () => setInspectionText(
|
|
817
|
+
`TAB: ${tab.label.toUpperCase()}`,
|
|
818
|
+
tab.description || `Switch view to ${tab.label}`
|
|
819
|
+
),
|
|
820
|
+
onMouseLeave: () => setInspectionText(null),
|
|
821
|
+
className: `p-1.5 rounded transition-all flex items-center justify-center cursor-pointer disabled:opacity-30 disabled:cursor-not-allowed ${isActive ? "bg-[var(--skipo-accent,#38bdf8)] text-black shadow-xs" : "text-[var(--skipo-text-muted,#94a3b8)] hover:text-white hover:bg-[var(--skipo-surface,#14171f)]"}`,
|
|
822
|
+
"aria-label": tab.label,
|
|
823
|
+
children: /* @__PURE__ */ jsx5("span", { className: "w-4 h-4 flex items-center justify-center", children: tab.icon })
|
|
824
|
+
},
|
|
825
|
+
tab.id
|
|
826
|
+
);
|
|
827
|
+
})
|
|
828
|
+
}
|
|
829
|
+
),
|
|
830
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-1.5 shrink-0", children: [
|
|
831
|
+
actions,
|
|
832
|
+
onOpenSettings && /* @__PURE__ */ jsx5(
|
|
833
|
+
"button",
|
|
834
|
+
{
|
|
835
|
+
type: "button",
|
|
836
|
+
onClick: onOpenSettings,
|
|
837
|
+
onMouseEnter: () => setInspectionText("SETTINGS", "Open system preferences, theme and configurations"),
|
|
838
|
+
onMouseLeave: () => setInspectionText(null),
|
|
839
|
+
className: "p-1.5 rounded border border-[var(--skipo-border-subtle,#334155)] bg-[var(--skipo-bg-panel,#0f172a)] text-[var(--skipo-text-muted,#94a3b8)] hover:text-white hover:border-[var(--skipo-accent,#38bdf8)] cursor-pointer transition-colors",
|
|
840
|
+
"aria-label": "Settings",
|
|
841
|
+
children: /* @__PURE__ */ jsxs5("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
842
|
+
/* @__PURE__ */ jsx5("circle", { cx: "12", cy: "12", r: "3" }),
|
|
843
|
+
/* @__PURE__ */ jsx5("path", { d: "M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z" })
|
|
844
|
+
] })
|
|
845
|
+
}
|
|
846
|
+
),
|
|
847
|
+
user && /* @__PURE__ */ jsxs5("div", { className: "relative", ref: profileMenuRef, children: [
|
|
848
|
+
/* @__PURE__ */ jsx5(
|
|
849
|
+
"button",
|
|
850
|
+
{
|
|
851
|
+
type: "button",
|
|
852
|
+
onClick: () => setProfileOpen((prev) => !prev),
|
|
853
|
+
onMouseEnter: () => setInspectionText(
|
|
854
|
+
`USER: ${user.name || user.username || "PROFILE"}`,
|
|
855
|
+
`Active session as ${user.email || user.username || "user"}`
|
|
856
|
+
),
|
|
857
|
+
onMouseLeave: () => setInspectionText(null),
|
|
858
|
+
className: "w-7 h-7 rounded border border-[var(--skipo-border-subtle,#334155)] bg-[var(--skipo-bg-panel,#0f172a)] text-[var(--skipo-text-primary,#f8fafc)] hover:border-[var(--skipo-accent,#38bdf8)] flex items-center justify-center cursor-pointer transition-colors",
|
|
859
|
+
"aria-label": "User Profile",
|
|
860
|
+
"aria-expanded": profileOpen,
|
|
861
|
+
children: /* @__PURE__ */ jsx5("span", { children: user.avatarGlyph || "\u{1F464}" })
|
|
862
|
+
}
|
|
863
|
+
),
|
|
864
|
+
profileOpen && /* @__PURE__ */ jsxs5("div", { className: "absolute right-0 top-full mt-1.5 w-56 rounded border border-[var(--skipo-border-color,#334155)] bg-[var(--skipo-surface,#14171f)] p-3 shadow-xl z-50 flex flex-col gap-2.5 font-mono animate-fade-in", children: [
|
|
865
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2 pb-2 border-b border-[var(--skipo-border-subtle,#334155)]", children: [
|
|
866
|
+
/* @__PURE__ */ jsx5("div", { className: "w-8 h-8 rounded bg-[var(--skipo-bg-panel,#0f172a)] border border-[var(--skipo-border-subtle,#334155)] flex items-center justify-center text-sm", children: user.avatarGlyph || "\u{1F464}" }),
|
|
867
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex flex-col min-w-0", children: [
|
|
868
|
+
/* @__PURE__ */ jsx5("span", { className: "font-bold text-[var(--skipo-text-primary,#f8fafc)] truncate text-xs", children: user.name || user.username || "User" }),
|
|
869
|
+
/* @__PURE__ */ jsx5("span", { className: "text-[10px] text-[var(--skipo-text-muted,#94a3b8)] truncate", children: user.email || `@${user.username || "active"}` })
|
|
870
|
+
] })
|
|
871
|
+
] }),
|
|
872
|
+
user.role && /* @__PURE__ */ jsxs5("div", { className: "text-[10px] px-1.5 py-0.5 rounded bg-[var(--skipo-bg-panel,#0f172a)] border border-[var(--skipo-border-subtle,#334155)] text-[var(--skipo-accent,#38bdf8)] uppercase font-bold self-start", children: [
|
|
873
|
+
"ROLE: ",
|
|
874
|
+
user.role
|
|
875
|
+
] }),
|
|
876
|
+
onLogout && /* @__PURE__ */ jsxs5(
|
|
877
|
+
"button",
|
|
878
|
+
{
|
|
879
|
+
type: "button",
|
|
880
|
+
onClick: () => {
|
|
881
|
+
setProfileOpen(false);
|
|
882
|
+
onLogout();
|
|
883
|
+
},
|
|
884
|
+
className: "w-full flex items-center gap-2 px-2.5 py-1.5 rounded text-rose-400 hover:bg-rose-500/10 border border-transparent hover:border-rose-500/30 text-xs font-bold uppercase tracking-wider transition-colors cursor-pointer",
|
|
885
|
+
children: [
|
|
886
|
+
/* @__PURE__ */ jsxs5("svg", { width: "13", height: "13", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
887
|
+
/* @__PURE__ */ jsx5("path", { d: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" }),
|
|
888
|
+
/* @__PURE__ */ jsx5("polyline", { points: "16 17 21 12 16 7" }),
|
|
889
|
+
/* @__PURE__ */ jsx5("line", { x1: "21", y1: "12", x2: "9", y2: "12" })
|
|
890
|
+
] }),
|
|
891
|
+
/* @__PURE__ */ jsx5("span", { children: "Sign Out" })
|
|
892
|
+
]
|
|
893
|
+
}
|
|
894
|
+
)
|
|
895
|
+
] })
|
|
896
|
+
] })
|
|
897
|
+
] })
|
|
898
|
+
]
|
|
899
|
+
}
|
|
900
|
+
);
|
|
901
|
+
};
|
|
902
|
+
frnlTopBar.displayName = "frnlTopBar";
|
|
903
|
+
|
|
904
|
+
// src/frnl/frnlBottomBar.tsx
|
|
905
|
+
import { useState as useState3, useRef as useRef2, useEffect as useEffect2 } from "react";
|
|
906
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
907
|
+
var frnlBottomBar = ({
|
|
908
|
+
id = "frnl-bottom-bar",
|
|
909
|
+
onManualSave,
|
|
910
|
+
isSaving = false,
|
|
911
|
+
recentApiRequests = [],
|
|
912
|
+
className = ""
|
|
913
|
+
}) => {
|
|
914
|
+
const {
|
|
915
|
+
inspectionText,
|
|
916
|
+
inspectionDescription,
|
|
917
|
+
isDKeyPressed,
|
|
918
|
+
autoSave,
|
|
919
|
+
apiActivity,
|
|
920
|
+
setInspectionText
|
|
921
|
+
} = useBottomStrip();
|
|
922
|
+
const [apiPanelOpen, setApiPanelOpen] = useState3(false);
|
|
923
|
+
const apiPanelRef = useRef2(null);
|
|
924
|
+
useEffect2(() => {
|
|
925
|
+
const handleClick = (e) => {
|
|
926
|
+
if (apiPanelRef.current && !apiPanelRef.current.contains(e.target)) {
|
|
927
|
+
setApiPanelOpen(false);
|
|
928
|
+
}
|
|
929
|
+
};
|
|
930
|
+
if (apiPanelOpen) document.addEventListener("mousedown", handleClick);
|
|
931
|
+
return () => document.removeEventListener("mousedown", handleClick);
|
|
932
|
+
}, [apiPanelOpen]);
|
|
933
|
+
const showDescription = isDKeyPressed && Boolean(inspectionDescription);
|
|
934
|
+
const displayText = showDescription ? inspectionDescription : inspectionText || 'HOVER UI ELEMENT // HOLD "D" TO INSPECT';
|
|
935
|
+
return /* @__PURE__ */ jsxs6(
|
|
936
|
+
"footer",
|
|
937
|
+
{
|
|
938
|
+
id,
|
|
939
|
+
"data-frnl-component": "frnlBottomBar",
|
|
940
|
+
className: `h-[30px] min-h-[30px] border-t border-[var(--skipo-border-color,#334155)] bg-[var(--skipo-surface,#14171f)] px-3 flex items-center justify-between select-none shrink-0 font-mono text-[11px] z-40 relative text-[var(--skipo-text-muted,#94a3b8)] ${className}`,
|
|
941
|
+
children: [
|
|
942
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2 shrink-0", children: [
|
|
943
|
+
/* @__PURE__ */ jsxs6(
|
|
944
|
+
"button",
|
|
945
|
+
{
|
|
946
|
+
type: "button",
|
|
947
|
+
onClick: () => autoSave.toggle(),
|
|
948
|
+
onMouseEnter: () => setInspectionText(
|
|
949
|
+
autoSave.enabled ? "AUTO-SAVE: ACTIVE" : "AUTO-SAVE: PAUSED",
|
|
950
|
+
autoSave.enabled ? "Automatic background persistence active" : "Changes require manual save"
|
|
951
|
+
),
|
|
952
|
+
onMouseLeave: () => setInspectionText(null),
|
|
953
|
+
className: `flex items-center gap-1.5 px-2 py-0.5 rounded border transition-colors cursor-pointer ${autoSave.enabled ? "border-emerald-500/40 bg-emerald-500/10 text-emerald-400" : "border-[var(--skipo-border-subtle,#334155)] bg-[var(--skipo-bg-panel,#0f172a)] text-[var(--skipo-text-muted,#64748b)]"}`,
|
|
954
|
+
"aria-label": "Toggle Auto Save",
|
|
955
|
+
children: [
|
|
956
|
+
/* @__PURE__ */ jsx6(
|
|
957
|
+
"span",
|
|
958
|
+
{
|
|
959
|
+
className: `w-1.5 h-1.5 rounded-full ${autoSave.enabled ? "bg-emerald-400 animate-pulse" : "bg-slate-500"}`
|
|
960
|
+
}
|
|
961
|
+
),
|
|
962
|
+
/* @__PURE__ */ jsx6("span", { className: "text-[10px] font-bold uppercase tracking-wider", children: autoSave.enabled ? "AUTO" : "MANUAL" })
|
|
963
|
+
]
|
|
964
|
+
}
|
|
965
|
+
),
|
|
966
|
+
onManualSave && /* @__PURE__ */ jsxs6(
|
|
967
|
+
"button",
|
|
968
|
+
{
|
|
969
|
+
type: "button",
|
|
970
|
+
disabled: isSaving,
|
|
971
|
+
onClick: () => onManualSave(),
|
|
972
|
+
onMouseEnter: () => setInspectionText("MANUAL SAVE", "Trigger immediate persistence to disk / database"),
|
|
973
|
+
onMouseLeave: () => setInspectionText(null),
|
|
974
|
+
className: "flex items-center gap-1 px-2 py-0.5 rounded border border-[var(--skipo-border-subtle,#334155)] bg-[var(--skipo-bg-panel,#0f172a)] text-[var(--skipo-text-primary,#f8fafc)] hover:border-[var(--skipo-accent,#38bdf8)] hover:text-[var(--skipo-accent,#38bdf8)] transition-colors cursor-pointer disabled:opacity-40",
|
|
975
|
+
"aria-label": "Save Now",
|
|
976
|
+
children: [
|
|
977
|
+
isSaving ? /* @__PURE__ */ jsx6("span", { className: "w-2.5 h-2.5 border-2 border-current border-t-transparent rounded-full animate-spin" }) : /* @__PURE__ */ jsxs6("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
978
|
+
/* @__PURE__ */ jsx6("path", { d: "M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" }),
|
|
979
|
+
/* @__PURE__ */ jsx6("polyline", { points: "17 21 17 13 7 13 7 21" }),
|
|
980
|
+
/* @__PURE__ */ jsx6("polyline", { points: "7 3 7 8 15 8" })
|
|
981
|
+
] }),
|
|
982
|
+
/* @__PURE__ */ jsx6("span", { className: "text-[10px] font-bold uppercase tracking-wider", children: "Save" })
|
|
983
|
+
]
|
|
984
|
+
}
|
|
985
|
+
)
|
|
986
|
+
] }),
|
|
987
|
+
/* @__PURE__ */ jsx6("div", { className: "flex-1 flex items-center justify-center px-4 overflow-hidden", children: /* @__PURE__ */ jsxs6(
|
|
988
|
+
"div",
|
|
989
|
+
{
|
|
990
|
+
"data-testid": "frnl-bottom-strip-center",
|
|
991
|
+
className: "flex items-center gap-1.5 truncate transition-all duration-150 max-w-2xl",
|
|
992
|
+
children: [
|
|
993
|
+
showDescription && /* @__PURE__ */ jsx6("span", { className: "px-1 py-0.2 rounded text-[9px] font-bold uppercase tracking-wider bg-amber-400 text-black shrink-0", children: "D-INSPECT" }),
|
|
994
|
+
/* @__PURE__ */ jsx6(
|
|
995
|
+
"span",
|
|
996
|
+
{
|
|
997
|
+
className: `truncate ${showDescription ? "text-[11px] font-light italic text-[var(--skipo-text-primary,#f8fafc)]" : inspectionText ? "text-xs font-bold text-[var(--skipo-accent,#38bdf8)]" : "text-xs font-medium text-[var(--skipo-text-muted,#64748b)]"}`,
|
|
998
|
+
children: displayText
|
|
999
|
+
}
|
|
1000
|
+
)
|
|
1001
|
+
]
|
|
1002
|
+
}
|
|
1003
|
+
) }),
|
|
1004
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2 shrink-0 relative", ref: apiPanelRef, children: [
|
|
1005
|
+
/* @__PURE__ */ jsxs6(
|
|
1006
|
+
"button",
|
|
1007
|
+
{
|
|
1008
|
+
type: "button",
|
|
1009
|
+
onClick: () => setApiPanelOpen((prev) => !prev),
|
|
1010
|
+
onMouseEnter: () => setInspectionText(
|
|
1011
|
+
`API: ${apiActivity.activeCount} IN-FLIGHT`,
|
|
1012
|
+
"Toggle recent API requests inspector log"
|
|
1013
|
+
),
|
|
1014
|
+
onMouseLeave: () => setInspectionText(null),
|
|
1015
|
+
className: `flex items-center gap-1.5 px-2 py-0.5 rounded border transition-colors cursor-pointer ${apiActivity.activeCount > 0 ? "border-cyan-500/40 bg-cyan-500/10 text-cyan-400" : "border-[var(--skipo-border-subtle,#334155)] bg-[var(--skipo-bg-panel,#0f172a)] text-[var(--skipo-text-muted,#94a3b8)] hover:text-white"}`,
|
|
1016
|
+
"aria-label": "API Requests",
|
|
1017
|
+
"aria-expanded": apiPanelOpen,
|
|
1018
|
+
children: [
|
|
1019
|
+
/* @__PURE__ */ jsx6(
|
|
1020
|
+
"svg",
|
|
1021
|
+
{
|
|
1022
|
+
width: "12",
|
|
1023
|
+
height: "12",
|
|
1024
|
+
viewBox: "0 0 24 24",
|
|
1025
|
+
fill: "none",
|
|
1026
|
+
stroke: "currentColor",
|
|
1027
|
+
strokeWidth: "2",
|
|
1028
|
+
strokeLinecap: "round",
|
|
1029
|
+
strokeLinejoin: "round",
|
|
1030
|
+
className: apiActivity.activeCount > 0 ? "animate-pulse" : "",
|
|
1031
|
+
children: /* @__PURE__ */ jsx6("polyline", { points: "22 12 18 12 15 21 9 3 6 12 2 12" })
|
|
1032
|
+
}
|
|
1033
|
+
),
|
|
1034
|
+
/* @__PURE__ */ jsxs6("span", { className: "text-[10px] font-bold uppercase tracking-wider", children: [
|
|
1035
|
+
"API ",
|
|
1036
|
+
apiActivity.activeCount > 0 ? `(${apiActivity.activeCount})` : "LOG"
|
|
1037
|
+
] })
|
|
1038
|
+
]
|
|
1039
|
+
}
|
|
1040
|
+
),
|
|
1041
|
+
apiPanelOpen && /* @__PURE__ */ jsxs6("div", { className: "absolute right-0 bottom-full mb-1.5 w-80 rounded border border-[var(--skipo-border-color,#334155)] bg-[var(--skipo-surface,#14171f)] p-3 shadow-2xl z-50 flex flex-col gap-2 font-mono text-xs animate-fade-in max-h-72 overflow-hidden", children: [
|
|
1042
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-between pb-1.5 border-b border-[var(--skipo-border-subtle,#334155)]", children: [
|
|
1043
|
+
/* @__PURE__ */ jsx6("span", { className: "font-bold text-[var(--skipo-text-primary,#f8fafc)] uppercase text-[10px] tracking-wider", children: "Recent API Requests" }),
|
|
1044
|
+
/* @__PURE__ */ jsxs6("span", { className: "text-[10px] text-[var(--skipo-accent,#38bdf8)] font-bold", children: [
|
|
1045
|
+
apiActivity.activeCount,
|
|
1046
|
+
" In-Flight"
|
|
1047
|
+
] })
|
|
1048
|
+
] }),
|
|
1049
|
+
/* @__PURE__ */ jsx6("div", { className: "overflow-y-auto max-h-52 flex flex-col gap-1.5 pr-1", children: recentApiRequests.length > 0 ? recentApiRequests.map((req) => /* @__PURE__ */ jsxs6(
|
|
1050
|
+
"div",
|
|
1051
|
+
{
|
|
1052
|
+
className: "p-1.5 rounded bg-[var(--skipo-bg-panel,#0f172a)] border border-[var(--skipo-border-subtle,#334155)] flex items-center justify-between text-[10px]",
|
|
1053
|
+
children: [
|
|
1054
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-1.5 truncate mr-2", children: [
|
|
1055
|
+
/* @__PURE__ */ jsx6("span", { className: "font-bold text-[var(--skipo-accent,#38bdf8)]", children: req.method }),
|
|
1056
|
+
/* @__PURE__ */ jsx6("span", { className: "truncate text-[var(--skipo-text-primary,#f8fafc)]", children: req.endpoint })
|
|
1057
|
+
] }),
|
|
1058
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-1 shrink-0", children: [
|
|
1059
|
+
req.status && /* @__PURE__ */ jsx6(
|
|
1060
|
+
"span",
|
|
1061
|
+
{
|
|
1062
|
+
className: `font-bold ${req.status < 400 ? "text-emerald-400" : "text-rose-400"}`,
|
|
1063
|
+
children: req.status
|
|
1064
|
+
}
|
|
1065
|
+
),
|
|
1066
|
+
req.durationMs !== void 0 && /* @__PURE__ */ jsxs6("span", { className: "text-[var(--skipo-text-muted,#64748b)]", children: [
|
|
1067
|
+
req.durationMs,
|
|
1068
|
+
"ms"
|
|
1069
|
+
] })
|
|
1070
|
+
] })
|
|
1071
|
+
]
|
|
1072
|
+
},
|
|
1073
|
+
req.id
|
|
1074
|
+
)) : /* @__PURE__ */ jsx6("div", { className: "py-4 text-center text-[var(--skipo-text-muted,#64748b)] text-[10px]", children: "No recent backend requests recorded." }) })
|
|
1075
|
+
] })
|
|
1076
|
+
] })
|
|
1077
|
+
]
|
|
1078
|
+
}
|
|
1079
|
+
);
|
|
1080
|
+
};
|
|
1081
|
+
frnlBottomBar.displayName = "frnlBottomBar";
|
|
1082
|
+
|
|
1083
|
+
// src/frnl/frnlPage.tsx
|
|
1084
|
+
import React7, { useEffect as useEffect3, useState as useState4 } from "react";
|
|
1085
|
+
import { mountFrnlOrchestration } from "@ultrst-armada/frnlts";
|
|
1086
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1087
|
+
function FrnlGlobalHoverInspector() {
|
|
1088
|
+
const { setInspectionText } = useBottomStrip();
|
|
1089
|
+
useEffect3(() => {
|
|
1090
|
+
const handleMouseOver = (e) => {
|
|
1091
|
+
const target = e.target;
|
|
1092
|
+
if (!target) return;
|
|
1093
|
+
const el = target.closest(
|
|
1094
|
+
'[data-skipo-label], [data-hover], [title], [aria-label], button, a, input, select, textarea, [role="tab"], [role="button"]'
|
|
1095
|
+
);
|
|
1096
|
+
if (el) {
|
|
1097
|
+
const label = el.getAttribute("data-skipo-label") || el.getAttribute("data-hover") || el.getAttribute("title") || el.getAttribute("aria-label");
|
|
1098
|
+
const desc = el.getAttribute("data-skipo-desc");
|
|
1099
|
+
if (label || desc) {
|
|
1100
|
+
if (el.hasAttribute("title") && !el.hasAttribute("data-frnl-original-title")) {
|
|
1101
|
+
el.setAttribute("data-frnl-original-title", el.getAttribute("title") || "");
|
|
1102
|
+
el.removeAttribute("title");
|
|
1103
|
+
}
|
|
1104
|
+
setInspectionText(label || null, desc || null);
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
};
|
|
1108
|
+
const handleMouseOut = (e) => {
|
|
1109
|
+
const target = e.target;
|
|
1110
|
+
if (!target) return;
|
|
1111
|
+
const el = target.closest("[data-frnl-original-title]");
|
|
1112
|
+
if (el) {
|
|
1113
|
+
const orig = el.getAttribute("data-frnl-original-title");
|
|
1114
|
+
if (orig) {
|
|
1115
|
+
el.setAttribute("title", orig);
|
|
1116
|
+
el.removeAttribute("data-frnl-original-title");
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
setInspectionText(null);
|
|
1120
|
+
};
|
|
1121
|
+
document.addEventListener("mouseover", handleMouseOver);
|
|
1122
|
+
document.addEventListener("mouseout", handleMouseOut);
|
|
1123
|
+
return () => {
|
|
1124
|
+
document.removeEventListener("mouseover", handleMouseOver);
|
|
1125
|
+
document.removeEventListener("mouseout", handleMouseOut);
|
|
1126
|
+
};
|
|
1127
|
+
}, [setInspectionText]);
|
|
1128
|
+
return null;
|
|
1129
|
+
}
|
|
1130
|
+
function FrnlPageInner({
|
|
1131
|
+
id = "frnl-page",
|
|
1132
|
+
appName = "Skipo App",
|
|
1133
|
+
appLogo,
|
|
1134
|
+
breadcrumbs,
|
|
1135
|
+
tabs = [],
|
|
1136
|
+
activeTabId,
|
|
1137
|
+
onTabChange,
|
|
1138
|
+
showTopBar = true,
|
|
1139
|
+
showBottomBar = true,
|
|
1140
|
+
topBarActions,
|
|
1141
|
+
user,
|
|
1142
|
+
onLogout,
|
|
1143
|
+
onOpenSettings,
|
|
1144
|
+
onSave,
|
|
1145
|
+
isSaving = false,
|
|
1146
|
+
recentApiRequests = [],
|
|
1147
|
+
orchestrations = [],
|
|
1148
|
+
className = "",
|
|
1149
|
+
children
|
|
1150
|
+
}) {
|
|
1151
|
+
const store = useFloksFrnlStore();
|
|
1152
|
+
const [internalActiveTab, setInternalActiveTab] = useState4(tabs[0]?.id);
|
|
1153
|
+
const activeTab = activeTabId ?? internalActiveTab;
|
|
1154
|
+
useEffect3(() => {
|
|
1155
|
+
const cleanups = orchestrations.map(
|
|
1156
|
+
(orchestration) => mountFrnlOrchestration(store, orchestration)
|
|
1157
|
+
);
|
|
1158
|
+
return () => {
|
|
1159
|
+
for (const cleanup of cleanups.reverse()) cleanup();
|
|
1160
|
+
};
|
|
1161
|
+
}, [orchestrations, store]);
|
|
1162
|
+
const handleTabChange = (tabId) => {
|
|
1163
|
+
if (onTabChange) onTabChange(tabId);
|
|
1164
|
+
else setInternalActiveTab(tabId);
|
|
1165
|
+
};
|
|
1166
|
+
const currentTab = tabs.find((t) => t.id === activeTab);
|
|
1167
|
+
return /* @__PURE__ */ jsxs7(
|
|
1168
|
+
"div",
|
|
1169
|
+
{
|
|
1170
|
+
id,
|
|
1171
|
+
"data-frnl-component": "frnlPage",
|
|
1172
|
+
className: `h-screen w-screen flex flex-col bg-[var(--skipo-bg,#0a0c10)] text-[var(--skipo-text-primary,#f8fafc)] font-mono select-none overflow-hidden ${className}`,
|
|
1173
|
+
children: [
|
|
1174
|
+
/* @__PURE__ */ jsx7(FrnlGlobalHoverInspector, {}),
|
|
1175
|
+
showTopBar && React7.createElement(frnlTopBar, {
|
|
1176
|
+
appName,
|
|
1177
|
+
appLogo,
|
|
1178
|
+
breadcrumbs,
|
|
1179
|
+
tabs,
|
|
1180
|
+
activeTabId: activeTab,
|
|
1181
|
+
onTabChange: handleTabChange,
|
|
1182
|
+
user,
|
|
1183
|
+
onLogout,
|
|
1184
|
+
onOpenSettings,
|
|
1185
|
+
actions: topBarActions
|
|
1186
|
+
}),
|
|
1187
|
+
/* @__PURE__ */ jsx7("main", { className: "flex-1 flex flex-col min-h-0 overflow-y-auto relative p-4", children: currentTab?.content ?? children }),
|
|
1188
|
+
showBottomBar && React7.createElement(frnlBottomBar, {
|
|
1189
|
+
onManualSave: onSave,
|
|
1190
|
+
isSaving,
|
|
1191
|
+
recentApiRequests
|
|
1192
|
+
})
|
|
1193
|
+
]
|
|
1194
|
+
}
|
|
1195
|
+
);
|
|
1196
|
+
}
|
|
1197
|
+
var frnlPage = (props) => {
|
|
1198
|
+
return /* @__PURE__ */ jsx7(BottomStripProvider, { initialAutoSave: props.autoSaveEnabled ?? true, children: /* @__PURE__ */ jsx7(FrnlPageInner, { ...props }) });
|
|
1199
|
+
};
|
|
1200
|
+
frnlPage.displayName = "frnlPage";
|
|
1201
|
+
|
|
1202
|
+
export {
|
|
1203
|
+
frnlButtonDefinition,
|
|
1204
|
+
frnlButton,
|
|
1205
|
+
frnlTextInputDefinition,
|
|
1206
|
+
frnlTextInput,
|
|
1207
|
+
frnlSelectDefinition,
|
|
1208
|
+
frnlSelect,
|
|
1209
|
+
frnlToggleDefinition,
|
|
1210
|
+
frnlToggle,
|
|
1211
|
+
frnlSlider,
|
|
1212
|
+
frnlBadge,
|
|
1213
|
+
frnlTabWindow,
|
|
1214
|
+
frnlTopBar,
|
|
1215
|
+
frnlBottomBar,
|
|
1216
|
+
frnlPage
|
|
1217
|
+
};
|