legend-state-dev-tools 0.0.1 → 0.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.
@@ -1,96 +0,0 @@
1
- import { renderPanel, type PanelData } from './template-engine';
2
-
3
- export class Panel {
4
- private container: HTMLElement | null = null;
5
- private visible = false;
6
- private rootName: string;
7
- private readOnly: boolean;
8
- private onClose?: () => void;
9
- private position: 'left' | 'right';
10
-
11
- constructor(options: {
12
- rootName?: string;
13
- readOnly?: boolean;
14
- onClose?: () => void;
15
- position?: 'left' | 'right';
16
- } = {}) {
17
- this.rootName = options.rootName || 'state$';
18
- this.readOnly = options.readOnly || false;
19
- this.onClose = options.onClose;
20
- this.position = options.position || 'right';
21
- }
22
-
23
- public toggle(): void {
24
- if (this.visible) {
25
- this.hide();
26
- } else {
27
- this.show();
28
- }
29
- }
30
-
31
- public show(): void {
32
- this.visible = true;
33
-
34
- if (!this.container) {
35
- this.container = document.createElement('div');
36
- this.container.id = 'lsdt-panel';
37
- if (this.position === 'left') {
38
- this.container.classList.add('lsdt-panel-left');
39
- }
40
- document.body.appendChild(this.container);
41
- this.attachEventListeners();
42
- }
43
-
44
- this.render();
45
- }
46
-
47
- public hide(): void {
48
- this.visible = false;
49
- this.container?.remove();
50
- this.container = null;
51
- }
52
-
53
- public isVisible(): boolean {
54
- return this.visible;
55
- }
56
-
57
- public getEditorRoot(): HTMLElement | null {
58
- return this.container?.querySelector('#lsdt-json-editor-root') || null;
59
- }
60
-
61
- private render(): void {
62
- if (!this.container) return;
63
-
64
- const data: PanelData = {
65
- rootName: this.rootName,
66
- readOnly: this.readOnly,
67
- };
68
-
69
- this.container.innerHTML = renderPanel(data);
70
- }
71
-
72
- private attachEventListeners(): void {
73
- if (!this.container) return;
74
- this.container.addEventListener('click', this.handleClick);
75
- }
76
-
77
- private handleClick = (e: Event): void => {
78
- const target = e.target as HTMLElement;
79
- const actionElement = target.closest('[data-action]');
80
- if (!actionElement) return;
81
-
82
- const action = actionElement.getAttribute('data-action');
83
- if (action === 'close-panel') {
84
- this.onClose?.();
85
- }
86
- };
87
-
88
- public unmount(): void {
89
- if (this.container) {
90
- this.container.removeEventListener('click', this.handleClick);
91
- this.container.remove();
92
- this.container = null;
93
- }
94
- this.visible = false;
95
- }
96
- }
@@ -1,49 +0,0 @@
1
- /**
2
- * Shared UI utilities for Legend State Dev Tools
3
- */
4
-
5
- const STORAGE_PREFIX = 'lsdt';
6
-
7
- export function escapeHtml(str: string): string {
8
- const div = document.createElement('div');
9
- div.textContent = str;
10
- return div.innerHTML;
11
- }
12
-
13
- export function getStoredBoolean(key: string, defaultValue: boolean): boolean {
14
- const stored = localStorage.getItem(`${STORAGE_PREFIX}-${key}`);
15
- if (stored === null) return defaultValue;
16
- return stored === 'true';
17
- }
18
-
19
- export function setStoredBoolean(key: string, value: boolean): void {
20
- localStorage.setItem(`${STORAGE_PREFIX}-${key}`, String(value));
21
- }
22
-
23
- export function getStoredString(key: string, defaultValue: string): string {
24
- return localStorage.getItem(`${STORAGE_PREFIX}-${key}`) || defaultValue;
25
- }
26
-
27
- export function setStoredString(key: string, value: string): void {
28
- localStorage.setItem(`${STORAGE_PREFIX}-${key}`, value);
29
- }
30
-
31
- export function createCleanup(): {
32
- add: (fn: () => void) => void;
33
- run: () => void;
34
- } {
35
- const cleanupFns: (() => void)[] = [];
36
- return {
37
- add: (fn: () => void) => cleanupFns.push(fn),
38
- run: () => {
39
- cleanupFns.forEach((fn) => {
40
- try {
41
- fn();
42
- } catch (e) {
43
- console.error('[Legend State DevTools] Cleanup error:', e);
44
- }
45
- });
46
- cleanupFns.length = 0;
47
- },
48
- };
49
- }
@@ -1,55 +0,0 @@
1
- /**
2
- * Eta template engine wrapper for Legend State Dev Tools
3
- */
4
- import { Eta } from 'eta';
5
-
6
- import toolbarTemplate from './templates/toolbar.eta';
7
- import panelTemplate from './templates/panel.eta';
8
-
9
- const eta = new Eta({
10
- autoEscape: true,
11
- autoTrim: false,
12
- });
13
-
14
- const templates: Record<string, string> = {
15
- toolbar: toolbarTemplate,
16
- panel: panelTemplate,
17
- };
18
-
19
- export function renderTemplate<T extends Record<string, unknown>>(
20
- name: string,
21
- data: T
22
- ): string {
23
- const template = templates[name];
24
- if (!template) {
25
- console.error(`[LSDT] Template not found: ${name}`);
26
- return '';
27
- }
28
- try {
29
- return eta.renderString(template, data);
30
- } catch (error) {
31
- console.error(`[LSDT] Error rendering template ${name}:`, error);
32
- return '';
33
- }
34
- }
35
-
36
- export interface ToolbarData {
37
- [key: string]: unknown;
38
- isMinimized: boolean;
39
- panelVisible: boolean;
40
- rootName: string;
41
- }
42
-
43
- export function renderToolbar(data: ToolbarData): string {
44
- return renderTemplate('toolbar', data);
45
- }
46
-
47
- export interface PanelData {
48
- [key: string]: unknown;
49
- rootName: string;
50
- readOnly: boolean;
51
- }
52
-
53
- export function renderPanel(data: PanelData): string {
54
- return renderTemplate('panel', data);
55
- }
@@ -1,12 +0,0 @@
1
- <div class="lsdt-panel-header">
2
- <h3><%= it.rootName %></h3>
3
- <div class="lsdt-panel-actions">
4
- <% if (it.readOnly) { %>
5
- <span class="lsdt-readonly-badge">Read-only</span>
6
- <% } %>
7
- <button class="lsdt-close-btn" data-action="close-panel" title="Close">&times;</button>
8
- </div>
9
- </div>
10
- <div class="lsdt-panel-content">
11
- <div id="lsdt-json-editor-root"></div>
12
- </div>
@@ -1,13 +0,0 @@
1
- <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>
@@ -1,108 +0,0 @@
1
- import { renderToolbar, type ToolbarData } from './template-engine';
2
- import { getStoredBoolean, setStoredBoolean } from './shared-utils';
3
-
4
- export class Toolbar {
5
- private container: HTMLElement | null = null;
6
- private isDragging = false;
7
- private offsetX = 0;
8
- private offsetY = 0;
9
- private isMinimized: boolean = getStoredBoolean('toolbar-minimized', false);
10
- private panelVisible = false;
11
-
12
- private onTogglePanel?: () => void;
13
- private rootName: string;
14
-
15
- constructor(options: { onTogglePanel?: () => void; rootName?: string } = {}) {
16
- this.onTogglePanel = options.onTogglePanel;
17
- this.rootName = options.rootName || 'state$';
18
- }
19
-
20
- public mount(): void {
21
- if (this.container) return;
22
-
23
- this.container = document.createElement('div');
24
- this.container.id = 'lsdt-toolbar';
25
- if (this.isMinimized) {
26
- this.container.classList.add('lsdt-toolbar-minimized');
27
- }
28
-
29
- document.body.appendChild(this.container);
30
- this.render();
31
- this.attachEventListeners();
32
- }
33
-
34
- private render(): void {
35
- if (!this.container) return;
36
-
37
- const data: ToolbarData = {
38
- isMinimized: this.isMinimized,
39
- panelVisible: this.panelVisible,
40
- rootName: this.rootName,
41
- };
42
-
43
- this.container.innerHTML = renderToolbar(data);
44
- }
45
-
46
- private attachEventListeners(): void {
47
- if (!this.container) return;
48
-
49
- this.container.addEventListener('click', this.handleClick);
50
- this.container.addEventListener('mousedown', this.handleMouseDown);
51
- document.addEventListener('mousemove', this.handleMouseMove);
52
- document.addEventListener('mouseup', this.handleMouseUp);
53
- }
54
-
55
- private handleClick = (e: Event): void => {
56
- const target = e.target as HTMLElement;
57
- const actionElement = target.closest('[data-action]');
58
- if (!actionElement) return;
59
-
60
- const action = actionElement.getAttribute('data-action');
61
- if (action === 'toggle-panel') {
62
- this.onTogglePanel?.();
63
- }
64
- };
65
-
66
- private handleMouseDown = (e: MouseEvent): void => {
67
- const target = e.target as HTMLElement;
68
- if (target.tagName === 'BUTTON') return;
69
-
70
- this.isDragging = true;
71
- if (this.container) {
72
- this.container.classList.add('dragging');
73
- const rect = this.container.getBoundingClientRect();
74
- this.offsetX = e.clientX - rect.left;
75
- this.offsetY = e.clientY - rect.top;
76
- }
77
- };
78
-
79
- private handleMouseMove = (e: MouseEvent): void => {
80
- if (!this.isDragging || !this.container) return;
81
-
82
- this.container.style.left = `${e.clientX - this.offsetX}px`;
83
- this.container.style.top = `${e.clientY - this.offsetY}px`;
84
- this.container.style.right = 'auto';
85
- this.container.style.bottom = 'auto';
86
- };
87
-
88
- private handleMouseUp = (): void => {
89
- this.isDragging = false;
90
- this.container?.classList.remove('dragging');
91
- };
92
-
93
- public setPanelVisible(visible: boolean): void {
94
- this.panelVisible = visible;
95
- this.render();
96
- }
97
-
98
- public unmount(): void {
99
- if (this.container) {
100
- this.container.removeEventListener('click', this.handleClick);
101
- this.container.removeEventListener('mousedown', this.handleMouseDown);
102
- this.container.remove();
103
- this.container = null;
104
- }
105
- document.removeEventListener('mousemove', this.handleMouseMove);
106
- document.removeEventListener('mouseup', this.handleMouseUp);
107
- }
108
- }
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "./dist",
5
- "rootDir": "./src"
6
- },
7
- "include": ["src/**/*"],
8
- "exclude": ["dist/**/*"]
9
- }
@@ -1,71 +0,0 @@
1
- import { defineConfig } from 'vite';
2
- import { resolve } from 'path';
3
- import { copyFileSync } from 'fs';
4
- import dts from 'vite-plugin-dts';
5
-
6
- export default defineConfig({
7
- plugins: [
8
- {
9
- name: 'eta-raw-loader',
10
- transform(code, id) {
11
- if (id.endsWith('.eta')) {
12
- return {
13
- code: `export default ${JSON.stringify(code)};`,
14
- map: null,
15
- };
16
- }
17
- },
18
- },
19
- dts({
20
- include: ['src/**/*.ts', 'src/**/*.tsx'],
21
- exclude: ['src/**/*.test.ts'],
22
- rollupTypes: true,
23
- insertTypesEntry: true,
24
- }),
25
- {
26
- name: 'copy-styles',
27
- closeBundle() {
28
- copyFileSync(
29
- resolve(__dirname, 'src/styles.css'),
30
- resolve(__dirname, 'dist/styles.css')
31
- );
32
- },
33
- },
34
- ],
35
- build: {
36
- lib: {
37
- entry: resolve(__dirname, 'src/index.ts'),
38
- name: 'LegendStateDevTools',
39
- formats: ['es', 'cjs'],
40
- fileName: (format) => (format === 'es' ? 'index.mjs' : 'index.js'),
41
- },
42
- rollupOptions: {
43
- external: [
44
- 'react',
45
- 'react-dom',
46
- 'react-dom/client',
47
- 'react/jsx-runtime',
48
- 'eta',
49
- 'json-edit-react',
50
- '@legendapp/state',
51
- ],
52
- output: {
53
- globals: {
54
- react: 'React',
55
- 'react-dom': 'ReactDOM',
56
- 'react-dom/client': 'ReactDOMClient',
57
- eta: 'Eta',
58
- 'json-edit-react': 'JsonEditReact',
59
- '@legendapp/state': 'LegendState',
60
- },
61
- },
62
- },
63
- outDir: 'dist',
64
- emptyOutDir: true,
65
- sourcemap: true,
66
- minify: 'esbuild',
67
- },
68
- resolve: {
69
- extensions: ['.ts', '.tsx', '.js', '.jsx', '.eta'],
70
- },
71
- });
package/tsconfig.json DELETED
@@ -1,17 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2020",
4
- "module": "ESNext",
5
- "lib": ["ES2020", "DOM", "DOM.Iterable"],
6
- "moduleResolution": "bundler",
7
- "strict": true,
8
- "skipLibCheck": true,
9
- "esModuleInterop": true,
10
- "resolveJsonModule": true,
11
- "isolatedModules": true,
12
- "declaration": true,
13
- "declarationMap": true,
14
- "jsx": "react-jsx"
15
- },
16
- "exclude": ["examples/**/*", "node_modules/**/*", "**/dist/**/*"]
17
- }
File without changes