fastapi-voyager 0.15.0__py3-none-any.whl → 0.15.1__py3-none-any.whl

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,116 +1,135 @@
1
- import SchemaCodeDisplay from "./component/schema-code-display.js";
2
- import RouteCodeDisplay from "./component/route-code-display.js";
3
- import Demo from "./component/demo.js";
4
- import RenderGraph from "./component/render-graph.js";
5
- import { GraphUI } from "./graph-ui.js";
6
- import { store } from "./store.js";
7
-
8
- const { createApp, onMounted, ref, watch } = window.Vue;
1
+ import SchemaCodeDisplay from "./component/schema-code-display.js"
2
+ import RouteCodeDisplay from "./component/route-code-display.js"
3
+ import Demo from "./component/demo.js"
4
+ import RenderGraph from "./component/render-graph.js"
5
+ import { GraphUI } from "./graph-ui.js"
6
+ import { store } from "./store.js"
7
+
8
+ const { createApp, onMounted, ref, watch } = window.Vue
9
+
10
+ // Load toggle states from localStorage
11
+ function loadToggleState(key, defaultValue = false) {
12
+ if (typeof window === "undefined") return defaultValue
13
+ try {
14
+ const saved = localStorage.getItem(key)
15
+ return saved !== null ? JSON.parse(saved) : defaultValue
16
+ } catch (e) {
17
+ console.warn(`Failed to load ${key} from localStorage`, e)
18
+ return defaultValue
19
+ }
20
+ }
9
21
 
10
22
  const app = createApp({
11
23
  setup() {
12
- let graphUI = null;
13
- const allSchemaOptions = ref([]);
14
- const erDiagramLoading = ref(false);
15
- const erDiagramCache = ref("");
24
+ let graphUI = null
25
+ const allSchemaOptions = ref([])
26
+ const erDiagramLoading = ref(false)
27
+ const erDiagramCache = ref("")
28
+
29
+ // Initialize toggle states from localStorage
30
+ store.state.modeControl.pydanticResolveMetaEnabled = loadToggleState(
31
+ "pydantic_resolve_meta",
32
+ false
33
+ )
34
+ store.state.filter.hidePrimitiveRoute = loadToggleState("hide_primitive", false)
35
+ store.state.filter.brief = loadToggleState("brief_mode", false)
36
+ store.state.filter.showModule = loadToggleState("show_module_cluster", false)
16
37
 
17
38
  function initGraphUI() {
18
39
  if (graphUI) {
19
- return;
40
+ return
20
41
  }
21
42
  graphUI = new GraphUI("#graph", {
22
43
  onSchemaShiftClick: (id) => {
23
44
  if (store.state.graph.schemaKeys.has(id)) {
24
- store.state.previousTagRoute.tag = store.state.leftPanel.tag;
25
- store.state.previousTagRoute.routeId = store.state.leftPanel.routeId;
26
- store.state.previousTagRoute.hasValue = true;
27
- store.state.search.mode = true;
28
- store.state.search.schemaName = id;
29
- onSearch();
45
+ store.state.previousTagRoute.tag = store.state.leftPanel.tag
46
+ store.state.previousTagRoute.routeId = store.state.leftPanel.routeId
47
+ store.state.previousTagRoute.hasValue = true
48
+ store.state.search.mode = true
49
+ store.state.search.schemaName = id
50
+ onSearch()
30
51
  }
31
52
  },
32
53
  onSchemaClick: (id) => {
33
- resetDetailPanels();
54
+ resetDetailPanels()
34
55
  if (store.state.graph.schemaKeys.has(id)) {
35
- store.state.schemaDetail.schemaCodeName = id;
36
- store.state.rightDrawer.drawer = true;
56
+ store.state.schemaDetail.schemaCodeName = id
57
+ store.state.rightDrawer.drawer = true
37
58
  }
38
59
  if (id in store.state.graph.routeItems) {
39
- store.state.routeDetail.routeCodeId = id;
40
- store.state.routeDetail.show = true;
60
+ store.state.routeDetail.routeCodeId = id
61
+ store.state.routeDetail.show = true
41
62
  }
42
63
  },
43
64
  resetCb: () => {
44
- resetDetailPanels();
65
+ resetDetailPanels()
45
66
  },
46
- });
67
+ })
47
68
  }
48
69
 
49
70
  function rebuildSchemaOptions() {
50
- const dict = store.state.graph.schemaMap || {};
71
+ const dict = store.state.graph.schemaMap || {}
51
72
  const opts = Object.values(dict).map((s) => ({
52
73
  label: s.name,
53
74
  desc: s.id,
54
75
  value: s.id,
55
- }));
56
- allSchemaOptions.value = opts;
57
- store.state.search.schemaOptions = opts.slice();
58
- populateFieldOptions(store.state.search.schemaName);
76
+ }))
77
+ allSchemaOptions.value = opts
78
+ store.state.search.schemaOptions = opts.slice()
79
+ populateFieldOptions(store.state.search.schemaName)
59
80
  }
