fastapi-voyager 0.12.3__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.
- fastapi_voyager/render.py +4 -1
- fastapi_voyager/type_helper.py +1 -1
- fastapi_voyager/version.py +1 -1
- fastapi_voyager/web/component/demo.js +17 -0
- fastapi_voyager/web/component/schema-field-filter.js +0 -1
- fastapi_voyager/web/graph-ui.js +14 -11
- fastapi_voyager/web/index.html +40 -117
- fastapi_voyager/web/store.js +101 -0
- fastapi_voyager/web/vue-main.js +96 -211
- {fastapi_voyager-0.12.3.dist-info → fastapi_voyager-0.12.5.dist-info}/METADATA +20 -3
- {fastapi_voyager-0.12.3.dist-info → fastapi_voyager-0.12.5.dist-info}/RECORD +14 -12
- {fastapi_voyager-0.12.3.dist-info → fastapi_voyager-0.12.5.dist-info}/WHEEL +1 -1
- {fastapi_voyager-0.12.3.dist-info → fastapi_voyager-0.12.5.dist-info}/entry_points.txt +0 -0
- {fastapi_voyager-0.12.3.dist-info → fastapi_voyager-0.12.5.dist-info}/licenses/LICENSE +0 -0
fastapi_voyager/render.py
CHANGED
|
@@ -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 = "{
|
|
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}
|
fastapi_voyager/type_helper.py
CHANGED
|
@@ -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__
|
|
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)
|
fastapi_voyager/version.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
__all__ = ["__version__"]
|
|
2
|
-
__version__ = "0.12.
|
|
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"],
|
fastapi_voyager/web/graph-ui.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
//
|
|
135
|
-
|
|
138
|
+
// click background to reset highlight
|
|
136
139
|
$(document)
|
|
137
140
|
.off("click.graphui")
|
|
138
141
|
.on("click.graphui", function (evt) {
|
|
139
|
-
//
|
|
142
|
+
// if outside container, do nothing
|
|
140
143
|
const graphContainer = $(self.selector)[0];
|
|
141
144
|
if (
|
|
142
145
|
!graphContainer ||
|
fastapi_voyager/web/index.html
CHANGED
|
@@ -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="
|
|
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.
|
|
180
|
-
:width="state.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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;
|
|
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-
|
|
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="
|
|
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.
|
|
364
|
-
:schema-name="
|
|
365
|
-
@close="
|
|
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="
|
|
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="
|
|
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
|
|
@@ -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
|
+
}
|
fastapi_voyager/web/vue-main.js
CHANGED
|
@@ -1,64 +1,17 @@
|
|
|
1
1
|
import SchemaFieldFilter from "./component/schema-field-filter.js";
|
|
2
2
|
import SchemaCodeDisplay from "./component/schema-code-display.js";
|
|
3
3
|
import RouteCodeDisplay from "./component/route-code-display.js";
|
|
4
|
+
import Demo from './component/demo.js'
|
|
4
5
|
import RenderGraph from "./component/render-graph.js";
|
|
5
6
|
import { GraphUI } from "./graph-ui.js";
|
|
6
|
-
|
|
7
|
+
import { store } from './store.js'
|
|
8
|
+
|
|
9
|
+
const { createApp, onMounted, ref } = window.Vue;
|
|
7
10
|
|
|
8
11
|
const app = createApp({
|
|
9
12
|
setup() {
|
|
10
|
-
const state = reactive({
|
|
11
|
-
// options and selections
|
|
12
|
-
tag: null, // picked tag
|
|
13
|
-
_tag: null, // display tag
|
|
14
|
-
routeId: null, // picked route
|
|
15
|
-
schemaId: null, // picked schema
|
|
16
|
-
showFields: "object",
|
|
17
|
-
fieldOptions: [
|
|
18
|
-
{ label: "No field", value: "single" },
|
|
19
|
-
{ label: "Object fields", value: "object" },
|
|
20
|
-
{ label: "All fields", value: "all" },
|
|
21
|
-
],
|
|
22
|
-
enableBriefMode: false,
|
|
23
|
-
brief: false,
|
|
24
|
-
focus: false,
|
|
25
|
-
hidePrimitiveRoute: false,
|
|
26
|
-
generating: false,
|
|
27
|
-
swaggerUrl: null,
|
|
28
|
-
rawTags: [], // [{ name, routes: [{ id, name }] }]
|
|
29
|
-
rawSchemas: new Set(), // [{ name, id }]
|
|
30
|
-
rawSchemasFull: {}, // full schemas dict: { [schema.id]: schema }
|
|
31
|
-
initializing: true,
|
|
32
|
-
// Splitter size (left panel width in px)
|
|
33
|
-
splitter: 300,
|
|
34
|
-
detailDrawer: false,
|
|
35
|
-
drawerWidth: 300, // drawer 宽度
|
|
36
|
-
version: "", // version from backend
|
|
37
|
-
showModule: true,
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
const showDetail = ref(false);
|
|
41
|
-
const showSchemaFieldFilter = ref(false);
|
|
42
|
-
const showDumpDialog = ref(false);
|
|
43
|
-
const dumpJson = ref("");
|
|
44
|
-
const showImportDialog = ref(false);
|
|
45
|
-
const importJsonText = ref("");
|
|
46
|
-
const showRenderGraph = ref(false);
|
|
47
|
-
const renderCoreData = ref(null);
|
|
48
|
-
const schemaName = ref(""); // used by detail dialog
|
|
49
|
-
const schemaFieldFilterSchema = ref(null); // external schemaName for schema-field-filter
|
|
50
|
-
const schemaCodeName = ref("");
|
|
51
|
-
const routeCodeId = ref("");
|
|
52
|
-
const showRouteDetail = ref(false);
|
|
53
13
|
let graphUI = null;
|
|
54
14
|
|
|
55
|
-
function openDetail() {
|
|
56
|
-
showDetail.value = true;
|
|
57
|
-
}
|
|
58
|
-
function closeDetail() {
|
|
59
|
-
showDetail.value = false;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
15
|
function readQuerySelection() {
|
|
63
16
|
if (typeof window === "undefined") {
|
|
64
17
|
return { tag: null, route: null };
|
|
@@ -72,7 +25,7 @@ const app = createApp({
|
|
|
72
25
|
|
|
73
26
|
function findTagByRoute(routeId) {
|
|
74
27
|
return (
|
|
75
|
-
state.
|
|
28
|
+
store.state.leftPanel.tags.find((tag) =>
|
|
76
29
|
(tag.routes || []).some((route) => route.id === routeId)
|
|
77
30
|
)?.name || null
|
|
78
31
|
);
|
|
@@ -83,13 +36,13 @@ const app = createApp({
|
|
|
83
36
|
return;
|
|
84
37
|
}
|
|
85
38
|
const params = new URLSearchParams(window.location.search);
|
|
86
|
-
if (state.tag) {
|
|
87
|
-
params.set("tag", state.tag);
|
|
39
|
+
if (store.state.leftPanel.tag) {
|
|
40
|
+
params.set("tag", store.state.leftPanel.tag);
|
|
88
41
|
} else {
|
|
89
42
|
params.delete("tag");
|
|
90
43
|
}
|
|
91
|
-
if (state.routeId) {
|
|
92
|
-
params.set("route", state.routeId);
|
|
44
|
+
if (store.state.leftPanel.routeId) {
|
|
45
|
+
params.set("route", store.state.leftPanel.routeId);
|
|
93
46
|
} else {
|
|
94
47
|
params.delete("route");
|
|
95
48
|
}
|
|
@@ -102,45 +55,47 @@ const app = createApp({
|
|
|
102
55
|
|
|
103
56
|
function applySelectionFromQuery(selection) {
|
|
104
57
|
let applied = false;
|
|
105
|
-
if (selection.tag && state.
|
|
106
|
-
state.tag = selection.tag;
|
|
107
|
-
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;
|
|
108
61
|
applied = true;
|
|
109
62
|
}
|
|
110
|
-
if (selection.route && state.routeItems?.[selection.route]) {
|
|
111
|
-
state.routeId = selection.route;
|
|
63
|
+
if (selection.route && store.state.graph.routeItems?.[selection.route]) {
|
|
64
|
+
store.state.leftPanel.routeId = selection.route;
|
|
112
65
|
applied = true;
|
|
113
66
|
const inferredTag = findTagByRoute(selection.route);
|
|
114
67
|
if (inferredTag) {
|
|
115
|
-
state.tag = inferredTag;
|
|
116
|
-
state._tag = inferredTag;
|
|
68
|
+
store.state.leftPanel.tag = inferredTag;
|
|
69
|
+
store.state.leftPanel._tag = inferredTag;
|
|
117
70
|
}
|
|
118
71
|
}
|
|
119
72
|
return applied;
|
|
120
73
|
}
|
|
121
74
|
|
|
122
75
|
async function loadInitial() {
|
|
123
|
-
state.initializing = true;
|
|
76
|
+
store.state.initializing = true;
|
|
124
77
|
try {
|
|
125
78
|
const res = await fetch("dot");
|
|
126
79
|
const data = await res.json();
|
|
127
|
-
state.
|
|
80
|
+
store.state.leftPanel.tags = Array.isArray(data.tags) ? data.tags : [];
|
|
81
|
+
|
|
128
82
|
const schemasArr = Array.isArray(data.schemas) ? data.schemas : [];
|
|
129
83
|
// Build dict keyed by id for faster lookups and simpler prop passing
|
|
130
|
-
|
|
84
|
+
const schemaMap = Object.fromEntries(
|
|
131
85
|
schemasArr.map((s) => [s.id, s])
|
|
132
86
|
);
|
|
133
|
-
state.
|
|
134
|
-
state.
|
|
87
|
+
store.state.graph.schemaMap = schemaMap
|
|
88
|
+
store.state.graph.schemaKeys = new Set(Object.keys(schemaMap));
|
|
89
|
+
store.state.graph.routeItems = data.tags
|
|
135
90
|
.map((t) => t.routes)
|
|
136
91
|
.flat()
|
|
137
92
|
.reduce((acc, r) => {
|
|
138
93
|
acc[r.id] = r;
|
|
139
94
|
return acc;
|
|
140
95
|
}, {});
|
|
141
|
-
state.
|
|
142
|
-
state.version = data.version || "";
|
|
143
|
-
state.
|
|
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
|
|
144
99
|
|
|
145
100
|
const querySelection = readQuerySelection();
|
|
146
101
|
const restoredFromQuery = applySelectionFromQuery(querySelection);
|
|
@@ -157,8 +112,8 @@ const app = createApp({
|
|
|
157
112
|
case "empty":
|
|
158
113
|
return
|
|
159
114
|
case "first":
|
|
160
|
-
state.tag = state.
|
|
161
|
-
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;
|
|
162
117
|
onGenerate();
|
|
163
118
|
return
|
|
164
119
|
}
|
|
@@ -167,7 +122,7 @@ const app = createApp({
|
|
|
167
122
|
} catch (e) {
|
|
168
123
|
console.error("Initial load failed", e);
|
|
169
124
|
} finally {
|
|
170
|
-
state.initializing = false;
|
|
125
|
+
store.state.initializing = false;
|
|
171
126
|
}
|
|
172
127
|
}
|
|
173
128
|
|
|
@@ -177,24 +132,24 @@ const app = createApp({
|
|
|
177
132
|
} else {
|
|
178
133
|
await onGenerate(false);
|
|
179
134
|
setTimeout(() => {
|
|
180
|
-
const ele = $(`[data-name='${schemaCodeName
|
|
135
|
+
const ele = $(`[data-name='${store.state.schemaDetail.schemaCodeName}'] polygon`);
|
|
181
136
|
ele.dblclick();
|
|
182
137
|
}, 1);
|
|
183
138
|
}
|
|
184
139
|
}
|
|
185
140
|
|
|
186
141
|
async function onGenerate(resetZoom = true) {
|
|
187
|
-
const schema_name = state.focus ? schemaCodeName
|
|
188
|
-
state.generating = true;
|
|
142
|
+
const schema_name = store.state.modeControl.focus ? store.state.schemaDetail.schemaCodeName : null;
|
|
143
|
+
store.state.generating = true;
|
|
189
144
|
try {
|
|
190
145
|
const payload = {
|
|
191
|
-
tags: state.tag ? [state.tag] : null,
|
|
146
|
+
tags: store.state.leftPanel.tag ? [store.state.leftPanel.tag] : null,
|
|
192
147
|
schema_name: schema_name || null,
|
|
193
|
-
route_name: state.routeId || null,
|
|
194
|
-
show_fields: state.showFields,
|
|
195
|
-
brief: state.brief,
|
|
196
|
-
hide_primitive_route: state.hidePrimitiveRoute,
|
|
197
|
-
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,
|
|
198
153
|
};
|
|
199
154
|
const res = await fetch("dot", {
|
|
200
155
|
method: "POST",
|
|
@@ -207,21 +162,21 @@ const app = createApp({
|
|
|
207
162
|
if (!graphUI) {
|
|
208
163
|
graphUI = new GraphUI("#graph", {
|
|
209
164
|
onSchemaShiftClick: (id) => {
|
|
210
|
-
if (state.
|
|
165
|
+
if (store.state.graph.schemaKeys.has(id)) {
|
|
211
166
|
resetDetailPanels();
|
|
212
|
-
|
|
213
|
-
|
|
167
|
+
store.state.searchDialog.show = true;
|
|
168
|
+
store.state.searchDialog.schema = id;
|
|
214
169
|
}
|
|
215
170
|
},
|
|
216
171
|
onSchemaClick: (id) => {
|
|
217
172
|
resetDetailPanels();
|
|
218
|
-
if (state.
|
|
219
|
-
schemaCodeName
|
|
220
|
-
state.
|
|
173
|
+
if (store.state.graph.schemaKeys.has(id)) {
|
|
174
|
+
store.state.schemaDetail.schemaCodeName = id;
|
|
175
|
+
store.state.rightDrawer.drawer = true;
|
|
221
176
|
}
|
|
222
|
-
if (id in state.routeItems) {
|
|
223
|
-
routeCodeId
|
|
224
|
-
|
|
177
|
+
if (id in store.state.graph.routeItems) {
|
|
178
|
+
store.state.routeDetail.routeCodeId = id;
|
|
179
|
+
store.state.routeDetail.show = true;
|
|
225
180
|
}
|
|
226
181
|
},
|
|
227
182
|
resetCb: () => {
|
|
@@ -233,149 +188,95 @@ const app = createApp({
|
|
|
233
188
|
} catch (e) {
|
|
234
189
|
console.error("Generate failed", e);
|
|
235
190
|
} finally {
|
|
236
|
-
state.generating = false;
|
|
191
|
+
store.state.generating = false;
|
|
237
192
|
}
|
|
238
193
|
}
|
|
239
194
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
tags: state.tag ? [state.tag] : null,
|
|
244
|
-
schema_name: state.schemaId || null,
|
|
245
|
-
route_name: state.routeId || null,
|
|
246
|
-
show_fields: state.showFields,
|
|
247
|
-
brief: state.brief,
|
|
248
|
-
};
|
|
249
|
-
const res = await fetch("dot-core-data", {
|
|
250
|
-
method: "POST",
|
|
251
|
-
headers: { "Content-Type": "application/json" },
|
|
252
|
-
body: JSON.stringify(payload),
|
|
253
|
-
});
|
|
254
|
-
const json = await res.json();
|
|
255
|
-
dumpJson.value = JSON.stringify(json, null, 2);
|
|
256
|
-
showDumpDialog.value = true;
|
|
257
|
-
} catch (e) {
|
|
258
|
-
console.error("Dump data failed", e);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
async function copyDumpJson() {
|
|
263
|
-
try {
|
|
264
|
-
await navigator.clipboard.writeText(dumpJson.value || "");
|
|
265
|
-
if (window.Quasar?.Notify) {
|
|
266
|
-
window.Quasar.Notify.create({ type: "positive", message: "Copied" });
|
|
267
|
-
}
|
|
268
|
-
} catch (e) {
|
|
269
|
-
console.error("Copy failed", e);
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
function openImportDialog() {
|
|
274
|
-
importJsonText.value = "";
|
|
275
|
-
showImportDialog.value = true;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
async function onImportConfirm() {
|
|
279
|
-
let payloadObj = null;
|
|
280
|
-
try {
|
|
281
|
-
payloadObj = JSON.parse(importJsonText.value || "{}");
|
|
282
|
-
} catch (e) {
|
|
283
|
-
if (window.Quasar?.Notify) {
|
|
284
|
-
window.Quasar.Notify.create({
|
|
285
|
-
type: "negative",
|
|
286
|
-
message: "Invalid JSON",
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
|
-
return;
|
|
290
|
-
}
|
|
291
|
-
// Move the request into RenderGraph component: pass the parsed object and let the component call /dot-render-core-data
|
|
292
|
-
renderCoreData.value = payloadObj;
|
|
293
|
-
showRenderGraph.value = true;
|
|
294
|
-
showImportDialog.value = false;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
function showDialog() {
|
|
298
|
-
schemaFieldFilterSchema.value = null;
|
|
299
|
-
showSchemaFieldFilter.value = true;
|
|
195
|
+
function showSearchDialog() {
|
|
196
|
+
store.state.searchDialog.show = true;
|
|
197
|
+
store.state.searchDialog.schema = null;
|
|
300
198
|
}
|
|
301
199
|
|
|
302
200
|
function resetDetailPanels() {
|
|
303
|
-
state.
|
|
304
|
-
|
|
305
|
-
schemaCodeName
|
|
201
|
+
store.state.rightDrawer.drawer = false;
|
|
202
|
+
store.state.routeDetail.show = false
|
|
203
|
+
store.state.schemaDetail.schemaCodeName = "";
|
|
306
204
|
}
|
|
307
205
|
|
|
308
206
|
async function onReset() {
|
|
309
|
-
state.tag = null;
|
|
310
|
-
state._tag = null;
|
|
311
|
-
state.routeId =
|
|
312
|
-
|
|
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
|
+
|
|
313
213
|
// state.showFields = "object";
|
|
314
|
-
state.focus = false;
|
|
315
|
-
schemaCodeName
|
|
214
|
+
store.state.modeControl.focus = false;
|
|
215
|
+
store.state.schemaDetail.schemaCodeName = "";
|
|
316
216
|
onGenerate();
|
|
317
217
|
syncSelectionToUrl();
|
|
318
218
|
}
|
|
319
219
|
|
|
320
220
|
function toggleTag(tagName, expanded = null) {
|
|
321
221
|
if (expanded === true) {
|
|
322
|
-
state._tag = tagName;
|
|
323
|
-
state.tag = tagName;
|
|
324
|
-
state.routeId = "";
|
|
325
|
-
|
|
326
|
-
|
|
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 = "";
|
|
327
228
|
onGenerate();
|
|
328
229
|
} else {
|
|
329
|
-
state._tag = null;
|
|
230
|
+
store.state.leftPanel._tag = null;
|
|
330
231
|
}
|
|
331
232
|
|
|
332
|
-
state.
|
|
333
|
-
|
|
233
|
+
store.state.rightDrawer.drawer = false;
|
|
234
|
+
store.state.routeDetail.show = false
|
|
334
235
|
syncSelectionToUrl();
|
|
335
236
|
}
|
|
336
237
|
|
|
337
238
|
function selectRoute(routeId) {
|
|
338
|
-
if (state.routeId === routeId) {
|
|
339
|
-
state.routeId = "";
|
|
239
|
+
if (store.state.leftPanel.routeId === routeId) {
|
|
240
|
+
store.state.leftPanel.routeId = "";
|
|
340
241
|
} else {
|
|
341
|
-
state.routeId = routeId;
|
|
242
|
+
store.state.leftPanel.routeId = routeId;
|
|
342
243
|
}
|
|
343
|
-
state.
|
|
344
|
-
|
|
345
|
-
state.focus = false;
|
|
346
|
-
schemaCodeName
|
|
244
|
+
store.state.rightDrawer.drawer = false;
|
|
245
|
+
store.state.routeDetail.show = false
|
|
246
|
+
store.state.modeControl.focus = false;
|
|
247
|
+
store.state.schemaDetail.schemaCodeName = "";
|
|
347
248
|
onGenerate();
|
|
348
249
|
syncSelectionToUrl();
|
|
349
250
|
}
|
|
350
251
|
|
|
351
252
|
function toggleShowModule(val) {
|
|
352
|
-
state.showModule = val;
|
|
253
|
+
store.state.filter.showModule = val;
|
|
353
254
|
onGenerate()
|
|
354
255
|
}
|
|
355
256
|
|
|
356
257
|
function toggleShowField(field) {
|
|
357
|
-
state.showFields = field;
|
|
258
|
+
store.state.filter.showFields = field;
|
|
358
259
|
onGenerate(false);
|
|
359
260
|
}
|
|
360
261
|
|
|
361
262
|
function toggleBrief(val) {
|
|
362
|
-
state.brief = val;
|
|
263
|
+
store.state.modeControl.brief = val;
|
|
363
264
|
onGenerate();
|
|
364
265
|
}
|
|
365
266
|
|
|
366
267
|
function toggleHidePrimitiveRoute(val) {
|
|
367
|
-
state.hidePrimitiveRoute = val;
|
|
268
|
+
store.state.filter.hidePrimitiveRoute = val;
|
|
368
269
|
onGenerate(false);
|
|
369
270
|
}
|
|
370
271
|
|
|
371
272
|
function startDragDrawer(e) {
|
|
372
273
|
const startX = e.clientX;
|
|
373
|
-
const startWidth = state.
|
|
274
|
+
const startWidth = store.state.rightDrawer.width;
|
|
374
275
|
|
|
375
276
|
function onMouseMove(moveEvent) {
|
|
376
277
|
const deltaX = startX - moveEvent.clientX;
|
|
377
278
|
const newWidth = Math.max(300, Math.min(800, startWidth + deltaX));
|
|
378
|
-
state.
|
|
279
|
+
store.state.rightDrawer.width = newWidth;
|
|
379
280
|
}
|
|
380
281
|
|
|
381
282
|
function onMouseUp() {
|
|
@@ -399,49 +300,33 @@ const app = createApp({
|
|
|
399
300
|
});
|
|
400
301
|
|
|
401
302
|
return {
|
|
402
|
-
|
|
303
|
+
store,
|
|
403
304
|
toggleTag,
|
|
404
305
|
toggleBrief,
|
|
405
306
|
toggleHidePrimitiveRoute,
|
|
406
307
|
selectRoute,
|
|
407
308
|
onGenerate,
|
|
408
309
|
onReset,
|
|
409
|
-
|
|
410
|
-
showRouteDetail,
|
|
411
|
-
openDetail,
|
|
412
|
-
closeDetail,
|
|
413
|
-
schemaName,
|
|
414
|
-
showSchemaFieldFilter,
|
|
415
|
-
schemaFieldFilterSchema,
|
|
416
|
-
showDialog,
|
|
417
|
-
schemaCodeName,
|
|
418
|
-
routeCodeId,
|
|
419
|
-
// dump/import
|
|
420
|
-
showDumpDialog,
|
|
421
|
-
dumpJson,
|
|
422
|
-
copyDumpJson,
|
|
423
|
-
onDumpData,
|
|
424
|
-
showImportDialog,
|
|
425
|
-
importJsonText,
|
|
426
|
-
openImportDialog,
|
|
427
|
-
onImportConfirm,
|
|
428
|
-
// render graph dialog
|
|
429
|
-
showRenderGraph,
|
|
430
|
-
renderCoreData,
|
|
310
|
+
showSearchDialog,
|
|
431
311
|
toggleShowField,
|
|
432
312
|
startDragDrawer,
|
|
433
313
|
onFocusChange,
|
|
434
|
-
toggleShowModule
|
|
314
|
+
toggleShowModule,
|
|
435
315
|
};
|
|
436
316
|
},
|
|
437
317
|
});
|
|
318
|
+
|
|
438
319
|
app.use(window.Quasar);
|
|
320
|
+
|
|
439
321
|
// Set Quasar primary theme color to green
|
|
440
322
|
if (window.Quasar && typeof window.Quasar.setCssVar === "function") {
|
|
441
323
|
window.Quasar.setCssVar("primary", "#009485");
|
|
442
324
|
}
|
|
443
|
-
|
|
444
|
-
app.component("schema-
|
|
445
|
-
app.component("
|
|
446
|
-
app.component("
|
|
325
|
+
|
|
326
|
+
app.component("schema-field-filter", SchemaFieldFilter); // shift click and see relationships
|
|
327
|
+
app.component("schema-code-display", SchemaCodeDisplay); // double click to see node details
|
|
328
|
+
app.component("route-code-display", RouteCodeDisplay); // double click to see route details
|
|
329
|
+
app.component("render-graph", RenderGraph); // for debug, render pasted dot content
|
|
330
|
+
app.component('demo-component', Demo)
|
|
331
|
+
|
|
447
332
|
app.mount("#q-app");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-voyager
|
|
3
|
-
Version: 0.12.
|
|
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
|
[](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
|
-
|
|
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)
|
|
@@ -2,23 +2,25 @@ fastapi_voyager/__init__.py,sha256=kqwzThE1YhmQ_7jPKGlnWvqRGC-hFrRqq_lKhKaleYU,2
|
|
|
2
2
|
fastapi_voyager/cli.py,sha256=td3yIIigEomhSdDO-Xkh-CgpEwCafwlwnpvxnT9QsBo,10488
|
|
3
3
|
fastapi_voyager/filter.py,sha256=AN_HIu8-DtKisIq5mFt7CnqRHtxKewedNGyyaI82hSY,11529
|
|
4
4
|
fastapi_voyager/module.py,sha256=h9YR3BpS-CAcJW9WCdVkF4opqwY32w9T67g9GfdLytk,3425
|
|
5
|
-
fastapi_voyager/render.py,sha256=
|
|
5
|
+
fastapi_voyager/render.py,sha256=7jSChISqTV2agaO55thhwyrOhqVOMux4x7k8rSQROnU,9960
|
|
6
6
|
fastapi_voyager/server.py,sha256=MZNRpcXor2q8Rj3OSf6EH8NkgDChxfzUtnIY8ilRkaY,7053
|
|
7
7
|
fastapi_voyager/type.py,sha256=7EL1zaIwKVRGpLig7fqaOrZGN5k0Rm31C9COfck3CSs,1750
|
|
8
|
-
fastapi_voyager/type_helper.py,sha256=
|
|
9
|
-
fastapi_voyager/version.py,sha256=
|
|
8
|
+
fastapi_voyager/type_helper.py,sha256=JXD_OE_xTARkGjWDsnO_xfvyZ0vcwViYyqCp6oEHBTM,9719
|
|
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=
|
|
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=
|
|
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/
|
|
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
|
|
18
20
|
fastapi_voyager/web/component/render-graph.js,sha256=e8Xgh2Kl-nYU0P1gstEmAepCgFnk2J6UdxW8TlMafGs,2322
|
|
19
21
|
fastapi_voyager/web/component/route-code-display.js,sha256=8NJPPjNRUC21gjpY8XYEQs4RBbhX1pCiqEhJp39ku6k,3678
|
|
20
22
|
fastapi_voyager/web/component/schema-code-display.js,sha256=qKUMV2RFQzR8deof2iC4vyp65UaWadtVsDAXjY-i3vE,7042
|
|
21
|
-
fastapi_voyager/web/component/schema-field-filter.js,sha256=
|
|
23
|
+
fastapi_voyager/web/component/schema-field-filter.js,sha256=i6Zp02wfSBWQb4AJ7H_Q9vgCVzLaVnH1I2JIot0sV-0,6172
|
|
22
24
|
fastapi_voyager/web/icon/android-chrome-192x192.png,sha256=35sBy6jmUFJCcquStaafHH1qClZIbd-X3PIKSeLkrNo,37285
|
|
23
25
|
fastapi_voyager/web/icon/android-chrome-512x512.png,sha256=eb2eDjCwIruc05029_0L9hcrkVkv8KceLn1DJMYU0zY,210789
|
|
24
26
|
fastapi_voyager/web/icon/apple-touch-icon.png,sha256=gnWK46tPnvSw1-oYZjgI02wpoO4OrIzsVzGHC5oKWO0,33187
|
|
@@ -26,8 +28,8 @@ fastapi_voyager/web/icon/favicon-16x16.png,sha256=JC07jEzfIYxBIoQn_FHXvyHuxESdhW
|
|
|
26
28
|
fastapi_voyager/web/icon/favicon-32x32.png,sha256=C7v1h58cfWOsiLp9yOIZtlx-dLasBcq3NqpHVGRmpt4,1859
|
|
27
29
|
fastapi_voyager/web/icon/favicon.ico,sha256=tZolYIXkkBcFiYl1A8ksaXN2VjGamzcSdes838dLvNc,15406
|
|
28
30
|
fastapi_voyager/web/icon/site.webmanifest,sha256=ep4Hzh9zhmiZF2At3Fp1dQrYQuYF_3ZPZxc1KcGBvwQ,263
|
|
29
|
-
fastapi_voyager-0.12.
|
|
30
|
-
fastapi_voyager-0.12.
|
|
31
|
-
fastapi_voyager-0.12.
|
|
32
|
-
fastapi_voyager-0.12.
|
|
33
|
-
fastapi_voyager-0.12.
|
|
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,,
|
|
File without changes
|
|
File without changes
|