fastapi-voyager 0.12.4__tar.gz → 0.12.6__tar.gz

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.
Files changed (61) hide show
  1. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/PKG-INFO +1 -1
  2. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/docs/changelog.md +7 -1
  3. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/src/fastapi_voyager/version.py +1 -1
  4. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/src/fastapi_voyager/web/component/demo.js +1 -1
  5. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/src/fastapi_voyager/web/component/schema-field-filter.js +0 -1
  6. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/src/fastapi_voyager/web/graph-ui.js +41 -21
  7. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/src/fastapi_voyager/web/graphviz.svg.js +82 -0
  8. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/src/fastapi_voyager/web/index.html +32 -113
  9. fastapi_voyager-0.12.6/src/fastapi_voyager/web/store.js +101 -0
  10. fastapi_voyager-0.12.6/src/fastapi_voyager/web/vue-main.js +332 -0
  11. fastapi_voyager-0.12.4/src/fastapi_voyager/web/store.js +0 -17
  12. fastapi_voyager-0.12.4/src/fastapi_voyager/web/vue-main.js +0 -458
  13. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  14. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  15. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/.github/workflows/publish.yml +0 -0
  16. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/.gitignore +0 -0
  17. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/.python-version +0 -0
  18. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/CONTRIBUTING.md +0 -0
  19. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/LICENSE +0 -0
  20. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/README.md +0 -0
  21. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/docs/idea.md +0 -0
  22. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/pyproject.toml +0 -0
  23. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/release.md +0 -0
  24. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/src/fastapi_voyager/__init__.py +0 -0
  25. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/src/fastapi_voyager/cli.py +0 -0
  26. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/src/fastapi_voyager/filter.py +0 -0
  27. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/src/fastapi_voyager/module.py +0 -0
  28. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/src/fastapi_voyager/render.py +0 -0
  29. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/src/fastapi_voyager/server.py +0 -0
  30. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/src/fastapi_voyager/type.py +0 -0
  31. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/src/fastapi_voyager/type_helper.py +0 -0
  32. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/src/fastapi_voyager/voyager.py +0 -0
  33. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/src/fastapi_voyager/web/component/render-graph.js +0 -0
  34. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/src/fastapi_voyager/web/component/route-code-display.js +0 -0
  35. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/src/fastapi_voyager/web/component/schema-code-display.js +0 -0
  36. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/src/fastapi_voyager/web/graphviz.svg.css +0 -0
  37. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/src/fastapi_voyager/web/icon/android-chrome-192x192.png +0 -0
  38. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/src/fastapi_voyager/web/icon/android-chrome-512x512.png +0 -0
  39. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/src/fastapi_voyager/web/icon/apple-touch-icon.png +0 -0
  40. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/src/fastapi_voyager/web/icon/favicon-16x16.png +0 -0
  41. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/src/fastapi_voyager/web/icon/favicon-32x32.png +0 -0
  42. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/src/fastapi_voyager/web/icon/favicon.ico +0 -0
  43. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/src/fastapi_voyager/web/icon/site.webmanifest +0 -0
  44. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/src/fastapi_voyager/web/quasar.min.css +0 -0
  45. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/src/fastapi_voyager/web/quasar.min.js +0 -0
  46. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/tests/__init__.py +0 -0
  47. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/tests/demo.py +0 -0
  48. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/tests/demo_anno.py +0 -0
  49. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/tests/programatic.py +0 -0
  50. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/tests/service/__init__.py +0 -0
  51. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/tests/service/schema/__init__.py +0 -0
  52. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/tests/service/schema/extra.py +0 -0
  53. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/tests/service/schema/schema.py +0 -0
  54. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/tests/test_analysis.py +0 -0
  55. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/tests/test_filter.py +0 -0
  56. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/tests/test_generic.py +0 -0
  57. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/tests/test_import.py +0 -0
  58. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/tests/test_module.py +0 -0
  59. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/tests/test_type_helper.py +0 -0
  60. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/uv.lock +0 -0
  61. {fastapi_voyager-0.12.4 → fastapi_voyager-0.12.6}/voyager.jpg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-voyager
3
- Version: 0.12.4
3
+ Version: 0.12.6
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
@@ -113,8 +113,14 @@
113
113
  - [x] fix logger exception
114
114
  - 0.12.5
115
115
  - [x] fix nested cluster with same color
116
+ - [x] refactor fe with store based on reactive
117
+ - [x] fix duplicated focus toggle
118
+ - 0.12.6
119
+ - [x] fix overlapped edges
120
+ - [x] click link(edge) to highlight related nodes
121
+ - [x] on hover cursor effect
122
+ - 0.12.7
116
123
  - [ ] refactor render.py
117
- - [ ] refactor fe with store based on reactive (demo.js)
118
124
  - [ ] remove search component, integrated into main page
119
125
 
120
126
  ## 0.13
@@ -1,2 +1,2 @@
1
1
  __all__ = ["__version__"]