60
81
 
61
82
  function populateFieldOptions(schemaId) {
62
83
  if (!schemaId) {
63
- store.state.search.fieldOptions = [];
64
- store.state.search.fieldName = null;
65
- return;
84
+ store.state.search.fieldOptions = []
85
+ store.state.search.fieldName = null
86
+ return
66
87
  }
67
- const schema = store.state.graph.schemaMap?.[schemaId];
88
+ const schema = store.state.graph.schemaMap?.[schemaId]
68
89
  if (!schema) {
69
- store.state.search.fieldOptions = [];
70
- store.state.search.fieldName = null;
71
- return;
72
- }
73
- const fields = Array.isArray(schema.fields)
74
- ? schema.fields.map((f) => f.name)
75
- : [];
76
- store.state.search.fieldOptions = fields;
90
+ store.state.search.fieldOptions = []
91
+ store.state.search.fieldName = null
92
+ return
93
+ }
94
+ const fields = Array.isArray(schema.fields) ? schema.fields.map((f) => f.name) : []
95
+ store.state.search.fieldOptions = fields
77
96
  if (!fields.includes(store.state.search.fieldName)) {
78
- store.state.search.fieldName = null;
97
+ store.state.search.fieldName = null
79
98
  }
80
99
  }
81
100
 
82
101
  function filterSearchSchemas(val, update) {
83
- const needle = (val || "").toLowerCase();
102
+ const needle = (val || "").toLowerCase()
84
103
  update(() => {
85
104
  if (!needle) {
86
- store.state.search.schemaOptions = allSchemaOptions.value.slice();
87
- return;
105
+ store.state.search.schemaOptions = allSchemaOptions.value.slice()
106
+ return
88
107
  }
89
108
  store.state.search.schemaOptions = allSchemaOptions.value.filter((option) =>
90
109
  option.label.toLowerCase().includes(needle)
91
- );
92
- });
110
+ )
111
+ })
93
112
  }
94
113
 
95
114
  function onSearchSchemaChange(val) {
96
- store.state.search.schemaName = val;
97
- store.state.search.mode = false;
115
+ store.state.search.schemaName = val
116
+ store.state.search.mode = false
98
117
  if (!val) {
99
118
  // Clearing the select should only run resetSearch via @clear
100
- return;
119
+ return
101
120
  }
102
- onSearch();
121
+ onSearch()
103
122
  }
104
123
 
105
124
  function readQuerySelection() {
106
125
  if (typeof window === "undefined") {
107
- return { tag: null, route: null };
126
+ return { tag: null, route: null }
108
127
  }
109
- const params = new URLSearchParams(window.location.search);
128
+ const params = new URLSearchParams(window.location.search)
110
129
  return {
111
130
  tag: params.get("tag") || null,
112
131
  route: params.get("route") || null,
113
- };
132
+ }
114
133
  }
115
134
 
116
135
  function findTagByRoute(routeId) {
@@ -118,82 +137,79 @@ const app = createApp({
118
137
  store.state.leftPanel.tags.find((tag) =>
119
138
  (tag.routes || []).some((route) => route.id === routeId)
120
139
  )?.name || null
121
- );
140
+ )
122
141
  }
123
142
 
