@decido/shell 4.0.3 → 4.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/dist/CenterComposite-V3LL23PG.mjs +64 -0
  2. package/dist/DebugPanel-NFXQOZQ6.mjs +1 -0
  3. package/dist/MorphShell-4JGDAN46.mjs +1 -0
  4. package/dist/PlaygroundAppSidebar-QI73PNRI.mjs +1 -0
  5. package/dist/PlaygroundChat-CBIIQMWU.mjs +1 -0
  6. package/dist/PlaygroundTerminal-MVZKXTYW.mjs +1 -0
  7. package/dist/PluginSandbox-MUNBFWGH.mjs +1 -0
  8. package/dist/ReactFlowEditor-2ALQWCHQ.mjs +6 -0
  9. package/dist/ReactFlowEditor-ROZ6RF3K.css +1 -0
  10. package/dist/TimelineEditor-UPRRXXVM.mjs +3 -0
  11. package/dist/WidgetSlotPanel-YX2VBQAP.mjs +1 -0
  12. package/dist/chunk-3YEFIKFH.mjs +1 -0
  13. package/dist/chunk-53FADIUX.mjs +1 -0
  14. package/dist/chunk-5FWJFDER.mjs +285 -0
  15. package/dist/chunk-6K3AFDP5.mjs +1 -0
  16. package/dist/chunk-AGAAZ2MU.mjs +1 -0
  17. package/dist/chunk-BCP43BRV.mjs +2 -0
  18. package/dist/chunk-BSJBW64M.mjs +1 -0
  19. package/dist/chunk-C5FD536O.mjs +1 -0
  20. package/dist/chunk-DTH34ILQ.mjs +1 -0
  21. package/dist/chunk-DWP77YZU.mjs +1 -0
  22. package/dist/chunk-O3OWL3Q2.mjs +1 -0
  23. package/dist/chunk-POBZMHZ5.mjs +7 -0
  24. package/dist/chunk-S3AQ4QDD.mjs +42 -0
  25. package/dist/chunk-XDA6ZRIQ.mjs +1 -0
  26. package/dist/chunk-XTDMHHMW.mjs +5 -0
  27. package/dist/chunk-YAYNNUEX.mjs +12 -0
  28. package/dist/chunk-YEKQJ4YC.mjs +1 -0
  29. package/dist/chunk-YHFKY5MB.mjs +1 -0
  30. package/dist/chunk-ZKD74U4X.mjs +1 -0
  31. package/dist/index.css +1 -561
  32. package/dist/index.js +462 -65086
  33. package/dist/index.mjs +50 -40209
  34. package/dist/useIntentLens-OCAUBNOQ.mjs +1 -0
  35. package/dist/useSuggestionsStore-Q4KVPU7L.mjs +1 -0
  36. package/dist/wasm-A4BTVJWV.mjs +1 -0
  37. package/package.json +15 -15
  38. package/dist/CenterComposite-RPEGBKQU.mjs +0 -1697
  39. package/dist/DebugPanel-KEKDMHDN.mjs +0 -14
  40. package/dist/MorphShell-FKDBB7E5.mjs +0 -14
  41. package/dist/PlaygroundAppSidebar-ALYJJGAO.mjs +0 -9
  42. package/dist/PlaygroundChat-MI2KXMK6.mjs +0 -15
  43. package/dist/PlaygroundTerminal-5AV4BJAI.mjs +0 -7
  44. package/dist/PluginSandbox-WMNAUQOJ.mjs +0 -188
  45. package/dist/ReactFlowEditor-RTF2652X.mjs +0 -3574
  46. package/dist/ReactFlowEditor-ZW5MCN5Y.css +0 -561
  47. package/dist/TimelineEditor-N4HRMHTB.mjs +0 -226
  48. package/dist/WidgetSlotPanel-KJI4CHHD.mjs +0 -11
  49. package/dist/chunk-2YMI4N5I.mjs +0 -2004
  50. package/dist/chunk-3BZX7LF2.mjs +0 -139
  51. package/dist/chunk-3P4P3M54.mjs +0 -136
  52. package/dist/chunk-F3OTFHNO.mjs +0 -40
  53. package/dist/chunk-IMHORBTL.mjs +0 -48
  54. package/dist/chunk-JF5QSJYT.mjs +0 -295
  55. package/dist/chunk-LWMMFTJC.mjs +0 -382
  56. package/dist/chunk-MSVEFEXE.mjs +0 -179
  57. package/dist/chunk-OCHGY2MN.mjs +0 -1662
  58. package/dist/chunk-PMYAM764.mjs +0 -813
  59. package/dist/chunk-Q64KZXPK.mjs +0 -43
  60. package/dist/chunk-QHQW2HMU.mjs +0 -155
  61. package/dist/chunk-RWZ4BOIN.mjs +0 -385
  62. package/dist/chunk-UHT6FIYF.mjs +0 -195
  63. package/dist/chunk-UJCSKKID.mjs +0 -30
  64. package/dist/chunk-V3CYNPGL.mjs +0 -8758
  65. package/dist/chunk-VBPGEFNM.mjs +0 -2381
  66. package/dist/chunk-XMSU6UWD.mjs +0 -158
  67. package/dist/chunk-ZCCCBHE6.mjs +0 -55
  68. package/dist/useIntentLens-LEQCAXCK.mjs +0 -13
  69. package/dist/useSuggestionsStore-4L2AIZ2D.mjs +0 -7
  70. package/dist/wasm-QFXGEYGP.mjs +0 -81
