fastapi-voyager 0.12.3__tar.gz → 0.12.5__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 (60) hide show
  1. fastapi_voyager-0.12.5/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
  2. fastapi_voyager-0.12.5/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  3. fastapi_voyager-0.12.5/CONTRIBUTING.md +23 -0
  4. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/PKG-INFO +20 -3
  5. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/README.md +19 -2
  6. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/docs/changelog.md +12 -6
  7. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/docs/idea.md +10 -7
  8. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/src/fastapi_voyager/render.py +4 -1
  9. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/src/fastapi_voyager/type_helper.py +1 -1
  10. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/src/fastapi_voyager/version.py +1 -1
  11. fastapi_voyager-0.12.5/src/fastapi_voyager/web/component/demo.js +17 -0
  12. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/src/fastapi_voyager/web/component/schema-field-filter.js +0 -1
  13. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/src/fastapi_voyager/web/graph-ui.js +14 -11
  14. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/src/fastapi_voyager/web/index.html +40 -117
  15. fastapi_voyager-0.12.5/src/fastapi_voyager/web/store.js +101 -0
  16. fastapi_voyager-0.12.5/src/fastapi_voyager/web/vue-main.js +332 -0
  17. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/tests/demo.py +5 -5
  18. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/tests/demo_anno.py +5 -5
  19. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/tests/programatic.py +2 -2
  20. fastapi_voyager-0.12.5/tests/service/schema/__init__.py +0 -0
  21. fastapi_voyager-0.12.5/tests/service/schema/extra.py +8 -0
  22. {fastapi_voyager-0.12.3/tests/service → fastapi_voyager-0.12.5/tests/service/schema}/schema.py +0 -7
  23. fastapi_voyager-0.12.3/src/fastapi_voyager/web/vue-main.js +0 -447
  24. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/.github/workflows/publish.yml +0 -0
  25. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/.gitignore +0 -0
  26. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/.python-version +0 -0
  27. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/LICENSE +0 -0
  28. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/pyproject.toml +0 -0
  29. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/release.md +0 -0
  30. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/src/fastapi_voyager/__init__.py +0 -0
  31. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/src/fastapi_voyager/cli.py +0 -0
  32. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/src/fastapi_voyager/filter.py +0 -0
  33. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/src/fastapi_voyager/module.py +0 -0
  34. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/src/fastapi_voyager/server.py +0 -0
  35. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/src/fastapi_voyager/type.py +0 -0
  36. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/src/fastapi_voyager/voyager.py +0 -0
  37. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/src/fastapi_voyager/web/component/render-graph.js +0 -0
  38. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/src/fastapi_voyager/web/component/route-code-display.js +0 -0
  39. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/src/fastapi_voyager/web/component/schema-code-display.js +0 -0
  40. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/src/fastapi_voyager/web/graphviz.svg.css +0 -0
  41. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/src/fastapi_voyager/web/graphviz.svg.js +0 -0
  42. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/src/fastapi_voyager/web/icon/android-chrome-192x192.png +0 -0
  43. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/src/fastapi_voyager/web/icon/android-chrome-512x512.png +0 -0
  44. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/src/fastapi_voyager/web/icon/apple-touch-icon.png +0 -0
  45. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/src/fastapi_voyager/web/icon/favicon-16x16.png +0 -0
  46. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/src/fastapi_voyager/web/icon/favicon-32x32.png +0 -0
  47. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/src/fastapi_voyager/web/icon/favicon.ico +0 -0
  48. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/src/fastapi_voyager/web/icon/site.webmanifest +0 -0
  49. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/src/fastapi_voyager/web/quasar.min.css +0 -0
  50. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/src/fastapi_voyager/web/quasar.min.js +0 -0
  51. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/tests/__init__.py +0 -0
  52. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/tests/service/__init__.py +0 -0
  53. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/tests/test_analysis.py +0 -0
  54. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/tests/test_filter.py +0 -0
  55. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/tests/test_generic.py +0 -0
  56. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/tests/test_import.py +0 -0
  57. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/tests/test_module.py +0 -0
  58. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/tests/test_type_helper.py +0 -0
  59. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/uv.lock +0 -0
  60. {fastapi_voyager-0.12.3 → fastapi_voyager-0.12.5}/voyager.jpg +0 -0
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: Bug report
3
+ about: Create a report to help us improve
4
+ title: ''
5
+ labels: ''
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ **Describe the bug**
11
+ A clear and concise description of what the bug is.
12
+
13
+ **To Reproduce**
14
+ Steps to reproduce the behavior:
15
+ 1. Go to '...'
16
+ 2. Click on '....'
17
+ 3. Scroll down to '....'
18
+ 4. See error
19
+
20
+ **Expected behavior**
21
+ A clear and concise description of what you expected to happen.
22
+
23
+ **Screenshots**
24
+ If applicable, add screenshots to help explain your problem.
25
+
26
+ **Desktop (please complete the following information):**
27
+ - OS: [e.g. iOS]
28
+ - Browser [e.g. chrome, safari]
29
+ - Version [e.g. 22]
30
+
31
+ **Smartphone (please complete the following information):**
32
+ - Device: [e.g. iPhone6]
33
+ - OS: [e.g. iOS8.1]
34
+ - Browser [e.g. stock browser, safari]
35
+ - Version [e.g. 22]
36
+
37
+ **Additional context**
38
+ Add any other context about the problem here.
@@ -0,0 +1,20 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest an idea for this project
4
+ title: ''
5
+ labels: ''
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ **Is your feature request related to a problem? Please describe.**
11
+ A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12
+
13
+ **Describe the solution you'd like**
14
+ A clear and concise description of what you want to happen.
15
+
16
+ **Describe alternatives you've considered**
17
+ A clear and concise description of any alternative solutions or features you've considered.
18
+
19
+ **Additional context**
20
+ Add any other context or screenshots about the feature request here.
@@ -0,0 +1,23 @@
1
+ # How to develop & contribute?
2
+
3
+ fork, clone.
4
+
5
+ install uv.
6
+
7
+ ```shell
8
+ uv venv
9
+ source .venv/bin/activate
10
+ uv pip install ".[dev]"
11
+ uvicorn tests.programatic:app --reload
12
+ ```
13
+
14
+ open `localhost:8000/voyager`
15
+
16
+
17
+ frontend:
18
+ - `src/web/vue-main.js`: main js
19
+
20
+ backend:
21
+ - `voyager.py`: main entry
22
+ - `render.py`: generate dot file
23
+ - `server.py`: serve mode
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-voyager
3
- Version: 0.12.3
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
@@ -31,14 +31,31 @@ Description-Content-Type: text/markdown
31
31
  [![PyPI Downloads](https://static.pepy.tech/badge/fastapi-voyager/month)](https://pepy.tech/projects/fastapi-voyager)
32
32
 
33
33
 
34
- > This repo is still in early stage, it supports pydantic v2 only
35
34
 
36
35
  Visualize your FastAPI endpoints, and explore them interactively.
37
36
 
38
- [visit online demo](https://www.newsyeah.fun/voyager/) of project: [composition oriented development pattern](https://github.com/allmonday/composition-oriented-development-pattern)
37
+ > This repo is still in early stage, it supports pydantic v2 only
38
+
39
+ [live demo](https://www.newsyeah.fun/voyager/) of project: [composition oriented development pattern](https://github.com/allmonday/composition-oriented-development-pattern)
39
40
 
40
41
  <img width="1600" height="986" alt="image" src="https://github.com/user-attachments/assets/8829cda0-f42d-4c84-be2f-b019bb5fe7e1" />
41
42
 
43
+ with configuration:
44
+
45
+ ```python
46
+ app.mount('/voyager',
47
+ create_voyager(
48
+ app,
49
+ module_color={'src.services': 'tomato'},
50
+ module_prefix='src.services',
51
+ swagger_url="/docs",
52
+ ga_id="G-R64S7Q49VL",
53
+ initial_page_policy='first',
54
+ online_repo_url='https://github.com/allmonday/composition-oriented-development-pattern/blob/master'))
55
+ ```
56
+
57
+ https://github.com/allmonday/composition-oriented-development-pattern/blob/master/src/main.py#L48
58
+
42
59
  ## Plan & Raodmap
43
60
  - [ideas](./docs/idea.md)
44
61
  - [changelog & roadmap](./docs/changelog.md)
@@ -3,14 +3,31 @@
3
3
  [![PyPI Downloads](https://static.pepy.tech/badge/fastapi-voyager/month)](https://pepy.tech/projects/fastapi-voyager)
4
4
 
5
5
 
6
- > This repo is still in early stage, it supports pydantic v2 only
7
6
 
8
7
  Visualize your FastAPI endpoints, and explore them interactively.
9
8
 
10
- [visit online demo](https://www.newsyeah.fun/voyager/) of project: [composition oriented development pattern](https://github.com/allmonday/composition-oriented-development-pattern)
9
+ > This repo is still in early stage, it supports pydantic v2 only
10
+
11
+ [live demo](https://www.newsyeah.fun/voyager/) of project: [composition oriented development pattern](https://github.com/allmonday/composition-oriented-development-pattern)
11
12
 
12
13
  <img width="1600" height="986" alt="image" src="https://github.com/user-attachments/assets/8829cda0-f42d-4c84-be2f-b019bb5fe7e1" />
13
14
 
15
+ with configuration:
16
+
17
+ ```python
18
+ app.mount('/voyager',
19
+ create_voyager(
20
+ app,
21
+ module_color={'src.services': 'tomato'},
22
+ module_prefix='src.services',
23
+ swagger_url="/docs",
24
+ ga_id="G-R64S7Q49VL",
25
+ initial_page_policy='first',
26
+ online_repo_url='https://github.com/allmonday/composition-oriented-development-pattern/blob/master'))
27
+ ```
28
+
29
+ https://github.com/allmonday/composition-oriented-development-pattern/blob/master/src/main.py#L48
30
+
14
31
  ## Plan & Raodmap
15
32
  - [ideas](./docs/idea.md)
16
33
  - [changelog & roadmap](./docs/changelog.md)
@@ -1,4 +1,4 @@
1
- # Changelog & roadmap
1
+ # Changelog & plan
2
2
 
3
3
  ## <0.9:
4
4
  - [x] group schemas by module hierarchy
@@ -110,17 +110,23 @@
110
110
  - 0.12.3
111
111
  - [x] fix bug in `update_forward_refs`, class should not be skipped if it's parent class has been visited.
112
112
  - 0.12.4
113
- - [ ] search tag/ route
113
+ - [x] fix logger exception
114
+ - 0.12.5
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
114
119
  - [ ] refactor render.py
115
- - [ ] reorg: move variable into reactive in vue-main.js
120
+ - [ ] remove search component, integrated into main page
121
+ - [ ] click link to highlight related nodes
116
122
 
117
123
  ## 0.13
118
- - 0.12.0
124
+ - 0.13.2
125
+ - [ ] if er diagram is provided, show it first.
126
+ - 0.13.1
119
127
  - [ ] integration with pydantic-resolve
120
128
  - [ ] show hint for resolve, post fields
121
129
  - [ ] display loader as edges
122
130
  - [ ] add tests
123
131
 
124
- ## 0.13
125
- todo
126
132
 
@@ -9,16 +9,19 @@
9
9
  - [ ] add route/tag list
10
10
  - [ ] type alias should not be kept as node instead of compiling to original type
11
11
  - [ ] how to correctly handle the generic type ?
12
- - [ ] support Google analysis config
13
- - [ ] sort field name in nodes (only table inside right panel), pending
14
- - [ ] set max limit for fields in nodes
15
- - [ ] minimap
12
+ - for example `Page[Student]` of `Page[T]` will be marked in `Page[T]`'s module
13
+ - [ ] sort field name in nodes (only table inside right panel)
14
+ - [ ] set max limit for fields in nodes (? need further thinking)
15
+ - [ ] minimap (good to have)
16
16
  - ref: https://observablehq.com/@rabelais/d3-js-zoom-minimap
17
+ - [ ] debug mode
18
+ - [ ] export dot content, load dot content
19
+
17
20
 
18
21
  ## in analysis
19
- - [ ] upgrade network algorithm (optional)
20
- - [ ] click field to highlight links
22
+ - [ ] upgrade network algorithm (optional, for example networkx)
23
+ - [ ] click field to highlight links or click link to highlight related nodes
21
24
  - [ ] animation effect for edges
22
25
  - [ ] display standard ER diagram spec. `hard but important`
23
26
  - [ ] display potential invalid links
24
- - [ ] highlight relationship belongs to ER diagram
27
+ - [ ] highlight relationship belongs to ER diagram
@@ -93,6 +93,8 @@ class Renderer:
93
93
 
94
94
  def render_module_schema(mod: ModuleNode, inherit_color: str | None=None, show_cluster:bool=True) -> str:
95
95
  color: str | None = inherit_color
96
+ cluster_color: str | None = None
97
+
96
98
 
97
99
  # recursively vist module from short to long: 'a', 'a.b', 'a.b.c'
98
100
  # color_flag: {'a', 'a.b.c'}
@@ -103,6 +105,7 @@ class Renderer:
103
105
  if mod.fullname.startswith(k):
104
106
  module_color_flag.remove(k)
105
107
  color = self.module_color[k]
108
+ cluster_color = color if color != inherit_color else None
106
109
  break
107
110
 
108
111
  inner_nodes = [ render_node(node, color) for node in mod.schema_nodes ]
@@ -117,7 +120,7 @@ class Renderer:
117
120
  style="rounded"
118
121
  label = " {mod.name}"
119
122
  labeljust = "l"
120
- {(f'pencolor = "{color}"' if color else 'pencolor="#ccc"')}
123
+ {(f'pencolor = "{cluster_color}"' if cluster_color else 'pencolor="#ccc"')}
121
124
  {('penwidth = 3' if color else 'penwidth=""')}
122
125
  {inner_nodes_str}
123
126
  {child_str}
@@ -260,7 +260,7 @@ def update_forward_refs(kls):
260
260
 
261
261
  local_attrs = getattr(shelled_type, '__dict__', {})
262
262
  if local_attrs.get(const.PYDANTIC_FORWARD_REF_UPDATED, False):
263
- logger.debug(shelled_type.__qualname__, 'visited')
263
+ logger.debug("%s visited", shelled_type.__qualname__)
264
264
  continue
265
265
  if safe_issubclass(shelled_type, BaseModel):
266
266
  update_pydantic_forward_refs(shelled_type)
@@ -1,2 +1,2 @@
1
1
  __all__ = ["__version__"]
2
- __version__ = "0.12.3"
2
+ __version__ = "0.12.5"
@@ -0,0 +1,17 @@
1
+ const { defineComponent, computed } = window.Vue;
2
+
3
+ import { store } from '../store.js'
4
+
5
+ export default defineComponent({
6
+ name: "Demo",
7
+ emits: ["close"],
8
+ setup() {
9
+ return { store };
10
+ },
11
+ template: `
12
+ <div>
13
+ <p>Count: {{ store.state.item.count }}</p>
14
+ <button @click="store.mutations.increment()">Add</button>
15
+ </div>
16
+ `
17
+ });
@@ -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,20 +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 背景点击高亮清空
135
-
138
+ // click background to reset highlight
136
139
  $(document)
137
140
  .off("click.graphui")
138
141
  .on("click.graphui", function (evt) {
139
- // 如果点击目标不在 graph 容器内,直接退出
142
+ // if outside container, do nothing
140
143
  const graphContainer = $(self.selector)[0];
141
144
  if (
142
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>
@@ -298,22 +298,12 @@
298
298
  <template #after>
299
299
  <div style="position: relative; width: 100%; height: 100%;">
300
300
  <div id="graph" class="adjust-fit"></div>
301
- <div style="position: absolute; left: 8px; bottom: 8px; z-index: 10; background: rgba(255,255,255,0.85); border-radius: 4px; padding: 2px 8px;">
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"
308
- 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"
304
+ v-if="store.state.modeControl.briefModeEnabled"
315
305
  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,120 +320,53 @@
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
337
327
  title="show module cluster"
338
328
  />
339
329
  </div>
330
+ <div class="q-mt-sm">
331
+ <q-toggle
332
+ v-model="store.state.modeControl.focus"
333
+ v-show="store.state.schemaDetail.schemaCodeName"
334
+ @update:model-value="val => onFocusChange(val)"
335
+ label="Focus"
336
+ dense
337
+ title="pick a schema and toggle focus on to display related nodes only"
338
+ />
340
339
  </div>
341
340
  </div>
342
341
  </template>
343
342
  </q-splitter>
344
343
  </q-page-container>
345
344
  </q-layout>
346
- <!-- Detail Dialog -->
347
- <q-dialog v-model="showDetail" :persistent="true" :maximized="true">
348
- <detail-dialog
349
- :schema-name="schemaName"
350
- :show-fields="state.showFields"
351
- :model-value="showDetail"
352
- @close="closeDetail"
353
- />
354
- </q-dialog>
355
345
 
356
346
  <!-- Schema Field Filter Dialog -->
357
347
  <q-dialog
358
- v-model="showSchemaFieldFilter"
348
+ v-model="store.state.searchDialog.show"
359
349
  :persistent="true"
360
350
  :maximized="true"
361
351
  >
362
352
  <schema-field-filter
363
- :schemas="state.rawSchemasFull"
364
- :schema-name="schemaFieldFilterSchema"
365
- @close="showSchemaFieldFilter = false"
353
+ :schemas="store.state.graph.schemaMap"
354
+ :schema-name="store.state.searchDialog.schema"
355
+ @close="store.state.searchDialog.show = false"
366
356
  />
367
357
  </q-dialog>
368
358
 
369
- <q-dialog v-model="showRouteDetail" seamless position="bottom">
359
+ <q-dialog v-model="store.state.routeDetail.show" seamless position="bottom">
370
360
  <q-card style="width: 1100px; max-width: 1100px; max-height: 40vh">
371
361
  <route-code-display
372
- :route-id="routeCodeId"
373
- @close="showRouteDetail=false"
362
+ :route-id="store.state.routeDetail.routeCodeId"
363
+ @close="store.state.routeDetail.show = false"
374
364
  />
375
365
  </q-card>
376
366
  </q-dialog>
377
367
 
378
- <!-- Dump Core Data Dialog -->
379
- <q-dialog v-model="showDumpDialog" :maximized="true" :persistent="false">
380
- <div style="height: 100%; position: relative; background: #fff">
381
- <q-btn
382
- flat
383
- dense
384
- round
385
- icon="content_copy"
386
- aria-label="Copy"
387
- @click="copyDumpJson"
388
- style="
389
- position: absolute;
390
- top: 6px;
391
- right: 62px;
392
- z-index: 11;
393
- background: rgba(255, 255, 255, 0.85);
394
- "
395
- ></q-btn>
396
- <q-btn
397
- flat
398
- dense
399
- round
400
- icon="close"
401
- aria-label="Close"
402
- @click="showDumpDialog = false"
403
- style="
404
- position: absolute;
405
- top: 6px;
406
- right: 6px;
407
- z-index: 11;
408
- background: rgba(255, 255, 255, 0.85);
409
- "
410
- ></q-btn>
411
- <div>
412
- <pre
413
- style="padding: 20px; overflow: auto"
414
- ><code>{{ dumpJson }}</code></pre>
415
- </div>
416
- </div>
417
- </q-dialog>
418
-
419
- <!-- Import Core Data Dialog -->
420
- <q-dialog v-model="showImportDialog" :persistent="true">
421
- <q-card style="min-width: 70vw; max-width: 90vw">
422
- <q-card-section class="text-h6">Import core data JSON</q-card-section>
423
- <q-card-section>
424
- <q-btn color="primary" label="Render" @click="onImportConfirm" />
425
- </q-card-section>
426
- <q-card-section>
427
- <q-input
428
- v-model="importJsonText"
429
- type="textarea"
430
- autogrow
431
- filled
432
- label="Paste JSON here"
433
- />
434
- </q-card-section>
435
- </q-card>
436
- </q-dialog>
437
-
438
- <!-- Render Graph Dialog (from imported core data) -->
439
- <q-dialog v-model="showRenderGraph" :maximized="true" :persistent="false">
440
- <render-graph
441
- :core-data="renderCoreData"
442
- @close="showRenderGraph = false"
443
- />
444
- </q-dialog>
445
368
  </div>
446
- <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>
447
370
  <script src="fastapi-voyager-static/quasar.min.js"></script>
448
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>
449
372
  <script