124
143
  function syncSelectionToUrl() {
125
144
  if (typeof window === "undefined") {
126
- return;
145
+ return
127
146
  }
128
- const params = new URLSearchParams(window.location.search);
147
+ const params = new URLSearchParams(window.location.search)
129
148
  if (store.state.leftPanel.tag) {
130
- params.set("tag", store.state.leftPanel.tag);
149
+ params.set("tag", store.state.leftPanel.tag)
131
150
  } else {
132
- params.delete("tag");
151
+ params.delete("tag")
133
152
  }
134
153
  if (store.state.leftPanel.routeId) {
135
- params.set("route", store.state.leftPanel.routeId);
154
+ params.set("route", store.state.leftPanel.routeId)
136
155
  } else {
137
- params.delete("route");
156
+ params.delete("route")
138
157
  }
139
- const hash = window.location.hash || "";
140
- const search = params.toString();
141
- const base = window.location.pathname;
142
- const newUrl = search ? `${base}?${search}${hash}` : `${base}${hash}`;
143
- window.history.replaceState({}, "", newUrl);
158
+ const hash = window.location.hash || ""
159
+ const search = params.toString()
160
+ const base = window.location.pathname
161
+ const newUrl = search ? `${base}?${search}${hash}` : `${base}${hash}`
162
+ window.history.replaceState({}, "", newUrl)
144
163
  }
145
164
 
146
165
  function applySelectionFromQuery(selection) {
147
- let applied = false;
148
- if (
149
- selection.tag &&
150
- store.state.leftPanel.tags.some((tag) => tag.name === selection.tag)
151
- ) {
152
- store.state.leftPanel.tag = selection.tag;
153
- store.state.leftPanel._tag = selection.tag;
154
- applied = true;
166
+ let applied = false
167
+ if (selection.tag && store.state.leftPanel.tags.some((tag) => tag.name === selection.tag)) {
168
+ store.state.leftPanel.tag = selection.tag
169
+ store.state.leftPanel._tag = selection.tag
170
+ applied = true
155
171
  }
156
172
  if (selection.route && store.state.graph.routeItems?.[selection.route]) {
157
- store.state.leftPanel.routeId = selection.route;
158
- applied = true;
159
- const inferredTag = findTagByRoute(selection.route);
173
+ store.state.leftPanel.routeId = selection.route
174
+ applied = true
175
+ const inferredTag = findTagByRoute(selection.route)
160
176
  if (inferredTag) {
161
- store.state.leftPanel.tag = inferredTag;
162
- store.state.leftPanel._tag = inferredTag;
177
+ store.state.leftPanel.tag = inferredTag
178
+ store.state.leftPanel._tag = inferredTag
163
179
  }
164
180
  }
165
- return applied;
181
+ return applied
166
182
  }
167
183
 
168
184
  async function resetSearch() {
169
- store.state.search.mode = false;
185
+ store.state.search.mode = false
170
186
  console.log(store.state.previousTagRoute.hasValue)
171
187
  console.log(store.state.previousTagRoute)
172
188
  if (store.state.previousTagRoute.hasValue) {
173
- store.state.leftPanel.tag = store.state.previousTagRoute.tag;
174
- store.state.leftPanel._tag = store.state.previousTagRoute.tag;
175
- store.state.leftPanel.routeId = store.state.previousTagRoute.routeId;
176
- store.state.previousTagRoute.hasValue = false;
189
+ store.state.leftPanel.tag = store.state.previousTagRoute.tag
190
+ store.state.leftPanel._tag = store.state.previousTagRoute.tag
191
+ store.state.leftPanel.routeId = store.state.previousTagRoute.routeId
192
+ store.state.previousTagRoute.hasValue = false
177
193
  } else {
178
- store.state.leftPanel.tag = null;
179
- store.state.leftPanel._tag = null;
180
- store.state.leftPanel.routeId = null;
194
+ store.state.leftPanel.tag = null
195
+ store.state.leftPanel._tag = null
196
+ store.state.leftPanel.routeId = null
181
197
  }
182
198
 
183
199
  syncSelectionToUrl()
184
- await loadSearchedTags();
200
+ await loadSearchedTags()
185
201
  renderBasedOnInitialPolicy()
186
202
  }
187
203
 
188
204
  async function onSearch() {
189
- console.log('start search')
190
- store.state.search.mode = true;
191
- store.state.leftPanel.tag = null;
192
- store.state.leftPanel._tag = null;
193
- store.state.leftPanel.routeId = null;
205
+ console.log("start search")
206
+ store.state.search.mode = true
207
+ store.state.leftPanel.tag = null
208
+ store.state.leftPanel._tag = null
209
+ store.state.leftPanel.routeId = null
194
210
  syncSelectionToUrl()
195
- await loadSearchedTags();
196
- await onGenerate();
211
+ await loadSearchedTags()
212
+ await onGenerate()
197
213
  }
198
214
  async function loadSearchedTags() {
199
215
  try {
@@ -204,56 +220,55 @@ const app = createApp({
204
220
  brief: store.state.filter.brief,
205
221
  hide_primitive_route: store.state.filter.hidePrimitiveRoute,
206
222
  show_module: store.state.filter.showModule,
207
- };
223
+ }
208
224
  const res = await fetch("dot-search", {
209
225
  method: "POST",
210
226
  headers: { "Content-Type": "application/json" },
211
227
  body: JSON.stringify(payload),
212
- });
228
+ })
213
229
  if (res.ok) {
214
- const data = await res.json();
215
- const tags = Array.isArray(data.tags) ? data.tags : [];
216
- store.state.leftPanel.tags = tags;
230
+ const data = await res.json()
231
+ const tags = Array.isArray(data.tags) ? data.tags : []
232
+ store.state.leftPanel.tags = tags
217
233
  }
218
234
  } catch (err) {
219
- console.error("dot-search failed", err);
235
+ console.error("dot-search failed", err)
220
236
  }