@@ -1,382 +0,0 @@
1
- import {
2
- useMorphInstanceStore
3
- } from "./chunk-UHT6FIYF.mjs";
4
- import {
5
- usePlaygroundStore
6
- } from "./chunk-XMSU6UWD.mjs";
7
-
8
- // src/contexts/NetworkProvider.tsx
9
- import { createContext, useContext, useEffect, useRef, useCallback } from "react";
10
- import { jsx } from "react/jsx-runtime";
11
- var isTauri = typeof window !== "undefined" && window.__TAURI_INTERNALS__ !== void 0;
12
- var getTauriEvent = () => import(
13
- /* @vite-ignore */
14
- "@tauri-apps/api/event"
15
- );
16
- var NetworkContext = createContext({
17
- socket: null,
18
- isConnected: false,
19
- isNativeOS: false,
20
- emitEvent: () => {
21
- },
22
- onEvent: () => {
23
- },
24
- offEvent: () => {
25
- }
26
- });
27
- var useNetwork = () => useContext(NetworkContext);
28
- var useNetworkEvent = (eventName, callback) => {
29
- const { onEvent, offEvent } = useNetwork();
30
- useEffect(() => {
31
- onEvent(eventName, callback);
32
- return () => offEvent(eventName, callback);
33
- }, [eventName, callback, onEvent, offEvent]);
34
- };
35
- var NetworkProvider = ({ children, socketInstance = null, isConnected = false }) => {
36
- const nativeListeners = useRef(/* @__PURE__ */ new Map());
37
- const tauriListeners = useRef(/* @__PURE__ */ new Map());
38
- const emitEvent = useCallback(async (event, payload) => {
39
- if (isTauri) {
40
- try {
41
- const { emit } = await getTauriEvent();
42
- await emit(event, payload);
43
- } catch (err) {
44
- console.error(`[Tauri IPC] Error emitiendo ${event}:`, err);
45
- }
46
- return;
47
- }
48
- if (!socketInstance) {
49
- console.warn(`[NetworkProvider] Emit '${event}' ignorado: No hay red disponible.`);
50
- return;
51
- }
52
- if (typeof socketInstance.emit === "function") {
53
- socketInstance.emit(event, payload);
54
- } else if (typeof socketInstance.send === "function") {
55
- socketInstance.send(JSON.stringify({ event, payload }));
56
- }
57
- }, [socketInstance]);
58
- const onEvent = useCallback((event, callback) => {
59
- if (isTauri) {
60
- const unlistenPromise = getTauriEvent().then(
61
- ({ listen }) => listen(event, (tauriEvent) => {
62
- callback(tauriEvent.payload);
63
- })
64
- );
65
- if (!tauriListeners.current.has(event)) {
66
- tauriListeners.current.set(event, []);
67
- }
68
- tauriListeners.current.get(event)?.push({ originalCallback: callback, unlistenPromise });
69
- return;
70
- }
71
- if (!socketInstance) return;
72
- if (typeof socketInstance.on === "function") {
73
- socketInstance.on(event, callback);
74
- } else if (typeof socketInstance.addEventListener === "function") {
75
- const wrappedCallback = (messageEvent) => {
76
- try {
77
- const data = JSON.parse(messageEvent.data);
78
- if (data.event === event) callback(data.payload);
79
- } catch (e) {
80
- }
81
- };
82
- socketInstance.addEventListener("message", wrappedCallback);
83
- if (!nativeListeners.current.has(event)) {
84
- nativeListeners.current.set(event, []);
85
- }
86
- nativeListeners.current.get(event)?.push({ originalCallback: callback, wrappedCallback });
87
- }
88
- }, [socketInstance]);
89
- const offEvent = useCallback((event, callback) => {
90
- if (isTauri) {
91
- const listeners = tauriListeners.current.get(event) || [];
92
- const index = listeners.findIndex((l) => l.originalCallback === callback);
93
- if (index > -1) {
94
- listeners[index].unlistenPromise.then((unlisten) => unlisten());
95
- listeners.splice(index, 1);
96
- }
97
- return;
98
- }
99
- if (!socketInstance) return;
100
- if (typeof socketInstance.off === "function") {
101
- socketInstance.off(event, callback);
102
- } else if (typeof socketInstance.removeEventListener === "function") {
103
- const listeners = nativeListeners.current.get(event) || [];
104
- const foundIndex = listeners.findIndex((l) => l.originalCallback === callback);
105
- if (foundIndex > -1) {
106
- const { wrappedCallback } = listeners[foundIndex];
107
- socketInstance.removeEventListener("message", wrappedCallback);
108
- listeners.splice(foundIndex, 1);
109
- }
110
- }
111
- }, [socketInstance]);
112
- const effectiveConnection = isTauri ? true : isConnected;
113
- return /* @__PURE__ */ jsx(NetworkContext.Provider, { value: {
114
- socket: socketInstance,
115
- isConnected: effectiveConnection,
116
- isNativeOS: isTauri,
117
- emitEvent,
118
- onEvent,
119
- offEvent
120
- }, children });
121
- };
122
-
123
- // src/components/playground/PlaygroundErrorBoundary.tsx
124
- import { Component } from "react";
125
- import { AlertTriangle, RefreshCw } from "lucide-react";
126
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
127
- var PlaygroundErrorBoundary = class extends Component {
128
- state = {
129
- hasError: false,
130
- error: null
131
- };
132
- static getDerivedStateFromError(error) {
133
- return { hasError: true, error };
134
- }
135
- componentDidCatch(error, errorInfo) {
136
- console.error(`[PlaygroundErrorBoundary] Error capturado en ${this.props.fallbackName || "componente"}:`, error, errorInfo);
137
- }
138
- handleReset = () => {
139
- this.setState({ hasError: false, error: null });
140
- };
141
- render() {
142
- if (this.state.hasError) {
143
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center w-full h-full min-h-[150px] bg-red-500/10 border border-red-500/20 rounded-xl p-4 text-center backdrop-blur-xs", children: [
144
- /* @__PURE__ */ jsx2(AlertTriangle, { className: "text-red-400 mb-3", size: 32 }),
145
- /* @__PURE__ */ jsxs("h3", { className: "text-red-400 font-bold mb-1", children: [
146
- "Fallo en ",
147
- this.props.fallbackName || "M\xF3dulo"
148
- ] }),
149
- /* @__PURE__ */ jsx2("p", { className: "text-xs text-red-300/80 mb-4 max-w-xs truncate", title: this.state.error?.message, children: this.state.error?.message || "Excepci\xF3n desconocida en el renderizado." }),
150
- /* @__PURE__ */ jsxs(
151
- "button",
152
- {
153
- onClick: this.handleReset,
154
- className: "flex items-center gap-2 px-3 py-1.5 bg-red-500/20 hover:bg-red-500/30 text-red-300 rounded-lg text-xs transition-colors border border-red-500/20",
155
- children: [
156
- /* @__PURE__ */ jsx2(RefreshCw, { size: 12 }),
157
- "Reiniciar M\xF3dulo"
158
- ]
159
- }
160
- )
161
- ] });
162
- }
163
- return this.props.children;
164
- }
165
- };
166
-
167
- // src/hooks/morphBridge.ts
168
- function bridgeToMorph(opts) {
169
- const { chatId, shellType, label, data = {}, artifact } = opts;
170
- useMorphInstanceStore.getState().upsertInstance({
171
- id: chatId,
172
- sourceChatId: chatId,
173
- shellType,
174
- label,
175
- data
176
- });
177
- if (artifact) {
178
- useMorphInstanceStore.getState().pushArtifact(chatId, {
179
- id: artifact.id,
180
- name: artifact.name,
181
- shellType: artifact.shellType || shellType,
182
- version: artifact.version || 1,
183
- data: artifact.data
184
- });
185
- }
186
- usePlaygroundStore.getState().setShowCanvas(true);
187
- console.log(`[MorphBridge] \u{1F300} ${shellType} instance "${label}" \u2192 tab ${chatId}`);
188
- }
189
- function detectShellType(content) {
190
- if (typeof content === "string") {
191
- if (/<html|<div|<body|<script/i.test(content)) return "iframe";
192
- if (/^#{1,6}\s|^\*\*|^\- \[/m.test(content)) return "markdown";
193
- return "markdown";
194
- }
195
- if (content.artifact) return "iframe";
196
- if (content.schema) return "iframe";
197
- if (content.keyframes || content.nodes) return "shell-canvas";
198
- return "markdown";
199
- }
200
-
201
- // src/hooks/listeners/useUIStateListener.ts
202
- import { useEffect as useEffect2, useRef as useRef2 } from "react";
203
- import { useTimelineStore } from "@decido/engine";
204
- import { useEngineStore } from "@decido/engine";
205
-
206
- // src/hooks/useSoundEffects.ts
207
- var playSound = (type, isMuted = false) => {
208
- if (isMuted) return;
209
- try {
210
- const ctx = new (window.AudioContext || window.webkitAudioContext)();
211
- const osc = ctx.createOscillator();
212
- const gain = ctx.createGain();
213
- osc.connect(gain);
214
- gain.connect(ctx.destination);
215
- if (type === "beep") {
216
- osc.frequency.setValueAtTime(600, ctx.currentTime);
217
- gain.gain.setValueAtTime(0.1, ctx.currentTime);
218
- gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.1);
219
- osc.start(ctx.currentTime);
220
- osc.stop(ctx.currentTime + 0.1);
221
- } else if (type === "success") {
222
- osc.frequency.setValueAtTime(400, ctx.currentTime);
223
- osc.frequency.exponentialRampToValueAtTime(800, ctx.currentTime + 0.2);
224
- gain.gain.setValueAtTime(0.1, ctx.currentTime);
225
- gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.3);
226
- osc.start(ctx.currentTime);
227
- osc.stop(ctx.currentTime + 0.3);
228
- } else if (type === "alert") {
229
- osc.type = "square";
230
- osc.frequency.setValueAtTime(300, ctx.currentTime);
231
- osc.frequency.setValueAtTime(400, ctx.currentTime + 0.1);
232
- gain.gain.setValueAtTime(0.1, ctx.currentTime);
233
- gain.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + 0.3);
234
- osc.start(ctx.currentTime);
235
- osc.stop(ctx.currentTime + 0.3);
236
- }
237
- } catch (e) {
238
- console.error("Audio not supported");
239
- }
240
- };
241
-
242
- // src/hooks/listeners/useUIStateListener.ts
243
- var useUIStateListener = () => {
244
- const activeTimelineId = usePlaygroundStore((state) => state.prototypeBrand);
245
- const timelines = useTimelineStore((state) => state.timelines);
246
- const activeNodeIds = useEngineStore((state) => state.activeNodeIds);
247
- const { addLog, addChatMessage, isMuted, demoState, setDemoState, showCanvas, setShowCanvas } = usePlaygroundStore();
248
- const processedNodes = useRef2(/* @__PURE__ */ new Set());
249
- useEffect2(() => {
250
- if (activeNodeIds.length === 0) {
251
- processedNodes.current.clear();
252
- setDemoState("idle");
253
- }
254
- }, [activeNodeIds, setDemoState]);
255
- useEffect2(() => {
256
- const blueprint = timelines[activeTimelineId]?.blueprint || { nodes: timelines[activeTimelineId]?.keyframes || [] };
257
- if (!blueprint || activeNodeIds.length === 0) return;
258
- const activeUINodes = blueprint.nodes.filter(
259
- (n) => activeNodeIds.includes(n.id) && n.track === "ui"
260
- );
261
- activeUINodes.forEach((node) => {
262
- if (!processedNodes.current.has(node.id)) {
263
- processedNodes.current.add(node.id);
264
- const newState = node.state || "idle";
265
- setDemoState(newState);
266
- if (node.canvas !== void 0) {
267
- setShowCanvas(node.canvas);
268
- }
269
- addLog(`\u{1F500} System Authored Transition: UI Context [${newState}]`, "system");
270
- if (newState.includes("whatsapp") && !newState.includes("resolved") && !newState.includes("idle")) {
271
- addChatMessage({ type: "alert", sender: "system", text: "Alerta de SLA Cr\xEDtico detectada v\xEDa integraci\xF3n." });
272
- } else if (newState.includes("analyzing")) {
273
- addChatMessage({ type: "workflow", sender: "system", text: "Analizando informaci\xF3n contextual..." });
274
- }
275
- if (newState.includes("alert")) playSound("alert", isMuted);
276
- else playSound("beep", isMuted);
277
- }
278
- });
279
- }, [activeNodeIds, activeTimelineId, timelines, isMuted, addLog, addChatMessage, setDemoState, setShowCanvas]);
280
- return { demoState, setDemoState, showCanvas, setShowCanvas };
281
- };
282
-
283
- // src/hooks/useStudioConfig.ts
284
- import { useState, useEffect as useEffect3, useMemo, useCallback as useCallback2 } from "react";
285
-
286
- // src/mocks/decido.tsx
287
- var useAuthStore = Object.assign(
288
- (selector) => {
289
- const state = {
290
- availableLicenses: [
291
- { id: "1", name: "Decido Personal", type: "personal" },
292
- { id: "2", name: "K\xFAspide Corp", type: "enterprise" },
293
- { id: "3", name: "Madefront Ind", type: "enterprise" }
294
- ]
295
- };
296
- return selector ? selector(state) : state;
297
- },
298
- {
299
- getState: () => ({
300
- availableLicenses: [
301
- { id: "1", name: "Decido Personal", type: "personal" },
302
- { id: "2", name: "K\xFAspide Corp", type: "enterprise" },
303
- { id: "3", name: "Madefront Ind", type: "enterprise" }
304
- ],
305
- login: (role, context, licenses) => {
306
- },
307
- setActiveContext: (id) => {
308
- }
309
- })
310
- }
311
- );
312
-
313
- // src/hooks/useStudioConfig.ts
314
- function useStudioConfig(config, prototypeBrand) {
315
- const safeConfig = config || { environments: {}, voices: [] };
316
- const activeConfig = useMemo(() => {
317
- return safeConfig.environments[prototypeBrand] || Object.values(safeConfig.environments)[0] || {
318
- gradient: "from-gray-400 to-gray-600",
319
- greetingText: "Console",
320
- subtitle: "Sin configuraci\xF3n",
321
- borderColor: "border-border-strong",
322
- glowColor: "shadow-none",
323
- buttonColor: "bg-surface-glass",
324
- buttonGlow: "shadow-none",
325
- suggestions: [],
326
- widgets: []
327
- };
328
- }, [safeConfig, prototypeBrand]);
329
- const [suggestions, setSuggestions] = useState([]);
330
- useEffect3(() => {
331
- setSuggestions((prev) => {
332
- const next = activeConfig.suggestions || [];
333
- if (JSON.stringify(prev) === JSON.stringify(next)) return prev;
334
- return next;
335
- });
336
- }, [activeConfig.suggestions]);
337
- const authAvailableLicenses = useAuthStore(
338
- (state) => state.availableLicenses
339
- );
340
- const enterpriseLicense = authAvailableLicenses?.find(
341
- (l) => l.type === "enterprise"
342
- );
343
- const companyName = enterpriseLicense ? enterpriseLicense.name : "Empresa Local";
344
- const dynamicPersona = useMemo(() => ({
345
- id: config?.persona?.id || "agent-core",
346
- name: config?.persona?.name || "Agent",
347
- voiceName: config?.persona?.voiceName || "Zephyr",
348
- systemInstruction: config?.persona?.systemInstruction || "Eres un agente inteligente del Marco de Trabajo Decido OS. Est\xE1s dise\xF1ado para proporcionar asistencia r\xE1pida, ejecutar comandos del sistema de forma segura, y ayudar al usuario en el uso de la interfaz."
349
- }), [config]);
350
- const handleProfileSelect = useCallback2((license) => {
351
- const { setSelectedProfile, setStep, addLog } = usePlaygroundStore.getState();
352
- setSelectedProfile(license.name);
353
- setStep("action");
354
- setTimeout(() => {
355
- useAuthStore.getState().login("admin", `${license.name} Context`, authAvailableLicenses);
356
- useAuthStore.getState().setActiveContext(license.id);
357
- setStep("chat");
358
- addLog(`Perfil cargado: ${license.name}`, "success");
359
- }, 2500);
360
- }, [authAvailableLicenses]);
361
- return {
362
- safeConfig,
363
- activeConfig,
364
- suggestions,
365
- setSuggestions,
366
- dynamicPersona,
367
- authAvailableLicenses,
368
- handleProfileSelect
369
- };
370
- }
371
-
372
- export {
373
- useNetwork,
374
- useNetworkEvent,
375
- NetworkProvider,
376
- PlaygroundErrorBoundary,
377
- bridgeToMorph,
378
- detectShellType,
379
- playSound,
380
- useUIStateListener,
381
- useStudioConfig
382
- };
@@ -1,179 +0,0 @@
1
- import {
2
- dlog
3
- } from "./chunk-3P4P3M54.mjs";
4
-
5
- // src/store/layoutPresets.ts
6
- var PRESET_CREATOR = {
7
- R1: "workspace-chat",
8
- R2: null,
9
- R3: "graph-editor",
10
- R4: "debug",
11
- R5: null,
12
- R6: null
13
- };
14
- var PRESET_PLAYER = {
15
- R1: "workspace-chat",
16
- R2: null,
17
- R3: null,
18
- R4: null,
19
- R5: null,
20
- R6: null
21
- };
22
- var PRESET_MINIMAL = {
23
- R1: null,
24
- R2: null,
25
- R3: null,
26
- R4: null,
27
- R5: null,
28
- R6: null
29
- };
30
- var PRESET_STUDIO = {
31
- R1: "app-sidebar",
32
- R2: null,
33
- R3: "graph-editor",
34
- R4: "terminal",
35
- R5: null,
36
- R6: null
37
- };
38
- var BUILT_IN_PRESETS = {
39
- creator: PRESET_CREATOR,
40
- player: PRESET_PLAYER,
41
- minimal: PRESET_MINIMAL,
42
- studio: PRESET_STUDIO
43
- };
44
-
45
- // src/store/useLayoutStore.ts
46
- import { create } from "zustand";
47
- import { persist, createJSONStorage } from "zustand/middleware";
48
- import { get, set as idbSet, del } from "idb-keyval";
49
- var idbStorage = {
50
- getItem: async (name) => await get(name) || null,
51
- setItem: async (name, value) => {
52
- await idbSet(name, value);
53
- },
54
- removeItem: async (name) => {
55
- await del(name);
56
- }
57
- };
58
- var useLayoutStore = create()(
59
- persist(
60
- (set, get2) => ({
61
- // ── Slot assignment ──
62
- slots: { ...BUILT_IN_PRESETS["player"] },
63
- setSlot: (region, componentId) => set((s) => ({ slots: { ...s.slots, [region]: componentId } })),
64
- swapSlots: (r1, r2) => set((s) => ({
65
- slots: { ...s.slots, [r1]: s.slots[r2], [r2]: s.slots[r1] }
66
- })),
67
- resetLayout: (preset) => {
68
- const p = BUILT_IN_PRESETS[preset];
69
- if (p) set({ slots: { ...p } });
70
- },
71
- // ── UI toggles ──
72
- isCreatorMode: false,
73
- setIsCreatorMode: (val) => {
74
- set({ isCreatorMode: val });
75
- const preset = val ? BUILT_IN_PRESETS["creator"] : BUILT_IN_PRESETS["player"];
76
- set({ slots: { ...preset } });
77
- },
78
- showCanvas: false,
79
- setShowCanvas: (val) => set({ showCanvas: val }),
80
- showChat: true,
81
- setShowChat: (val) => set({ showChat: val }),
82
- isSidebarOpen: false,
83
- setIsSidebarOpen: (val) => set({ isSidebarOpen: val }),
84
- isImmersive: false,
85
- setIsImmersive: (val) => set({ isImmersive: val }),
86
- // ── Morph stages ──
87
- activeStage: null,
88
- stageHistory: [],
89
- setStage: (stage) => {
90
- dlog.layout(`setStage \u2192 ${stage?.type || "null"}`, stage);
91
- set({ activeStage: stage });
92
- },
93
- pushStage: (stage) => {
94
- const { activeStage, stageHistory } = get2();
95
- dlog.layout(`pushStage \u2192 ${stage.type}${stage.label ? ` (${stage.label})` : ""} | depth: ${stageHistory.length + 1}`, { stage, previousStage: activeStage?.type });
96
- const newHistory = activeStage ? [...stageHistory, activeStage] : stageHistory;
97
- set({ activeStage: stage, stageHistory: newHistory });
98
- },
99
- popStage: () => {
100
- const { stageHistory, activeStage } = get2();
101
- if (stageHistory.length === 0) {
102
- dlog.layout("popStage \u2192 null (history empty)", { poppedStage: activeStage?.type }, "warn");
103
- set({ activeStage: null });
104
- return;
105
- }
106
- const prev = stageHistory[stageHistory.length - 1];
107
- dlog.layout(`popStage \u2192 ${prev.type}${prev.label ? ` (${prev.label})` : ""} | depth: ${stageHistory.length - 1}`, { poppedStage: activeStage?.type, restoredStage: prev.type });
108
- set({
109
- activeStage: prev,
110
- stageHistory: stageHistory.slice(0, -1)
111
- });
112
- },
113
- clearStages: () => {
114
- dlog.layout("clearStages \u2192 all cleared", { previousDepth: get2().stageHistory.length });
115
- set({ activeStage: null, stageHistory: [] });
116
- },
117
- // ── T2 Tabs ──
118
- tabs: [],
119
- activeTabId: null,
120
- addTab: (tab) => {
121
- const { tabs } = get2();
122
- if (tabs.some((t) => t.id === tab.id)) {
123
- set({ activeTabId: tab.id });
124
- return;
125
- }
126
- set({ tabs: [...tabs, tab], activeTabId: tab.id });
127
- },
128
- removeTab: (tabId) => {
129
- const { tabs, activeTabId } = get2();
130
- const newTabs = tabs.filter((t) => t.id !== tabId);
131
- const newActive = activeTabId === tabId ? newTabs.length > 0 ? newTabs[newTabs.length - 1].id : null : activeTabId;
132
- set({ tabs: newTabs, activeTabId: newActive });
133
- },
134
- setActiveTab: (tabId) => set({ activeTabId: tabId }),
135
- // ── Presets ──
136
- savedPresets: [],
137
- savePreset: (name) => {
138
- const { slots, savedPresets } = get2();
139
- const preset = {
140
- id: Date.now().toString(36),
141
- name,
142
- slots: { ...slots },
143
- createdAt: Date.now()
144
- };
145
- set({ savedPresets: [...savedPresets, preset] });
146
- },
147
- loadPreset: (presetId) => {
148
- const { savedPresets } = get2();
149
- const preset = savedPresets.find((p) => p.id === presetId);
150
- if (preset) set({ slots: { ...preset.slots } });
151
- },
152
- deletePreset: (presetId) => {
153
- set((s) => ({
154
- savedPresets: s.savedPresets.filter((p) => p.id !== presetId)
155
- }));
156
- }
157
- }),
158
- {
159
- name: "decido-layout-v4",
160
- // v4 forces eviction to clear corrupted react-resizable-panels flex ratios
161
- storage: createJSONStorage(() => idbStorage),
162
- partialize: (state) => ({
163
- slots: state.slots,
164
- isCreatorMode: state.isCreatorMode,
165
- isSidebarOpen: state.isSidebarOpen,
166
- showCanvas: state.showCanvas,
167
- showChat: state.showChat,
168
- tabs: state.tabs,
169
- activeTabId: state.activeTabId,
170
- savedPresets: state.savedPresets
171
- })
172
- }
173
- )
174
- );
175
-
176
- export {
177
- BUILT_IN_PRESETS,
178
- useLayoutStore
179
- };