2
- __version__ = "0.12.4"
2
+ __version__ = "0.12.6"
@@ -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"],
@@ -9,14 +9,31 @@ export class GraphUI {
9
9
  this._init();
10
10
  }
11
11
 
12
- _highlight() {
12
+ _highlight(mode = "bidirectional") {
13
13
  let highlightedNodes = $();
14
14
  for (const selection of this.currentSelection) {
15
- const nodes = this._getAffectedNodes(selection.set, "bidirectional");
15
+ const nodes = this._getAffectedNodes(selection.set, mode);
16
16
  highlightedNodes = highlightedNodes.add(nodes);
17
17
  }
18
18
  if (this.gv) {
19
19
  this.gv.highlight(highlightedNodes, true);
20
+ this.gv.bringToFront(highlightedNodes);
21
+ }
22
+ }
23
+
24
+ _highlightEdgeNodes() {
25
+ let highlightedNodes = $();
26
+ const [up, down, edge] = this.currentSelection
27
+ highlightedNodes = highlightedNodes.add(
28
+ this._getAffectedNodes(up.set, up.direction)
29
+ );
30
+ highlightedNodes = highlightedNodes.add(
31
+ this._getAffectedNodes(down.set, down.direction)
32
+ );
33
+ highlightedNodes = highlightedNodes.add(edge.set)
34
+ if (this.gv) {
35
+ this.gv.highlight(highlightedNodes, true);
36
+ this.gv.bringToFront(highlightedNodes);
20
37
  }
21
38
  }
22
39
 
@@ -65,6 +82,7 @@ export class GraphUI {
65
82
  }
66
83
  }
67
84
 