221
237
  }
222
238
 
223
239
  async function loadInitial() {
224
- store.state.initializing = true;
240
+ store.state.initializing = true
225
241
  try {
226
- const res = await fetch("dot");
227
- const data = await res.json();
228
- store.state.leftPanel.tags = Array.isArray(data.tags) ? data.tags : [];
242
+ const res = await fetch("dot")
243
+ const data = await res.json()
244
+ store.state.leftPanel.tags = Array.isArray(data.tags) ? data.tags : []
229
245
 
230
- const schemasArr = Array.isArray(data.schemas) ? data.schemas : [];
246
+ const schemasArr = Array.isArray(data.schemas) ? data.schemas : []
231
247
  // Build dict keyed by id for faster lookups and simpler prop passing
232
- const schemaMap = Object.fromEntries(schemasArr.map((s) => [s.id, s]));
233
- store.state.graph.schemaMap = schemaMap;
234
- store.state.graph.schemaKeys = new Set(Object.keys(schemaMap));
248
+ const schemaMap = Object.fromEntries(schemasArr.map((s) => [s.id, s]))
249
+ store.state.graph.schemaMap = schemaMap
250
+ store.state.graph.schemaKeys = new Set(Object.keys(schemaMap))
235
251
  store.state.graph.routeItems = data.tags
236
252
  .map((t) => t.routes)
237
253
  .flat()
238
254
  .reduce((acc, r) => {
239
- acc[r.id] = r;
240
- return acc;
241
- }, {});
242
- store.state.modeControl.briefModeEnabled =
243
- data.enable_brief_mode || false;
244
- store.state.version = data.version || "";
245
- store.state.swagger.url = data.swagger_url || null;
246
- store.state.config.has_er_diagram = data.has_er_diagram || false;
247
- store.state.config.enable_pydantic_resolve_meta = data.enable_pydantic_resolve_meta || false;
248
-
249
- rebuildSchemaOptions();
250
-
251
- const querySelection = readQuerySelection();
252
- const restoredFromQuery = applySelectionFromQuery(querySelection);
255
+ acc[r.id] = r
256
+ return acc
257
+ }, {})
258
+ store.state.modeControl.briefModeEnabled = data.enable_brief_mode || false
259
+ store.state.version = data.version || ""
260
+ store.state.swagger.url = data.swagger_url || null
261
+ store.state.config.has_er_diagram = data.has_er_diagram || false
262
+ store.state.config.enable_pydantic_resolve_meta = data.enable_pydantic_resolve_meta || false
263
+
264
+ rebuildSchemaOptions()
265
+
266
+ const querySelection = readQuerySelection()
267
+ const restoredFromQuery = applySelectionFromQuery(querySelection)
253
268
  if (restoredFromQuery) {
254
- syncSelectionToUrl();
255
- onGenerate();
256
- return;
269
+ syncSelectionToUrl()
270
+ onGenerate()
271
+ return
257
272
  } else {
258
273
  store.state.config.initial_page_policy = data.initial_page_policy
259
274
  renderBasedOnInitialPolicy()
@@ -261,50 +276,44 @@ const app = createApp({
261
276
 
262
277
  // default route options placeholder
263
278
  } catch (e) {
264
- console.error("Initial load failed", e);
279
+ console.error("Initial load failed", e)
265
280
  } finally {
266
- store.state.initializing = false;
281
+ store.state.initializing = false
267
282
  }
268
283
  }
269
284
 
270
285
  async function renderBasedOnInitialPolicy() {
271
- switch (store.state.config.initial_page_policy) {
272
- case "full":
273
- onGenerate();
274
- return;
275
- case "empty":
276
- return;
277
- case "first":
278
- store.state.leftPanel.tag =
279
- store.state.leftPanel.tags.length > 0
280
- ? store.state.leftPanel.tags[0].name
281
- : null;
282
- store.state.leftPanel._tag = store.state.leftPanel.tag;
283
- syncSelectionToUrl()
284
- onGenerate();
285
- return;
286
- }
286
+ switch (store.state.config.initial_page_policy) {
287
+ case "full":
288
+ onGenerate()
289
+ return
290
+ case "empty":
291
+ return
292
+ case "first":
293
+ store.state.leftPanel.tag =
294
+ store.state.leftPanel.tags.length > 0 ? store.state.leftPanel.tags[0].name : null
295
+ store.state.leftPanel._tag = store.state.leftPanel.tag
296
+ syncSelectionToUrl()
297
+ onGenerate()
298
+ return
299
+ }
287
300
  }
288
301
 
289
302
  async function onGenerate(resetZoom = true) {
290
303
  switch (store.state.mode) {
291
304
  case "voyager":
292
- await renderVoyager(resetZoom);
293
- break;
305
+ await renderVoyager(resetZoom)
306
+ break
294
307
  case "er-diagram":
295
- await renderErDiagram(resetZoom);
296
- break;
308
+ await renderErDiagram(resetZoom)
309
+ break
297
310
  }
298
311
  }
299
312
 
300
313
  async function renderVoyager(resetZoom = true) {
301
- const activeSchema = store.state.search.mode
302
- ? store.state.search.schemaName
303
- : null;
304
- const activeField = store.state.search.mode
305
- ? store.state.search.fieldName
306
- : null;
307
- store.state.generating = true;
314
+ const activeSchema = store.state.search.mode ? store.state.search.schemaName : null
315
+ const activeField = store.state.search.mode ? store.state.search.fieldName : null
316
+ store.state.generating = true
308
317
  try {
309
318
  const payload = {
310
319
  tags: store.state.leftPanel.tag ? [store.state.leftPanel.tag] : null,
@@ -315,212 +324,232 @@ const app = createApp({
315
324
  brief: store.state.filter.brief,
316
325
  hide_primitive_route: store.state.filter.hidePrimitiveRoute,
317
326
  show_module: store.state.filter.showModule,
318
- show_pydantic_resolve_meta: store.state.modeControl.pydanticResolveMetaEnabled
319
- };
320
- initGraphUI();
327
+ show_pydantic_resolve_meta: store.state.modeControl.pydanticResolveMetaEnabled,
328
+ }
329
+ initGraphUI()
321
330
  const res = await fetch("dot", {
322
331
  method: "POST",
323
332
  headers: { "Content-Type": "application/json" },
324
333
  body: JSON.stringify(payload),
325
- });
326
- const dotText = await res.text();
334
+ })
335
+ const dotText = await res.text()
327
336
 
328
- await graphUI.render(dotText, resetZoom);
337
+ await graphUI.render(dotText, resetZoom)
329
338
  } catch (e) {
330
- console.error("Generate failed", e);
339
+ console.error("Generate failed", e)
331
340
  } finally {
332
- store.state.generating = false;
341
+ store.state.generating = false
333
342
  }
334
343
  }
