project-graph-mcp 2.3.2 → 2.4.1
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 +3 -2
- package/src/analysis/analysis-cache.ctx +9 -0
- package/src/analysis/analysis-cache.js +1 -1
- package/src/analysis/complexity.ctx +6 -0
- package/src/analysis/complexity.js +1 -1
- package/src/analysis/custom-rules.ctx +14 -0
- package/src/analysis/custom-rules.js +1 -1
- package/src/analysis/db-analysis.ctx +7 -0
- package/src/analysis/db-analysis.js +1 -1
- package/src/analysis/dead-code.ctx +6 -0
- package/src/analysis/dead-code.js +1 -1
- package/src/analysis/full-analysis.ctx +9 -0
- package/src/analysis/full-analysis.js +1 -1
- package/src/analysis/jsdoc-checker.ctx +10 -0
- package/src/analysis/jsdoc-checker.js +1 -1
- package/src/analysis/jsdoc-generator.ctx +9 -0
- package/src/analysis/jsdoc-generator.js +1 -1
- package/src/analysis/large-files.ctx +6 -0
- package/src/analysis/large-files.js +1 -1
- package/src/analysis/outdated-patterns.ctx +7 -0
- package/src/analysis/outdated-patterns.js +1 -1
- package/src/analysis/similar-functions.ctx +6 -0
- package/src/analysis/similar-functions.js +1 -1
- package/src/analysis/test-annotations.ctx +11 -0
- package/src/analysis/test-annotations.js +1 -1
- package/src/analysis/type-checker.ctx +6 -0
- package/src/analysis/type-checker.js +1 -1
- package/src/analysis/undocumented.ctx +8 -0
- package/src/analysis/undocumented.js +1 -1
- package/src/cli/cli-handlers.ctx +7 -0
- package/src/cli/cli-handlers.js +1 -1
- package/src/cli/cli.ctx +6 -0
- package/src/cli/cli.js +1 -1
- package/src/compact/ai-context.ctx +6 -0
- package/src/compact/ai-context.js +1 -1
- package/src/compact/compact-migrate.ctx +8 -0
- package/src/compact/compact-migrate.js +1 -1
- package/src/compact/compact.ctx +11 -0
- package/src/compact/compact.js +1 -1
- package/src/compact/compress.ctx +7 -0
- package/src/compact/compress.js +1 -1
- package/src/compact/ctx-resolver.ctx +2 -0
- package/src/compact/ctx-resolver.js +1 -1
- package/src/compact/ctx-to-jsdoc.ctx +11 -0
- package/src/compact/ctx-to-jsdoc.js +1 -1
- package/src/compact/doc-dialect.ctx +11 -0
- package/src/compact/doc-dialect.js +2 -2
- package/src/compact/expand.ctx +14 -0
- package/src/compact/expand.js +1 -1
- package/src/compact/framework-references.ctx +7 -0
- package/src/compact/framework-references.js +1 -1
- package/src/compact/instructions.ctx +6 -0
- package/src/compact/instructions.js +1 -1
- package/src/compact/jsdoc-builder.ctx +4 -0
- package/src/compact/jsdoc-builder.js +1 -1
- package/src/compact/mode-config.ctx +8 -0
- package/src/compact/mode-config.js +1 -1
- package/src/compact/split-declarations.ctx +6 -0
- package/src/compact/split-declarations.js +1 -1
- package/src/compact/validate-pipeline.ctx +12 -0
- package/src/compact/validate-pipeline.js +1 -1
- package/src/core/event-bus.ctx +9 -0
- package/src/core/event-bus.js +1 -1
- package/src/core/file-walker.ctx +1 -0
- package/src/core/file-walker.js +1 -1
- package/src/core/filters.ctx +12 -0
- package/src/core/filters.js +1 -1
- package/src/core/graph-builder.ctx +7 -0
- package/src/core/graph-builder.js +1 -1
- package/src/core/parser.ctx +12 -0
- package/src/core/parser.js +1 -1
- package/src/core/utils.ctx +1 -0
- package/src/core/utils.js +1 -1
- package/src/core/workspace.ctx +7 -0
- package/src/core/workspace.js +1 -1
- package/src/lang/lang-go.ctx +8 -0
- package/src/lang/lang-go.js +1 -1
- package/src/lang/lang-python.ctx +5 -0
- package/src/lang/lang-python.js +1 -1
- package/src/lang/lang-sql.ctx +10 -0
- package/src/lang/lang-sql.js +1 -1
- package/src/lang/lang-typescript.ctx +6 -0
- package/src/lang/lang-typescript.js +1 -1
- package/src/lang/lang-utils.ctx +5 -0
- package/src/lang/lang-utils.js +1 -1
- package/src/mcp/mcp-server.ctx +6 -0
- package/src/mcp/mcp-server.js +1 -1
- package/src/mcp/tool-defs.ctx +2 -0
- package/src/mcp/tool-defs.js +1 -1
- package/src/mcp/tools.ctx +13 -0
- package/src/mcp/tools.js +1 -1
- package/src/network/backend-lifecycle.ctx +10 -0
- package/src/network/backend-lifecycle.js +1 -1
- package/src/network/backend.ctx +5 -0
- package/src/network/backend.js +1 -1
- package/src/network/local-gateway.ctx +9 -0
- package/src/network/local-gateway.js +1 -1
- package/src/network/mdns.ctx +6 -0
- package/src/network/mdns.js +1 -1
- package/src/network/server.ctx +2 -0
- package/src/network/server.js +2 -2
- package/src/network/web-server.ctx +17 -0
- package/src/network/web-server.js +2 -2
- package/web/follow-controller.js +94 -25
- package/web/panels/dep-graph.js +207 -21
- package/project-graph-mcp-2.3.0.tgz +0 -0
- package/vendor/symbiote-node/CHANGELOG.md +0 -31
- package/vendor/symbiote-node/LICENSE +0 -21
- package/vendor/symbiote-node/README.md +0 -206
- package/vendor/symbiote-node/canvas/AutoLayout.js +0 -725
- package/vendor/symbiote-node/canvas/Breadcrumb/Breadcrumb.css.js +0 -73
- package/vendor/symbiote-node/canvas/Breadcrumb/Breadcrumb.js +0 -93
- package/vendor/symbiote-node/canvas/Breadcrumb/Breadcrumb.tpl.js +0 -9
- package/vendor/symbiote-node/canvas/CanvasConnectionRenderer.js +0 -962
- package/vendor/symbiote-node/canvas/ConnectionRenderer.js +0 -1468
- package/vendor/symbiote-node/canvas/FlowSimulator.js +0 -323
- package/vendor/symbiote-node/canvas/ForceLayout.js +0 -189
- package/vendor/symbiote-node/canvas/ForceWorker.js +0 -1325
- package/vendor/symbiote-node/canvas/GraphTabs/GraphTabs.css.js +0 -97
- package/vendor/symbiote-node/canvas/GraphTabs/GraphTabs.js +0 -176
- package/vendor/symbiote-node/canvas/GraphTabs/GraphTabs.tpl.js +0 -12
- package/vendor/symbiote-node/canvas/LODManager.js +0 -88
- package/vendor/symbiote-node/canvas/Minimap/Minimap.css.js +0 -71
- package/vendor/symbiote-node/canvas/Minimap/Minimap.js +0 -207
- package/vendor/symbiote-node/canvas/Minimap/Minimap.tpl.js +0 -9
- package/vendor/symbiote-node/canvas/NodeCanvas/NodeCanvas.css.js +0 -261
- package/vendor/symbiote-node/canvas/NodeCanvas/NodeCanvas.js +0 -1840
- package/vendor/symbiote-node/canvas/NodeCanvas/NodeCanvas.tpl.js +0 -22
- package/vendor/symbiote-node/canvas/NodeSearch/NodeSearch.css.js +0 -97
- package/vendor/symbiote-node/canvas/NodeSearch/NodeSearch.js +0 -132
- package/vendor/symbiote-node/canvas/NodeSearch/NodeSearch.tpl.js +0 -21
- package/vendor/symbiote-node/canvas/NodeViewManager.js +0 -584
- package/vendor/symbiote-node/canvas/PinExpansion.js +0 -131
- package/vendor/symbiote-node/canvas/PseudoConnection.js +0 -80
- package/vendor/symbiote-node/canvas/SubgraphManager.js +0 -201
- package/vendor/symbiote-node/canvas/SubgraphRouter.js +0 -443
- package/vendor/symbiote-node/canvas/ViewportActions.js +0 -446
- package/vendor/symbiote-node/core/Connection.js +0 -45
- package/vendor/symbiote-node/core/Editor.js +0 -451
- package/vendor/symbiote-node/core/Frame.js +0 -31
- package/vendor/symbiote-node/core/GraphMermaid.js +0 -348
- package/vendor/symbiote-node/core/GraphText.js +0 -210
- package/vendor/symbiote-node/core/Node.js +0 -143
- package/vendor/symbiote-node/core/Portal.js +0 -104
- package/vendor/symbiote-node/core/Socket.js +0 -185
- package/vendor/symbiote-node/core/SubgraphNode.js +0 -125
- package/vendor/symbiote-node/engine/AgentUICommands.js +0 -100
- package/vendor/symbiote-node/engine/Executor.js +0 -371
- package/vendor/symbiote-node/engine/Graph.js +0 -314
- package/vendor/symbiote-node/engine/GraphServer.js +0 -353
- package/vendor/symbiote-node/engine/HandlerLoader.js +0 -145
- package/vendor/symbiote-node/engine/History.js +0 -83
- package/vendor/symbiote-node/engine/Lifecycle.js +0 -118
- package/vendor/symbiote-node/engine/Persistence.js +0 -84
- package/vendor/symbiote-node/engine/Registry.js +0 -264
- package/vendor/symbiote-node/engine/SocketTypes.js +0 -79
- package/vendor/symbiote-node/engine/cli.js +0 -404
- package/vendor/symbiote-node/engine/index.js +0 -56
- package/vendor/symbiote-node/engine/nanoid.js +0 -28
- package/vendor/symbiote-node/engine/package.json +0 -26
- package/vendor/symbiote-node/engine/packs/ai/beat-detect.handler.js +0 -215
- package/vendor/symbiote-node/engine/packs/ai/content-adapt.handler.js +0 -238
- package/vendor/symbiote-node/engine/packs/ai/face-detect.handler.js +0 -287
- package/vendor/symbiote-node/engine/packs/ai/grok-generate.handler.js +0 -565
- package/vendor/symbiote-node/engine/packs/ai/kling-lipsync.handler.js +0 -414
- package/vendor/symbiote-node/engine/packs/ai/lesson-generate.handler.js +0 -343
- package/vendor/symbiote-node/engine/packs/ai/opencode.handler.js +0 -164
- package/vendor/symbiote-node/engine/packs/ai/replicate-lipsync.handler.js +0 -341
- package/vendor/symbiote-node/engine/packs/ai/tts.handler.js +0 -241
- package/vendor/symbiote-node/engine/packs/ai/whisper.handler.js +0 -191
- package/vendor/symbiote-node/engine/packs/data/db-query.handler.js +0 -67
- package/vendor/symbiote-node/engine/packs/data/news-accumulate.handler.js +0 -281
- package/vendor/symbiote-node/engine/packs/data/personas.handler.js +0 -160
- package/vendor/symbiote-node/engine/packs/data/prompt-loader.handler.js +0 -193
- package/vendor/symbiote-node/engine/packs/data/roles.handler.js +0 -216
- package/vendor/symbiote-node/engine/packs/data/rss-feed.handler.js +0 -244
- package/vendor/symbiote-node/engine/packs/debug/inject.handler.js +0 -52
- package/vendor/symbiote-node/engine/packs/flow/agent.handler.js +0 -73
- package/vendor/symbiote-node/engine/packs/flow/if.handler.js +0 -107
- package/vendor/symbiote-node/engine/packs/flow/loop.handler.js +0 -58
- package/vendor/symbiote-node/engine/packs/flow/merge.handler.js +0 -60
- package/vendor/symbiote-node/engine/packs/flow/retry.handler.js +0 -65
- package/vendor/symbiote-node/engine/packs/flow/switch.handler.js +0 -64
- package/vendor/symbiote-node/engine/packs/flow/wait-all.handler.js +0 -39
- package/vendor/symbiote-node/engine/packs/io/http-request.handler.js +0 -82
- package/vendor/symbiote-node/engine/packs/io/read-file.handler.js +0 -60
- package/vendor/symbiote-node/engine/packs/io/write-file.handler.js +0 -63
- package/vendor/symbiote-node/engine/packs/transform/anchor-match.handler.js +0 -494
- package/vendor/symbiote-node/engine/packs/transform/effects-skeleton.handler.js +0 -417
- package/vendor/symbiote-node/engine/packs/transform/json-parse.handler.js +0 -43
- package/vendor/symbiote-node/engine/packs/transform/lipsync-select.handler.js +0 -339
- package/vendor/symbiote-node/engine/packs/transform/riopla-adapt.handler.js +0 -432
- package/vendor/symbiote-node/engine/packs/transform/set.handler.js +0 -57
- package/vendor/symbiote-node/engine/packs/transform/template-builder.handler.js +0 -134
- package/vendor/symbiote-node/engine/packs/transform/template.handler.js +0 -79
- package/vendor/symbiote-node/engine/packs/transform/timeline-build.handler.js +0 -399
- package/vendor/symbiote-node/engine/packs/util/delay.handler.js +0 -39
- package/vendor/symbiote-node/engine/packs/util/log.handler.js +0 -44
- package/vendor/symbiote-node/engine/packs/video-pack.js +0 -323
- package/vendor/symbiote-node/index.js +0 -103
- package/vendor/symbiote-node/inspector/InspectorPanel/InspectorPanel.css.js +0 -361
- package/vendor/symbiote-node/inspector/InspectorPanel/InspectorPanel.js +0 -332
- package/vendor/symbiote-node/inspector/InspectorPanel/InspectorPanel.tpl.js +0 -96
- package/vendor/symbiote-node/inspector/TemplatePreview/TemplatePreview.css.js +0 -104
- package/vendor/symbiote-node/inspector/TemplatePreview/TemplatePreview.js +0 -133
- package/vendor/symbiote-node/inspector/TemplatePreview/TemplatePreview.tpl.js +0 -33
- package/vendor/symbiote-node/interactions/ConnectFlow.js +0 -307
- package/vendor/symbiote-node/interactions/Drag.js +0 -102
- package/vendor/symbiote-node/interactions/Selector.js +0 -132
- package/vendor/symbiote-node/interactions/SnapGrid.js +0 -65
- package/vendor/symbiote-node/interactions/Zoom.js +0 -140
- package/vendor/symbiote-node/layout/ActionZone/ActionZone.css.js +0 -88
- package/vendor/symbiote-node/layout/ActionZone/ActionZone.js +0 -254
- package/vendor/symbiote-node/layout/ActionZone/ActionZone.tpl.js +0 -11
- package/vendor/symbiote-node/layout/Layout/Layout.css.js +0 -88
- package/vendor/symbiote-node/layout/Layout/Layout.js +0 -622
- package/vendor/symbiote-node/layout/Layout/Layout.tpl.js +0 -25
- package/vendor/symbiote-node/layout/LayoutNode/LayoutNode.css.js +0 -293
- package/vendor/symbiote-node/layout/LayoutNode/LayoutNode.js +0 -467
- package/vendor/symbiote-node/layout/LayoutNode/LayoutNode.tpl.js +0 -33
- package/vendor/symbiote-node/layout/LayoutPreview/LayoutPreview.css.js +0 -46
- package/vendor/symbiote-node/layout/LayoutPreview/LayoutPreview.js +0 -102
- package/vendor/symbiote-node/layout/LayoutPreview/LayoutPreview.tpl.js +0 -6
- package/vendor/symbiote-node/layout/LayoutRouter/LayoutRouter.js +0 -156
- package/vendor/symbiote-node/layout/LayoutRouter/routerSync.js +0 -250
- package/vendor/symbiote-node/layout/LayoutSidebar/LayoutSidebar.css.js +0 -379
- package/vendor/symbiote-node/layout/LayoutSidebar/LayoutSidebar.js +0 -263
- package/vendor/symbiote-node/layout/LayoutSidebar/LayoutSidebar.tpl.js +0 -20
- package/vendor/symbiote-node/layout/LayoutSidebar/SidebarSection.js +0 -183
- package/vendor/symbiote-node/layout/LayoutTree.js +0 -246
- package/vendor/symbiote-node/layout/PanelMenu/PanelMenu.css.js +0 -43
- package/vendor/symbiote-node/layout/PanelMenu/PanelMenu.js +0 -89
- package/vendor/symbiote-node/layout/PanelMenu/PanelMenu.tpl.js +0 -14
- package/vendor/symbiote-node/layout/index.js +0 -16
- package/vendor/symbiote-node/menu/ContextMenu/ContextMenu.css.js +0 -61
- package/vendor/symbiote-node/menu/ContextMenu/ContextMenu.js +0 -79
- package/vendor/symbiote-node/menu/ContextMenu/ContextMenu.tpl.js +0 -19
- package/vendor/symbiote-node/node/CtrlItem/CtrlItem.css.js +0 -41
- package/vendor/symbiote-node/node/CtrlItem/CtrlItem.js +0 -24
- package/vendor/symbiote-node/node/CtrlItem/CtrlItem.tpl.js +0 -16
- package/vendor/symbiote-node/node/GraphFrame/GraphFrame.css.js +0 -65
- package/vendor/symbiote-node/node/GraphFrame/GraphFrame.js +0 -29
- package/vendor/symbiote-node/node/GraphFrame/GraphFrame.tpl.js +0 -13
- package/vendor/symbiote-node/node/GraphNode/GraphNode.css.js +0 -683
- package/vendor/symbiote-node/node/GraphNode/GraphNode.js +0 -92
- package/vendor/symbiote-node/node/GraphNode/GraphNode.tpl.js +0 -17
- package/vendor/symbiote-node/node/NodeSocket/NodeSocket.js +0 -25
- package/vendor/symbiote-node/node/NodeSocket/NodeSocket.tpl.js +0 -7
- package/vendor/symbiote-node/node/PortItem/PortItem.css.js +0 -90
- package/vendor/symbiote-node/node/PortItem/PortItem.js +0 -87
- package/vendor/symbiote-node/node/PortItem/PortItem.tpl.js +0 -10
- package/vendor/symbiote-node/package.json +0 -59
- package/vendor/symbiote-node/palette/PaletteBrowser/PaletteBrowser.css.js +0 -143
- package/vendor/symbiote-node/palette/PaletteBrowser/PaletteBrowser.js +0 -131
- package/vendor/symbiote-node/palette/PaletteBrowser/PaletteBrowser.tpl.js +0 -16
- package/vendor/symbiote-node/plugins/History.js +0 -384
- package/vendor/symbiote-node/plugins/Readonly.js +0 -59
- package/vendor/symbiote-node/shapes/CircleShape.js +0 -80
- package/vendor/symbiote-node/shapes/CommentShape.js +0 -35
- package/vendor/symbiote-node/shapes/DiamondShape.js +0 -115
- package/vendor/symbiote-node/shapes/NodeShape.js +0 -80
- package/vendor/symbiote-node/shapes/PillShape.js +0 -91
- package/vendor/symbiote-node/shapes/RectShape.js +0 -72
- package/vendor/symbiote-node/shapes/SVGShape.js +0 -494
- package/vendor/symbiote-node/shapes/index.js +0 -53
- package/vendor/symbiote-node/themes/Palette.js +0 -32
- package/vendor/symbiote-node/themes/Skin.js +0 -113
- package/vendor/symbiote-node/themes/Theme.js +0 -84
- package/vendor/symbiote-node/themes/carbon.js +0 -137
- package/vendor/symbiote-node/themes/dark.js +0 -137
- package/vendor/symbiote-node/themes/ebook.js +0 -138
- package/vendor/symbiote-node/themes/grey.js +0 -137
- package/vendor/symbiote-node/themes/light.js +0 -137
- package/vendor/symbiote-node/themes/neon.js +0 -138
- package/vendor/symbiote-node/themes/pcb.js +0 -273
- package/vendor/symbiote-node/themes/synthwave.js +0 -137
- package/vendor/symbiote-node/toolbar/QuickToolbar/QuickToolbar.css.js +0 -86
- package/vendor/symbiote-node/toolbar/QuickToolbar/QuickToolbar.js +0 -128
- package/vendor/symbiote-node/toolbar/QuickToolbar/QuickToolbar.tpl.js +0 -29
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LayoutRouter — universal hash-based router for layout system
|
|
3
|
-
*
|
|
4
|
-
* Uses Symbiote PubSub named data context (ROUTER) to provide
|
|
5
|
-
* reactive routing across the application.
|
|
6
|
-
*
|
|
7
|
-
* URL format: #panel/subpath?param1=value¶m2=value
|
|
8
|
-
*
|
|
9
|
-
* Usage in templates: {{ROUTER/panel}}, {{ROUTER/subpath}}, {{ROUTER/query}}
|
|
10
|
-
* Usage in code: this.$['ROUTER/panel'], this.sub('ROUTER/panel', cb)
|
|
11
|
-
*
|
|
12
|
-
* @module symbiote-node/layout/LayoutRouter
|
|
13
|
-
*/
|
|
14
|
-
import { PubSub } from '@symbiotejs/symbiote';
|
|
15
|
-
|
|
16
|
-
const CTX = 'ROUTER';
|
|
17
|
-
|
|
18
|
-
const routerCtx = PubSub.registerCtx({
|
|
19
|
-
panel: 'default',
|
|
20
|
-
subpath: '',
|
|
21
|
-
query: '',
|
|
22
|
-
}, CTX);
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Parse query string into object
|
|
26
|
-
* @param {string} str - Query string (without leading ?)
|
|
27
|
-
* @returns {Object<string, string>}
|
|
28
|
-
*/
|
|
29
|
-
export function parseQuery(str) {
|
|
30
|
-
if (!str) return {};
|
|
31
|
-
const result = {};
|
|
32
|
-
for (const pair of str.split('&')) {
|
|
33
|
-
const eqIdx = pair.indexOf('=');
|
|
34
|
-
if (eqIdx >= 0) {
|
|
35
|
-
result[decodeURIComponent(pair.substring(0, eqIdx))] = decodeURIComponent(pair.substring(eqIdx + 1));
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
return result;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Build query string from key-value object
|
|
43
|
-
* @param {Object<string, string>} params
|
|
44
|
-
* @returns {string}
|
|
45
|
-
*/
|
|
46
|
-
export function buildQuery(params) {
|
|
47
|
-
const entries = Object.entries(params).filter(([, v]) => v !== '' && v != null);
|
|
48
|
-
if (entries.length === 0) return '';
|
|
49
|
-
return entries.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join('&');
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Build full hash string from parts
|
|
54
|
-
* @param {string} panel
|
|
55
|
-
* @param {string} [subpath]
|
|
56
|
-
* @param {Object} [params]
|
|
57
|
-
* @returns {string}
|
|
58
|
-
*/
|
|
59
|
-
export function buildHash(panel, subpath, params) {
|
|
60
|
-
let hash = panel;
|
|
61
|
-
if (subpath) hash += '/' + subpath;
|
|
62
|
-
const q = params ? buildQuery(params) : '';
|
|
63
|
-
if (q) hash += '?' + q;
|
|
64
|
-
return hash;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Navigate to a new route — updates URL and PubSub context
|
|
69
|
-
* @param {string} panel - Master panel section ID
|
|
70
|
-
* @param {string} [subpath] - Sub-path (entity ID, etc.)
|
|
71
|
-
* @param {Object} [params] - Query parameters
|
|
72
|
-
*/
|
|
73
|
-
export function navigate(panel, subpath = '', params = {}) {
|
|
74
|
-
if (typeof location === 'undefined') return;
|
|
75
|
-
const hash = buildHash(panel, subpath, params);
|
|
76
|
-
// Use pushState instead of location.hash to ensure clean URL
|
|
77
|
-
// (location.hash preserves stale query strings like ?monitoring)
|
|
78
|
-
history.pushState(null, '', location.pathname + '#' + hash);
|
|
79
|
-
syncFromHash();
|
|
80
|
-
if (typeof window !== 'undefined') {
|
|
81
|
-
window.dispatchEvent(new Event('hashchange'));
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Update only query params of current route (keeps panel/subpath)
|
|
87
|
-
* Uses replaceState to avoid cluttering browser history
|
|
88
|
-
* @param {Object} params - Params to merge
|
|
89
|
-
*/
|
|
90
|
-
export function updateParams(params) {
|
|
91
|
-
if (typeof location === 'undefined') return;
|
|
92
|
-
const currentQuery = parseQuery(routerCtx.read('query'));
|
|
93
|
-
const merged = { ...currentQuery };
|
|
94
|
-
for (const [k, v] of Object.entries(params)) {
|
|
95
|
-
if (v === '' || v == null) {
|
|
96
|
-
delete merged[k];
|
|
97
|
-
} else {
|
|
98
|
-
merged[k] = v;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
const query = buildQuery(merged);
|
|
102
|
-
const hash = buildHash(routerCtx.read('panel'), routerCtx.read('subpath'), merged);
|
|
103
|
-
history.replaceState(null, '', '#' + hash);
|
|
104
|
-
routerCtx.pub('query', query);
|
|
105
|
-
if (typeof window !== 'undefined') {
|
|
106
|
-
window.dispatchEvent(new Event('hashchange'));
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Sync PubSub context from current URL hash
|
|
112
|
-
*/
|
|
113
|
-
function syncFromHash() {
|
|
114
|
-
const raw = location.hash.replace(/^#/, '') || 'default';
|
|
115
|
-
|
|
116
|
-
const qIdx = raw.indexOf('?');
|
|
117
|
-
const pathPart = qIdx >= 0 ? raw.substring(0, qIdx) : raw;
|
|
118
|
-
const queryPart = qIdx >= 0 ? raw.substring(qIdx + 1) : '';
|
|
119
|
-
|
|
120
|
-
const slashIdx = pathPart.indexOf('/');
|
|
121
|
-
const panel = slashIdx >= 0 ? pathPart.substring(0, slashIdx) : pathPart;
|
|
122
|
-
const subpath = slashIdx >= 0 ? pathPart.substring(slashIdx + 1) : '';
|
|
123
|
-
|
|
124
|
-
routerCtx.pub('panel', panel);
|
|
125
|
-
routerCtx.pub('subpath', subpath);
|
|
126
|
-
routerCtx.pub('query', queryPart);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Get current route state
|
|
131
|
-
* @returns {{ panel: string, subpath: string, query: string }}
|
|
132
|
-
*/
|
|
133
|
-
export function getRoute() {
|
|
134
|
-
return {
|
|
135
|
-
panel: routerCtx.read('panel'),
|
|
136
|
-
subpath: routerCtx.read('subpath'),
|
|
137
|
-
query: routerCtx.read('query'),
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Set default panel (first section to show if hash is empty)
|
|
143
|
-
* @param {string} panel
|
|
144
|
-
*/
|
|
145
|
-
export function setDefaultPanel(panel) {
|
|
146
|
-
if (typeof location === 'undefined') return;
|
|
147
|
-
if (!location.hash || location.hash === '#') {
|
|
148
|
-
navigate(panel);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Initial sync + listen to hashchange (browser-only)
|
|
153
|
-
if (typeof location !== 'undefined' && typeof window !== 'undefined') {
|
|
154
|
-
syncFromHash();
|
|
155
|
-
window.addEventListener('hashchange', syncFromHash);
|
|
156
|
-
}
|
|
@@ -1,250 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* routerSync — bidirectional URL ↔ component state sync
|
|
3
|
-
*
|
|
4
|
-
* Maps URL query params to component init$ properties and vice versa.
|
|
5
|
-
* Only syncs when the component's panel is active.
|
|
6
|
-
*
|
|
7
|
-
* Supports two mapping formats:
|
|
8
|
-
*
|
|
9
|
-
* Simple: { componentProp: 'urlParam' }
|
|
10
|
-
* Extended: { componentProp: { param: 'urlParam', default: 'all', type: 'number' } }
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* // Simple format:
|
|
14
|
-
* syncWithRouter(this, 'jobs', {
|
|
15
|
-
* filterStatus: 'status',
|
|
16
|
-
* filterRegion: 'region',
|
|
17
|
-
* });
|
|
18
|
-
*
|
|
19
|
-
* // Extended format:
|
|
20
|
-
* syncWithRouter(this, 'jobs', {
|
|
21
|
-
* filterStatus: { param: 'status', default: 'all' },
|
|
22
|
-
* currentPage: { param: 'page', default: 1, type: 'number' },
|
|
23
|
-
* });
|
|
24
|
-
*
|
|
25
|
-
* @module symbiote-node/layout/LayoutRouter/routerSync
|
|
26
|
-
*/
|
|
27
|
-
import { parseQuery, updateParams } from './LayoutRouter.js';
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Normalize mapping entry to { param, defaultVal, type }
|
|
31
|
-
* @param {string | { param: string, default?: *, type?: string }} entry
|
|
32
|
-
* @returns {{ param: string, defaultVal: *, type: string }}
|
|
33
|
-
*/
|
|
34
|
-
function normalizeMapping(entry) {
|
|
35
|
-
if (typeof entry === 'string') {
|
|
36
|
-
return { param: entry, defaultVal: undefined, type: 'string' };
|
|
37
|
-
}
|
|
38
|
-
return {
|
|
39
|
-
param: entry.param,
|
|
40
|
-
defaultVal: entry.default,
|
|
41
|
-
type: entry.type ?? 'string',
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Cast value to the target type
|
|
47
|
-
* @param {string} value
|
|
48
|
-
* @param {string} type
|
|
49
|
-
* @returns {*}
|
|
50
|
-
*/
|
|
51
|
-
function castValue(value, type) {
|
|
52
|
-
if (type === 'number') return Number(value);
|
|
53
|
-
if (type === 'boolean') return value === 'true';
|
|
54
|
-
return value;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Sync component state with router URL params
|
|
59
|
-
*
|
|
60
|
-
* @param {import('@symbiotejs/symbiote').default} component - Symbiote component
|
|
61
|
-
* @param {string} panelName - Panel this component belongs to
|
|
62
|
-
* @param {Object<string, string | { param: string, default?: *, type?: string }>} mapping
|
|
63
|
-
*/
|
|
64
|
-
export function syncWithRouter(component, panelName, mapping) {
|
|
65
|
-
let syncing = false;
|
|
66
|
-
|
|
67
|
-
// Pre-normalize all mapping entries
|
|
68
|
-
const normalizedMap = {};
|
|
69
|
-
for (const [prop, entry] of Object.entries(mapping)) {
|
|
70
|
-
normalizedMap[prop] = normalizeMapping(entry);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Read URL params into component state
|
|
75
|
-
*/
|
|
76
|
-
function readFromURL() {
|
|
77
|
-
if (syncing) return;
|
|
78
|
-
syncing = true;
|
|
79
|
-
const query = parseQuery(component.$['ROUTER/query']);
|
|
80
|
-
for (const [prop, { param, defaultVal, type }] of Object.entries(normalizedMap)) {
|
|
81
|
-
const rawValue = query[param];
|
|
82
|
-
if (rawValue !== undefined) {
|
|
83
|
-
const val = castValue(rawValue, type);
|
|
84
|
-
if (component.$[prop] !== val) {
|
|
85
|
-
component.$[prop] = val;
|
|
86
|
-
}
|
|
87
|
-
} else if (defaultVal !== undefined) {
|
|
88
|
-
// Apply default when param missing from URL
|
|
89
|
-
if (component.$[prop] !== defaultVal) {
|
|
90
|
-
component.$[prop] = defaultVal;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
syncing = false;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Write component state to URL params
|
|
99
|
-
* @param {string} prop - Changed property name
|
|
100
|
-
*/
|
|
101
|
-
function writeToURL(prop) {
|
|
102
|
-
if (syncing) return;
|
|
103
|
-
if (component.$['ROUTER/panel'] !== panelName) return;
|
|
104
|
-
syncing = true;
|
|
105
|
-
const { param, defaultVal } = normalizedMap[prop];
|
|
106
|
-
const value = component.$[prop];
|
|
107
|
-
// Skip writing default values to keep URL clean
|
|
108
|
-
if (value === defaultVal) {
|
|
109
|
-
updateParams({ [param]: '' });
|
|
110
|
-
} else {
|
|
111
|
-
updateParams({ [param]: String(value) });
|
|
112
|
-
}
|
|
113
|
-
syncing = false;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Subscribe to route changes — read URL when this panel becomes active
|
|
117
|
-
component.sub('ROUTER/panel', (panel) => {
|
|
118
|
-
if (panel === panelName) {
|
|
119
|
-
readFromURL();
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
// Subscribe to query changes — update component when URL params change
|
|
124
|
-
component.sub('ROUTER/query', () => {
|
|
125
|
-
if (component.$['ROUTER/panel'] !== panelName) return;
|
|
126
|
-
readFromURL();
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
// Subscribe to component property changes — write to URL
|
|
130
|
-
for (const prop of Object.keys(normalizedMap)) {
|
|
131
|
-
component.sub(prop, () => {
|
|
132
|
-
if (component.$['ROUTER/panel'] === panelName) {
|
|
133
|
-
writeToURL(prop);
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Initial read if already on this panel
|
|
139
|
-
if (component.$['ROUTER/panel'] === panelName) {
|
|
140
|
-
readFromURL();
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* setupPanelRouting — high-level panel routing setup
|
|
146
|
-
*
|
|
147
|
-
* Centralizes all routing logic for a panel:
|
|
148
|
-
* - Panel activation (onActivate callback)
|
|
149
|
-
* - List/detail switching via ROUTER/subpath
|
|
150
|
-
* - Tab sync via ?tab= query param
|
|
151
|
-
*
|
|
152
|
-
* Convention:
|
|
153
|
-
* #panel → list view, default tab
|
|
154
|
-
* #panel?tab=groups → list view, groups tab
|
|
155
|
-
* #panel/{id} → detail view
|
|
156
|
-
*
|
|
157
|
-
* Component requirements:
|
|
158
|
-
* - ref="listWrap" → container for list view (hidden when detail)
|
|
159
|
-
* - <detail-component> → detail view element (hidden when list)
|
|
160
|
-
* - $.activeTab → tab state property (if tabs configured)
|
|
161
|
-
*
|
|
162
|
-
* @param {import('@symbiotejs/symbiote').default} component
|
|
163
|
-
* @param {string} panelName - Panel section ID (e.g. 'users')
|
|
164
|
-
* @param {Object} config
|
|
165
|
-
* @param {string[]} [config.tabs] - Tab names, first is default
|
|
166
|
-
* @param {{ component: string, loadMethod: string }} [config.detail] - Detail view config
|
|
167
|
-
* @param {Function} [config.onActivate] - Called when panel becomes active (list mode)
|
|
168
|
-
* @param {Object} [config.syncParams] - Additional params to sync via syncWithRouter
|
|
169
|
-
*
|
|
170
|
-
* @example
|
|
171
|
-
* renderCallback() {
|
|
172
|
-
* setupPanelRouting(this, 'users', {
|
|
173
|
-
* tabs: ['users', 'groups'],
|
|
174
|
-
* detail: { component: 'user-detail-view', loadMethod: 'loadUser' },
|
|
175
|
-
* onActivate: () => this.#loadData(),
|
|
176
|
-
* });
|
|
177
|
-
* }
|
|
178
|
-
*/
|
|
179
|
-
export function setupPanelRouting(component, panelName, config = {}) {
|
|
180
|
-
const { tabs, detail, onActivate, syncParams } = config;
|
|
181
|
-
|
|
182
|
-
// --- Tab sync via ?tab= ---
|
|
183
|
-
if (tabs && tabs.length > 0) {
|
|
184
|
-
const defaultTab = tabs[0];
|
|
185
|
-
syncWithRouter(component, panelName, {
|
|
186
|
-
activeTab: { param: 'tab', default: defaultTab },
|
|
187
|
-
...(syncParams || {}),
|
|
188
|
-
});
|
|
189
|
-
} else if (syncParams) {
|
|
190
|
-
syncWithRouter(component, panelName, syncParams);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Check and apply list/detail mode based on ROUTER/subpath
|
|
195
|
-
*/
|
|
196
|
-
function checkDetailMode() {
|
|
197
|
-
if (component.$['ROUTER/panel'] !== panelName) return;
|
|
198
|
-
|
|
199
|
-
const subpath = component.$['ROUTER/subpath'];
|
|
200
|
-
const listWrap = component.ref?.listWrap;
|
|
201
|
-
const isDetail = !!(detail && subpath);
|
|
202
|
-
|
|
203
|
-
// Global signal — CSS can hide tabs/actions via [data-detail]
|
|
204
|
-
component.toggleAttribute('data-detail', isDetail);
|
|
205
|
-
|
|
206
|
-
if (isDetail) {
|
|
207
|
-
// --- Detail mode ---
|
|
208
|
-
if (listWrap) listWrap.hidden = true;
|
|
209
|
-
|
|
210
|
-
const detailEl = component.querySelector(detail.component);
|
|
211
|
-
if (detailEl) {
|
|
212
|
-
detailEl.hidden = false;
|
|
213
|
-
if (typeof detailEl[detail.loadMethod] === 'function') {
|
|
214
|
-
detailEl[detail.loadMethod](subpath);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
} else {
|
|
218
|
-
// --- List mode ---
|
|
219
|
-
if (listWrap) listWrap.hidden = false;
|
|
220
|
-
|
|
221
|
-
if (detail) {
|
|
222
|
-
const detailEl = component.querySelector(detail.component);
|
|
223
|
-
if (detailEl) detailEl.hidden = true;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
if (onActivate) onActivate();
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// Subscribe to panel activation
|
|
231
|
-
component.sub('ROUTER/panel', (panel) => {
|
|
232
|
-
if (panel === panelName) {
|
|
233
|
-
checkDetailMode();
|
|
234
|
-
}
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
// Subscribe to subpath changes (list ↔ detail)
|
|
238
|
-
if (detail) {
|
|
239
|
-
component.sub('ROUTER/subpath', () => {
|
|
240
|
-
if (component.$['ROUTER/panel'] === panelName) {
|
|
241
|
-
checkDetailMode();
|
|
242
|
-
}
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// Initial check if already on this panel
|
|
247
|
-
if (component.$['ROUTER/panel'] === panelName) {
|
|
248
|
-
checkDetailMode();
|
|
249
|
-
}
|
|
250
|
-
}
|