fastapi-voyager 0.15.1__py3-none-any.whl → 0.15.3__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/version.py +1 -1
- fastapi_voyager/web/index.html +38 -1
- fastapi_voyager/web/store.js +422 -8
- fastapi_voyager/web/vue-main.js +54 -311
- {fastapi_voyager-0.15.1.dist-info → fastapi_voyager-0.15.3.dist-info}/METADATA +1 -1
- {fastapi_voyager-0.15.1.dist-info → fastapi_voyager-0.15.3.dist-info}/RECORD +9 -9
- {fastapi_voyager-0.15.1.dist-info → fastapi_voyager-0.15.3.dist-info}/WHEEL +0 -0
- {fastapi_voyager-0.15.1.dist-info → fastapi_voyager-0.15.3.dist-info}/entry_points.txt +0 -0
- {fastapi_voyager-0.15.1.dist-info → fastapi_voyager-0.15.3.dist-info}/licenses/LICENSE +0 -0
fastapi_voyager/version.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
__all__ = ["__version__"]
|
|
2
|
-
__version__ = "0.15.
|
|
2
|
+
__version__ = "0.15.3"
|
fastapi_voyager/web/index.html
CHANGED
|
@@ -101,6 +101,30 @@
|
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
/* Tag Navigator Collapse Button */
|
|
105
|
+
.tag-navigator-collapse-btn-right {
|
|
106
|
+
position: absolute;
|
|
107
|
+
bottom: 8px;
|
|
108
|
+
left: 8px;
|
|
109
|
+
width: 32px;
|
|
110
|
+
height: 32px;
|
|
111
|
+
border-radius: 50%;
|
|
112
|
+
background: #009485;
|
|
113
|
+
color: white;
|
|
114
|
+
border: 2px solid white;
|
|
115
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
|
116
|
+
cursor: pointer;
|
|
117
|
+
display: flex;
|
|
118
|
+
align-items: center;
|
|
119
|
+
justify-content: center;
|
|
120
|
+
z-index: 100;
|
|
121
|
+
transition: all 0.2s ease;
|
|
122
|
+
}
|
|
123
|
+
.tag-navigator-collapse-btn-right:hover {
|
|
124
|
+
background: #007a6d;
|
|
125
|
+
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.3);
|
|
126
|
+
}
|
|
127
|
+
|
|
104
128
|
/* App boot loading overlay & gating */
|
|
105
129
|
#app-loading-overlay {
|
|
106
130
|
position: fixed;
|
|
@@ -352,7 +376,7 @@
|
|
|
352
376
|
<q-splitter
|
|
353
377
|
v-model="store.state.leftPanel.width"
|
|
354
378
|
unit="px"
|
|
355
|
-
:limits="store.state.mode === 'voyager' ? [
|
|
379
|
+
:limits="store.state.mode === 'voyager' ? [0, 800] : [0, 0]"
|
|
356
380
|
class="adjust-fit"
|
|
357
381
|
>
|
|
358
382
|
<template #before>
|
|
@@ -509,6 +533,19 @@
|
|
|
509
533
|
/>
|
|
510
534
|
</div>
|
|
511
535
|
</div>
|
|
536
|
+
|
|
537
|
+
<!-- Collapse toggle button for tag navigator - at bottom-left of right panel -->
|
|
538
|
+
<div
|
|
539
|
+
v-show="store.state.mode === 'voyager'"
|
|
540
|
+
class="tag-navigator-collapse-btn-right"
|
|
541
|
+
@click="toggleTagNavigatorCollapse"
|
|
542
|
+
:title="store.state.leftPanel.collapsed ? 'Expand tag navigator' : 'Collapse tag navigator'"
|
|
543
|
+
>
|
|
544
|
+
<q-icon
|
|
545
|
+
:name="store.state.leftPanel.collapsed ? 'chevron_right' : 'chevron_left'"
|
|
546
|
+
size="18px"
|
|
547
|
+
/>
|
|
548
|
+
</div>
|
|
512
549
|
</div>
|
|
513
550
|
</template>
|
|
514
551
|
</q-splitter>
|
fastapi_voyager/web/store.js
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
const { reactive } = window.Vue
|
|
2
2
|
|
|
3
3
|
const state = reactive({
|
|
4
|
-
item: {
|
|
5
|
-
count: 0,
|
|
6
|
-
},
|
|
7
|
-
|
|
8
4
|
version: "",
|
|
9
5
|
config: {
|
|
10
6
|
initial_page_policy: "first",
|
|
@@ -15,7 +11,8 @@ const state = reactive({
|
|
|
15
11
|
mode: "voyager", // voyager / er-diagram
|
|
16
12
|
|
|
17
13
|
previousTagRoute: {
|
|
18
|
-
//
|
|
14
|
+
// Store the last non-search tag/route selection for restoration when clearing search
|
|
15
|
+
// Used by resetSearch to return to the state before entering search mode
|
|
19
16
|
hasValue: false,
|
|
20
17
|
tag: null,
|
|
21
18
|
routeId: null,
|
|
@@ -41,9 +38,11 @@ const state = reactive({
|
|
|
41
38
|
width: 300,
|
|
42
39
|
previousWidth: 300,
|
|
43
40
|
tags: null,
|
|
41
|
+
fullTagsCache: null, // Cache for full tags (before search)
|
|
44
42
|
tag: null,
|
|
45
43
|
_tag: null,
|
|
46
44
|
routeId: null,
|
|
45
|
+
collapsed: false,
|
|
47
46
|
},
|
|
48
47
|
|
|
49
48
|
graph: {
|
|
@@ -63,6 +62,9 @@ const state = reactive({
|
|
|
63
62
|
fieldOptions: [],
|
|
64
63
|
},
|
|
65
64
|
|
|
65
|
+
// cache all schema options for filtering
|
|
66
|
+
allSchemaOptions: [],
|
|
67
|
+
|
|
66
68
|
// route information
|
|
67
69
|
routeDetail: {
|
|
68
70
|
show: false,
|
|
@@ -103,13 +105,425 @@ const state = reactive({
|
|
|
103
105
|
},
|
|
104
106
|
})
|
|
105
107
|
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
108
|
+
const getters = {
|
|
109
|
+
/**
|
|
110
|
+
* Find tag name by route ID
|
|
111
|
+
* Used to determine which tag a route belongs to
|
|
112
|
+
*/
|
|
113
|
+
findTagByRoute(routeId) {
|
|
114
|
+
return (
|
|
115
|
+
state.leftPanel.tags.find((tag) => (tag.routes || []).some((route) => route.id === routeId))
|
|
116
|
+
?.name || null
|
|
117
|
+
)
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const actions = {
|
|
122
|
+
/**
|
|
123
|
+
* Read tag and route from URL query parameters
|
|
124
|
+
* @returns {{ tag: string|null, route: string|null }}
|
|
125
|
+
*/
|
|
126
|
+
readQuerySelection() {
|
|
127
|
+
if (typeof window === "undefined") {
|
|
128
|
+
return { tag: null, route: null }
|
|
129
|
+
}
|
|
130
|
+
const params = new URLSearchParams(window.location.search)
|
|
131
|
+
return {
|
|
132
|
+
tag: params.get("tag") || null,
|
|
133
|
+
route: params.get("route") || null,
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Sync current tag and route selection to URL
|
|
139
|
+
* Updates browser URL without reloading the page
|
|
140
|
+
*/
|
|
141
|
+
syncSelectionToUrl() {
|
|
142
|
+
if (typeof window === "undefined") {
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
const params = new URLSearchParams(window.location.search)
|
|
146
|
+
if (state.leftPanel.tag) {
|
|
147
|
+
params.set("tag", state.leftPanel.tag)
|
|
148
|
+
} else {
|
|
149
|
+
params.delete("tag")
|
|
150
|
+
}
|
|
151
|
+
if (state.leftPanel.routeId) {
|
|
152
|
+
params.set("route", state.leftPanel.routeId)
|
|
153
|
+
} else {
|
|
154
|
+
params.delete("route")
|
|
155
|
+
}
|
|
156
|
+
const hash = window.location.hash || ""
|
|
157
|
+
const search = params.toString()
|
|
158
|
+
const base = window.location.pathname
|
|
159
|
+
const newUrl = search ? `${base}?${search}${hash}` : `${base}${hash}`
|
|
160
|
+
window.history.replaceState({}, "", newUrl)
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Apply selection from URL query parameters to state
|
|
165
|
+
* @param {{ tag: string|null, route: string|null }} selection
|
|
166
|
+
* @returns {boolean} - true if any selection was applied
|
|
167
|
+
*/
|
|
168
|
+
applySelectionFromQuery(selection) {
|
|
169
|
+
let applied = false
|
|
170
|
+
if (selection.tag && state.leftPanel.tags.some((tag) => tag.name === selection.tag)) {
|
|
171
|
+
state.leftPanel.tag = selection.tag
|
|
172
|
+
state.leftPanel._tag = selection.tag
|
|
173
|
+
applied = true
|
|
174
|
+
}
|
|
175
|
+
if (selection.route && state.graph.routeItems?.[selection.route]) {
|
|
176
|
+
state.leftPanel.routeId = selection.route
|
|
177
|
+
applied = true
|
|
178
|
+
const inferredTag = getters.findTagByRoute(selection.route)
|
|
179
|
+
if (inferredTag) {
|
|
180
|
+
state.leftPanel.tag = inferredTag
|
|
181
|
+
state.leftPanel._tag = inferredTag
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return applied
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Restore full tags from cache
|
|
189
|
+
* Used when resetting search mode
|
|
190
|
+
*/
|
|
191
|
+
loadFullTags() {
|
|
192
|
+
state.leftPanel.tags = state.leftPanel.fullTagsCache
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Populate field options based on selected schema
|
|
197
|
+
* @param {string} schemaId - Schema ID
|
|
198
|
+
*/
|
|
199
|
+
populateFieldOptions(schemaId) {
|
|
200
|
+
if (!schemaId) {
|
|
201
|
+
state.search.fieldOptions = []
|
|
202
|
+
state.search.fieldName = null
|
|
203
|
+
return
|
|
204
|
+
}
|
|
205
|
+
const schema = state.graph.schemaMap?.[schemaId]
|
|
206
|
+
if (!schema) {
|
|
207
|
+
state.search.fieldOptions = []
|
|
208
|
+
state.search.fieldName = null
|
|
209
|
+
return
|
|
210
|
+
}
|
|
211
|
+
const fields = Array.isArray(schema.fields) ? schema.fields.map((f) => f.name) : []
|
|
212
|
+
state.search.fieldOptions = fields
|
|
213
|
+
if (!fields.includes(state.search.fieldName)) {
|
|
214
|
+
state.search.fieldName = null
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Rebuild schema options from schema map
|
|
220
|
+
* Should be called when schema map changes
|
|
221
|
+
*/
|
|
222
|
+
rebuildSchemaOptions() {
|
|
223
|
+
const dict = state.graph.schemaMap || {}
|
|
224
|
+
const opts = Object.values(dict).map((s) => ({
|
|
225
|
+
label: s.name,
|
|
226
|
+
desc: s.id,
|
|
227
|
+
value: s.id,
|
|
228
|
+
}))
|
|
229
|
+
state.allSchemaOptions = opts
|
|
230
|
+
state.search.schemaOptions = opts.slice()
|
|
231
|
+
this.populateFieldOptions(state.search.schemaName)
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Load tags based on search criteria
|
|
236
|
+
* @returns {Promise<void>}
|
|
237
|
+
*/
|
|
238
|
+
async loadSearchedTags() {
|
|
239
|
+
try {
|
|
240
|
+
const payload = {
|
|
241
|
+
schema_name: state.search.schemaName,
|
|
242
|
+
schema_field: state.search.fieldName || null,
|
|
243
|
+
show_fields: state.filter.showFields,
|
|
244
|
+
brief: state.filter.brief,
|
|
245
|
+
hide_primitive_route: state.filter.hidePrimitiveRoute,
|
|
246
|
+
show_module: state.filter.showModule,
|
|
247
|
+
}
|
|
248
|
+
const res = await fetch("dot-search", {
|
|
249
|
+
method: "POST",
|
|
250
|
+
headers: { "Content-Type": "application/json" },
|
|
251
|
+
body: JSON.stringify(payload),
|
|
252
|
+
})
|
|
253
|
+
if (res.ok) {
|
|
254
|
+
const data = await res.json()
|
|
255
|
+
const tags = Array.isArray(data.tags) ? data.tags : []
|
|
256
|
+
state.leftPanel.tags = tags
|
|
257
|
+
}
|
|
258
|
+
} catch (err) {
|
|
259
|
+
console.error("dot-search failed", err)
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Load initial data from API
|
|
265
|
+
* @param {Function} onGenerate - Callback to generate graph after load
|
|
266
|
+
* @param {Function} renderBasedOnInitialPolicy - Callback to render based on policy
|
|
267
|
+
* @returns {Promise<void>}
|
|
268
|
+
*/
|
|
269
|
+
async loadInitial(onGenerate, renderBasedOnInitialPolicy) {
|
|
270
|
+
state.initializing = true
|
|
271
|
+
try {
|
|
272
|
+
const res = await fetch("dot")
|
|
273
|
+
const data = await res.json()
|
|
274
|
+
const tags = Array.isArray(data.tags) ? data.tags : []
|
|
275
|
+
state.leftPanel.tags = tags
|
|
276
|
+
// Cache the full tags for later use (e.g., resetSearch)
|
|
277
|
+
state.leftPanel.fullTagsCache = tags
|
|
278
|
+
|
|
279
|
+
const schemasArr = Array.isArray(data.schemas) ? data.schemas : []
|
|
280
|
+
// Build dict keyed by id for faster lookups and simpler prop passing
|
|
281
|
+
const schemaMap = Object.fromEntries(schemasArr.map((s) => [s.id, s]))
|
|
282
|
+
state.graph.schemaMap = schemaMap
|
|
283
|
+
state.graph.schemaKeys = new Set(Object.keys(schemaMap))
|
|
284
|
+
state.graph.routeItems = data.tags
|
|
285
|
+
.map((t) => t.routes)
|
|
286
|
+
.flat()
|
|
287
|
+
.reduce((acc, r) => {
|
|
288
|
+
acc[r.id] = r
|
|
289
|
+
return acc
|
|
290
|
+
}, {})
|
|
291
|
+
state.modeControl.briefModeEnabled = data.enable_brief_mode || false
|
|
292
|
+
state.version = data.version || ""
|
|
293
|
+
state.swagger.url = data.swagger_url || null
|
|
294
|
+
state.config.has_er_diagram = data.has_er_diagram || false
|
|
295
|
+
state.config.enable_pydantic_resolve_meta = data.enable_pydantic_resolve_meta || false
|
|
296
|
+
|
|
297
|
+
this.rebuildSchemaOptions()
|
|
298
|
+
|
|
299
|
+
const querySelection = this.readQuerySelection()
|
|
300
|
+
const restoredFromQuery = this.applySelectionFromQuery(querySelection)
|
|
301
|
+
if (restoredFromQuery) {
|
|
302
|
+
this.syncSelectionToUrl()
|
|
303
|
+
onGenerate()
|
|
304
|
+
return
|
|
305
|
+
} else {
|
|
306
|
+
state.config.initial_page_policy = data.initial_page_policy
|
|
307
|
+
renderBasedOnInitialPolicy()
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// default route options placeholder
|
|
311
|
+
} catch (e) {
|
|
312
|
+
console.error("Initial load failed", e)
|
|
313
|
+
} finally {
|
|
314
|
+
state.initializing = false
|
|
315
|
+
}
|
|
316
|
+
},
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Filter schema options based on search text
|
|
320
|
+
* Used by Quasar select component's filter function
|
|
321
|
+
* @param {string} val - Search text
|
|
322
|
+
* @param {Function} update - Quasar update callback
|
|
323
|
+
*/
|
|
324
|
+
filterSearchSchemas(val, update) {
|
|
325
|
+
const needle = (val || "").toLowerCase()
|
|
326
|
+
update(() => {
|
|
327
|
+
if (!needle) {
|
|
328
|
+
state.search.schemaOptions = state.allSchemaOptions.slice()
|
|
329
|
+
return
|
|
330
|
+
}
|
|
331
|
+
state.search.schemaOptions = state.allSchemaOptions.filter((option) =>
|
|
332
|
+
option.label.toLowerCase().includes(needle)
|
|
333
|
+
)
|
|
334
|
+
})
|
|
335
|
+
},
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Handle schema selection change
|
|
339
|
+
* Updates state and triggers search if a schema is selected
|
|
340
|
+
* @param {string} val - Selected schema ID
|
|
341
|
+
* @param {Function} onSearch - Callback to trigger search
|
|
342
|
+
*/
|
|
343
|
+
onSearchSchemaChange(val, onSearch) {
|
|
344
|
+
state.search.schemaName = val
|
|
345
|
+
state.search.mode = false
|
|
346
|
+
if (!val) {
|
|
347
|
+
// Clearing the select should only run resetSearch via @clear
|
|
348
|
+
return
|
|
349
|
+
}
|
|
350
|
+
onSearch()
|
|
351
|
+
},
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Reset detail panels (right drawer and route detail)
|
|
355
|
+
*/
|
|
356
|
+
resetDetailPanels() {
|
|
357
|
+
state.rightDrawer.drawer = false
|
|
358
|
+
state.routeDetail.show = false
|
|
359
|
+
state.schemaDetail.schemaCodeName = ""
|
|
360
|
+
},
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Reset left panel selection and regenerate
|
|
364
|
+
* @param {Function} onGenerate - Callback to regenerate graph
|
|
365
|
+
*/
|
|
366
|
+
onReset(onGenerate) {
|
|
367
|
+
state.leftPanel.tag = null
|
|
368
|
+
state.leftPanel._tag = null
|
|
369
|
+
state.leftPanel.routeId = null
|
|
370
|
+
this.syncSelectionToUrl()
|
|
371
|
+
onGenerate()
|
|
372
|
+
},
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Toggle pydantic resolve meta visibility
|
|
376
|
+
* @param {boolean} val - New value
|
|
377
|
+
* @param {Function} onGenerate - Callback to regenerate graph
|
|
378
|
+
*/
|
|
379
|
+
togglePydanticResolveMeta(val, onGenerate) {
|
|
380
|
+
state.modeControl.pydanticResolveMetaEnabled = val
|
|
381
|
+
try {
|
|
382
|
+
localStorage.setItem("pydantic_resolve_meta", JSON.stringify(val))
|
|
383
|
+
} catch (e) {
|
|
384
|
+
console.warn("Failed to save pydantic_resolve_meta to localStorage", e)
|
|
385
|
+
}
|
|
386
|
+
onGenerate()
|
|
387
|
+
},
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Toggle show module clustering
|
|
391
|
+
* @param {boolean} val - New value
|
|
392
|
+
* @param {Function} onGenerate - Callback to regenerate graph
|
|
393
|
+
*/
|
|
394
|
+
toggleShowModule(val, onGenerate) {
|
|
395
|
+
state.filter.showModule = val
|
|
396
|
+
try {
|
|
397
|
+
localStorage.setItem("show_module_cluster", JSON.stringify(val))
|
|
398
|
+
} catch (e) {
|
|
399
|
+
console.warn("Failed to save show_module_cluster to localStorage", e)
|
|
400
|
+
}
|
|
401
|
+
onGenerate()
|
|
402
|
+
},
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Toggle show fields option
|
|
406
|
+
* @param {string} field - Field display option ("single", "object", "all")
|
|
407
|
+
* @param {Function} onGenerate - Callback to regenerate graph
|
|
408
|
+
*/
|
|
409
|
+
toggleShowField(field, onGenerate) {
|
|
410
|
+
state.filter.showFields = field
|
|
411
|
+
onGenerate(false)
|
|
412
|
+
},
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Toggle brief mode
|
|
416
|
+
* @param {boolean} val - New value
|
|
417
|
+
* @param {Function} onGenerate - Callback to regenerate graph
|
|
418
|
+
*/
|
|
419
|
+
toggleBrief(val, onGenerate) {
|
|
420
|
+
state.filter.brief = val
|
|
421
|
+
try {
|
|
422
|
+
localStorage.setItem("brief_mode", JSON.stringify(val))
|
|
423
|
+
} catch (e) {
|
|
424
|
+
console.warn("Failed to save brief_mode to localStorage", e)
|
|
425
|
+
}
|
|
426
|
+
onGenerate()
|
|
427
|
+
},
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Toggle hide primitive route
|
|
431
|
+
* @param {boolean} val - New value
|
|
432
|
+
* @param {Function} onGenerate - Callback to regenerate graph
|
|
433
|
+
*/
|
|
434
|
+
toggleHidePrimitiveRoute(val, onGenerate) {
|
|
435
|
+
state.filter.hidePrimitiveRoute = val
|
|
436
|
+
try {
|
|
437
|
+
localStorage.setItem("hide_primitive", JSON.stringify(val))
|
|
438
|
+
} catch (e) {
|
|
439
|
+
console.warn("Failed to save hide_primitive to localStorage", e)
|
|
440
|
+
}
|
|
441
|
+
onGenerate(false)
|
|
442
|
+
},
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Render based on initial page policy
|
|
446
|
+
* @param {Function} onGenerate - Callback to regenerate graph
|
|
447
|
+
*/
|
|
448
|
+
renderBasedOnInitialPolicy(onGenerate) {
|
|
449
|
+
switch (state.config.initial_page_policy) {
|
|
450
|
+
case "full":
|
|
451
|
+
onGenerate()
|
|
452
|
+
return
|
|
453
|
+
case "empty":
|
|
454
|
+
return
|
|
455
|
+
case "first":
|
|
456
|
+
state.leftPanel.tag = state.leftPanel.tags.length > 0 ? state.leftPanel.tags[0].name : null
|
|
457
|
+
state.leftPanel._tag = state.leftPanel.tag
|
|
458
|
+
this.syncSelectionToUrl()
|
|
459
|
+
onGenerate()
|
|
460
|
+
return
|
|
461
|
+
}
|
|
462
|
+
},
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Build payload for Voyager rendering
|
|
466
|
+
* @returns {Object} Payload for dot API
|
|
467
|
+
*/
|
|
468
|
+
buildVoyagerPayload() {
|
|
469
|
+
const activeSchema = state.search.mode ? state.search.schemaName : null
|
|
470
|
+
const activeField = state.search.mode ? state.search.fieldName : null
|
|
471
|
+
return {
|
|
472
|
+
tags: state.leftPanel.tag ? [state.leftPanel.tag] : null,
|
|
473
|
+
schema_name: activeSchema || null,
|
|
474
|
+
schema_field: activeField || null,
|
|
475
|
+
route_name: state.leftPanel.routeId || null,
|
|
476
|
+
show_fields: state.filter.showFields,
|
|
477
|
+
brief: state.filter.brief,
|
|
478
|
+
hide_primitive_route: state.filter.hidePrimitiveRoute,
|
|
479
|
+
show_module: state.filter.showModule,
|
|
480
|
+
show_pydantic_resolve_meta: state.modeControl.pydanticResolveMetaEnabled,
|
|
481
|
+
}
|
|
482
|
+
},
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Build payload for ER Diagram rendering
|
|
486
|
+
* @returns {Object} Payload for er-diagram API
|
|
487
|
+
*/
|
|
488
|
+
buildErDiagramPayload() {
|
|
489
|
+
return {
|
|
490
|
+
show_fields: state.filter.showFields,
|
|
491
|
+
show_module: state.filter.showModule,
|
|
492
|
+
}
|
|
493
|
+
},
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Restore search state and return whether to regenerate
|
|
497
|
+
* @returns {boolean} - true if should regenerate with previous selection
|
|
498
|
+
*/
|
|
499
|
+
resetSearchState() {
|
|
500
|
+
state.search.mode = false
|
|
501
|
+
const hadPreviousValue = state.previousTagRoute.hasValue
|
|
502
|
+
|
|
503
|
+
if (hadPreviousValue) {
|
|
504
|
+
state.leftPanel.tag = state.previousTagRoute.tag
|
|
505
|
+
state.leftPanel._tag = state.previousTagRoute.tag
|
|
506
|
+
state.leftPanel.routeId = state.previousTagRoute.routeId
|
|
507
|
+
// Clear the saved state
|
|
508
|
+
state.previousTagRoute.hasValue = false
|
|
509
|
+
} else {
|
|
510
|
+
state.leftPanel.tag = null
|
|
511
|
+
state.leftPanel._tag = null
|
|
512
|
+
state.leftPanel.routeId = null
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
this.syncSelectionToUrl()
|
|
516
|
+
this.loadFullTags()
|
|
517
|
+
|
|
518
|
+
return hadPreviousValue
|
|
109
519
|
},
|
|
110
520
|
}
|
|
111
521
|
|
|
522
|
+
const mutations = {}
|
|
523
|
+
|
|
112
524
|
export const store = {
|
|
113
525
|
state,
|
|
526
|
+
getters,
|
|
527
|
+
actions,
|
|
114
528
|
mutations,
|
|
115
529
|
}
|
fastapi_voyager/web/vue-main.js
CHANGED
|
@@ -22,7 +22,6 @@ function loadToggleState(key, defaultValue = false) {
|
|
|
22
22
|
const app = createApp({
|
|
23
23
|
setup() {
|
|
24
24
|
let graphUI = null
|
|
25
|
-
const allSchemaOptions = ref([])
|
|
26
25
|
const erDiagramLoading = ref(false)
|
|
27
26
|
const erDiagramCache = ref("")
|
|
28
27
|
|
|
@@ -42,16 +41,13 @@ const app = createApp({
|
|
|
42
41
|
graphUI = new GraphUI("#graph", {
|
|
43
42
|
onSchemaShiftClick: (id) => {
|
|
44
43
|
if (store.state.graph.schemaKeys.has(id)) {
|
|
45
|
-
store.state.previousTagRoute.tag = store.state.leftPanel.tag
|
|
46
|
-
store.state.previousTagRoute.routeId = store.state.leftPanel.routeId
|
|
47
|
-
store.state.previousTagRoute.hasValue = true
|
|
48
44
|
store.state.search.mode = true
|
|
49
45
|
store.state.search.schemaName = id
|
|
50
46
|
onSearch()
|
|
51
47
|
}
|
|
52
48
|
},
|
|
53
49
|
onSchemaClick: (id) => {
|
|
54
|
-
resetDetailPanels()
|
|
50
|
+
store.actions.resetDetailPanels()
|
|
55
51
|
if (store.state.graph.schemaKeys.has(id)) {
|
|
56
52
|
store.state.schemaDetail.schemaCodeName = id
|
|
57
53
|
store.state.rightDrawer.drawer = true
|
|
@@ -62,241 +58,44 @@ const app = createApp({
|
|
|
62
58
|
}
|
|
63
59
|
},
|
|
64
60
|
resetCb: () => {
|
|
65
|
-
resetDetailPanels()
|
|
61
|
+
store.actions.resetDetailPanels()
|
|
66
62
|
},
|
|
67
63
|
})
|
|
68
64
|
}
|
|
69
65
|
|
|
70
|
-
function
|
|
71
|
-
const
|
|
72
|
-
const opts = Object.values(dict).map((s) => ({
|
|
73
|
-
label: s.name,
|
|
74
|
-
desc: s.id,
|
|
75
|
-
value: s.id,
|
|
76
|
-
}))
|
|
77
|
-
allSchemaOptions.value = opts
|
|
78
|
-
store.state.search.schemaOptions = opts.slice()
|
|
79
|
-
populateFieldOptions(store.state.search.schemaName)
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function populateFieldOptions(schemaId) {
|
|
83
|
-
if (!schemaId) {
|
|
84
|
-
store.state.search.fieldOptions = []
|
|
85
|
-
store.state.search.fieldName = null
|
|
86
|
-
return
|
|
87
|
-
}
|
|
88
|
-
const schema = store.state.graph.schemaMap?.[schemaId]
|
|
89
|
-
if (!schema) {
|
|
90
|
-
store.state.search.fieldOptions = []
|
|
91
|
-
store.state.search.fieldName = null
|
|
92
|
-
return
|
|
93
|
-
}
|
|
94
|
-
const fields = Array.isArray(schema.fields) ? schema.fields.map((f) => f.name) : []
|
|
95
|
-
store.state.search.fieldOptions = fields
|
|
96
|
-
if (!fields.includes(store.state.search.fieldName)) {
|
|
97
|
-
store.state.search.fieldName = null
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function filterSearchSchemas(val, update) {
|
|
102
|
-
const needle = (val || "").toLowerCase()
|
|
103
|
-
update(() => {
|
|
104
|
-
if (!needle) {
|
|
105
|
-
store.state.search.schemaOptions = allSchemaOptions.value.slice()
|
|
106
|
-
return
|
|
107
|
-
}
|
|
108
|
-
store.state.search.schemaOptions = allSchemaOptions.value.filter((option) =>
|
|
109
|
-
option.label.toLowerCase().includes(needle)
|
|
110
|
-
)
|
|
111
|
-
})
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function onSearchSchemaChange(val) {
|
|
115
|
-
store.state.search.schemaName = val
|
|
116
|
-
store.state.search.mode = false
|
|
117
|
-
if (!val) {
|
|
118
|
-
// Clearing the select should only run resetSearch via @clear
|
|
119
|
-
return
|
|
120
|
-
}
|
|
121
|
-
onSearch()
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function readQuerySelection() {
|
|
125
|
-
if (typeof window === "undefined") {
|
|
126
|
-
return { tag: null, route: null }
|
|
127
|
-
}
|
|
128
|
-
const params = new URLSearchParams(window.location.search)
|
|
129
|
-
return {
|
|
130
|
-
tag: params.get("tag") || null,
|
|
131
|
-
route: params.get("route") || null,
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function findTagByRoute(routeId) {
|
|
136
|
-
return (
|
|
137
|
-
store.state.leftPanel.tags.find((tag) =>
|
|
138
|
-
(tag.routes || []).some((route) => route.id === routeId)
|
|
139
|
-
)?.name || null
|
|
140
|
-
)
|
|
141
|
-
}
|
|
66
|
+
async function resetSearch() {
|
|
67
|
+
const hadPreviousValue = store.actions.resetSearchState()
|
|
142
68
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const params = new URLSearchParams(window.location.search)
|
|
148
|
-
if (store.state.leftPanel.tag) {
|
|
149
|
-
params.set("tag", store.state.leftPanel.tag)
|
|
150
|
-
} else {
|
|
151
|
-
params.delete("tag")
|
|
152
|
-
}
|
|
153
|
-
if (store.state.leftPanel.routeId) {
|
|
154
|
-
params.set("route", store.state.leftPanel.routeId)
|
|
69
|
+
// If we restored a previous tag/route, generate with it
|
|
70
|
+
// Otherwise, fall back to initial policy
|
|
71
|
+
if (hadPreviousValue) {
|
|
72
|
+
onGenerate()
|
|
155
73
|
} else {
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
const hash = window.location.hash || ""
|
|
159
|
-
const search = params.toString()
|
|
160
|
-
const base = window.location.pathname
|
|
161
|
-
const newUrl = search ? `${base}?${search}${hash}` : `${base}${hash}`
|
|
162
|
-
window.history.replaceState({}, "", newUrl)
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function applySelectionFromQuery(selection) {
|
|
166
|
-
let applied = false
|
|
167
|
-
if (selection.tag && store.state.leftPanel.tags.some((tag) => tag.name === selection.tag)) {
|
|
168
|
-
store.state.leftPanel.tag = selection.tag
|
|
169
|
-
store.state.leftPanel._tag = selection.tag
|
|
170
|
-
applied = true
|
|
74
|
+
store.actions.renderBasedOnInitialPolicy(onGenerate)
|
|
171
75
|
}
|
|
172
|
-
if (selection.route && store.state.graph.routeItems?.[selection.route]) {
|
|
173
|
-
store.state.leftPanel.routeId = selection.route
|
|
174
|
-
applied = true
|
|
175
|
-
const inferredTag = findTagByRoute(selection.route)
|
|
176
|
-
if (inferredTag) {
|
|
177
|
-
store.state.leftPanel.tag = inferredTag
|
|
178
|
-
store.state.leftPanel._tag = inferredTag
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
return applied
|
|
182
76
|
}
|
|
183
77
|
|
|
184
|
-
async function
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
store.state.
|
|
190
|
-
store.state.leftPanel._tag = store.state.previousTagRoute.tag
|
|
191
|
-
store.state.leftPanel.routeId = store.state.previousTagRoute.routeId
|
|
192
|
-
store.state.previousTagRoute.hasValue = false
|
|
193
|
-
} else {
|
|
194
|
-
store.state.leftPanel.tag = null
|
|
195
|
-
store.state.leftPanel._tag = null
|
|
196
|
-
store.state.leftPanel.routeId = null
|
|
78
|
+
async function onSearch() {
|
|
79
|
+
// Save current state before entering search mode (only if not already saved)
|
|
80
|
+
if (!store.state.previousTagRoute.hasValue) {
|
|
81
|
+
store.state.previousTagRoute.tag = store.state.leftPanel.tag
|
|
82
|
+
store.state.previousTagRoute.routeId = store.state.leftPanel.routeId
|
|
83
|
+
store.state.previousTagRoute.hasValue = true
|
|
197
84
|
}
|
|
198
85
|
|
|
199
|
-
syncSelectionToUrl()
|
|
200
|
-
await loadSearchedTags()
|
|
201
|
-
renderBasedOnInitialPolicy()
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
async function onSearch() {
|
|
205
|
-
console.log("start search")
|
|
206
86
|
store.state.search.mode = true
|
|
207
87
|
store.state.leftPanel.tag = null
|
|
208
88
|
store.state.leftPanel._tag = null
|
|
209
89
|
store.state.leftPanel.routeId = null
|
|
210
|
-
syncSelectionToUrl()
|
|
211
|
-
await loadSearchedTags()
|
|
90
|
+
store.actions.syncSelectionToUrl()
|
|
91
|
+
await store.actions.loadSearchedTags()
|
|
212
92
|
await onGenerate()
|
|
213
93
|
}
|
|
214
|
-
async function loadSearchedTags() {
|
|
215
|
-
try {
|
|
216
|
-
const payload = {
|
|
217
|
-
schema_name: store.state.search.schemaName,
|
|
218
|
-
schema_field: store.state.search.fieldName || null,
|
|
219
|
-
show_fields: store.state.filter.showFields,
|
|
220
|
-
brief: store.state.filter.brief,
|
|
221
|
-
hide_primitive_route: store.state.filter.hidePrimitiveRoute,
|
|
222
|
-
show_module: store.state.filter.showModule,
|
|
223
|
-
}
|
|
224
|
-
const res = await fetch("dot-search", {
|
|
225
|
-
method: "POST",
|
|
226
|
-
headers: { "Content-Type": "application/json" },
|
|
227
|
-
body: JSON.stringify(payload),
|
|
228
|
-
})
|
|
229
|
-
if (res.ok) {
|
|
230
|
-
const data = await res.json()
|
|
231
|
-
const tags = Array.isArray(data.tags) ? data.tags : []
|
|
232
|
-
store.state.leftPanel.tags = tags
|
|
233
|
-
}
|
|
234
|
-
} catch (err) {
|
|
235
|
-
console.error("dot-search failed", err)
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
94
|
|
|
239
95
|
async function loadInitial() {
|
|
240
|
-
store.
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
const data = await res.json()
|
|
244
|
-
store.state.leftPanel.tags = Array.isArray(data.tags) ? data.tags : []
|
|
245
|
-
|
|
246
|
-
const schemasArr = Array.isArray(data.schemas) ? data.schemas : []
|
|
247
|
-
// Build dict keyed by id for faster lookups and simpler prop passing
|
|
248
|
-
const schemaMap = Object.fromEntries(schemasArr.map((s) => [s.id, s]))
|
|
249
|
-
store.state.graph.schemaMap = schemaMap
|
|
250
|
-
store.state.graph.schemaKeys = new Set(Object.keys(schemaMap))
|
|
251
|
-
store.state.graph.routeItems = data.tags
|
|
252
|
-
.map((t) => t.routes)
|
|
253
|
-
.flat()
|
|
254
|
-
.reduce((acc, r) => {
|
|
255
|
-
acc[r.id] = r
|
|
256
|
-
return acc
|
|
257
|
-
}, {})
|
|
258
|
-
store.state.modeControl.briefModeEnabled = data.enable_brief_mode || false
|
|
259
|
-
store.state.version = data.version || ""
|
|
260
|
-
store.state.swagger.url = data.swagger_url || null
|
|
261
|
-
store.state.config.has_er_diagram = data.has_er_diagram || false
|
|
262
|
-
store.state.config.enable_pydantic_resolve_meta = data.enable_pydantic_resolve_meta || false
|
|
263
|
-
|
|
264
|
-
rebuildSchemaOptions()
|
|
265
|
-
|
|
266
|
-
const querySelection = readQuerySelection()
|
|
267
|
-
const restoredFromQuery = applySelectionFromQuery(querySelection)
|
|
268
|
-
if (restoredFromQuery) {
|
|
269
|
-
syncSelectionToUrl()
|
|
270
|
-
onGenerate()
|
|
271
|
-
return
|
|
272
|
-
} else {
|
|
273
|
-
store.state.config.initial_page_policy = data.initial_page_policy
|
|
274
|
-
renderBasedOnInitialPolicy()
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// default route options placeholder
|
|
278
|
-
} catch (e) {
|
|
279
|
-
console.error("Initial load failed", e)
|
|
280
|
-
} finally {
|
|
281
|
-
store.state.initializing = false
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
async function renderBasedOnInitialPolicy() {
|
|
286
|
-
switch (store.state.config.initial_page_policy) {
|
|
287
|
-
case "full":
|
|
288
|
-
onGenerate()
|
|
289
|
-
return
|
|
290
|
-
case "empty":
|
|
291
|
-
return
|
|
292
|
-
case "first":
|
|
293
|
-
store.state.leftPanel.tag =
|
|
294
|
-
store.state.leftPanel.tags.length > 0 ? store.state.leftPanel.tags[0].name : null
|
|
295
|
-
store.state.leftPanel._tag = store.state.leftPanel.tag
|
|
296
|
-
syncSelectionToUrl()
|
|
297
|
-
onGenerate()
|
|
298
|
-
return
|
|
299
|
-
}
|
|
96
|
+
await store.actions.loadInitial(onGenerate, (cb) =>
|
|
97
|
+
store.actions.renderBasedOnInitialPolicy(cb)
|
|
98
|
+
)
|
|
300
99
|
}
|
|
301
100
|
|
|
302
101
|
async function onGenerate(resetZoom = true) {
|
|
@@ -311,21 +110,9 @@ const app = createApp({
|
|
|
311
110
|
}
|
|
312
111
|
|
|
313
112
|
async function renderVoyager(resetZoom = true) {
|
|
314
|
-
const activeSchema = store.state.search.mode ? store.state.search.schemaName : null
|
|
315
|
-
const activeField = store.state.search.mode ? store.state.search.fieldName : null
|
|
316
113
|
store.state.generating = true
|
|
317
114
|
try {
|
|
318
|
-
const payload =
|
|
319
|
-
tags: store.state.leftPanel.tag ? [store.state.leftPanel.tag] : null,
|
|
320
|
-
schema_name: activeSchema || null,
|
|
321
|
-
schema_field: activeField || null,
|
|
322
|
-
route_name: store.state.leftPanel.routeId || null,
|
|
323
|
-
show_fields: store.state.filter.showFields,
|
|
324
|
-
brief: store.state.filter.brief,
|
|
325
|
-
hide_primitive_route: store.state.filter.hidePrimitiveRoute,
|
|
326
|
-
show_module: store.state.filter.showModule,
|
|
327
|
-
show_pydantic_resolve_meta: store.state.modeControl.pydanticResolveMetaEnabled,
|
|
328
|
-
}
|
|
115
|
+
const payload = store.actions.buildVoyagerPayload()
|
|
329
116
|
initGraphUI()
|
|
330
117
|
const res = await fetch("dot", {
|
|
331
118
|
method: "POST",
|
|
@@ -342,37 +129,10 @@ const app = createApp({
|
|
|
342
129
|
}
|
|
343
130
|
}
|
|
344
131
|
|
|
345
|
-
function resetDetailPanels() {
|
|
346
|
-
store.state.rightDrawer.drawer = false
|
|
347
|
-
store.state.routeDetail.show = false
|
|
348
|
-
store.state.schemaDetail.schemaCodeName = ""
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
async function onReset() {
|
|
352
|
-
store.state.leftPanel.tag = null
|
|
353
|
-
store.state.leftPanel._tag = null
|
|
354
|
-
store.state.leftPanel.routeId = null
|
|
355
|
-
syncSelectionToUrl()
|
|
356
|
-
onGenerate()
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
async function togglePydanticResolveMeta(val) {
|
|
360
|
-
store.state.modeControl.pydanticResolveMetaEnabled = val
|
|
361
|
-
try {
|
|
362
|
-
localStorage.setItem("pydantic_resolve_meta", JSON.stringify(val))
|
|
363
|
-
} catch (e) {
|
|
364
|
-
console.warn("Failed to save pydantic_resolve_meta to localStorage", e)
|
|
365
|
-
}
|
|
366
|
-
onGenerate()
|
|
367
|
-
}
|
|
368
|
-
|
|
369
132
|
async function renderErDiagram(resetZoom = true) {
|
|
370
133
|
initGraphUI()
|
|
371
134
|
erDiagramLoading.value = true
|
|
372
|
-
const payload =
|
|
373
|
-
show_fields: store.state.filter.showFields,
|
|
374
|
-
show_module: store.state.filter.showModule,
|
|
375
|
-
}
|
|
135
|
+
const payload = store.actions.buildErDiagramPayload()
|
|
376
136
|
try {
|
|
377
137
|
const res = await fetch("er-diagram", {
|
|
378
138
|
method: "POST",
|
|
@@ -427,12 +187,28 @@ const app = createApp({
|
|
|
427
187
|
|
|
428
188
|
store.state.rightDrawer.drawer = false
|
|
429
189
|
store.state.routeDetail.show = false
|
|
430
|
-
syncSelectionToUrl()
|
|
190
|
+
store.actions.syncSelectionToUrl()
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function toggleTagNavigatorCollapse() {
|
|
194
|
+
if (store.state.leftPanel.collapsed) {
|
|
195
|
+
// Expand: restore previous width
|
|
196
|
+
const fallbackWidth = store.state.leftPanel.previousWidth || 300
|
|
197
|
+
store.state.leftPanel.width = fallbackWidth
|
|
198
|
+
store.state.leftPanel.collapsed = false
|
|
199
|
+
} else {
|
|
200
|
+
// Collapse: save current width and set to 0
|
|
201
|
+
if (store.state.leftPanel.width > 0) {
|
|
202
|
+
store.state.leftPanel.previousWidth = store.state.leftPanel.width
|
|
203
|
+
}
|
|
204
|
+
store.state.leftPanel.width = 0
|
|
205
|
+
store.state.leftPanel.collapsed = true
|
|
206
|
+
}
|
|
431
207
|
}
|
|
432
208
|
|
|
433
209
|
function selectRoute(routeId) {
|
|
434
210
|
// find belonging tag
|
|
435
|
-
const belongingTag = findTagByRoute(routeId)
|
|
211
|
+
const belongingTag = store.getters.findTagByRoute(routeId)
|
|
436
212
|
if (belongingTag) {
|
|
437
213
|
store.state.leftPanel.tag = belongingTag
|
|
438
214
|
store.state.leftPanel._tag = belongingTag
|
|
@@ -447,45 +223,10 @@ const app = createApp({
|
|
|
447
223
|
store.state.rightDrawer.drawer = false
|
|
448
224
|
store.state.routeDetail.show = false
|
|
449
225
|
store.state.schemaDetail.schemaCodeName = ""
|
|
450
|
-
syncSelectionToUrl()
|
|
451
|
-
onGenerate()
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
function toggleShowModule(val) {
|
|
455
|
-
store.state.filter.showModule = val
|
|
456
|
-
try {
|
|
457
|
-
localStorage.setItem("show_module_cluster", JSON.stringify(val))
|
|
458
|
-
} catch (e) {
|
|
459
|
-
console.warn("Failed to save show_module_cluster to localStorage", e)
|
|
460
|
-
}
|
|
461
|
-
onGenerate()
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
function toggleShowField(field) {
|
|
465
|
-
store.state.filter.showFields = field
|
|
466
|
-
onGenerate(false)
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
function toggleBrief(val) {
|
|
470
|
-
store.state.filter.brief = val
|
|
471
|
-
try {
|
|
472
|
-
localStorage.setItem("brief_mode", JSON.stringify(val))
|
|
473
|
-
} catch (e) {
|
|
474
|
-
console.warn("Failed to save brief_mode to localStorage", e)
|
|
475
|
-
}
|
|
226
|
+
store.actions.syncSelectionToUrl()
|
|
476
227
|
onGenerate()
|
|
477
228
|
}
|
|
478
229
|
|
|
479
|
-
function toggleHidePrimitiveRoute(val) {
|
|
480
|
-
store.state.filter.hidePrimitiveRoute = val
|
|
481
|
-
try {
|
|
482
|
-
localStorage.setItem("hide_primitive", JSON.stringify(val))
|
|
483
|
-
} catch (e) {
|
|
484
|
-
console.warn("Failed to save hide_primitive to localStorage", e)
|
|
485
|
-
}
|
|
486
|
-
onGenerate(false)
|
|
487
|
-
}
|
|
488
|
-
|
|
489
230
|
function startDragDrawer(e) {
|
|
490
231
|
const startX = e.clientX
|
|
491
232
|
const startWidth = store.state.rightDrawer.width
|
|
@@ -513,7 +254,7 @@ const app = createApp({
|
|
|
513
254
|
watch(
|
|
514
255
|
() => store.state.graph.schemaMap,
|
|
515
256
|
() => {
|
|
516
|
-
rebuildSchemaOptions()
|
|
257
|
+
store.actions.rebuildSchemaOptions()
|
|
517
258
|
},
|
|
518
259
|
{ deep: false }
|
|
519
260
|
)
|
|
@@ -537,8 +278,8 @@ const app = createApp({
|
|
|
537
278
|
watch(
|
|
538
279
|
() => store.state.search.schemaName,
|
|
539
280
|
(schemaId) => {
|
|
540
|
-
store.state.search.schemaOptions = allSchemaOptions.
|
|
541
|
-
populateFieldOptions(schemaId)
|
|
281
|
+
store.state.search.schemaOptions = store.state.allSchemaOptions.slice()
|
|
282
|
+
store.actions.populateFieldOptions(schemaId)
|
|
542
283
|
if (!schemaId) {
|
|
543
284
|
store.state.search.mode = false
|
|
544
285
|
}
|
|
@@ -555,20 +296,22 @@ const app = createApp({
|
|
|
555
296
|
store,
|
|
556
297
|
onSearch,
|
|
557
298
|
resetSearch,
|
|
558
|
-
filterSearchSchemas,
|
|
559
|
-
onSearchSchemaChange,
|
|
299
|
+
filterSearchSchemas: (val, update) => store.actions.filterSearchSchemas(val, update),
|
|
300
|
+
onSearchSchemaChange: (val) => store.actions.onSearchSchemaChange(val, onSearch),
|
|
560
301
|
toggleTag,
|
|
561
|
-
|
|
562
|
-
|
|
302
|
+
toggleTagNavigatorCollapse,
|
|
303
|
+
toggleBrief: (val) => store.actions.toggleBrief(val, onGenerate),
|
|
304
|
+
toggleHidePrimitiveRoute: (val) => store.actions.toggleHidePrimitiveRoute(val, onGenerate),
|
|
563
305
|
selectRoute,
|
|
564
306
|
onGenerate,
|
|
565
|
-
onReset,
|
|
566
|
-
toggleShowField,
|
|
307
|
+
onReset: () => store.actions.onReset(onGenerate),
|
|
308
|
+
toggleShowField: (field) => store.actions.toggleShowField(field, onGenerate),
|
|
567
309
|
startDragDrawer,
|
|
568
|
-
toggleShowModule,
|
|
310
|
+
toggleShowModule: (val) => store.actions.toggleShowModule(val, onGenerate),
|
|
569
311
|
onModeChange,
|
|
570
312
|
renderErDiagram,
|
|
571
|
-
togglePydanticResolveMeta,
|
|
313
|
+
togglePydanticResolveMeta: (val) => store.actions.togglePydanticResolveMeta(val, onGenerate),
|
|
314
|
+
resetDetailPanels: () => store.actions.resetDetailPanels(),
|
|
572
315
|
}
|
|
573
316
|
},
|
|
574
317
|
})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-voyager
|
|
3
|
-
Version: 0.15.
|
|
3
|
+
Version: 0.15.3
|
|
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
|
|
@@ -9,7 +9,7 @@ fastapi_voyager/render_style.py,sha256=mPOuChEl71-3agCbPwkMt2sFmax2AEKDI6dK90eFP
|
|
|
9
9
|
fastapi_voyager/server.py,sha256=MUc_ia3_QIbYQ8VenOxv3CEYVTiPDs7e_H40YoalxZs,8970
|
|
10
10
|
fastapi_voyager/type.py,sha256=zluWvh5vpnjXJ9aAmyNJTSmXZPjAHCvgRT5oQRAjHrg,2104
|
|
11
11
|
fastapi_voyager/type_helper.py,sha256=FmfrZAI3Z4uDdh3sH_kH7UGoY6yNVPapneSN86qY_wo,10209
|
|
12
|
-
fastapi_voyager/version.py,sha256=
|
|
12
|
+
fastapi_voyager/version.py,sha256=mV_h7Kelwuchi4ZvsHaY1gjoGeeu7EK0TqrMYIGLwgI,49
|
|
13
13
|
fastapi_voyager/voyager.py,sha256=4vonmL-xt54C5San-DRBq4mjoV8Q96eoWRy68MJ1IJw,14169
|
|
14
14
|
fastapi_voyager/templates/dot/cluster.j2,sha256=I2z9KkfCzmAtqXe0gXBnxnOfBXUSpdlATs3uf-O8_B8,307
|
|
15
15
|
fastapi_voyager/templates/dot/cluster_container.j2,sha256=2tH1mOJvPoVKE_aHVMR3t06TfH_dYa9OeH6DBqSHt_A,204
|
|
@@ -27,11 +27,11 @@ fastapi_voyager/templates/html/schema_table.j2,sha256=rzphiGk1il7uv4Gr2p_HLPHqyL
|
|
|
27
27
|
fastapi_voyager/web/graph-ui.js,sha256=cmEcM35rQsR6eQA2Sc8zJ_MOA0UfEQ9WvGA1zNInUUc,6285
|
|
28
28
|
fastapi_voyager/web/graphviz.svg.css,sha256=K218ov_mdSe3ga4KwhiBB92ynVvm5zaAk9_D9a3d8hE,1546
|
|
29
29
|
fastapi_voyager/web/graphviz.svg.js,sha256=deI815RgxpZ3_MpELeV-TBYy2MVuUvZtQOHfS3aeXHY,18203
|
|
30
|
-
fastapi_voyager/web/index.html,sha256=
|
|
30
|
+
fastapi_voyager/web/index.html,sha256=lA59Op4u1bb-B105Iadn2KN_n11-AtXFdJglUezZIDg,23225
|
|
31
31
|
fastapi_voyager/web/quasar.min.css,sha256=F5jQe7X2XT54VlvAaa2V3GsBFdVD-vxDZeaPLf6U9CU,203145
|
|
32
32
|
fastapi_voyager/web/quasar.min.js,sha256=h0ftyPMW_CRiyzeVfQqiup0vrVt4_QWojpqmpnpn07E,502974
|
|
33
|
-
fastapi_voyager/web/store.js,sha256=
|
|
34
|
-
fastapi_voyager/web/vue-main.js,sha256=
|
|
33
|
+
fastapi_voyager/web/store.js,sha256=zjmtx1HGN_umfyOQVwQgET-2V5hvXbS50YgXCr294Ok,14370
|
|
34
|
+
fastapi_voyager/web/vue-main.js,sha256=1s11NOjILgjncJ4WbN1DGzTeLUb0a2-LYIA3lUIX8Bc,10564
|
|
35
35
|
fastapi_voyager/web/component/demo.js,sha256=sAklFGhKGmMy9-ofgOw2oPIidAoIOgHu6yvV51L_MAA,350
|
|
36
36
|
fastapi_voyager/web/component/render-graph.js,sha256=9wnO70n3eyPKTpa744idgs5PSwgvzbfv4InZ68eEOKs,2454
|
|
37
37
|
fastapi_voyager/web/component/route-code-display.js,sha256=a823nBz3EEjutW2pfi73rcF3hodCBmgYNmuZi94sXE4,3615
|
|
@@ -43,8 +43,8 @@ fastapi_voyager/web/icon/favicon-16x16.png,sha256=JC07jEzfIYxBIoQn_FHXvyHuxESdhW
|
|
|
43
43
|
fastapi_voyager/web/icon/favicon-32x32.png,sha256=C7v1h58cfWOsiLp9yOIZtlx-dLasBcq3NqpHVGRmpt4,1859
|
|
44
44
|
fastapi_voyager/web/icon/favicon.ico,sha256=tZolYIXkkBcFiYl1A8ksaXN2VjGamzcSdes838dLvNc,15406
|
|
45
45
|
fastapi_voyager/web/icon/site.webmanifest,sha256=GRozZ5suTykYcPMap1QhjrAB8PLW0mbT_phhzw_utvQ,316
|
|
46
|
-
fastapi_voyager-0.15.
|
|
47
|
-
fastapi_voyager-0.15.
|
|
48
|
-
fastapi_voyager-0.15.
|
|
49
|
-
fastapi_voyager-0.15.
|
|
50
|
-
fastapi_voyager-0.15.
|
|
46
|
+
fastapi_voyager-0.15.3.dist-info/METADATA,sha256=_NFcPd07mWHADnTH01kf6c2y0Lw9AQgqQNnHMK0Y5xE,8513
|
|
47
|
+
fastapi_voyager-0.15.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
48
|
+
fastapi_voyager-0.15.3.dist-info/entry_points.txt,sha256=pEIKoUnIDXEtdMBq8EmXm70m16vELIu1VPz9-TBUFWM,53
|
|
49
|
+
fastapi_voyager-0.15.3.dist-info/licenses/LICENSE,sha256=lNVRR3y_bFVoFKuK2JM8N4sFaj3m-7j29kvL3olFi5Y,1067
|
|
50
|
+
fastapi_voyager-0.15.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|