335
344
 
336
345
  function resetDetailPanels() {
337
- store.state.rightDrawer.drawer = false;
338
- store.state.routeDetail.show = false;
339
- store.state.schemaDetail.schemaCodeName = "";
346
+ store.state.rightDrawer.drawer = false
347
+ store.state.routeDetail.show = false
348
+ store.state.schemaDetail.schemaCodeName = ""
340
349
  }
341
350
 
342
351
  async function onReset() {
343
- store.state.leftPanel.tag = null;
344
- store.state.leftPanel._tag = null;
345
- store.state.leftPanel.routeId = null;
346
- syncSelectionToUrl()
347
- onGenerate()
352
+ store.state.leftPanel.tag = null
353
+ store.state.leftPanel._tag = null
354
+ store.state.leftPanel.routeId = null
355
+ syncSelectionToUrl()
356
+ onGenerate()
348
357
  }
349
358
 
350
359
  async function togglePydanticResolveMeta(val) {
351
- store.state.modeControl.pydanticResolveMetaEnabled = val;
352
- onGenerate();
360
+ store.state.modeControl.pydanticResolveMetaEnabled = val
361
+ try {
362
+ localStorage.setItem("pydantic_resolve_meta", JSON.stringify(val))
363
+ } catch (e) {
364
+ console.warn("Failed to save pydantic_resolve_meta to localStorage", e)
365
+ }
366
+ onGenerate()
353
367
  }