85
+
68
86
  _init() {
69
87
  const self = this;
70
88
  $(this.selector).graphviz({
@@ -82,9 +100,7 @@ export class GraphUI {
82
100
  }
83
101
  const set = $();
84
102
  set.push(this);
85
- // const obj = { set, direction: "bidirectional" };
86
103
  const schemaName = event.currentTarget.dataset.name;
87
- // self.currentSelection = [obj];
88
104
  if (schemaName) {
89
105
  try {
90
106
  self.options.onSchemaClick(schemaName);
@@ -93,6 +109,24 @@ export class GraphUI {
93
109
  }
94
110
  }
95
111
  });
112
+
113
+ self.gv.edges().click(function (event) {
114
+ const up = $();
115
+ const down = $();
116
+ const edge = $();
117
+ const [upStreamNode, downStreamNode] = event.currentTarget.dataset.name.split("->");
118
+ const nodes = self.gv.nodesByName();
119
+ up.push(nodes[upStreamNode]);
120
+ down.push(nodes[downStreamNode]);
121
+ edge.push(this)
122
+ const upObj = { set: up, direction: "upstream" };
123
+ const downObj = { set: down, direction: "downstream" };
124
+ const edgeOjb = { set: edge, direction: "single"};
125
+ self.currentSelection = [upObj, downObj, edgeOjb];
126
+
127
+ self._highlightEdgeNodes();
128
+ })
129
+
96
130
  self.gv.nodes().click(function (event) {
97
131
  const set = $();
98
132
  set.push(this);
@@ -100,7 +134,6 @@ export class GraphUI {
100
134
 
101
135
  const schemaName = event.currentTarget.dataset.name;
102
136
  if (event.shiftKey && self.options.onSchemaClick) {
103
- // try data-name or title text
104
137
  if (schemaName) {
105
138
  try {
106
139
  self.options.onSchemaShiftClick(schemaName);
@@ -119,23 +152,10 @@ export class GraphUI {
119
152
  }
120
153
  });
121
154
 
122
- self.gv.clusters().click(function (event) {
123
- const set = $();
124
- set.push(this);
125
- 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
- }
131
- self._highlight();
132
- });
133
-
134
- // svg 背景点击高亮清空
135
155
  $(document)
136
156
  .off("click.graphui")
137
157
  .on("click.graphui", function (evt) {
138
- // 如果点击目标不在 graph 容器内,直接退出
158
+ // if outside container, do nothing
139
159
  const graphContainer = $(self.selector)[0];
140
160
  if (
141
161
  !graphContainer ||
@@ -146,9 +166,9 @@ export class GraphUI {
146
166
  }
147
167
 
148
168
  let isNode = false;
149
- const $nodes = self.gv.nodes();
169
+ const $everything = self.gv.$nodes.add(self.gv.$edges).add(self.gv.$clusters);
150
170
  const node = evt.target.parentNode;
151
- $nodes.each(function () {
171
+ $everything.each(function () {
152
172
  if (this === node) {
153
173
  isNode = true;
154
174
  }
@@ -21,6 +21,8 @@
21
21
  url: null,
22
22
  svg: null,
23
23
  shrink: "0.125pt",
24
+ edgeHitPadding: 12,
25
+ pointerCursor: true,
24
26
  tooltips: {
25
27
  init: function ($graph) {
26
28
  var $a = $(this);
@@ -168,9 +170,20 @@
168
170
  var that = this;
169
171
  var options = this.options;
170
172
 
173
+ if (type === "edge" && options.edgeHitPadding) {
174
+ this.ensureEdgeHitArea($el, options.edgeHitPadding);
175
+ }
176
+
177
+ if (options.pointerCursor && (type === "edge" || type === "node")) {
178
+ this.setInteractiveCursor($el, type === "edge");
179
+ }
180
+
171
181
  // save the colors of the paths, ellipses and polygons
172
182
  $el.find("polygon, ellipse, path").each(function () {
173
183
  var $this = $(this);
184
+ if ($this.attr("data-graphviz-hitbox") === "true") {
185
+ return;
186
+ }
174
187
  // save original colors
175
188
  $this.data("graphviz.svg.color", {
176
189
  fill: $this.attr("fill"),
@@ -314,6 +327,69 @@
314
327
  }
315
328
  };
316
329
 
330
+ GraphvizSvg.prototype.ensureEdgeHitArea = function ($edge, padding) {
331
+ var width = parseFloat(padding);
332
+ if (!isFinite(width) || width <= 0) {
333
+ return;
334
+ }
335
+ var $paths = $edge
336
+ .children("path")
337
+ .filter(function () {
338
+ return $(this).attr("data-graphviz-hitbox") !== "true";
339
+ });
340
+ if (!$paths.length) {
341
+ return;
342
+ }
343
+ $paths.each(function () {
344
+ var $path = $(this);
345
+ var $existing = $path.prev('[data-graphviz-hitbox="true"]');
346
+ if ($existing.length) {
347
+ $existing.attr("stroke-width", width);
348
+ return;
349
+ }
350
+ var clone = this.cloneNode(false);
351
+
352
+ /**
353
+ * gtp-5-codex:
354
+ * Cloning the edge paths without copying D3’s data binding caused those Cannot
355
+ * read properties of undefined (reading 'key') errors when d3-graphviz re-rendered.
356
+ * I now copy the original path’s bound datum (__data__) onto the transparent hitbox
357
+ * clone inside ensureEdgeHitArea, so D3 still finds the expected metadata.
358
+ */
359
+ if (this.__data__) {
360
+ clone.__data__ = this.__data__;
361
+ }
362
+
363
+ var $clone = $(clone);
364
+ $clone.attr({
365
+ "data-graphviz-hitbox": "true",
366
+ stroke: "transparent",
367
+ fill: "none",
368
+ "stroke-width": width,
369
+ });
370
+ $clone.attr("pointer-events", "stroke");
371
+ $clone.css("pointer-events", "stroke");
372
+ if (!$clone.attr("stroke-linecap")) {
373
+ $clone.attr("stroke-linecap", $path.attr("stroke-linecap") || "round");
374
+ }
375
+ $clone.insertBefore($path);
376
+ });
377
+ };
378
+
379
+ GraphvizSvg.prototype.setInteractiveCursor = function ($el, isEdge) {
380
+ $el.css("cursor", "pointer");
381
+ var selectors = "path, polygon, ellipse, rect, text";
382
+ $el.find(selectors).each(function () {
383
+ $(this).css("cursor", "pointer");
384
+ });
385
+ if (isEdge) {
386
+ $el.children('[data-graphviz-hitbox="true"]').css("cursor", "pointer");
387
+ }
388
+ $el.find("a").each(function () {
389
+ $(this).css("cursor", "pointer");
390
+ });
391
+ };
392
+
317
393
  GraphvizSvg.prototype.convertToPx = function (val) {
318
394
  var retval = val;
319
395
  if (typeof val == "string") {
@@ -372,6 +448,9 @@
372
448
  var bg = this.$element.css("background");
373
449
  $el.find("polygon, ellipse, path").each(function () {
374
450
  var $this = $(this);
451
+ if ($this.attr("data-graphviz-hitbox") === "true") {
452
+ return;
453
+ }
375
454
  var color = $this.data("graphviz.svg.color");
376
455
  if (color.fill && color.fill != "none") {
377
456
  $this.attr("fill", getColor(color.fill, bg)); // don't set fill if it's a path
@@ -386,6 +465,9 @@
386
465
  GraphvizSvg.prototype.restoreElement = function ($el) {
387
466
  $el.find("polygon, ellipse, path").each(function () {
388
467
  var $this = $(this);
468
+ if ($this.attr("data-graphviz-hitbox") === "true") {
469
+ return;
470
+ }
389
471
  var color = $this.data("graphviz.svg.color");
390
472
  if (color.fill && color.fill != "none") {
391
473
  $this.attr("fill", color.fill); // don't set fill if it's a path
@@ -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
@@ -0,0 +1,101 @@
1
+ const { reactive } = window.Vue;
2
+
3
+ const state = reactive({
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
+
89
+ })
90
+
91
+ const mutations = {
92
+ increment() {
93
+ state.item.count += 1
94
+ }
95
+ }
96
+
97
+
98
+ export const store = {
99
+ state,
100
+ mutations
101
+ }