legend-state-dev-tools 0.0.2 → 0.0.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/package.json +16 -1
- package/packages/core/dist/index.d.ts +17 -0
- package/packages/core/dist/index.js +28 -0
- package/packages/core/dist/index.js.map +1 -0
- package/packages/core/dist/index.mjs +349 -0
- package/packages/core/dist/index.mjs.map +1 -0
- package/examples/basic/index.html +0 -12
- package/examples/basic/package.json +0 -24
- package/examples/basic/src/App.tsx +0 -50
- package/examples/basic/src/main.tsx +0 -16
- package/examples/basic/src/state.ts +0 -17
- package/examples/basic/tsconfig.json +0 -15
- package/examples/basic/vite.config.ts +0 -6
- package/packages/core/package.json +0 -40
- package/packages/core/src/eta.d.ts +0 -4
- package/packages/core/src/index.ts +0 -130
- package/packages/core/src/state-bridge.ts +0 -56
- package/packages/core/src/ui/json-editor-mount.tsx +0 -144
- package/packages/core/src/ui/panel.ts +0 -96
- package/packages/core/src/ui/shared-utils.ts +0 -49
- package/packages/core/src/ui/template-engine.ts +0 -55
- package/packages/core/src/ui/templates/panel.eta +0 -12
- package/packages/core/src/ui/templates/toolbar.eta +0 -13
- package/packages/core/src/ui/toolbar.ts +0 -108
- package/packages/core/tsconfig.json +0 -9
- package/packages/core/vite.config.ts +0 -71
- package/tsconfig.json +0 -17
- /package/packages/core/{src → dist}/styles.css +0 -0
package/package.json
CHANGED
|
@@ -1,11 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "legend-state-dev-tools",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"private": false,
|
|
5
5
|
"workspaces": [
|
|
6
6
|
"packages/*",
|
|
7
7
|
"examples/*"
|
|
8
8
|
],
|
|
9
|
+
"main": "./packages/core/dist/index.js",
|
|
10
|
+
"module": "./packages/core/dist/index.mjs",
|
|
11
|
+
"types": "./packages/core/dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./packages/core/dist/index.d.ts",
|
|
15
|
+
"import": "./packages/core/dist/index.mjs",
|
|
16
|
+
"require": "./packages/core/dist/index.js",
|
|
17
|
+
"default": "./packages/core/dist/index.mjs"
|
|
18
|
+
},
|
|
19
|
+
"./packages/core/dist/styles.css": "./packages/core/dist/styles.css"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"packages/core/dist"
|
|
23
|
+
],
|
|
9
24
|
"scripts": {
|
|
10
25
|
"build": "npm run build -w packages/core",
|
|
11
26
|
"dev": "npm run build && npm run dev -w examples/basic",
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ObservableParam } from '@legendapp/state';
|
|
2
|
+
|
|
3
|
+
export declare interface DevTools {
|
|
4
|
+
destroy: () => void;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export declare interface DevToolsOptions {
|
|
8
|
+
enabled?: boolean;
|
|
9
|
+
readOnly?: boolean;
|
|
10
|
+
theme?: string;
|
|
11
|
+
rootName?: string;
|
|
12
|
+
position?: 'left' | 'right';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export declare function init(observable$: ObservableParam<any>, options?: DevToolsOptions): DevTools;
|
|
16
|
+
|
|
17
|
+
export { }
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const S=require("eta"),g=require("react/jsx-runtime"),f=require("react"),C=require("react-dom/client"),h=require("json-edit-react"),M=`<div class="lsdt-toolbar-header">
|
|
2
|
+
<div class="lsdt-toolbar-title">
|
|
3
|
+
<span class="lsdt-toolbar-indicator"></span>
|
|
4
|
+
Legend State
|
|
5
|
+
</div>
|
|
6
|
+
<button
|
|
7
|
+
class="lsdt-toggle-btn <%= it.panelVisible ? 'active' : '' %>"
|
|
8
|
+
data-action="toggle-panel"
|
|
9
|
+
title="<%= it.panelVisible ? 'Hide panel' : 'Show panel' %>"
|
|
10
|
+
>
|
|
11
|
+
<%= it.panelVisible ? 'Hide' : 'Show' %> <%= it.rootName %>
|
|
12
|
+
</button>
|
|
13
|
+
</div>
|
|
14
|
+
`,k=`<div class="lsdt-panel-header">
|
|
15
|
+
<h3><%= it.rootName %></h3>
|
|
16
|
+
<div class="lsdt-panel-actions">
|
|
17
|
+
<% if (it.readOnly) { %>
|
|
18
|
+
<span class="lsdt-readonly-badge">Read-only</span>
|
|
19
|
+
<% } %>
|
|
20
|
+
<button class="lsdt-close-btn" data-action="close-panel" title="Close">×</button>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
<div class="lsdt-panel-content">
|
|
24
|
+
<div id="lsdt-json-editor-root"></div>
|
|
25
|
+
</div>
|
|
26
|
+
`,N=new S.Eta({autoEscape:!0,autoTrim:!1}),w={toolbar:M,panel:k};function b(n,t){const e=w[n];if(!e)return console.error(`[LSDT] Template not found: ${n}`),"";try{return N.renderString(e,t)}catch(o){return console.error(`[LSDT] Error rendering template ${n}:`,o),""}}function P(n){return b("toolbar",n)}function O(n){return b("panel",n)}const R="lsdt";function V(n,t){const e=localStorage.getItem(`${R}-${n}`);return e===null?t:e==="true"}function j(){const n=[];return{add:t=>n.push(t),run:()=>{n.forEach(t=>{try{t()}catch(e){console.error("[Legend State DevTools] Cleanup error:",e)}}),n.length=0}}}class ${constructor(t={}){this.container=null,this.isDragging=!1,this.offsetX=0,this.offsetY=0,this.isMinimized=V("toolbar-minimized",!1),this.panelVisible=!1,this.handleClick=e=>{var d;const i=e.target.closest("[data-action]");if(!i)return;i.getAttribute("data-action")==="toggle-panel"&&((d=this.onTogglePanel)==null||d.call(this))},this.handleMouseDown=e=>{if(e.target.tagName!=="BUTTON"&&(this.isDragging=!0,this.container)){this.container.classList.add("dragging");const i=this.container.getBoundingClientRect();this.offsetX=e.clientX-i.left,this.offsetY=e.clientY-i.top}},this.handleMouseMove=e=>{!this.isDragging||!this.container||(this.container.style.left=`${e.clientX-this.offsetX}px`,this.container.style.top=`${e.clientY-this.offsetY}px`,this.container.style.right="auto",this.container.style.bottom="auto")},this.handleMouseUp=()=>{var e;this.isDragging=!1,(e=this.container)==null||e.classList.remove("dragging")},this.onTogglePanel=t.onTogglePanel,this.rootName=t.rootName||"state$"}mount(){this.container||(this.container=document.createElement("div"),this.container.id="lsdt-toolbar",this.isMinimized&&this.container.classList.add("lsdt-toolbar-minimized"),document.body.appendChild(this.container),this.render(),this.attachEventListeners())}render(){if(!this.container)return;const t={isMinimized:this.isMinimized,panelVisible:this.panelVisible,rootName:this.rootName};this.container.innerHTML=P(t)}attachEventListeners(){this.container&&(this.container.addEventListener("click",this.handleClick),this.container.addEventListener("mousedown",this.handleMouseDown),document.addEventListener("mousemove",this.handleMouseMove),document.addEventListener("mouseup",this.handleMouseUp))}setPanelVisible(t){this.panelVisible=t,this.render()}unmount(){this.container&&(this.container.removeEventListener("click",this.handleClick),this.container.removeEventListener("mousedown",this.handleMouseDown),this.container.remove(),this.container=null),document.removeEventListener("mousemove",this.handleMouseMove),document.removeEventListener("mouseup",this.handleMouseUp)}}class x{constructor(t={}){this.container=null,this.visible=!1,this.handleClick=e=>{var d;const i=e.target.closest("[data-action]");if(!i)return;i.getAttribute("data-action")==="close-panel"&&((d=this.onClose)==null||d.call(this))},this.rootName=t.rootName||"state$",this.readOnly=t.readOnly||!1,this.onClose=t.onClose,this.position=t.position||"right"}toggle(){this.visible?this.hide():this.show()}show(){this.visible=!0,this.container||(this.container=document.createElement("div"),this.container.id="lsdt-panel",this.position==="left"&&this.container.classList.add("lsdt-panel-left"),document.body.appendChild(this.container),this.attachEventListeners()),this.render()}hide(){var t;this.visible=!1,(t=this.container)==null||t.remove(),this.container=null}isVisible(){return this.visible}getEditorRoot(){var t;return((t=this.container)==null?void 0:t.querySelector("#lsdt-json-editor-root"))||null}render(){if(!this.container)return;const t={rootName:this.rootName,readOnly:this.readOnly};this.container.innerHTML=O(t)}attachEventListeners(){this.container&&this.container.addEventListener("click",this.handleClick)}unmount(){this.container&&(this.container.removeEventListener("click",this.handleClick),this.container.remove(),this.container=null),this.visible=!1}}function z(n,t){const e=()=>{try{return JSON.parse(JSON.stringify(n.peek()))}catch{return}};let o=null;try{o=n.onChange(()=>{const i=e();t.onSnapshot(i)},{trackingType:!1})}catch{console.warn("[Legend State DevTools] Could not subscribe to observable changes via onChange")}return{getSnapshot:e,setData:i=>{try{n.set(i)}catch(c){console.error("[Legend State DevTools] Failed to set data:",c)}},destroy:()=>{o&&(o(),o=null)}}}const q={githubDark:h.githubDarkTheme,githubLight:h.githubLightTheme,monoDark:h.monoDarkTheme,monoLight:h.monoLightTheme};class J extends f.Component{constructor(){super(...arguments),this.state={error:null}}static getDerivedStateFromError(t){return{error:t}}componentDidCatch(t,e){console.error("[Legend State DevTools] React error:",t,e)}render(){return this.state.error?f.createElement("pre",{style:{color:"#ff6b6b",padding:16,fontSize:12}},`DevTools Error: ${this.state.error.message}
|
|
27
|
+
${this.state.error.stack}`):this.props.children}}function U({data:n,onEdit:t,readOnly:e,theme:o,rootName:i}){const c=q[o]??h.githubDarkTheme;return g.jsx(h.JsonEditor,{data:n,setData:t,rootName:i,theme:c,collapse:2,restrictEdit:e,restrictDelete:e,restrictAdd:e,restrictTypeSelection:e?!0:void 0})}function X(n){const[t,e]=f.useState(n.initialData);f.useEffect(()=>{n.registerUpdater(i=>{e(i)})},[]);const o=i=>{e(i),n.onEdit(i)};return g.jsx(U,{data:t,onEdit:o,readOnly:n.readOnly,theme:n.theme,rootName:n.rootName})}function B(n,t){let e=null,o=null;return e=C.createRoot(n),e.render(g.jsx(J,{children:g.jsx(X,{initialData:t.initialData,onEdit:t.onEdit,readOnly:t.readOnly,theme:t.theme,rootName:t.rootName,registerUpdater:i=>{o=i}})})),{updateData:i=>{o==null||o(i)},destroy:()=>{e&&(e.unmount(),e=null)}}}function Y(n,t={}){const{enabled:e=!0,readOnly:o=!1,theme:i="githubDark",rootName:c="state$",position:d="right"}=t;if(!e)return{destroy:()=>{}};const u=j();let s=null,r=null,l=null,a=null;s=new x({rootName:c,readOnly:o,position:d,onClose:()=>{v()}});const y=()=>{if(!s)return;s.show(),r==null||r.setPanelVisible(!0);const m=(p=10)=>{const E=s==null?void 0:s.getEditorRoot();if(!E){p>0?setTimeout(()=>m(p-1),16):console.warn("[Legend State DevTools] Could not find #lsdt-json-editor-root after retries");return}if(a)return;const D=(l==null?void 0:l.getSnapshot())??{};a=B(E,{initialData:D,onEdit:L=>{l==null||l.setData(L)},readOnly:o,theme:i,rootName:c})};m()},v=()=>{a&&(a.destroy(),a=null),s==null||s.hide(),r==null||r.setPanelVisible(!1)},T=()=>{s!=null&&s.isVisible()?v():y()};return r=new $({onTogglePanel:T,rootName:c}),r.mount(),u.add(()=>r==null?void 0:r.unmount()),l=z(n,{onSnapshot:m=>{a==null||a.updateData(m)}}),u.add(()=>l==null?void 0:l.destroy()),u.add(()=>{a&&(a.destroy(),a=null)}),u.add(()=>s==null?void 0:s.unmount()),{destroy:()=>{u.run()}}}exports.init=Y;
|
|
28
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/ui/templates/toolbar.eta","../src/ui/templates/panel.eta","../src/ui/template-engine.ts","../src/ui/shared-utils.ts","../src/ui/toolbar.ts","../src/ui/panel.ts","../src/state-bridge.ts","../src/ui/json-editor-mount.tsx","../src/index.ts"],"sourcesContent":["<div class=\"lsdt-toolbar-header\">\n <div class=\"lsdt-toolbar-title\">\n <span class=\"lsdt-toolbar-indicator\"></span>\n Legend State\n </div>\n <button\n class=\"lsdt-toggle-btn <%= it.panelVisible ? 'active' : '' %>\"\n data-action=\"toggle-panel\"\n title=\"<%= it.panelVisible ? 'Hide panel' : 'Show panel' %>\"\n >\n <%= it.panelVisible ? 'Hide' : 'Show' %> <%= it.rootName %>\n </button>\n</div>\n","<div class=\"lsdt-panel-header\">\n <h3><%= it.rootName %></h3>\n <div class=\"lsdt-panel-actions\">\n <% if (it.readOnly) { %>\n <span class=\"lsdt-readonly-badge\">Read-only</span>\n <% } %>\n <button class=\"lsdt-close-btn\" data-action=\"close-panel\" title=\"Close\">×</button>\n </div>\n</div>\n<div class=\"lsdt-panel-content\">\n <div id=\"lsdt-json-editor-root\"></div>\n</div>\n","/**\n * Eta template engine wrapper for Legend State Dev Tools\n */\nimport { Eta } from 'eta';\n\nimport toolbarTemplate from './templates/toolbar.eta';\nimport panelTemplate from './templates/panel.eta';\n\nconst eta = new Eta({\n autoEscape: true,\n autoTrim: false,\n});\n\nconst templates: Record<string, string> = {\n toolbar: toolbarTemplate,\n panel: panelTemplate,\n};\n\nexport function renderTemplate<T extends Record<string, unknown>>(\n name: string,\n data: T\n): string {\n const template = templates[name];\n if (!template) {\n console.error(`[LSDT] Template not found: ${name}`);\n return '';\n }\n try {\n return eta.renderString(template, data);\n } catch (error) {\n console.error(`[LSDT] Error rendering template ${name}:`, error);\n return '';\n }\n}\n\nexport interface ToolbarData {\n [key: string]: unknown;\n isMinimized: boolean;\n panelVisible: boolean;\n rootName: string;\n}\n\nexport function renderToolbar(data: ToolbarData): string {\n return renderTemplate('toolbar', data);\n}\n\nexport interface PanelData {\n [key: string]: unknown;\n rootName: string;\n readOnly: boolean;\n}\n\nexport function renderPanel(data: PanelData): string {\n return renderTemplate('panel', data);\n}\n","/**\n * Shared UI utilities for Legend State Dev Tools\n */\n\nconst STORAGE_PREFIX = 'lsdt';\n\nexport function escapeHtml(str: string): string {\n const div = document.createElement('div');\n div.textContent = str;\n return div.innerHTML;\n}\n\nexport function getStoredBoolean(key: string, defaultValue: boolean): boolean {\n const stored = localStorage.getItem(`${STORAGE_PREFIX}-${key}`);\n if (stored === null) return defaultValue;\n return stored === 'true';\n}\n\nexport function setStoredBoolean(key: string, value: boolean): void {\n localStorage.setItem(`${STORAGE_PREFIX}-${key}`, String(value));\n}\n\nexport function getStoredString(key: string, defaultValue: string): string {\n return localStorage.getItem(`${STORAGE_PREFIX}-${key}`) || defaultValue;\n}\n\nexport function setStoredString(key: string, value: string): void {\n localStorage.setItem(`${STORAGE_PREFIX}-${key}`, value);\n}\n\nexport function createCleanup(): {\n add: (fn: () => void) => void;\n run: () => void;\n} {\n const cleanupFns: (() => void)[] = [];\n return {\n add: (fn: () => void) => cleanupFns.push(fn),\n run: () => {\n cleanupFns.forEach((fn) => {\n try {\n fn();\n } catch (e) {\n console.error('[Legend State DevTools] Cleanup error:', e);\n }\n });\n cleanupFns.length = 0;\n },\n };\n}\n","import { renderToolbar, type ToolbarData } from './template-engine';\nimport { getStoredBoolean, setStoredBoolean } from './shared-utils';\n\nexport class Toolbar {\n private container: HTMLElement | null = null;\n private isDragging = false;\n private offsetX = 0;\n private offsetY = 0;\n private isMinimized: boolean = getStoredBoolean('toolbar-minimized', false);\n private panelVisible = false;\n\n private onTogglePanel?: () => void;\n private rootName: string;\n\n constructor(options: { onTogglePanel?: () => void; rootName?: string } = {}) {\n this.onTogglePanel = options.onTogglePanel;\n this.rootName = options.rootName || 'state$';\n }\n\n public mount(): void {\n if (this.container) return;\n\n this.container = document.createElement('div');\n this.container.id = 'lsdt-toolbar';\n if (this.isMinimized) {\n this.container.classList.add('lsdt-toolbar-minimized');\n }\n\n document.body.appendChild(this.container);\n this.render();\n this.attachEventListeners();\n }\n\n private render(): void {\n if (!this.container) return;\n\n const data: ToolbarData = {\n isMinimized: this.isMinimized,\n panelVisible: this.panelVisible,\n rootName: this.rootName,\n };\n\n this.container.innerHTML = renderToolbar(data);\n }\n\n private attachEventListeners(): void {\n if (!this.container) return;\n\n this.container.addEventListener('click', this.handleClick);\n this.container.addEventListener('mousedown', this.handleMouseDown);\n document.addEventListener('mousemove', this.handleMouseMove);\n document.addEventListener('mouseup', this.handleMouseUp);\n }\n\n private handleClick = (e: Event): void => {\n const target = e.target as HTMLElement;\n const actionElement = target.closest('[data-action]');\n if (!actionElement) return;\n\n const action = actionElement.getAttribute('data-action');\n if (action === 'toggle-panel') {\n this.onTogglePanel?.();\n }\n };\n\n private handleMouseDown = (e: MouseEvent): void => {\n const target = e.target as HTMLElement;\n if (target.tagName === 'BUTTON') return;\n\n this.isDragging = true;\n if (this.container) {\n this.container.classList.add('dragging');\n const rect = this.container.getBoundingClientRect();\n this.offsetX = e.clientX - rect.left;\n this.offsetY = e.clientY - rect.top;\n }\n };\n\n private handleMouseMove = (e: MouseEvent): void => {\n if (!this.isDragging || !this.container) return;\n\n this.container.style.left = `${e.clientX - this.offsetX}px`;\n this.container.style.top = `${e.clientY - this.offsetY}px`;\n this.container.style.right = 'auto';\n this.container.style.bottom = 'auto';\n };\n\n private handleMouseUp = (): void => {\n this.isDragging = false;\n this.container?.classList.remove('dragging');\n };\n\n public setPanelVisible(visible: boolean): void {\n this.panelVisible = visible;\n this.render();\n }\n\n public unmount(): void {\n if (this.container) {\n this.container.removeEventListener('click', this.handleClick);\n this.container.removeEventListener('mousedown', this.handleMouseDown);\n this.container.remove();\n this.container = null;\n }\n document.removeEventListener('mousemove', this.handleMouseMove);\n document.removeEventListener('mouseup', this.handleMouseUp);\n }\n}\n","import { renderPanel, type PanelData } from './template-engine';\n\nexport class Panel {\n private container: HTMLElement | null = null;\n private visible = false;\n private rootName: string;\n private readOnly: boolean;\n private onClose?: () => void;\n private position: 'left' | 'right';\n\n constructor(options: {\n rootName?: string;\n readOnly?: boolean;\n onClose?: () => void;\n position?: 'left' | 'right';\n } = {}) {\n this.rootName = options.rootName || 'state$';\n this.readOnly = options.readOnly || false;\n this.onClose = options.onClose;\n this.position = options.position || 'right';\n }\n\n public toggle(): void {\n if (this.visible) {\n this.hide();\n } else {\n this.show();\n }\n }\n\n public show(): void {\n this.visible = true;\n\n if (!this.container) {\n this.container = document.createElement('div');\n this.container.id = 'lsdt-panel';\n if (this.position === 'left') {\n this.container.classList.add('lsdt-panel-left');\n }\n document.body.appendChild(this.container);\n this.attachEventListeners();\n }\n\n this.render();\n }\n\n public hide(): void {\n this.visible = false;\n this.container?.remove();\n this.container = null;\n }\n\n public isVisible(): boolean {\n return this.visible;\n }\n\n public getEditorRoot(): HTMLElement | null {\n return this.container?.querySelector('#lsdt-json-editor-root') || null;\n }\n\n private render(): void {\n if (!this.container) return;\n\n const data: PanelData = {\n rootName: this.rootName,\n readOnly: this.readOnly,\n };\n\n this.container.innerHTML = renderPanel(data);\n }\n\n private attachEventListeners(): void {\n if (!this.container) return;\n this.container.addEventListener('click', this.handleClick);\n }\n\n private handleClick = (e: Event): void => {\n const target = e.target as HTMLElement;\n const actionElement = target.closest('[data-action]');\n if (!actionElement) return;\n\n const action = actionElement.getAttribute('data-action');\n if (action === 'close-panel') {\n this.onClose?.();\n }\n };\n\n public unmount(): void {\n if (this.container) {\n this.container.removeEventListener('click', this.handleClick);\n this.container.remove();\n this.container = null;\n }\n this.visible = false;\n }\n}\n","import type { ObservableParam } from '@legendapp/state';\n\nexport interface StateBridgeOptions {\n onSnapshot: (snapshot: unknown) => void;\n}\n\nexport interface StateBridge {\n getSnapshot: () => unknown;\n setData: (newData: unknown) => void;\n destroy: () => void;\n}\n\nexport function createStateBridge(\n observable$: ObservableParam<any>,\n options: StateBridgeOptions\n): StateBridge {\n // Get initial snapshot\n const getSnapshot = () => {\n try {\n return JSON.parse(JSON.stringify((observable$ as any).peek()));\n } catch {\n return undefined;\n }\n };\n\n // Subscribe to changes using onChange\n let dispose: (() => void) | null = null;\n try {\n dispose = (observable$ as any).onChange(\n () => {\n const snapshot = getSnapshot();\n options.onSnapshot(snapshot);\n },\n { trackingType: false }\n );\n } catch {\n console.warn('[Legend State DevTools] Could not subscribe to observable changes via onChange');\n }\n\n return {\n getSnapshot,\n setData: (newData: unknown) => {\n try {\n (observable$ as any).set(newData);\n } catch (e) {\n console.error('[Legend State DevTools] Failed to set data:', e);\n }\n },\n destroy: () => {\n if (dispose) {\n dispose();\n dispose = null;\n }\n },\n };\n}\n","import React, { Component, useEffect, useState, type ErrorInfo, type ReactNode } from 'react';\nimport { createRoot, type Root } from 'react-dom/client';\nimport { JsonEditor, githubDarkTheme, githubLightTheme, monoDarkTheme, monoLightTheme } from 'json-edit-react';\n\nconst themeMap: Record<string, object> = {\n githubDark: githubDarkTheme,\n githubLight: githubLightTheme,\n monoDark: monoDarkTheme,\n monoLight: monoLightTheme,\n};\n\nclass ErrorBoundary extends Component<\n { children: ReactNode },\n { error: Error | null }\n> {\n state = { error: null as Error | null };\n static getDerivedStateFromError(error: Error) {\n return { error };\n }\n componentDidCatch(error: Error, info: ErrorInfo) {\n console.error('[Legend State DevTools] React error:', error, info);\n }\n render() {\n if (this.state.error) {\n return React.createElement(\n 'pre',\n { style: { color: '#ff6b6b', padding: 16, fontSize: 12 } },\n `DevTools Error: ${this.state.error.message}\\n${this.state.error.stack}`\n );\n }\n return this.props.children;\n }\n}\n\ninterface JsonEditorWrapperProps {\n data: unknown;\n onEdit: (newData: unknown) => void;\n readOnly: boolean;\n theme: string;\n rootName: string;\n}\n\nfunction JsonEditorWrapper({\n data,\n onEdit,\n readOnly,\n theme,\n rootName,\n}: JsonEditorWrapperProps) {\n const resolvedTheme = themeMap[theme] ?? githubDarkTheme;\n return (\n <JsonEditor\n data={data as Record<string, unknown>}\n setData={onEdit as any}\n rootName={rootName}\n theme={resolvedTheme as any}\n collapse={2}\n restrictEdit={readOnly}\n restrictDelete={readOnly}\n restrictAdd={readOnly}\n restrictTypeSelection={readOnly ? true : undefined}\n />\n );\n}\n\nexport interface JsonEditorBridge {\n updateData: (data: unknown) => void;\n destroy: () => void;\n}\n\n// Wrapper component that receives data via a callback registration\nfunction JsonEditorBridgeWrapper(props: {\n initialData: unknown;\n onEdit: (newData: unknown) => void;\n readOnly: boolean;\n theme: string;\n rootName: string;\n registerUpdater: (updater: (data: unknown) => void) => void;\n}) {\n const [data, setData] = useState<unknown>(props.initialData);\n\n useEffect(() => {\n props.registerUpdater((newData: unknown) => {\n setData(newData);\n });\n }, []);\n\n const handleEdit = (newData: unknown) => {\n setData(newData);\n props.onEdit(newData);\n };\n\n return (\n <JsonEditorWrapper\n data={data}\n onEdit={handleEdit}\n readOnly={props.readOnly}\n theme={props.theme}\n rootName={props.rootName}\n />\n );\n}\n\nexport function mountJsonEditor(\n container: HTMLElement,\n options: {\n initialData: unknown;\n onEdit: (newData: unknown) => void;\n readOnly: boolean;\n theme: string;\n rootName: string;\n }\n): JsonEditorBridge {\n let root: Root | null = null;\n let updaterFn: ((data: unknown) => void) | null = null;\n\n root = createRoot(container);\n root.render(\n <ErrorBoundary>\n <JsonEditorBridgeWrapper\n initialData={options.initialData}\n onEdit={options.onEdit}\n readOnly={options.readOnly}\n theme={options.theme}\n rootName={options.rootName}\n registerUpdater={(updater) => {\n updaterFn = updater;\n }}\n />\n </ErrorBoundary>\n );\n\n return {\n updateData: (data: unknown) => {\n updaterFn?.(data);\n },\n destroy: () => {\n if (root) {\n root.unmount();\n root = null;\n }\n },\n };\n}\n","import type { ObservableParam } from '@legendapp/state';\nimport { Toolbar } from './ui/toolbar';\nimport { Panel } from './ui/panel';\nimport { createStateBridge, type StateBridge } from './state-bridge';\nimport { mountJsonEditor, type JsonEditorBridge } from './ui/json-editor-mount';\nimport { createCleanup } from './ui/shared-utils';\n\nexport interface DevToolsOptions {\n enabled?: boolean;\n readOnly?: boolean;\n theme?: string;\n rootName?: string;\n position?: 'left' | 'right';\n}\n\nexport interface DevTools {\n destroy: () => void;\n}\n\nexport function init(\n observable$: ObservableParam<any>,\n options: DevToolsOptions = {}\n): DevTools {\n const {\n enabled = true,\n readOnly = false,\n theme = 'githubDark',\n rootName = 'state$',\n position = 'right',\n } = options;\n\n if (!enabled) {\n return { destroy: () => {} };\n }\n\n const cleanup = createCleanup();\n let panel: Panel | null = null;\n let toolbar: Toolbar | null = null;\n let bridge: StateBridge | null = null;\n let editorBridge: JsonEditorBridge | null = null;\n\n // Create panel\n panel = new Panel({\n rootName,\n readOnly,\n position,\n onClose: () => {\n hidePanel();\n },\n });\n\n const showPanel = () => {\n if (!panel) return;\n panel.show();\n toolbar?.setPanelVisible(true);\n\n // Poll for editor root element (innerHTML may not be ready immediately)\n const tryMount = (retries = 10) => {\n const editorRoot = panel?.getEditorRoot();\n if (!editorRoot) {\n if (retries > 0) {\n setTimeout(() => tryMount(retries - 1), 16);\n } else {\n console.warn('[Legend State DevTools] Could not find #lsdt-json-editor-root after retries');\n }\n return;\n }\n if (editorBridge) return;\n\n const initialData = bridge?.getSnapshot() ?? {};\n\n editorBridge = mountJsonEditor(editorRoot, {\n initialData,\n onEdit: (newData: unknown) => {\n bridge?.setData(newData);\n },\n readOnly,\n theme,\n rootName,\n });\n };\n tryMount();\n };\n\n const hidePanel = () => {\n if (editorBridge) {\n editorBridge.destroy();\n editorBridge = null;\n }\n panel?.hide();\n toolbar?.setPanelVisible(false);\n };\n\n const togglePanel = () => {\n if (panel?.isVisible()) {\n hidePanel();\n } else {\n showPanel();\n }\n };\n\n // Create toolbar\n toolbar = new Toolbar({\n onTogglePanel: togglePanel,\n rootName,\n });\n toolbar.mount();\n cleanup.add(() => toolbar?.unmount());\n\n // Create state bridge\n bridge = createStateBridge(observable$, {\n onSnapshot: (snapshot) => {\n editorBridge?.updateData(snapshot);\n },\n });\n cleanup.add(() => bridge?.destroy());\n cleanup.add(() => {\n if (editorBridge) {\n editorBridge.destroy();\n editorBridge = null;\n }\n });\n cleanup.add(() => panel?.unmount());\n\n return {\n destroy: () => {\n cleanup.run();\n },\n };\n}\n"],"names":["toolbarTemplate","panelTemplate","eta","Eta","templates","renderTemplate","name","data","template","error","renderToolbar","renderPanel","STORAGE_PREFIX","getStoredBoolean","key","defaultValue","stored","createCleanup","cleanupFns","fn","Toolbar","options","actionElement","_a","rect","visible","Panel","createStateBridge","observable$","getSnapshot","dispose","snapshot","newData","e","themeMap","githubDarkTheme","githubLightTheme","monoDarkTheme","monoLightTheme","ErrorBoundary","Component","info","React","JsonEditorWrapper","onEdit","readOnly","theme","rootName","resolvedTheme","jsx","JsonEditor","JsonEditorBridgeWrapper","props","setData","useState","useEffect","handleEdit","mountJsonEditor","container","root","updaterFn","createRoot","updater","init","enabled","position","cleanup","panel","toolbar","bridge","editorBridge","hidePanel","showPanel","tryMount","retries","editorRoot","initialData","togglePanel"],"mappings":"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"}
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import { Eta as T } from "eta";
|
|
2
|
+
import { jsx as m } from "react/jsx-runtime";
|
|
3
|
+
import S, { Component as C, useState as M, useEffect as N } from "react";
|
|
4
|
+
import { createRoot as k } from "react-dom/client";
|
|
5
|
+
import { JsonEditor as w, monoLightTheme as P, monoDarkTheme as V, githubLightTheme as O, githubDarkTheme as v } from "json-edit-react";
|
|
6
|
+
const R = `<div class="lsdt-toolbar-header">
|
|
7
|
+
<div class="lsdt-toolbar-title">
|
|
8
|
+
<span class="lsdt-toolbar-indicator"></span>
|
|
9
|
+
Legend State
|
|
10
|
+
</div>
|
|
11
|
+
<button
|
|
12
|
+
class="lsdt-toggle-btn <%= it.panelVisible ? 'active' : '' %>"
|
|
13
|
+
data-action="toggle-panel"
|
|
14
|
+
title="<%= it.panelVisible ? 'Hide panel' : 'Show panel' %>"
|
|
15
|
+
>
|
|
16
|
+
<%= it.panelVisible ? 'Hide' : 'Show' %> <%= it.rootName %>
|
|
17
|
+
</button>
|
|
18
|
+
</div>
|
|
19
|
+
`, $ = `<div class="lsdt-panel-header">
|
|
20
|
+
<h3><%= it.rootName %></h3>
|
|
21
|
+
<div class="lsdt-panel-actions">
|
|
22
|
+
<% if (it.readOnly) { %>
|
|
23
|
+
<span class="lsdt-readonly-badge">Read-only</span>
|
|
24
|
+
<% } %>
|
|
25
|
+
<button class="lsdt-close-btn" data-action="close-panel" title="Close">×</button>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
<div class="lsdt-panel-content">
|
|
29
|
+
<div id="lsdt-json-editor-root"></div>
|
|
30
|
+
</div>
|
|
31
|
+
`, z = new T({
|
|
32
|
+
autoEscape: !0,
|
|
33
|
+
autoTrim: !1
|
|
34
|
+
}), J = {
|
|
35
|
+
toolbar: R,
|
|
36
|
+
panel: $
|
|
37
|
+
};
|
|
38
|
+
function E(n, t) {
|
|
39
|
+
const e = J[n];
|
|
40
|
+
if (!e)
|
|
41
|
+
return console.error(`[LSDT] Template not found: ${n}`), "";
|
|
42
|
+
try {
|
|
43
|
+
return z.renderString(e, t);
|
|
44
|
+
} catch (o) {
|
|
45
|
+
return console.error(`[LSDT] Error rendering template ${n}:`, o), "";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function U(n) {
|
|
49
|
+
return E("toolbar", n);
|
|
50
|
+
}
|
|
51
|
+
function X(n) {
|
|
52
|
+
return E("panel", n);
|
|
53
|
+
}
|
|
54
|
+
const x = "lsdt";
|
|
55
|
+
function B(n, t) {
|
|
56
|
+
const e = localStorage.getItem(`${x}-${n}`);
|
|
57
|
+
return e === null ? t : e === "true";
|
|
58
|
+
}
|
|
59
|
+
function Y() {
|
|
60
|
+
const n = [];
|
|
61
|
+
return {
|
|
62
|
+
add: (t) => n.push(t),
|
|
63
|
+
run: () => {
|
|
64
|
+
n.forEach((t) => {
|
|
65
|
+
try {
|
|
66
|
+
t();
|
|
67
|
+
} catch (e) {
|
|
68
|
+
console.error("[Legend State DevTools] Cleanup error:", e);
|
|
69
|
+
}
|
|
70
|
+
}), n.length = 0;
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
class j {
|
|
75
|
+
constructor(t = {}) {
|
|
76
|
+
this.container = null, this.isDragging = !1, this.offsetX = 0, this.offsetY = 0, this.isMinimized = B("toolbar-minimized", !1), this.panelVisible = !1, this.handleClick = (e) => {
|
|
77
|
+
var d;
|
|
78
|
+
const i = e.target.closest("[data-action]");
|
|
79
|
+
if (!i) return;
|
|
80
|
+
i.getAttribute("data-action") === "toggle-panel" && ((d = this.onTogglePanel) == null || d.call(this));
|
|
81
|
+
}, this.handleMouseDown = (e) => {
|
|
82
|
+
if (e.target.tagName !== "BUTTON" && (this.isDragging = !0, this.container)) {
|
|
83
|
+
this.container.classList.add("dragging");
|
|
84
|
+
const i = this.container.getBoundingClientRect();
|
|
85
|
+
this.offsetX = e.clientX - i.left, this.offsetY = e.clientY - i.top;
|
|
86
|
+
}
|
|
87
|
+
}, this.handleMouseMove = (e) => {
|
|
88
|
+
!this.isDragging || !this.container || (this.container.style.left = `${e.clientX - this.offsetX}px`, this.container.style.top = `${e.clientY - this.offsetY}px`, this.container.style.right = "auto", this.container.style.bottom = "auto");
|
|
89
|
+
}, this.handleMouseUp = () => {
|
|
90
|
+
var e;
|
|
91
|
+
this.isDragging = !1, (e = this.container) == null || e.classList.remove("dragging");
|
|
92
|
+
}, this.onTogglePanel = t.onTogglePanel, this.rootName = t.rootName || "state$";
|
|
93
|
+
}
|
|
94
|
+
mount() {
|
|
95
|
+
this.container || (this.container = document.createElement("div"), this.container.id = "lsdt-toolbar", this.isMinimized && this.container.classList.add("lsdt-toolbar-minimized"), document.body.appendChild(this.container), this.render(), this.attachEventListeners());
|
|
96
|
+
}
|
|
97
|
+
render() {
|
|
98
|
+
if (!this.container) return;
|
|
99
|
+
const t = {
|
|
100
|
+
isMinimized: this.isMinimized,
|
|
101
|
+
panelVisible: this.panelVisible,
|
|
102
|
+
rootName: this.rootName
|
|
103
|
+
};
|
|
104
|
+
this.container.innerHTML = U(t);
|
|
105
|
+
}
|
|
106
|
+
attachEventListeners() {
|
|
107
|
+
this.container && (this.container.addEventListener("click", this.handleClick), this.container.addEventListener("mousedown", this.handleMouseDown), document.addEventListener("mousemove", this.handleMouseMove), document.addEventListener("mouseup", this.handleMouseUp));
|
|
108
|
+
}
|
|
109
|
+
setPanelVisible(t) {
|
|
110
|
+
this.panelVisible = t, this.render();
|
|
111
|
+
}
|
|
112
|
+
unmount() {
|
|
113
|
+
this.container && (this.container.removeEventListener("click", this.handleClick), this.container.removeEventListener("mousedown", this.handleMouseDown), this.container.remove(), this.container = null), document.removeEventListener("mousemove", this.handleMouseMove), document.removeEventListener("mouseup", this.handleMouseUp);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
class A {
|
|
117
|
+
constructor(t = {}) {
|
|
118
|
+
this.container = null, this.visible = !1, this.handleClick = (e) => {
|
|
119
|
+
var d;
|
|
120
|
+
const i = e.target.closest("[data-action]");
|
|
121
|
+
if (!i) return;
|
|
122
|
+
i.getAttribute("data-action") === "close-panel" && ((d = this.onClose) == null || d.call(this));
|
|
123
|
+
}, this.rootName = t.rootName || "state$", this.readOnly = t.readOnly || !1, this.onClose = t.onClose, this.position = t.position || "right";
|
|
124
|
+
}
|
|
125
|
+
toggle() {
|
|
126
|
+
this.visible ? this.hide() : this.show();
|
|
127
|
+
}
|
|
128
|
+
show() {
|
|
129
|
+
this.visible = !0, this.container || (this.container = document.createElement("div"), this.container.id = "lsdt-panel", this.position === "left" && this.container.classList.add("lsdt-panel-left"), document.body.appendChild(this.container), this.attachEventListeners()), this.render();
|
|
130
|
+
}
|
|
131
|
+
hide() {
|
|
132
|
+
var t;
|
|
133
|
+
this.visible = !1, (t = this.container) == null || t.remove(), this.container = null;
|
|
134
|
+
}
|
|
135
|
+
isVisible() {
|
|
136
|
+
return this.visible;
|
|
137
|
+
}
|
|
138
|
+
getEditorRoot() {
|
|
139
|
+
var t;
|
|
140
|
+
return ((t = this.container) == null ? void 0 : t.querySelector("#lsdt-json-editor-root")) || null;
|
|
141
|
+
}
|
|
142
|
+
render() {
|
|
143
|
+
if (!this.container) return;
|
|
144
|
+
const t = {
|
|
145
|
+
rootName: this.rootName,
|
|
146
|
+
readOnly: this.readOnly
|
|
147
|
+
};
|
|
148
|
+
this.container.innerHTML = X(t);
|
|
149
|
+
}
|
|
150
|
+
attachEventListeners() {
|
|
151
|
+
this.container && this.container.addEventListener("click", this.handleClick);
|
|
152
|
+
}
|
|
153
|
+
unmount() {
|
|
154
|
+
this.container && (this.container.removeEventListener("click", this.handleClick), this.container.remove(), this.container = null), this.visible = !1;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function H(n, t) {
|
|
158
|
+
const e = () => {
|
|
159
|
+
try {
|
|
160
|
+
return JSON.parse(JSON.stringify(n.peek()));
|
|
161
|
+
} catch {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
let o = null;
|
|
166
|
+
try {
|
|
167
|
+
o = n.onChange(
|
|
168
|
+
() => {
|
|
169
|
+
const i = e();
|
|
170
|
+
t.onSnapshot(i);
|
|
171
|
+
},
|
|
172
|
+
{ trackingType: !1 }
|
|
173
|
+
);
|
|
174
|
+
} catch {
|
|
175
|
+
console.warn("[Legend State DevTools] Could not subscribe to observable changes via onChange");
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
getSnapshot: e,
|
|
179
|
+
setData: (i) => {
|
|
180
|
+
try {
|
|
181
|
+
n.set(i);
|
|
182
|
+
} catch (c) {
|
|
183
|
+
console.error("[Legend State DevTools] Failed to set data:", c);
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
destroy: () => {
|
|
187
|
+
o && (o(), o = null);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
const F = {
|
|
192
|
+
githubDark: v,
|
|
193
|
+
githubLight: O,
|
|
194
|
+
monoDark: V,
|
|
195
|
+
monoLight: P
|
|
196
|
+
};
|
|
197
|
+
class I extends C {
|
|
198
|
+
constructor() {
|
|
199
|
+
super(...arguments), this.state = { error: null };
|
|
200
|
+
}
|
|
201
|
+
static getDerivedStateFromError(t) {
|
|
202
|
+
return { error: t };
|
|
203
|
+
}
|
|
204
|
+
componentDidCatch(t, e) {
|
|
205
|
+
console.error("[Legend State DevTools] React error:", t, e);
|
|
206
|
+
}
|
|
207
|
+
render() {
|
|
208
|
+
return this.state.error ? S.createElement(
|
|
209
|
+
"pre",
|
|
210
|
+
{ style: { color: "#ff6b6b", padding: 16, fontSize: 12 } },
|
|
211
|
+
`DevTools Error: ${this.state.error.message}
|
|
212
|
+
${this.state.error.stack}`
|
|
213
|
+
) : this.props.children;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function W({
|
|
217
|
+
data: n,
|
|
218
|
+
onEdit: t,
|
|
219
|
+
readOnly: e,
|
|
220
|
+
theme: o,
|
|
221
|
+
rootName: i
|
|
222
|
+
}) {
|
|
223
|
+
const c = F[o] ?? v;
|
|
224
|
+
return /* @__PURE__ */ m(
|
|
225
|
+
w,
|
|
226
|
+
{
|
|
227
|
+
data: n,
|
|
228
|
+
setData: t,
|
|
229
|
+
rootName: i,
|
|
230
|
+
theme: c,
|
|
231
|
+
collapse: 2,
|
|
232
|
+
restrictEdit: e,
|
|
233
|
+
restrictDelete: e,
|
|
234
|
+
restrictAdd: e,
|
|
235
|
+
restrictTypeSelection: e ? !0 : void 0
|
|
236
|
+
}
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
function q(n) {
|
|
240
|
+
const [t, e] = M(n.initialData);
|
|
241
|
+
return N(() => {
|
|
242
|
+
n.registerUpdater((i) => {
|
|
243
|
+
e(i);
|
|
244
|
+
});
|
|
245
|
+
}, []), /* @__PURE__ */ m(
|
|
246
|
+
W,
|
|
247
|
+
{
|
|
248
|
+
data: t,
|
|
249
|
+
onEdit: (i) => {
|
|
250
|
+
e(i), n.onEdit(i);
|
|
251
|
+
},
|
|
252
|
+
readOnly: n.readOnly,
|
|
253
|
+
theme: n.theme,
|
|
254
|
+
rootName: n.rootName
|
|
255
|
+
}
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
function G(n, t) {
|
|
259
|
+
let e = null, o = null;
|
|
260
|
+
return e = k(n), e.render(
|
|
261
|
+
/* @__PURE__ */ m(I, { children: /* @__PURE__ */ m(
|
|
262
|
+
q,
|
|
263
|
+
{
|
|
264
|
+
initialData: t.initialData,
|
|
265
|
+
onEdit: t.onEdit,
|
|
266
|
+
readOnly: t.readOnly,
|
|
267
|
+
theme: t.theme,
|
|
268
|
+
rootName: t.rootName,
|
|
269
|
+
registerUpdater: (i) => {
|
|
270
|
+
o = i;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
) })
|
|
274
|
+
), {
|
|
275
|
+
updateData: (i) => {
|
|
276
|
+
o == null || o(i);
|
|
277
|
+
},
|
|
278
|
+
destroy: () => {
|
|
279
|
+
e && (e.unmount(), e = null);
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
function et(n, t = {}) {
|
|
284
|
+
const {
|
|
285
|
+
enabled: e = !0,
|
|
286
|
+
readOnly: o = !1,
|
|
287
|
+
theme: i = "githubDark",
|
|
288
|
+
rootName: c = "state$",
|
|
289
|
+
position: d = "right"
|
|
290
|
+
} = t;
|
|
291
|
+
if (!e)
|
|
292
|
+
return { destroy: () => {
|
|
293
|
+
} };
|
|
294
|
+
const h = Y();
|
|
295
|
+
let s = null, r = null, l = null, a = null;
|
|
296
|
+
s = new A({
|
|
297
|
+
rootName: c,
|
|
298
|
+
readOnly: o,
|
|
299
|
+
position: d,
|
|
300
|
+
onClose: () => {
|
|
301
|
+
g();
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
const b = () => {
|
|
305
|
+
if (!s) return;
|
|
306
|
+
s.show(), r == null || r.setPanelVisible(!0);
|
|
307
|
+
const u = (f = 10) => {
|
|
308
|
+
const p = s == null ? void 0 : s.getEditorRoot();
|
|
309
|
+
if (!p) {
|
|
310
|
+
f > 0 ? setTimeout(() => u(f - 1), 16) : console.warn("[Legend State DevTools] Could not find #lsdt-json-editor-root after retries");
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
if (a) return;
|
|
314
|
+
const D = (l == null ? void 0 : l.getSnapshot()) ?? {};
|
|
315
|
+
a = G(p, {
|
|
316
|
+
initialData: D,
|
|
317
|
+
onEdit: (L) => {
|
|
318
|
+
l == null || l.setData(L);
|
|
319
|
+
},
|
|
320
|
+
readOnly: o,
|
|
321
|
+
theme: i,
|
|
322
|
+
rootName: c
|
|
323
|
+
});
|
|
324
|
+
};
|
|
325
|
+
u();
|
|
326
|
+
}, g = () => {
|
|
327
|
+
a && (a.destroy(), a = null), s == null || s.hide(), r == null || r.setPanelVisible(!1);
|
|
328
|
+
}, y = () => {
|
|
329
|
+
s != null && s.isVisible() ? g() : b();
|
|
330
|
+
};
|
|
331
|
+
return r = new j({
|
|
332
|
+
onTogglePanel: y,
|
|
333
|
+
rootName: c
|
|
334
|
+
}), r.mount(), h.add(() => r == null ? void 0 : r.unmount()), l = H(n, {
|
|
335
|
+
onSnapshot: (u) => {
|
|
336
|
+
a == null || a.updateData(u);
|
|
337
|
+
}
|
|
338
|
+
}), h.add(() => l == null ? void 0 : l.destroy()), h.add(() => {
|
|
339
|
+
a && (a.destroy(), a = null);
|
|
340
|
+
}), h.add(() => s == null ? void 0 : s.unmount()), {
|
|
341
|
+
destroy: () => {
|
|
342
|
+
h.run();
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
export {
|
|
347
|
+
et as init
|
|
348
|
+
};
|
|
349
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../src/ui/templates/toolbar.eta","../src/ui/templates/panel.eta","../src/ui/template-engine.ts","../src/ui/shared-utils.ts","../src/ui/toolbar.ts","../src/ui/panel.ts","../src/state-bridge.ts","../src/ui/json-editor-mount.tsx","../src/index.ts"],"sourcesContent":["<div class=\"lsdt-toolbar-header\">\n <div class=\"lsdt-toolbar-title\">\n <span class=\"lsdt-toolbar-indicator\"></span>\n Legend State\n </div>\n <button\n class=\"lsdt-toggle-btn <%= it.panelVisible ? 'active' : '' %>\"\n data-action=\"toggle-panel\"\n title=\"<%= it.panelVisible ? 'Hide panel' : 'Show panel' %>\"\n >\n <%= it.panelVisible ? 'Hide' : 'Show' %> <%= it.rootName %>\n </button>\n</div>\n","<div class=\"lsdt-panel-header\">\n <h3><%= it.rootName %></h3>\n <div class=\"lsdt-panel-actions\">\n <% if (it.readOnly) { %>\n <span class=\"lsdt-readonly-badge\">Read-only</span>\n <% } %>\n <button class=\"lsdt-close-btn\" data-action=\"close-panel\" title=\"Close\">×</button>\n </div>\n</div>\n<div class=\"lsdt-panel-content\">\n <div id=\"lsdt-json-editor-root\"></div>\n</div>\n","/**\n * Eta template engine wrapper for Legend State Dev Tools\n */\nimport { Eta } from 'eta';\n\nimport toolbarTemplate from './templates/toolbar.eta';\nimport panelTemplate from './templates/panel.eta';\n\nconst eta = new Eta({\n autoEscape: true,\n autoTrim: false,\n});\n\nconst templates: Record<string, string> = {\n toolbar: toolbarTemplate,\n panel: panelTemplate,\n};\n\nexport function renderTemplate<T extends Record<string, unknown>>(\n name: string,\n data: T\n): string {\n const template = templates[name];\n if (!template) {\n console.error(`[LSDT] Template not found: ${name}`);\n return '';\n }\n try {\n return eta.renderString(template, data);\n } catch (error) {\n console.error(`[LSDT] Error rendering template ${name}:`, error);\n return '';\n }\n}\n\nexport interface ToolbarData {\n [key: string]: unknown;\n isMinimized: boolean;\n panelVisible: boolean;\n rootName: string;\n}\n\nexport function renderToolbar(data: ToolbarData): string {\n return renderTemplate('toolbar', data);\n}\n\nexport interface PanelData {\n [key: string]: unknown;\n rootName: string;\n readOnly: boolean;\n}\n\nexport function renderPanel(data: PanelData): string {\n return renderTemplate('panel', data);\n}\n","/**\n * Shared UI utilities for Legend State Dev Tools\n */\n\nconst STORAGE_PREFIX = 'lsdt';\n\nexport function escapeHtml(str: string): string {\n const div = document.createElement('div');\n div.textContent = str;\n return div.innerHTML;\n}\n\nexport function getStoredBoolean(key: string, defaultValue: boolean): boolean {\n const stored = localStorage.getItem(`${STORAGE_PREFIX}-${key}`);\n if (stored === null) return defaultValue;\n return stored === 'true';\n}\n\nexport function setStoredBoolean(key: string, value: boolean): void {\n localStorage.setItem(`${STORAGE_PREFIX}-${key}`, String(value));\n}\n\nexport function getStoredString(key: string, defaultValue: string): string {\n return localStorage.getItem(`${STORAGE_PREFIX}-${key}`) || defaultValue;\n}\n\nexport function setStoredString(key: string, value: string): void {\n localStorage.setItem(`${STORAGE_PREFIX}-${key}`, value);\n}\n\nexport function createCleanup(): {\n add: (fn: () => void) => void;\n run: () => void;\n} {\n const cleanupFns: (() => void)[] = [];\n return {\n add: (fn: () => void) => cleanupFns.push(fn),\n run: () => {\n cleanupFns.forEach((fn) => {\n try {\n fn();\n } catch (e) {\n console.error('[Legend State DevTools] Cleanup error:', e);\n }\n });\n cleanupFns.length = 0;\n },\n };\n}\n","import { renderToolbar, type ToolbarData } from './template-engine';\nimport { getStoredBoolean, setStoredBoolean } from './shared-utils';\n\nexport class Toolbar {\n private container: HTMLElement | null = null;\n private isDragging = false;\n private offsetX = 0;\n private offsetY = 0;\n private isMinimized: boolean = getStoredBoolean('toolbar-minimized', false);\n private panelVisible = false;\n\n private onTogglePanel?: () => void;\n private rootName: string;\n\n constructor(options: { onTogglePanel?: () => void; rootName?: string } = {}) {\n this.onTogglePanel = options.onTogglePanel;\n this.rootName = options.rootName || 'state$';\n }\n\n public mount(): void {\n if (this.container) return;\n\n this.container = document.createElement('div');\n this.container.id = 'lsdt-toolbar';\n if (this.isMinimized) {\n this.container.classList.add('lsdt-toolbar-minimized');\n }\n\n document.body.appendChild(this.container);\n this.render();\n this.attachEventListeners();\n }\n\n private render(): void {\n if (!this.container) return;\n\n const data: ToolbarData = {\n isMinimized: this.isMinimized,\n panelVisible: this.panelVisible,\n rootName: this.rootName,\n };\n\n this.container.innerHTML = renderToolbar(data);\n }\n\n private attachEventListeners(): void {\n if (!this.container) return;\n\n this.container.addEventListener('click', this.handleClick);\n this.container.addEventListener('mousedown', this.handleMouseDown);\n document.addEventListener('mousemove', this.handleMouseMove);\n document.addEventListener('mouseup', this.handleMouseUp);\n }\n\n private handleClick = (e: Event): void => {\n const target = e.target as HTMLElement;\n const actionElement = target.closest('[data-action]');\n if (!actionElement) return;\n\n const action = actionElement.getAttribute('data-action');\n if (action === 'toggle-panel') {\n this.onTogglePanel?.();\n }\n };\n\n private handleMouseDown = (e: MouseEvent): void => {\n const target = e.target as HTMLElement;\n if (target.tagName === 'BUTTON') return;\n\n this.isDragging = true;\n if (this.container) {\n this.container.classList.add('dragging');\n const rect = this.container.getBoundingClientRect();\n this.offsetX = e.clientX - rect.left;\n this.offsetY = e.clientY - rect.top;\n }\n };\n\n private handleMouseMove = (e: MouseEvent): void => {\n if (!this.isDragging || !this.container) return;\n\n this.container.style.left = `${e.clientX - this.offsetX}px`;\n this.container.style.top = `${e.clientY - this.offsetY}px`;\n this.container.style.right = 'auto';\n this.container.style.bottom = 'auto';\n };\n\n private handleMouseUp = (): void => {\n this.isDragging = false;\n this.container?.classList.remove('dragging');\n };\n\n public setPanelVisible(visible: boolean): void {\n this.panelVisible = visible;\n this.render();\n }\n\n public unmount(): void {\n if (this.container) {\n this.container.removeEventListener('click', this.handleClick);\n this.container.removeEventListener('mousedown', this.handleMouseDown);\n this.container.remove();\n this.container = null;\n }\n document.removeEventListener('mousemove', this.handleMouseMove);\n document.removeEventListener('mouseup', this.handleMouseUp);\n }\n}\n","import { renderPanel, type PanelData } from './template-engine';\n\nexport class Panel {\n private container: HTMLElement | null = null;\n private visible = false;\n private rootName: string;\n private readOnly: boolean;\n private onClose?: () => void;\n private position: 'left' | 'right';\n\n constructor(options: {\n rootName?: string;\n readOnly?: boolean;\n onClose?: () => void;\n position?: 'left' | 'right';\n } = {}) {\n this.rootName = options.rootName || 'state$';\n this.readOnly = options.readOnly || false;\n this.onClose = options.onClose;\n this.position = options.position || 'right';\n }\n\n public toggle(): void {\n if (this.visible) {\n this.hide();\n } else {\n this.show();\n }\n }\n\n public show(): void {\n this.visible = true;\n\n if (!this.container) {\n this.container = document.createElement('div');\n this.container.id = 'lsdt-panel';\n if (this.position === 'left') {\n this.container.classList.add('lsdt-panel-left');\n }\n document.body.appendChild(this.container);\n this.attachEventListeners();\n }\n\n this.render();\n }\n\n public hide(): void {\n this.visible = false;\n this.container?.remove();\n this.container = null;\n }\n\n public isVisible(): boolean {\n return this.visible;\n }\n\n public getEditorRoot(): HTMLElement | null {\n return this.container?.querySelector('#lsdt-json-editor-root') || null;\n }\n\n private render(): void {\n if (!this.container) return;\n\n const data: PanelData = {\n rootName: this.rootName,\n readOnly: this.readOnly,\n };\n\n this.container.innerHTML = renderPanel(data);\n }\n\n private attachEventListeners(): void {\n if (!this.container) return;\n this.container.addEventListener('click', this.handleClick);\n }\n\n private handleClick = (e: Event): void => {\n const target = e.target as HTMLElement;\n const actionElement = target.closest('[data-action]');\n if (!actionElement) return;\n\n const action = actionElement.getAttribute('data-action');\n if (action === 'close-panel') {\n this.onClose?.();\n }\n };\n\n public unmount(): void {\n if (this.container) {\n this.container.removeEventListener('click', this.handleClick);\n this.container.remove();\n this.container = null;\n }\n this.visible = false;\n }\n}\n","import type { ObservableParam } from '@legendapp/state';\n\nexport interface StateBridgeOptions {\n onSnapshot: (snapshot: unknown) => void;\n}\n\nexport interface StateBridge {\n getSnapshot: () => unknown;\n setData: (newData: unknown) => void;\n destroy: () => void;\n}\n\nexport function createStateBridge(\n observable$: ObservableParam<any>,\n options: StateBridgeOptions\n): StateBridge {\n // Get initial snapshot\n const getSnapshot = () => {\n try {\n return JSON.parse(JSON.stringify((observable$ as any).peek()));\n } catch {\n return undefined;\n }\n };\n\n // Subscribe to changes using onChange\n let dispose: (() => void) | null = null;\n try {\n dispose = (observable$ as any).onChange(\n () => {\n const snapshot = getSnapshot();\n options.onSnapshot(snapshot);\n },\n { trackingType: false }\n );\n } catch {\n console.warn('[Legend State DevTools] Could not subscribe to observable changes via onChange');\n }\n\n return {\n getSnapshot,\n setData: (newData: unknown) => {\n try {\n (observable$ as any).set(newData);\n } catch (e) {\n console.error('[Legend State DevTools] Failed to set data:', e);\n }\n },\n destroy: () => {\n if (dispose) {\n dispose();\n dispose = null;\n }\n },\n };\n}\n","import React, { Component, useEffect, useState, type ErrorInfo, type ReactNode } from 'react';\nimport { createRoot, type Root } from 'react-dom/client';\nimport { JsonEditor, githubDarkTheme, githubLightTheme, monoDarkTheme, monoLightTheme } from 'json-edit-react';\n\nconst themeMap: Record<string, object> = {\n githubDark: githubDarkTheme,\n githubLight: githubLightTheme,\n monoDark: monoDarkTheme,\n monoLight: monoLightTheme,\n};\n\nclass ErrorBoundary extends Component<\n { children: ReactNode },\n { error: Error | null }\n> {\n state = { error: null as Error | null };\n static getDerivedStateFromError(error: Error) {\n return { error };\n }\n componentDidCatch(error: Error, info: ErrorInfo) {\n console.error('[Legend State DevTools] React error:', error, info);\n }\n render() {\n if (this.state.error) {\n return React.createElement(\n 'pre',\n { style: { color: '#ff6b6b', padding: 16, fontSize: 12 } },\n `DevTools Error: ${this.state.error.message}\\n${this.state.error.stack}`\n );\n }\n return this.props.children;\n }\n}\n\ninterface JsonEditorWrapperProps {\n data: unknown;\n onEdit: (newData: unknown) => void;\n readOnly: boolean;\n theme: string;\n rootName: string;\n}\n\nfunction JsonEditorWrapper({\n data,\n onEdit,\n readOnly,\n theme,\n rootName,\n}: JsonEditorWrapperProps) {\n const resolvedTheme = themeMap[theme] ?? githubDarkTheme;\n return (\n <JsonEditor\n data={data as Record<string, unknown>}\n setData={onEdit as any}\n rootName={rootName}\n theme={resolvedTheme as any}\n collapse={2}\n restrictEdit={readOnly}\n restrictDelete={readOnly}\n restrictAdd={readOnly}\n restrictTypeSelection={readOnly ? true : undefined}\n />\n );\n}\n\nexport interface JsonEditorBridge {\n updateData: (data: unknown) => void;\n destroy: () => void;\n}\n\n// Wrapper component that receives data via a callback registration\nfunction JsonEditorBridgeWrapper(props: {\n initialData: unknown;\n onEdit: (newData: unknown) => void;\n readOnly: boolean;\n theme: string;\n rootName: string;\n registerUpdater: (updater: (data: unknown) => void) => void;\n}) {\n const [data, setData] = useState<unknown>(props.initialData);\n\n useEffect(() => {\n props.registerUpdater((newData: unknown) => {\n setData(newData);\n });\n }, []);\n\n const handleEdit = (newData: unknown) => {\n setData(newData);\n props.onEdit(newData);\n };\n\n return (\n <JsonEditorWrapper\n data={data}\n onEdit={handleEdit}\n readOnly={props.readOnly}\n theme={props.theme}\n rootName={props.rootName}\n />\n );\n}\n\nexport function mountJsonEditor(\n container: HTMLElement,\n options: {\n initialData: unknown;\n onEdit: (newData: unknown) => void;\n readOnly: boolean;\n theme: string;\n rootName: string;\n }\n): JsonEditorBridge {\n let root: Root | null = null;\n let updaterFn: ((data: unknown) => void) | null = null;\n\n root = createRoot(container);\n root.render(\n <ErrorBoundary>\n <JsonEditorBridgeWrapper\n initialData={options.initialData}\n onEdit={options.onEdit}\n readOnly={options.readOnly}\n theme={options.theme}\n rootName={options.rootName}\n registerUpdater={(updater) => {\n updaterFn = updater;\n }}\n />\n </ErrorBoundary>\n );\n\n return {\n updateData: (data: unknown) => {\n updaterFn?.(data);\n },\n destroy: () => {\n if (root) {\n root.unmount();\n root = null;\n }\n },\n };\n}\n","import type { ObservableParam } from '@legendapp/state';\nimport { Toolbar } from './ui/toolbar';\nimport { Panel } from './ui/panel';\nimport { createStateBridge, type StateBridge } from './state-bridge';\nimport { mountJsonEditor, type JsonEditorBridge } from './ui/json-editor-mount';\nimport { createCleanup } from './ui/shared-utils';\n\nexport interface DevToolsOptions {\n enabled?: boolean;\n readOnly?: boolean;\n theme?: string;\n rootName?: string;\n position?: 'left' | 'right';\n}\n\nexport interface DevTools {\n destroy: () => void;\n}\n\nexport function init(\n observable$: ObservableParam<any>,\n options: DevToolsOptions = {}\n): DevTools {\n const {\n enabled = true,\n readOnly = false,\n theme = 'githubDark',\n rootName = 'state$',\n position = 'right',\n } = options;\n\n if (!enabled) {\n return { destroy: () => {} };\n }\n\n const cleanup = createCleanup();\n let panel: Panel | null = null;\n let toolbar: Toolbar | null = null;\n let bridge: StateBridge | null = null;\n let editorBridge: JsonEditorBridge | null = null;\n\n // Create panel\n panel = new Panel({\n rootName,\n readOnly,\n position,\n onClose: () => {\n hidePanel();\n },\n });\n\n const showPanel = () => {\n if (!panel) return;\n panel.show();\n toolbar?.setPanelVisible(true);\n\n // Poll for editor root element (innerHTML may not be ready immediately)\n const tryMount = (retries = 10) => {\n const editorRoot = panel?.getEditorRoot();\n if (!editorRoot) {\n if (retries > 0) {\n setTimeout(() => tryMount(retries - 1), 16);\n } else {\n console.warn('[Legend State DevTools] Could not find #lsdt-json-editor-root after retries');\n }\n return;\n }\n if (editorBridge) return;\n\n const initialData = bridge?.getSnapshot() ?? {};\n\n editorBridge = mountJsonEditor(editorRoot, {\n initialData,\n onEdit: (newData: unknown) => {\n bridge?.setData(newData);\n },\n readOnly,\n theme,\n rootName,\n });\n };\n tryMount();\n };\n\n const hidePanel = () => {\n if (editorBridge) {\n editorBridge.destroy();\n editorBridge = null;\n }\n panel?.hide();\n toolbar?.setPanelVisible(false);\n };\n\n const togglePanel = () => {\n if (panel?.isVisible()) {\n hidePanel();\n } else {\n showPanel();\n }\n };\n\n // Create toolbar\n toolbar = new Toolbar({\n onTogglePanel: togglePanel,\n rootName,\n });\n toolbar.mount();\n cleanup.add(() => toolbar?.unmount());\n\n // Create state bridge\n bridge = createStateBridge(observable$, {\n onSnapshot: (snapshot) => {\n editorBridge?.updateData(snapshot);\n },\n });\n cleanup.add(() => bridge?.destroy());\n cleanup.add(() => {\n if (editorBridge) {\n editorBridge.destroy();\n editorBridge = null;\n }\n });\n cleanup.add(() => panel?.unmount());\n\n return {\n destroy: () => {\n cleanup.run();\n },\n };\n}\n"],"names":["toolbarTemplate","panelTemplate","eta","Eta","templates","renderTemplate","name","data","template","error","renderToolbar","renderPanel","STORAGE_PREFIX","getStoredBoolean","key","defaultValue","stored","createCleanup","cleanupFns","fn","Toolbar","options","actionElement","_a","rect","visible","Panel","createStateBridge","observable$","getSnapshot","dispose","snapshot","newData","e","themeMap","githubDarkTheme","githubLightTheme","monoDarkTheme","monoLightTheme","ErrorBoundary","Component","info","React","JsonEditorWrapper","onEdit","readOnly","theme","rootName","resolvedTheme","jsx","JsonEditor","JsonEditorBridgeWrapper","props","setData","useState","useEffect","mountJsonEditor","container","root","updaterFn","createRoot","updater","init","enabled","position","cleanup","panel","toolbar","bridge","editorBridge","hidePanel","showPanel","tryMount","retries","editorRoot","initialData","togglePanel"],"mappings":"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"}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<title>Legend State Dev Tools - Example</title>
|
|
7
|
-
</head>
|
|
8
|
-
<body>
|
|
9
|
-
<div id="root"></div>
|
|
10
|
-
<script type="module" src="/src/main.tsx"></script>
|
|
11
|
-
</body>
|
|
12
|
-
</html>
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "legend-state-dev-tools-example",
|
|
3
|
-
"private": true,
|
|
4
|
-
"version": "0.0.0",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"dev": "vite",
|
|
8
|
-
"build": "vite build"
|
|
9
|
-
},
|
|
10
|
-
"dependencies": {
|
|
11
|
-
"@legendapp/state": "^3.0.0-beta.43",
|
|
12
|
-
"react": "^18.3.0",
|
|
13
|
-
"react-dom": "^18.3.0",
|
|
14
|
-
"json-edit-react": "^1.16.0",
|
|
15
|
-
"legend-state-dev-tools": "*"
|
|
16
|
-
},
|
|
17
|
-
"devDependencies": {
|
|
18
|
-
"@types/react": "^18.2.0",
|
|
19
|
-
"@types/react-dom": "^18.2.0",
|
|
20
|
-
"@vitejs/plugin-react": "^4.3.0",
|
|
21
|
-
"typescript": "^5.3.0",
|
|
22
|
-
"vite": "^6.0.0"
|
|
23
|
-
}
|
|
24
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { observer } from '@legendapp/state/react';
|
|
3
|
-
import { state$ } from './state';
|
|
4
|
-
|
|
5
|
-
export const App = observer(function App() {
|
|
6
|
-
// In Legend State v3, use .get() to subscribe to values
|
|
7
|
-
const count = (state$.count as any).get();
|
|
8
|
-
const userName = (state$.user.name as any).get();
|
|
9
|
-
|
|
10
|
-
return (
|
|
11
|
-
<div style={{ padding: 40, fontFamily: 'sans-serif', maxWidth: 600 }}>
|
|
12
|
-
<h1>Legend State Dev Tools Demo</h1>
|
|
13
|
-
<p>Open the dev tools panel using the floating button at the bottom-right.</p>
|
|
14
|
-
|
|
15
|
-
<div style={{ marginTop: 24, padding: 16, background: '#f5f5f5', borderRadius: 8, color: '#1a1a1a' }}>
|
|
16
|
-
<h2>Counter: {count}</h2>
|
|
17
|
-
<button onClick={() => (state$.count as any).set((c: number) => c + 1)}>
|
|
18
|
-
Increment
|
|
19
|
-
</button>
|
|
20
|
-
<button onClick={() => (state$.count as any).set(0)} style={{ marginLeft: 8 }}>
|
|
21
|
-
Reset
|
|
22
|
-
</button>
|
|
23
|
-
</div>
|
|
24
|
-
|
|
25
|
-
<div style={{ marginTop: 24, padding: 16, background: '#f5f5f5', borderRadius: 8, color: '#1a1a1a' }}>
|
|
26
|
-
<h2>User: {userName}</h2>
|
|
27
|
-
<input
|
|
28
|
-
value={userName}
|
|
29
|
-
onChange={(e) => (state$.user.name as any).set(e.target.value)}
|
|
30
|
-
style={{ padding: '4px 8px', fontSize: 14 }}
|
|
31
|
-
/>
|
|
32
|
-
</div>
|
|
33
|
-
|
|
34
|
-
<div style={{ marginTop: 24, padding: 16, background: '#f5f5f5', borderRadius: 8, color: '#1a1a1a' }}>
|
|
35
|
-
<h2>Todos</h2>
|
|
36
|
-
<button
|
|
37
|
-
onClick={() =>
|
|
38
|
-
(state$.todos as any).push({
|
|
39
|
-
id: Date.now(),
|
|
40
|
-
text: `New todo ${Date.now()}`,
|
|
41
|
-
done: false,
|
|
42
|
-
})
|
|
43
|
-
}
|
|
44
|
-
>
|
|
45
|
-
Add Todo
|
|
46
|
-
</button>
|
|
47
|
-
</div>
|
|
48
|
-
</div>
|
|
49
|
-
);
|
|
50
|
-
});
|