354
368
 
355
369
  async function renderErDiagram(resetZoom = true) {
356
- initGraphUI();
357
- erDiagramLoading.value = true;
370
+ initGraphUI()
371
+ erDiagramLoading.value = true
358
372
  const payload = {
359
373
  show_fields: store.state.filter.showFields,
360
374
  show_module: store.state.filter.showModule,
361
- };
375
+ }
362
376
  try {
363
- const res = await fetch("er-diagram", {
377
+ const res = await fetch("er-diagram", {
364
378
  method: "POST",
365
379
  headers: { "Content-Type": "application/json" },
366
380
  body: JSON.stringify(payload),
367
- });
381
+ })
368
382
  if (!res.ok) {
369
- throw new Error(`failed with status ${res.status}`);
383
+ throw new Error(`failed with status ${res.status}`)
370
384
  }
371
- const dot = await res.text();
372
- erDiagramCache.value = dot;
373
- await graphUI.render(dot, resetZoom);
385
+ const dot = await res.text()
386
+ erDiagramCache.value = dot
387
+ await graphUI.render(dot, resetZoom)
374
388
  } catch (err) {
375
389
  console.error(err)
376
390
  } finally {
377
- erDiagramLoading.value = false;
391
+ erDiagramLoading.value = false
378
392
  }
379
393
  }
380
394
 
381
395
  async function onModeChange(val) {
382
396
  if (val === "er-diagram") {
383
- // clear search
397
+ // clear search
384
398
  store.state.search.schemaName = null
385
399
  store.state.search.fieldName = null
386
400
  store.state.search.invisible = true
387
401
 
388
402
  if (store.state.leftPanel.width > 0) {
389
- store.state.leftPanel.previousWidth = store.state.leftPanel.width;
403
+ store.state.leftPanel.previousWidth = store.state.leftPanel.width
390
404
  }
391
- store.state.leftPanel.width = 0;
392
- await renderErDiagram();
405
+ store.state.leftPanel.width = 0
406
+ await renderErDiagram()
393
407
  } else {
394
408
  store.state.search.invisible = false
395
-
396
- const fallbackWidth = store.state.leftPanel.previousWidth || 300;
397
- store.state.leftPanel.width = fallbackWidth;
398
- await onGenerate();
409
+
410
+ const fallbackWidth = store.state.leftPanel.previousWidth || 300
411
+ store.state.leftPanel.width = fallbackWidth
412
+ await onGenerate()
399
413
  }
400
414
  }
401
415
 
