fastapi-voyager 0.12.4__py3-none-any.whl → 0.12.5__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,2 +1,2 @@
1
1
  __all__ = ["__version__"]
2
- __version__ = "0.12.4"
2
+ __version__ = "0.12.5"
@@ -10,7 +10,7 @@ export default defineComponent({
10
10
  },
11
11
  template: `
12
12
  <div>
13
- <p>Count: {{ store.state.count }}</p>
13
+ <p>Count: {{ store.state.item.count }}</p>
14
14
  <button @click="store.mutations.increment()">Add</button>
15
15
  </div>
16
16
  `
@@ -14,7 +14,6 @@ export default defineComponent({
14
14
  name: "SchemaFieldFilter",
15
15
  props: {
16
16
  schemaName: { type: String, default: null }, // external injection triggers auto-query
17
- // externally provided schemas dict (state.rawSchemasFull): { [id]: schema }
18
17
  schemas: { type: Object, default: () => ({}) },
19
18
  },
20
19
  emits: ["queried", "close"],
@@ -82,9 +82,7 @@ export class GraphUI {
82
82
  }
83
83
  const set = $();
84
84
  set.push(this);
85
- // const obj = { set, direction: "bidirectional" };
86
85
  const schemaName = event.currentTarget.dataset.name;
87
- // self.currentSelection = [obj];
88
86
  if (schemaName) {
89
87
  try {
90
88
  self.options.onSchemaClick(schemaName);
@@ -93,6 +91,17 @@ export class GraphUI {
93
91
  }
94
92
  }
95
93
  });
94
+
95
+ self.gv.edges().click(function (event) {
96
+ // const set = $();
97
+ // const downStreamNode = event.currentTarget.dataset.name.split("->")[1];
98
+ // const nodes = self.gv.nodesByName();
99
+ // set.push(nodes[downStreamNode]);
100
+ // const obj = { set, direction: "single" };
101
+ // self.currentSelection = [obj];
102
+ // todo highlight edge and downstream node
103
+ })
104
+
96
105
  self.gv.nodes().click(function (event) {
97
106
  const set = $();
98
107
  set.push(this);
@@ -100,7 +109,6 @@ export class GraphUI {
100
109
 
101
110
  const schemaName = event.currentTarget.dataset.name;
102
111
  if (event.shiftKey && self.options.onSchemaClick) {
103
- // try data-name or title text
104
112
  if (schemaName) {
105
113
  try {
106
114
  self.options.onSchemaShiftClick(schemaName);
@@ -123,19 +131,15 @@ export class GraphUI {
123
131
  const set = $();
124
132
  set.push(this);
125
133
  const obj = { set, direction: "single" };
126
- if (event.ctrlKey || event.metaKey || event.shiftKey) {
127
- self.currentSelection.push(obj);
128
- } else {
129
- self.currentSelection = [obj];
130
- }
134
+ self.currentSelection = [obj];
131
135
  self._highlight();
132
136
  });
133
137
 
134
- // svg 背景点击高亮清空
138
+ // click background to reset highlight
135
139
  $(document)
136
140
  .off("click.graphui")
137
141
  .on("click.graphui", function (evt) {
138
- // 如果点击目标不在 graph 容器内,直接退出
142
+ // if outside container, do nothing
139
143
  const graphContainer = $(self.selector)[0];
140
144
  if (
141
145
  !graphContainer ||
@@ -105,13 +105,13 @@
105
105
  >
106
106
  <q-icon class="q-mr-sm" name="satellite_alt"></q-icon>
107
107
  <span> FastAPI Voyager </span>
108
- <span v-if="state.version" style="font-size: 12px; margin-left: 8px; font-weight: normal;">{{ state.version }}</span>
108
+ <span v-if="store.state.version" style="font-size: 12px; margin-left: 8px; font-weight: normal;">{{ store.state.version }}</span>
109
109
  </div>
110
110
  <div class="col-auto" style="font-size: 16px">
111
111
  <q-option-group
112
112
  style="margin-left: 80px"
113
- v-model="state.showFields"
114
- :options="state.fieldOptions"
113
+ v-model="store.state.filter.showFields"
114
+ :options="store.state.fieldOptions"
115
115
  @update:model-value="(val) => toggleShowField(val)"
116
116
  color="primary"
117
117
  type="radio"
@@ -137,7 +137,7 @@
137
137
  flat
138
138
  label="Search"
139
139
  class="q-mr-md"
140
- @click="showDialog()"
140
+ @click="showSearchDialog()"
141
141
  />
142
142
  </div>
143
143
  <div class="col-auto">
@@ -176,8 +176,8 @@
176
176
  </q-header>
177
177
 
178
178
  <q-drawer
179
- v-model="state.detailDrawer"
180
- :width="state.drawerWidth"
179
+ v-model="store.state.rightDrawer.drawer"
180
+ :width="store.state.rightDrawer.width"
181
181
  side="right"
182
182
  style="border-left: 1px solid #888;"
183
183
  bordered
@@ -201,7 +201,7 @@
201
201
 
202
202
  <div style="z-index: 11; position: absolute; left: -17px; top: 9px">
203
203
  <q-btn
204
- @click="state.detailDrawer = !state.detailDrawer"
204
+ @click="store.state.rightDrawer.drawer = !store.state.rightDrawer.drawer"
205
205
  round
206
206
  unelevated
207
207
  color="primary"
@@ -210,14 +210,14 @@
210
210
  />
211
211
  </div>
212
212
  <schema-code-display
213
- :schema-name="schemaCodeName"
214
- :schemas="state.rawSchemasFull"
213
+ :schema-name="store.state.schemaDetail.schemaCodeName"
214
+ :schemas="store.state.graph.schemaMap"
215
215
  />
216
216
  </q-drawer>
217
217
 
218
218
  <q-page-container>
219
219
  <q-splitter
220
- v-model="state.splitter"
220
+ v-model="store.state.leftPanel.width"
221
221
  unit="px"
222
222
  :limits="[200, 800]"
223
223
  class="adjust-fit"
@@ -236,12 +236,12 @@
236
236
  <q-scroll-area class="fit">
237
237
  <q-list dense separator>
238
238
  <q-expansion-item
239
- v-for="tag in state.rawTags"
239
+ v-for="tag in store.state.leftPanel.tags"
240
240
  :key="tag.name"
241
241
  expand-separator
242
- :model-value="state._tag === tag.name"
242
+ :model-value="store.state.leftPanel._tag === tag.name"
243
243
  @update:model-value="(val) => toggleTag(tag.name, val)"
244
- :header-class="state.tag === tag.name ? 'text-primary text-bold' : ''"
244
+ :header-class="store.state.leftPanel.tag === tag.name ? 'text-primary text-bold' : ''"
245
245
  content-class="q-pa-none"
246
246
  >
247
247
  <template #header>
@@ -249,21 +249,21 @@
249
249
  <q-icon
250
250
  style="vertical-align: top;"
251
251
  class="q-mr-sm"
252
- :name="state.tag == tag.name ? 'folder' : 'folder_open'"
252
+ :name="store.state.leftPanel.tag == tag.name ? 'folder' : 'folder_open'"
253
253
  ></q-icon>
254
254
  <span>{{ tag.name }} <q-chip style="position:relative; top: -1px;" class="q-ml-md" dense>{{ tag.routes.length }}</q-chip></span>
255
- <a v-if="state._tag == tag.name" target="_blank" class="q-ml-sm" v-if="state.swaggerUrl" :href="state.swaggerUrl + '#/' + tag.name">
255
+ <a v-if="store.state.leftPanel._tag == tag.name" target="_blank" class="q-ml-sm" v-if="store.state.swagger.url" :href="store.state.swagger.url + '#/' + tag.name">
256
256
  <q-icon color="primary" size="" name="link" title="open in swagger"></q-icon>
257
257
  </a>
258
258
  </div>
259
259
  </template>
260
260
  <q-list separator style="overflow: auto; max-height: 60vh;">
261
261
  <q-item
262
- v-for="route in (state.hidePrimitiveRoute ? tag.routes.filter(r => !r.is_primitive) :tag.routes || [])"
262
+ v-for="route in (store.state.filter.hidePrimitiveRoute ? tag.routes.filter(r => !r.is_primitive) :tag.routes || [])"
263
263
  :key="route.id"
264
264
  clickable
265
265
  v-ripple
266
- :active="state.routeId === route.id"
266
+ :active="store.state.leftPanel.routeId === route.id"
267
267
  active-class=""
268
268
  @click="selectRoute(route.id)"
269
269
  >
@@ -274,7 +274,7 @@
274
274
  name="data_object"
275
275
  ></q-icon>
276
276
  {{ route.name }}
277
- <a v-if="state.routeId == route.id" target="_blank" class="q-ml-md" v-if="state.swaggerUrl" :href="state.swaggerUrl + '#/' + tag.name + '/' + route.unique_id">
277
+ <a v-if="store.state.leftPanel.routeId == route.id" target="_blank" class="q-ml-md" v-if="store.state.swagger.url" :href="store.state.swagger.url + '#/' + tag.name + '/' + route.unique_id">
278
278
  <q-icon color="primary" size="" name="link" title="open in swagger"></q-icon>
279
279
  </a>
280
280
  </span>
@@ -301,19 +301,9 @@
301
301
  <div style="position: absolute; left: 8px; top: 8px; z-index: 10; background: rgba(255,255,255,0.85); border-radius: 4px; padding: 2px 8px;">
302
302
  <div class="q-mt-sm">
303
303
  <q-toggle
304
- v-model="state.focus"
305
- v-show="schemaCodeName"
306
- @update:model-value="val => onFocusChange(val)"
307
- label="Focus"
304
+ v-if="store.state.modeControl.briefModeEnabled"
308
305
  dense
309
- title="pick a schema and toggle focus on to display related nodes only"
310
- />
311
- </div>
312
- <div class="q-mt-sm">
313
- <q-toggle
314
- v-if="state.enableBriefMode"
315
- dense
316
- v-model="state.brief"
306
+ v-model="store.state.filter.brief"
317
307
  label="Brief Mode"
318
308
  @update:model-value="(val) => toggleBrief(val)"
319
309
  title="skip middle classes, config module_prefix to enable it"
@@ -321,7 +311,7 @@
321
311
  </div>
322
312
  <div class="q-mt-sm">
323
313
  <q-toggle
324
- v-model="state.hidePrimitiveRoute"
314
+ v-model="store.state.filter.hidePrimitiveRoute"
325
315
  @update:model-value="(val) => toggleHidePrimitiveRoute(val)"
326
316
  label="Hide Primitive"
327
317
  dense
@@ -330,7 +320,7 @@
330
320
  </div>
331
321
  <div class="q-mt-sm">
332
322
  <q-toggle
333
- v-model="state.showModule"
323
+ v-model="store.state.filter.showModule"
334
324
  @update:model-value="(val) => toggleShowModule(val)"
335
325
  label="Show Module Cluster"
336
326
  dense
@@ -339,17 +329,13 @@
339
329
  </div>
340
330
  <div class="q-mt-sm">
341
331
  <q-toggle
342
- v-model="state.focus"
343
- v-show="schemaCodeName"
332
+ v-model="store.state.modeControl.focus"
333
+ v-show="store.state.schemaDetail.schemaCodeName"
344
334
  @update:model-value="val => onFocusChange(val)"
345
335
  label="Focus"
346
336
  dense
347
337
  title="pick a schema and toggle focus on to display related nodes only"
348
338
  />
349
- </div>
350
- <!-- <div class="q-mt-sm">
351
- <demo-component></demo-component>
352
- </div> -->
353
339
  </div>
354
340
  </div>
355
341
  </template>
@@ -359,95 +345,28 @@
359
345
 
360
346
  <!-- Schema Field Filter Dialog -->
361
347
  <q-dialog
362
- v-model="showSchemaFieldFilter"
348
+ v-model="store.state.searchDialog.show"
363
349
  :persistent="true"
364
350
  :maximized="true"
365
351
  >
366
352
  <schema-field-filter
367
- :schemas="state.rawSchemasFull"
368
- :schema-name="schemaFieldFilterSchema"
369
- @close="showSchemaFieldFilter = false"
353
+ :schemas="store.state.graph.schemaMap"
354
+ :schema-name="store.state.searchDialog.schema"
355
+ @close="store.state.searchDialog.show = false"
370
356
  />
371
357
  </q-dialog>
372
358
 
373
- <q-dialog v-model="showRouteDetail" seamless position="bottom">
359
+ <q-dialog v-model="store.state.routeDetail.show" seamless position="bottom">
374
360
  <q-card style="width: 1100px; max-width: 1100px; max-height: 40vh">
375
361
  <route-code-display
376
- :route-id="routeCodeId"
377
- @close="showRouteDetail=false"
362
+ :route-id="store.state.routeDetail.routeCodeId"
363
+ @close="store.state.routeDetail.show = false"
378
364
  />
379
365
  </q-card>
380
366
  </q-dialog>
381
367
 
382
- <!-- Dump Core Data Dialog -->
383
- <q-dialog v-model="showDumpDialog" :maximized="true" :persistent="false">
384
- <div style="height: 100%; position: relative; background: #fff">
385
- <q-btn
386
- flat
387
- dense
388
- round
389
- icon="content_copy"
390
- aria-label="Copy"
391
- @click="copyDumpJson"
392
- style="
393
- position: absolute;
394
- top: 6px;
395
- right: 62px;
396
- z-index: 11;
397
- background: rgba(255, 255, 255, 0.85);
398
- "
399
- ></q-btn>
400
- <q-btn
401
- flat
402
- dense
403
- round
404
- icon="close"
405
- aria-label="Close"
406
- @click="showDumpDialog = false"
407
- style="
408
- position: absolute;
409
- top: 6px;
410
- right: 6px;
411
- z-index: 11;
412
- background: rgba(255, 255, 255, 0.85);
413
- "
414
- ></q-btn>
415
- <div>
416
- <pre
417
- style="padding: 20px; overflow: auto"
418
- ><code>{{ dumpJson }}</code></pre>
419
- </div>
420
- </div>
421
- </q-dialog>
422
-
423
- <!-- Import Core Data Dialog -->
424
- <q-dialog v-model="showImportDialog" :persistent="true">
425
- <q-card style="min-width: 70vw; max-width: 90vw">
426
- <q-card-section class="text-h6">Import core data JSON</q-card-section>
427
- <q-card-section>
428
- <q-btn color="primary" label="Render" @click="onImportConfirm" />
429
- </q-card-section>
430
- <q-card-section>
431
- <q-input
432
- v-model="importJsonText"
433
- type="textarea"
434
- autogrow
435
- filled
436
- label="Paste JSON here"
437
- />
438
- </q-card-section>
439
- </q-card>
440
- </q-dialog>
441
-
442
- <!-- Render Graph Dialog (from imported core data) -->
443
- <q-dialog v-model="showRenderGraph" :maximized="true" :persistent="false">
444
- <render-graph
445
- :core-data="renderCoreData"
446
- @close="showRenderGraph = false"
447
- />
448
- </q-dialog>
449
368
  </div>
450
- <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
369
+ <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
451
370
  <script src="fastapi-voyager-static/quasar.min.js"></script>
452
371
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js" integrity="sha512-egJ/Y+22P9NQ9aIyVCh0VCOsfydyn8eNmqBy+y2CnJG+fpRIxXMS6jbWP8tVKp0jp+NO5n8WtMUAnNnGoJKi4w==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
453
372
  <script
@@ -1,12 +1,96 @@
1
- const { reactive, watch, ref } = window.Vue;
1
+ const { reactive } = window.Vue;
2
2
 
3
3
  const state = reactive({
4
- count: 0
4
+ item: {
5
+ count: 0
6
+ },
7
+
8
+ version: '',
9
+
10
+ swagger: {
11
+ url: ''
12
+ },
13
+
14
+ rightDrawer: {
15
+ drawer: false,
16
+ width: 300
17
+ },
18
+
19
+ fieldOptions: [
20
+ { label: "No field", value: "single" },
21
+ { label: "Object fields", value: "object" },
22
+ { label: "All fields", value: "all" },
23
+ ],
24
+
25
+ // tags and routes
26
+ leftPanel: {
27
+ width: 300,
28
+ tags: null,
29
+ tag: null,
30
+ _tag: null,
31
+ routeId: null,
32
+ },
33
+
34
+ graph: {
35
+ schemaId: null,
36
+ schemaKeys: new Set(),
37
+ schemaMap: {},
38
+ routeItems: []
39
+ },
40
+
41
+ leftPanelFiltered: {
42
+
43
+ },
44
+
45
+ // schema options, schema, fields
46
+ search: {
47
+
48
+ },
49
+
50
+
51
+ // route information
52
+ routeDetail: {
53
+ show: false,
54
+ routeCodeId: ''
55
+ },
56
+
57
+ // schema information
58
+ schemaDetail: {
59
+ show: false,
60
+ schemaCodeName: '',
61
+ },
62
+
63
+ searchDialog: {
64
+ show: false,
65
+ schema: null
66
+ },
67
+
68
+ // global status
69
+ status: {
70
+ generating: false,
71
+ loading: false,
72
+ initializing: true,
73
+ },
74
+
75
+ // brief, hide primitive ...
76
+ modeControl: {
77
+ focus: false, // control the schema param
78
+ briefModeEnabled: false, // show brief mode toggle
79
+ },
80
+
81
+ // api filters
82
+ filter: {
83
+ hidePrimitiveRoute: false,
84
+ showFields: 'object',
85
+ brief: false,
86
+ showModule: true,
87
+ }
88
+
5
89
  })
6
90
 
7
91
  const mutations = {
8
92
  increment() {
9
- state.count += 1
93
+ state.item.count += 1
10
94
  }
11
95
  }
12
96
 
@@ -4,67 +4,14 @@ import RouteCodeDisplay from "./component/route-code-display.js";
4
4
  import Demo from './component/demo.js'
5
5
  import RenderGraph from "./component/render-graph.js";
6
6
  import { GraphUI } from "./graph-ui.js";
7
- const { createApp, reactive, onMounted, watch, ref } = window.Vue;
7
+ import { store } from './store.js'
8
+
9
+ const { createApp, onMounted, ref } = window.Vue;
8
10
 
9
11
  const app = createApp({
10
12
  setup() {
11
- const state = reactive({
12
- // options and selections
13
- tag: null, // picked tag
14
- _tag: null, // display tag
15
- routeId: null, // picked route
16
- schemaId: null, // picked schema
17
- showFields: "object",
18
- fieldOptions: [
19
- { label: "No field", value: "single" },
20
- { label: "Object fields", value: "object" },
21
- { label: "All fields", value: "all" },
22
- ],
23
- enableBriefMode: false,
24
- brief: false,
25
- focus: false,
26
- hidePrimitiveRoute: false,
27
- generating: false,
28
- swaggerUrl: null,
29
- rawTags: [], // [{ name, routes: [{ id, name }] }]
30
- rawSchemas: new Set(), // [{ name, id }]
31
- rawSchemasFull: {}, // full schemas dict: { [schema.id]: schema }
32
- initializing: true,
33
- // Splitter size (left panel width in px)
34
- splitter: 300,
35
- detailDrawer: false,
36
- drawerWidth: 300, // drawer 宽度
37
- version: "", // version from backend
38
- showModule: true,
39
- });
40
-
41
- const showDetail = ref(false);
42
- const showSchemaFieldFilter = ref(false);
43
-
44
- const showDumpDialog = ref(false);
45
- const dumpJson = ref("");
46
- const showImportDialog = ref(false);
47
- const importJsonText = ref("");
48
-
49
- const showRenderGraph = ref(false);
50
-
51
- const renderCoreData = ref(null);
52
-
53
- const schemaName = ref(""); // used by detail dialog
54
- const schemaFieldFilterSchema = ref(null); // external schemaName for schema-field-filter
55
- const schemaCodeName = ref("");
56
- const routeCodeId = ref("");
57
- const showRouteDetail = ref(false);
58
-
59
13
  let graphUI = null;
60
14
 
61
- function openDetail() {
62
- showDetail.value = true;
63
- }
64
- function closeDetail() {
65
- showDetail.value = false;
66
- }
67
-
68
15
  function readQuerySelection() {
69
16
  if (typeof window === "undefined") {
70
17
  return { tag: null, route: null };
@@ -78,7 +25,7 @@ const app = createApp({
78
25
 
79
26
  function findTagByRoute(routeId) {
80
27
  return (
81
- state.rawTags.find((tag) =>
28
+ store.state.leftPanel.tags.find((tag) =>
82
29
  (tag.routes || []).some((route) => route.id === routeId)
83
30
  )?.name || null
84
31
  );
@@ -89,13 +36,13 @@ const app = createApp({
89
36
  return;
90
37
  }
91
38
  const params = new URLSearchParams(window.location.search);
92
- if (state.tag) {
93
- params.set("tag", state.tag);
39
+ if (store.state.leftPanel.tag) {
40
+ params.set("tag", store.state.leftPanel.tag);
94
41
  } else {
95
42
  params.delete("tag");
96
43
  }
97
- if (state.routeId) {
98
- params.set("route", state.routeId);
44
+ if (store.state.leftPanel.routeId) {
45
+ params.set("route", store.state.leftPanel.routeId);
99
46
  } else {
100
47
  params.delete("route");
101
48
  }
@@ -108,45 +55,47 @@ const app = createApp({
108
55
 
109
56
  function applySelectionFromQuery(selection) {
110
57
  let applied = false;
111
- if (selection.tag && state.rawTags.some((tag) => tag.name === selection.tag)) {
112
- state.tag = selection.tag;
113
- state._tag = selection.tag;
58
+ if (selection.tag && store.state.leftPanel.tags.some((tag) => tag.name === selection.tag)) {
59
+ store.state.leftPanel.tag = selection.tag;
60
+ store.state.leftPanel._tag = selection.tag;
114
61
  applied = true;
115
62
  }
116
- if (selection.route && state.routeItems?.[selection.route]) {
117
- state.routeId = selection.route;
63
+ if (selection.route && store.state.graph.routeItems?.[selection.route]) {
64
+ store.state.leftPanel.routeId = selection.route;
118
65
  applied = true;
119
66
  const inferredTag = findTagByRoute(selection.route);
120
67
  if (inferredTag) {
121
- state.tag = inferredTag;
122
- state._tag = inferredTag;
68
+ store.state.leftPanel.tag = inferredTag;
69
+ store.state.leftPanel._tag = inferredTag;
123
70
  }
124
71
  }
125
72
  return applied;
126
73
  }
127
74
 
128
75
  async function loadInitial() {
129
- state.initializing = true;
76
+ store.state.initializing = true;
130
77
  try {
131
78
  const res = await fetch("dot");
132
79
  const data = await res.json();
133
- state.rawTags = Array.isArray(data.tags) ? data.tags : [];
80
+ store.state.leftPanel.tags = Array.isArray(data.tags) ? data.tags : [];
81
+
134
82
  const schemasArr = Array.isArray(data.schemas) ? data.schemas : [];
135
83
  // Build dict keyed by id for faster lookups and simpler prop passing
136
- state.rawSchemasFull = Object.fromEntries(
84
+ const schemaMap = Object.fromEntries(
137
85
  schemasArr.map((s) => [s.id, s])
138
86
  );
139
- state.rawSchemas = new Set(Object.keys(state.rawSchemasFull));
140
- state.routeItems = data.tags
87
+ store.state.graph.schemaMap = schemaMap
88
+ store.state.graph.schemaKeys = new Set(Object.keys(schemaMap));
89
+ store.state.graph.routeItems = data.tags
141
90
  .map((t) => t.routes)
142
91
  .flat()
143
92
  .reduce((acc, r) => {
144
93
  acc[r.id] = r;
145
94
  return acc;
146
95
  }, {});
147
- state.enableBriefMode = data.enable_brief_mode || false;
148
- state.version = data.version || "";
149
- state.swaggerUrl = data.swagger_url || null
96
+ store.state.modeControl.briefModeEnabled = data.enable_brief_mode || false;
97
+ store.state.version = data.version || "";
98
+ store.state.swagger.url = data.swagger_url || null
150
99
 
151
100
  const querySelection = readQuerySelection();
152
101
  const restoredFromQuery = applySelectionFromQuery(querySelection);
@@ -163,8 +112,8 @@ const app = createApp({
163
112
  case "empty":
164
113
  return
165
114
  case "first":
166
- state.tag = state.rawTags.length > 0 ? state.rawTags[0].name : null;
167
- state._tag = state.tag;
115
+ store.state.leftPanel.tag = store.state.leftPanel.tags.length > 0 ? store.state.leftPanel.tags[0].name : null;
116
+ store.state.leftPanel._tag = store.state.leftPanel.tag;
168
117
  onGenerate();
169
118
  return
170
119
  }
@@ -173,7 +122,7 @@ const app = createApp({
173
122
  } catch (e) {
174
123
  console.error("Initial load failed", e);
175
124
  } finally {
176
- state.initializing = false;
125
+ store.state.initializing = false;
177
126
  }
178
127
  }
179
128
 
@@ -183,24 +132,24 @@ const app = createApp({
183
132
  } else {
184
133
  await onGenerate(false);
185
134
  setTimeout(() => {
186
- const ele = $(`[data-name='${schemaCodeName.value}'] polygon`);
135
+ const ele = $(`[data-name='${store.state.schemaDetail.schemaCodeName}'] polygon`);
187
136
  ele.dblclick();
188
137
  }, 1);
189
138
  }
190
139
  }
191
140
 
192
141
  async function onGenerate(resetZoom = true) {
193
- const schema_name = state.focus ? schemaCodeName.value : null;
194
- state.generating = true;
142
+ const schema_name = store.state.modeControl.focus ? store.state.schemaDetail.schemaCodeName : null;
143
+ store.state.generating = true;
195
144
  try {
196
145
  const payload = {
197
- tags: state.tag ? [state.tag] : null,
146
+ tags: store.state.leftPanel.tag ? [store.state.leftPanel.tag] : null,
198
147
  schema_name: schema_name || null,
199
- route_name: state.routeId || null,
200
- show_fields: state.showFields,
201
- brief: state.brief,
202
- hide_primitive_route: state.hidePrimitiveRoute,
203
- show_module: state.showModule,
148
+ route_name: store.state.leftPanel.routeId || null,
149
+ show_fields: store.state.filter.showFields,
150
+ brief: store.state.modeControl.brief,
151
+ hide_primitive_route: store.state.filter.hidePrimitiveRoute,
152
+ show_module: store.state.filter.showModule,
204
153
  };
205
154
  const res = await fetch("dot", {
206
155
  method: "POST",
@@ -213,21 +162,21 @@ const app = createApp({
213
162
  if (!graphUI) {
214
163
  graphUI = new GraphUI("#graph", {
215
164
  onSchemaShiftClick: (id) => {
216
- if (state.rawSchemas.has(id)) {
165
+ if (store.state.graph.schemaKeys.has(id)) {
217
166
  resetDetailPanels();
218
- schemaFieldFilterSchema.value = id;
219
- showSchemaFieldFilter.value = true;
167
+ store.state.searchDialog.show = true;
168
+ store.state.searchDialog.schema = id;
220
169
  }
221
170
  },
222
171
  onSchemaClick: (id) => {
223
172
  resetDetailPanels();
224
- if (state.rawSchemas.has(id)) {
225
- schemaCodeName.value = id;
226
- state.detailDrawer = true;
173
+ if (store.state.graph.schemaKeys.has(id)) {
174
+ store.state.schemaDetail.schemaCodeName = id;
175
+ store.state.rightDrawer.drawer = true;
227
176
  }
228
- if (id in state.routeItems) {
229
- routeCodeId.value = id;
230
- showRouteDetail.value = true;
177
+ if (id in store.state.graph.routeItems) {
178
+ store.state.routeDetail.routeCodeId = id;
179
+ store.state.routeDetail.show = true;
231
180
  }
232
181
  },
233
182
  resetCb: () => {
@@ -239,149 +188,95 @@ const app = createApp({
239
188
  } catch (e) {
240
189
  console.error("Generate failed", e);
241
190
  } finally {
242
- state.generating = false;
243
- }
244
- }
245
-
246
- async function onDumpData() {
247
- try {
248
- const payload = {
249
- tags: state.tag ? [state.tag] : null,
250
- schema_name: state.schemaId || null,
251
- route_name: state.routeId || null,
252
- show_fields: state.showFields,
253
- brief: state.brief,
254
- };
255
- const res = await fetch("dot-core-data", {
256
- method: "POST",
257
- headers: { "Content-Type": "application/json" },
258
- body: JSON.stringify(payload),
259
- });
260
- const json = await res.json();
261
- dumpJson.value = JSON.stringify(json, null, 2);
262
- showDumpDialog.value = true;
263
- } catch (e) {
264
- console.error("Dump data failed", e);
191
+ store.state.generating = false;
265
192
  }
266
193
  }
267
194
 
268
- async function copyDumpJson() {
269
- try {
270
- await navigator.clipboard.writeText(dumpJson.value || "");
271
- if (window.Quasar?.Notify) {
272
- window.Quasar.Notify.create({ type: "positive", message: "Copied" });
273
- }
274
- } catch (e) {
275
- console.error("Copy failed", e);
276
- }
277
- }
278
-
279
- function openImportDialog() {
280
- importJsonText.value = "";
281
- showImportDialog.value = true;
282
- }
283
-
284
- async function onImportConfirm() {
285
- let payloadObj = null;
286
- try {
287
- payloadObj = JSON.parse(importJsonText.value || "{}");
288
- } catch (e) {
289
- if (window.Quasar?.Notify) {
290
- window.Quasar.Notify.create({
291
- type: "negative",
292
- message: "Invalid JSON",
293
- });
294
- }
295
- return;
296
- }
297
- // Move the request into RenderGraph component: pass the parsed object and let the component call /dot-render-core-data
298
- renderCoreData.value = payloadObj;
299
- showRenderGraph.value = true;
300
- showImportDialog.value = false;
301
- }
302
-
303
- function showDialog() {
304
- schemaFieldFilterSchema.value = null;
305
- showSchemaFieldFilter.value = true;
195
+ function showSearchDialog() {
196
+ store.state.searchDialog.show = true;
197
+ store.state.searchDialog.schema = null;
306
198
  }
307
199
 
308
200
  function resetDetailPanels() {
309
- state.detailDrawer = false;
310
- showRouteDetail.value = false;
311
- schemaCodeName.value = "";
201
+ store.state.rightDrawer.drawer = false;
202
+ store.state.routeDetail.show = false
203
+ store.state.schemaDetail.schemaCodeName = "";
312
204
  }
313
205
 
314
206
  async function onReset() {
315
- state.tag = null;
316
- state._tag = null;
317
- state.routeId = "";
318
- state.schemaId = null;
207
+ store.state.leftPanel.tag = null;
208
+ store.state.leftPanel._tag = null;
209
+ store.state.leftPanel.routeId = null;
210
+
211
+ store.state.graph.schemaId = null;
212
+
319
213
  // state.showFields = "object";
320
- state.focus = false;
321
- schemaCodeName.value = "";
214
+ store.state.modeControl.focus = false;
215
+ store.state.schemaDetail.schemaCodeName = "";
322
216
  onGenerate();
323
217
  syncSelectionToUrl();
324
218
  }
325
219
 
326
220
  function toggleTag(tagName, expanded = null) {
327
221
  if (expanded === true) {
328
- state._tag = tagName;
329
- state.tag = tagName;
330
- state.routeId = "";
331
- state.focus = false;
332
- schemaCodeName.value = "";
222
+ store.state.leftPanel._tag = tagName;
223
+ store.state.leftPanel.tag = tagName;
224
+ store.state.leftPanel.routeId = "";
225
+
226
+ store.state.modeControl.focus = false;
227
+ store.state.schemaDetail.schemaCodeName = "";
333
228
  onGenerate();
334
229
  } else {
335
- state._tag = null;
230
+ store.state.leftPanel._tag = null;
336
231
  }
337
232
 
338
- state.detailDrawer = false;
339
- showRouteDetail.value = false;
233
+ store.state.rightDrawer.drawer = false;
234
+ store.state.routeDetail.show = false
340
235
  syncSelectionToUrl();
341
236
  }
342
237
 
343
238
  function selectRoute(routeId) {
344
- if (state.routeId === routeId) {
345
- state.routeId = "";
239
+ if (store.state.leftPanel.routeId === routeId) {
240
+ store.state.leftPanel.routeId = "";
346
241
  } else {
347
- state.routeId = routeId;
242
+ store.state.leftPanel.routeId = routeId;
348
243
  }
349
- state.detailDrawer = false;
350
- showRouteDetail.value = false;
351
- state.focus = false;
352
- schemaCodeName.value = "";
244
+ store.state.rightDrawer.drawer = false;
245
+ store.state.routeDetail.show = false
246
+ store.state.modeControl.focus = false;
247
+ store.state.schemaDetail.schemaCodeName = "";
353
248
  onGenerate();
354
249
  syncSelectionToUrl();
355
250
  }
356
251
 
357
252
  function toggleShowModule(val) {
358
- state.showModule = val;
253
+ store.state.filter.showModule = val;
359
254
  onGenerate()
360
255
  }
361
256
 
362
257
  function toggleShowField(field) {
363
- state.showFields = field;
258
+ store.state.filter.showFields = field;
364
259
  onGenerate(false);
365
260
  }
366
261
 
367
262
  function toggleBrief(val) {
368
- state.brief = val;
263
+ store.state.modeControl.brief = val;
369
264
  onGenerate();
370
265
  }
371
266
 
372
267
  function toggleHidePrimitiveRoute(val) {
373
- state.hidePrimitiveRoute = val;
268
+ store.state.filter.hidePrimitiveRoute = val;
374
269
  onGenerate(false);
375
270
  }
376
271
 
377
272
  function startDragDrawer(e) {
378
273
  const startX = e.clientX;
379
- const startWidth = state.drawerWidth;
274
+ const startWidth = store.state.rightDrawer.width;
380
275
 
381
276
  function onMouseMove(moveEvent) {
382
277
  const deltaX = startX - moveEvent.clientX;
383
278
  const newWidth = Math.max(300, Math.min(800, startWidth + deltaX));
384
- state.drawerWidth = newWidth;
279
+ store.state.rightDrawer.width = newWidth;
385
280
  }
386
281
 
387
282
  function onMouseUp() {
@@ -405,39 +300,18 @@ const app = createApp({
405
300
  });
406
301
 
407
302
  return {
408
- state,
303
+ store,
409
304
  toggleTag,
410
305
  toggleBrief,
411
306
  toggleHidePrimitiveRoute,
412
307
  selectRoute,
413
308
  onGenerate,
414
309
  onReset,
415
- showDetail,
416
- showRouteDetail,
417
- openDetail,
418
- closeDetail,
419
- schemaName,
420
- showSchemaFieldFilter,
421
- schemaFieldFilterSchema,
422
- showDialog,
423
- schemaCodeName,
424
- routeCodeId,
425
- // dump/import
426
- showDumpDialog,
427
- dumpJson,
428
- copyDumpJson,
429
- onDumpData,
430
- showImportDialog,
431
- importJsonText,
432
- openImportDialog,
433
- onImportConfirm,
434
- // render graph dialog
435
- showRenderGraph,
436
- renderCoreData,
310
+ showSearchDialog,
437
311
  toggleShowField,
438
312
  startDragDrawer,
439
313
  onFocusChange,
440
- toggleShowModule
314
+ toggleShowModule,
441
315
  };
442
316
  },
443
317
  });
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-voyager
3
- Version: 0.12.4
3
+ Version: 0.12.5
4
4
  Summary: Visualize FastAPI application's routing tree and dependencies
5
5
  Project-URL: Homepage, https://github.com/allmonday/fastapi-voyager
6
6
  Project-URL: Source, https://github.com/allmonday/fastapi-voyager
@@ -6,21 +6,21 @@ fastapi_voyager/render.py,sha256=7jSChISqTV2agaO55thhwyrOhqVOMux4x7k8rSQROnU,996
6
6
  fastapi_voyager/server.py,sha256=MZNRpcXor2q8Rj3OSf6EH8NkgDChxfzUtnIY8ilRkaY,7053
7
7
  fastapi_voyager/type.py,sha256=7EL1zaIwKVRGpLig7fqaOrZGN5k0Rm31C9COfck3CSs,1750
8
8
  fastapi_voyager/type_helper.py,sha256=JXD_OE_xTARkGjWDsnO_xfvyZ0vcwViYyqCp6oEHBTM,9719
9
- fastapi_voyager/version.py,sha256=_-7Zp-y6CfERg3seD-eKm5cYiJTOf-VWfF-6iV8hzms,49
9
+ fastapi_voyager/version.py,sha256=0gsRRMzt46OBC-7IuF0VTu6UPwKs_X7qmw7KMjDhIeo,49
10
10
  fastapi_voyager/voyager.py,sha256=LiRUb0ZG2cfnyY_pwRqoeZjxb6Pu6xy_lqPiMupxoKM,13510
11
- fastapi_voyager/web/graph-ui.js,sha256=7ynB-o8Dse6WHWlp4bmuEfa1pUZJ2YFLJKKXjr4R6wU,5733
11
+ fastapi_voyager/web/graph-ui.js,sha256=NiwUFHCZPE4C-Hx4qwFvHwPyXw_lu2ar3WnPkmVGQN0,5850
12
12
  fastapi_voyager/web/graphviz.svg.css,sha256=zDCjjpT0Idufu5YOiZI76PL70-avP3vTyzGPh9M85Do,1563
13
13
  fastapi_voyager/web/graphviz.svg.js,sha256=lvAdbjHc-lMSk4GQp-iqYA2PCFX4RKnW7dFaoe0LUHs,16005
14
- fastapi_voyager/web/index.html,sha256=C8gqka2omSHqx40C5_CCSMKHP-VJSPV3UFTrqQHGRtU,19635
14
+ fastapi_voyager/web/index.html,sha256=3HixCgVF9VtTx89G3usDXpEh7NZnLk_p9siz4OUVUHE,17444
15
15
  fastapi_voyager/web/quasar.min.css,sha256=F5jQe7X2XT54VlvAaa2V3GsBFdVD-vxDZeaPLf6U9CU,203145
16
16
  fastapi_voyager/web/quasar.min.js,sha256=h0ftyPMW_CRiyzeVfQqiup0vrVt4_QWojpqmpnpn07E,502974
17
- fastapi_voyager/web/store.js,sha256=LPJdFxyFn8j7vOI41FKtWoB8agoqIUrR3tN5HRS_5e4,210
18
- fastapi_voyager/web/vue-main.js,sha256=qTPJ2CTT5T9kRR2h0DbroOGJVePRIF3e24u7JexqjPQ,13516
19
- fastapi_voyager/web/component/demo.js,sha256=FUwZqyMYjwf2J_YAzUBZ-gW9RFdOrz5zbxogBBYiYYE,347
17
+ fastapi_voyager/web/store.js,sha256=fW-3uUwWNUsW8vXbTqltHvvlIroBeZTJvFkMSjuWdVg,1630
18
+ fastapi_voyager/web/vue-main.js,sha256=H50LbB-g3jWaz3Xv2O8RgajlibjtoiHu1HDTumjf7sY,10766
19
+ fastapi_voyager/web/component/demo.js,sha256=bQb16Un4XZ3Mf8qL6gvyrXe_mmA3V3mSIRMQAWg2MNk,352
20
20
  fastapi_voyager/web/component/render-graph.js,sha256=e8Xgh2Kl-nYU0P1gstEmAepCgFnk2J6UdxW8TlMafGs,2322
21
21
  fastapi_voyager/web/component/route-code-display.js,sha256=8NJPPjNRUC21gjpY8XYEQs4RBbhX1pCiqEhJp39ku6k,3678
22
22
  fastapi_voyager/web/component/schema-code-display.js,sha256=qKUMV2RFQzR8deof2iC4vyp65UaWadtVsDAXjY-i3vE,7042
23
- fastapi_voyager/web/component/schema-field-filter.js,sha256=c--XiXJrhIS7sYo1x8ZwMoqak0k9xLkNYTWoli-zd38,6253
23
+ fastapi_voyager/web/component/schema-field-filter.js,sha256=i6Zp02wfSBWQb4AJ7H_Q9vgCVzLaVnH1I2JIot0sV-0,6172
24
24
  fastapi_voyager/web/icon/android-chrome-192x192.png,sha256=35sBy6jmUFJCcquStaafHH1qClZIbd-X3PIKSeLkrNo,37285
25
25
  fastapi_voyager/web/icon/android-chrome-512x512.png,sha256=eb2eDjCwIruc05029_0L9hcrkVkv8KceLn1DJMYU0zY,210789
26
26
  fastapi_voyager/web/icon/apple-touch-icon.png,sha256=gnWK46tPnvSw1-oYZjgI02wpoO4OrIzsVzGHC5oKWO0,33187
@@ -28,8 +28,8 @@ fastapi_voyager/web/icon/favicon-16x16.png,sha256=JC07jEzfIYxBIoQn_FHXvyHuxESdhW
28
28
  fastapi_voyager/web/icon/favicon-32x32.png,sha256=C7v1h58cfWOsiLp9yOIZtlx-dLasBcq3NqpHVGRmpt4,1859
29
29
  fastapi_voyager/web/icon/favicon.ico,sha256=tZolYIXkkBcFiYl1A8ksaXN2VjGamzcSdes838dLvNc,15406
30
30
  fastapi_voyager/web/icon/site.webmanifest,sha256=ep4Hzh9zhmiZF2At3Fp1dQrYQuYF_3ZPZxc1KcGBvwQ,263
31
- fastapi_voyager-0.12.4.dist-info/METADATA,sha256=9VUoRQ9sMRLqvukIAlC7kUi1Bcf2raXR9v9JRx7Ltbk,6523
32
- fastapi_voyager-0.12.4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
33
- fastapi_voyager-0.12.4.dist-info/entry_points.txt,sha256=pEIKoUnIDXEtdMBq8EmXm70m16vELIu1VPz9-TBUFWM,53
34
- fastapi_voyager-0.12.4.dist-info/licenses/LICENSE,sha256=lNVRR3y_bFVoFKuK2JM8N4sFaj3m-7j29kvL3olFi5Y,1067
35
- fastapi_voyager-0.12.4.dist-info/RECORD,,
31
+ fastapi_voyager-0.12.5.dist-info/METADATA,sha256=QO3zauma0JaAR_DyhX5yQqNkDf868_2cAw1mHHKY3o0,6523
32
+ fastapi_voyager-0.12.5.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
33
+ fastapi_voyager-0.12.5.dist-info/entry_points.txt,sha256=pEIKoUnIDXEtdMBq8EmXm70m16vELIu1VPz9-TBUFWM,53
34
+ fastapi_voyager-0.12.5.dist-info/licenses/LICENSE,sha256=lNVRR3y_bFVoFKuK2JM8N4sFaj3m-7j29kvL3olFi5Y,1067
35
+ fastapi_voyager-0.12.5.dist-info/RECORD,,