402
416
  function toggleTag(tagName, expanded = null) {
403
417
  if (expanded === true || store.state.search.mode === true) {
404
- store.state.leftPanel._tag = tagName;
405
- store.state.leftPanel.tag = tagName;
406
- store.state.leftPanel.routeId = "";
418
+ store.state.leftPanel._tag = tagName
419
+ store.state.leftPanel.tag = tagName
420
+ store.state.leftPanel.routeId = ""
407
421
 
408
- store.state.schemaDetail.schemaCodeName = "";
409
- onGenerate();
422
+ store.state.schemaDetail.schemaCodeName = ""
423
+ onGenerate()
410
424
  } else {
411
- store.state.leftPanel._tag = null;
425
+ store.state.leftPanel._tag = null
412
426
  }
413
427
 
414
- store.state.rightDrawer.drawer = false;
415
- store.state.routeDetail.show = false;
416
- syncSelectionToUrl();
428
+ store.state.rightDrawer.drawer = false
429
+ store.state.routeDetail.show = false
430
+ syncSelectionToUrl()
417
431
  }
418
432
 
419
433
  function selectRoute(routeId) {
420
434
  // find belonging tag
421
- const belongingTag = findTagByRoute(routeId);
435
+ const belongingTag = findTagByRoute(routeId)
422
436
  if (belongingTag) {
423
- store.state.leftPanel.tag = belongingTag;
424
- store.state.leftPanel._tag = belongingTag;
437
+ store.state.leftPanel.tag = belongingTag
438
+ store.state.leftPanel._tag = belongingTag
425
439
  }
426
440
 
427
441
  if (store.state.leftPanel.routeId === routeId) {
428
- store.state.leftPanel.routeId = "";
442
+ store.state.leftPanel.routeId = ""
429
443
  } else {
430
- store.state.leftPanel.routeId = routeId;
444
+ store.state.leftPanel.routeId = routeId
431
445
  }
432
446
 
433
- store.state.rightDrawer.drawer = false;
434
- store.state.routeDetail.show = false;
435
- store.state.schemaDetail.schemaCodeName = "";
436
- syncSelectionToUrl();
437
- onGenerate();
447
+ store.state.rightDrawer.drawer = false
448
+ store.state.routeDetail.show = false
449
+ store.state.schemaDetail.schemaCodeName = ""
450
+ syncSelectionToUrl()
451
+ onGenerate()
438
452
  }
439
453
 
440
454
  function toggleShowModule(val) {
441
- store.state.filter.showModule = val;
442
- onGenerate();
455
+ store.state.filter.showModule = val
456
+ try {
457
+ localStorage.setItem("show_module_cluster", JSON.stringify(val))
458
+ } catch (e) {
459
+ console.warn("Failed to save show_module_cluster to localStorage", e)
460
+ }
461
+ onGenerate()
443
462
  }
444
463
 
445
464
  function toggleShowField(field) {
446
- store.state.filter.showFields = field;
447
- onGenerate(false);
465
+ store.state.filter.showFields = field
466
+ onGenerate(false)
448
467
  }
449
468
 
450
469
  function toggleBrief(val) {
451
- store.state.filter.brief = val;
452
- onGenerate();
470
+ store.state.filter.brief = val
471
+ try {
472
+ localStorage.setItem("brief_mode", JSON.stringify(val))
473
+ } catch (e) {
474
+ console.warn("Failed to save brief_mode to localStorage", e)
475
+ }
476
+ onGenerate()
453
477
  }
454
478
 
455
479
  function toggleHidePrimitiveRoute(val) {
456
- store.state.filter.hidePrimitiveRoute = val;
457
- onGenerate(false);
480
+ store.state.filter.hidePrimitiveRoute = val
481
+ try {
482
+ localStorage.setItem("hide_primitive", JSON.stringify(val))
483
+ } catch (e) {
484
+ console.warn("Failed to save hide_primitive to localStorage", e)
485
+ }
486
+ onGenerate(false)
458
487
  }
459
488
 
460
489
  function startDragDrawer(e) {
461
- const startX = e.clientX;
462
- const startWidth = store.state.rightDrawer.width;
490
+ const startX = e.clientX
491
+ const startWidth = store.state.rightDrawer.width
463
492
 
464
493
  function onMouseMove(moveEvent) {
465
- const deltaX = startX - moveEvent.clientX;
466
- const newWidth = Math.max(300, Math.min(800, startWidth + deltaX));
467
- store.state.rightDrawer.width = newWidth;
494
+ const deltaX = startX - moveEvent.clientX
495
+ const newWidth = Math.max(300, Math.min(800, startWidth + deltaX))
496
+ store.state.rightDrawer.width = newWidth
468
497
  }
469
498
 
470
499
  function onMouseUp() {
471
- document.removeEventListener("mousemove", onMouseMove);
472
- document.removeEventListener("mouseup", onMouseUp);
473
- document.body.style.cursor = "";
474
- document.body.style.userSelect = "";
500
+ document.removeEventListener("mousemove", onMouseMove)
501
+ document.removeEventListener("mouseup", onMouseUp)
502
+ document.body.style.cursor = ""
503
+ document.body.style.userSelect = ""
475
504
  }
476
505
 
477
- document.addEventListener("mousemove", onMouseMove);
478
- document.addEventListener("mouseup", onMouseUp);
479
- document.body.style.cursor = "col-resize";
480
- document.body.style.userSelect = "none";
481
- e.preventDefault();
506
+ document.addEventListener("mousemove", onMouseMove)
507
+ document.addEventListener("mouseup", onMouseUp)
508
+ document.body.style.cursor = "col-resize"
509
+ document.body.style.userSelect = "none"
510
+ e.preventDefault()
482
511
  }
483
512
 
484
513
  watch(
485
514
  () => store.state.graph.schemaMap,
486
515
  () => {
487
- rebuildSchemaOptions();
516
+ rebuildSchemaOptions()
488
517
  },
489
518
  { deep: false }
490
- );
519
+ )
491
520
 
492
521
  watch(
493
522
  () => store.state.leftPanel.width,
494
523
  (val) => {
495
524
  if (store.state.mode === "voyager" && typeof val === "number" && val > 0) {
496
- store.state.leftPanel.previousWidth = val;
525
+ store.state.leftPanel.previousWidth = val
497
526
  }
498
527
  }
499
- );
528
+ )
500
529
 
501
530
  watch(
502
531
  () => store.state.mode,
503
532
  (mode) => {
504
- onModeChange(mode);
533
+ onModeChange(mode)
505
534
  }
506
- );
535
+ )
507
536
 
508
537
  watch(
509
538
  () => store.state.search.schemaName,
510
539
  (schemaId) => {
511
- store.state.search.schemaOptions = allSchemaOptions.value.slice();
512
- populateFieldOptions(schemaId);
540
+ store.state.search.schemaOptions = allSchemaOptions.value.slice()
541
+ populateFieldOptions(schemaId)
513
542
  if (!schemaId) {
514
- store.state.search.mode = false;
543
+ store.state.search.mode = false
515
544
  }
516
545
  }
517
- );
546
+ )
518
547
 
519
548
  onMounted(async () => {
520
- document.body.classList.remove("app-loading");
521
- await loadInitial();
549
+ document.body.classList.remove("app-loading")
550
+ await loadInitial()
522
551
  // Reveal app content only after initial JS/data is ready
523
- });
552
+ })
524
553
 
525
554
  return {
526
555
  store,
@@ -539,21 +568,21 @@ const app = createApp({
539
568
  toggleShowModule,
540
569
  onModeChange,
541
570
  renderErDiagram,
542
- togglePydanticResolveMeta
543
- };
571
+ togglePydanticResolveMeta,
572
+ }
544
573
  },
545
- });
574
+ })
546
575
 
547
- app.use(window.Quasar);
576
+ app.use(window.Quasar)
548
577
 
549
578
  // Set Quasar primary theme color to green
550
579
  if (window.Quasar && typeof window.Quasar.setCssVar === "function") {
551
- window.Quasar.setCssVar("primary", "#009485");
580
+ window.Quasar.setCssVar("primary", "#009485")
552
581
  }
553
582
 
554
- app.component("schema-code-display", SchemaCodeDisplay); // double click to see node details
555
- app.component("route-code-display", RouteCodeDisplay); // double click to see route details
556
- app.component("render-graph", RenderGraph); // for debug, render pasted dot content
557
- app.component("demo-component", Demo);
583
+ app.component("schema-code-display", SchemaCodeDisplay) // double click to see node details
584
+ app.component("route-code-display", RouteCodeDisplay) // double click to see route details
585
+ app.component("render-graph", RenderGraph) // for debug, render pasted dot content
586
+ app.component("demo-component", Demo)
558
587
 
559
- app.mount("#q-app");
588
+ app.mount("#q-app")