fastapi-voyager 0.15.0__py3-none-any.whl → 0.15.2__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/er_diagram.py +57 -109
- fastapi_voyager/render.py +12 -2
- fastapi_voyager/server.py +1 -0
- fastapi_voyager/templates/dot/er_diagram.j2 +29 -0
- fastapi_voyager/version.py +1 -1
- fastapi_voyager/web/component/demo.js +5 -5
- fastapi_voyager/web/component/render-graph.js +60 -61
- fastapi_voyager/web/component/route-code-display.js +35 -37
- fastapi_voyager/web/component/schema-code-display.js +50 -53
- fastapi_voyager/web/graph-ui.js +90 -101
- fastapi_voyager/web/graphviz.svg.css +10 -10
- fastapi_voyager/web/graphviz.svg.js +306 -316
- fastapi_voyager/web/icon/site.webmanifest +11 -1
- fastapi_voyager/web/index.html +263 -110
- fastapi_voyager/web/store.js +109 -111
- fastapi_voyager/web/vue-main.js +329 -263
- {fastapi_voyager-0.15.0.dist-info → fastapi_voyager-0.15.2.dist-info}/METADATA +16 -4
- {fastapi_voyager-0.15.0.dist-info → fastapi_voyager-0.15.2.dist-info}/RECORD +21 -20
- {fastapi_voyager-0.15.0.dist-info → fastapi_voyager-0.15.2.dist-info}/WHEEL +0 -0
- {fastapi_voyager-0.15.0.dist-info → fastapi_voyager-0.15.2.dist-info}/entry_points.txt +0 -0
- {fastapi_voyager-0.15.0.dist-info → fastapi_voyager-0.15.2.dist-info}/licenses/LICENSE +0 -0
fastapi_voyager/web/vue-main.js
CHANGED
|
@@ -1,116 +1,139 @@
|
|
|
1
|
-
import SchemaCodeDisplay from "./component/schema-code-display.js"
|
|
2
|
-
import RouteCodeDisplay from "./component/route-code-display.js"
|
|
3
|
-
import Demo from "./component/demo.js"
|
|
4
|
-
import RenderGraph from "./component/render-graph.js"
|
|
5
|
-
import { GraphUI } from "./graph-ui.js"
|
|
6
|
-
import { store } from "./store.js"
|
|
7
|
-
|
|
8
|
-
const { createApp, onMounted, ref, watch } = window.Vue
|
|
1
|
+
import SchemaCodeDisplay from "./component/schema-code-display.js"
|
|
2
|
+
import RouteCodeDisplay from "./component/route-code-display.js"
|
|
3
|
+
import Demo from "./component/demo.js"
|
|
4
|
+
import RenderGraph from "./component/render-graph.js"
|
|
5
|
+
import { GraphUI } from "./graph-ui.js"
|
|
6
|
+
import { store } from "./store.js"
|
|
7
|
+
|
|
8
|
+
const { createApp, onMounted, ref, watch } = window.Vue
|
|
9
|
+
|
|
10
|
+
// Load toggle states from localStorage
|
|
11
|
+
function loadToggleState(key, defaultValue = false) {
|
|
12
|
+
if (typeof window === "undefined") return defaultValue
|
|
13
|
+
try {
|
|
14
|
+
const saved = localStorage.getItem(key)
|
|
15
|
+
return saved !== null ? JSON.parse(saved) : defaultValue
|
|
16
|
+
} catch (e) {
|
|
17
|
+
console.warn(`Failed to load ${key} from localStorage`, e)
|
|
18
|
+
return defaultValue
|
|
19
|
+
}
|
|
20
|
+
}
|
|
9
21
|
|
|
10
22
|
const app = createApp({
|
|
11
23
|
setup() {
|
|
12
|
-
let graphUI = null
|
|
13
|
-
const allSchemaOptions = ref([])
|
|
14
|
-
const erDiagramLoading = ref(false)
|
|
15
|
-
const erDiagramCache = ref("")
|
|
24
|
+
let graphUI = null
|
|
25
|
+
const allSchemaOptions = ref([])
|
|
26
|
+
const erDiagramLoading = ref(false)
|
|
27
|
+
const erDiagramCache = ref("")
|
|
28
|
+
|
|
29
|
+
// Initialize toggle states from localStorage
|
|
30
|
+
store.state.modeControl.pydanticResolveMetaEnabled = loadToggleState(
|
|
31
|
+
"pydantic_resolve_meta",
|
|
32
|
+
false
|
|
33
|
+
)
|
|
34
|
+
store.state.filter.hidePrimitiveRoute = loadToggleState("hide_primitive", false)
|
|
35
|
+
store.state.filter.brief = loadToggleState("brief_mode", false)
|
|
36
|
+
store.state.filter.showModule = loadToggleState("show_module_cluster", false)
|
|
16
37
|
|
|
17
38
|
function initGraphUI() {
|
|
18
39
|
if (graphUI) {
|
|
19
|
-
return
|
|
40
|
+
return
|
|
20
41
|
}
|
|
21
42
|
graphUI = new GraphUI("#graph", {
|
|
22
43
|
onSchemaShiftClick: (id) => {
|
|
23
44
|
if (store.state.graph.schemaKeys.has(id)) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
store.state.previousTagRoute.hasValue
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
45
|
+
// Only save current tag/route if we're not already in search mode
|
|
46
|
+
// This prevents overwriting the saved state with null values
|
|
47
|
+
if (!store.state.previousTagRoute.hasValue && !store.state.search.mode) {
|
|
48
|
+
store.state.previousTagRoute.tag = store.state.leftPanel.tag
|
|
49
|
+
store.state.previousTagRoute.routeId = store.state.leftPanel.routeId
|
|
50
|
+
store.state.previousTagRoute.hasValue = true
|
|
51
|
+
}
|
|
52
|
+
store.state.search.mode = true
|
|
53
|
+
store.state.search.schemaName = id
|
|
54
|
+
onSearch()
|
|
30
55
|
}
|
|
31
56
|
},
|
|
32
57
|
onSchemaClick: (id) => {
|
|
33
|
-
resetDetailPanels()
|
|
58
|
+
resetDetailPanels()
|
|
34
59
|
if (store.state.graph.schemaKeys.has(id)) {
|
|
35
|
-
store.state.schemaDetail.schemaCodeName = id
|
|
36
|
-
store.state.rightDrawer.drawer = true
|
|
60
|
+
store.state.schemaDetail.schemaCodeName = id
|
|
61
|
+
store.state.rightDrawer.drawer = true
|
|
37
62
|
}
|
|
38
63
|
if (id in store.state.graph.routeItems) {
|
|
39
|
-
store.state.routeDetail.routeCodeId = id
|
|
40
|
-
store.state.routeDetail.show = true
|
|
64
|
+
store.state.routeDetail.routeCodeId = id
|
|
65
|
+
store.state.routeDetail.show = true
|
|
41
66
|
}
|
|
42
67
|
},
|
|
43
68
|
resetCb: () => {
|
|
44
|
-
resetDetailPanels()
|
|
69
|
+
resetDetailPanels()
|
|
45
70
|
},
|
|
46
|
-
})
|
|
71
|
+
})
|
|
47
72
|
}
|
|
48
73
|
|
|
49
74
|
function rebuildSchemaOptions() {
|
|
50
|
-
const dict = store.state.graph.schemaMap || {}
|
|
75
|
+
const dict = store.state.graph.schemaMap || {}
|
|
51
76
|
const opts = Object.values(dict).map((s) => ({
|
|
52
77
|
label: s.name,
|
|
53
78
|
desc: s.id,
|
|
54
79
|
value: s.id,
|
|
55
|
-
}))
|
|
56
|
-
allSchemaOptions.value = opts
|
|
57
|
-
store.state.search.schemaOptions = opts.slice()
|
|
58
|
-
populateFieldOptions(store.state.search.schemaName)
|
|
80
|
+
}))
|
|
81
|
+
allSchemaOptions.value = opts
|
|
82
|
+
store.state.search.schemaOptions = opts.slice()
|
|
83
|
+
populateFieldOptions(store.state.search.schemaName)
|
|
59
84
|
}
|
|
60
85
|
|
|
61
86
|
function populateFieldOptions(schemaId) {
|
|
62
87
|
if (!schemaId) {
|
|
63
|
-
store.state.search.fieldOptions = []
|
|
64
|
-
store.state.search.fieldName = null
|
|
65
|
-
return
|
|
88
|
+
store.state.search.fieldOptions = []
|
|
89
|
+
store.state.search.fieldName = null
|
|
90
|
+
return
|
|
66
91
|
}
|
|
67
|
-
const schema = store.state.graph.schemaMap?.[schemaId]
|
|
92
|
+
const schema = store.state.graph.schemaMap?.[schemaId]
|
|
68
93
|
if (!schema) {
|
|
69
|
-
store.state.search.fieldOptions = []
|
|
70
|
-
store.state.search.fieldName = null
|
|
71
|
-
return
|
|
72
|
-
}
|
|
73
|
-
const fields = Array.isArray(schema.fields)
|
|
74
|
-
|
|
75
|
-
: [];
|
|
76
|
-
store.state.search.fieldOptions = fields;
|
|
94
|
+
store.state.search.fieldOptions = []
|
|
95
|
+
store.state.search.fieldName = null
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
const fields = Array.isArray(schema.fields) ? schema.fields.map((f) => f.name) : []
|
|
99
|
+
store.state.search.fieldOptions = fields
|
|
77
100
|
if (!fields.includes(store.state.search.fieldName)) {
|
|
78
|
-
store.state.search.fieldName = null
|
|
101
|
+
store.state.search.fieldName = null
|
|
79
102
|
}
|
|
80
103
|
}
|
|
81
104
|
|
|
82
105
|
function filterSearchSchemas(val, update) {
|
|
83
|
-
const needle = (val || "").toLowerCase()
|
|
106
|
+
const needle = (val || "").toLowerCase()
|
|
84
107
|
update(() => {
|
|
85
108
|
if (!needle) {
|
|
86
|
-
store.state.search.schemaOptions = allSchemaOptions.value.slice()
|
|
87
|
-
return
|
|
109
|
+
store.state.search.schemaOptions = allSchemaOptions.value.slice()
|
|
110
|
+
return
|
|
88
111
|
}
|
|
89
112
|
store.state.search.schemaOptions = allSchemaOptions.value.filter((option) =>
|
|
90
113
|
option.label.toLowerCase().includes(needle)
|
|
91
|
-
)
|
|
92
|
-
})
|
|
114
|
+
)
|
|
115
|
+
})
|
|
93
116
|
}
|
|
94
117
|
|
|
95
118
|
function onSearchSchemaChange(val) {
|
|
96
|
-
store.state.search.schemaName = val
|
|
97
|
-
store.state.search.mode = false
|
|
119
|
+
store.state.search.schemaName = val
|
|
120
|
+
store.state.search.mode = false
|
|
98
121
|
if (!val) {
|
|
99
122
|
// Clearing the select should only run resetSearch via @clear
|
|
100
|
-
return
|
|
123
|
+
return
|
|
101
124
|
}
|
|
102
|
-
onSearch()
|
|
125
|
+
onSearch()
|
|
103
126
|
}
|
|
104
127
|
|
|
105
128
|
function readQuerySelection() {
|
|
106
129
|
if (typeof window === "undefined") {
|
|
107
|
-
return { tag: null, route: null }
|
|
130
|
+
return { tag: null, route: null }
|
|
108
131
|
}
|
|
109
|
-
const params = new URLSearchParams(window.location.search)
|
|
132
|
+
const params = new URLSearchParams(window.location.search)
|
|
110
133
|
return {
|
|
111
134
|
tag: params.get("tag") || null,
|
|
112
135
|
route: params.get("route") || null,
|
|
113
|
-
}
|
|
136
|
+
}
|
|
114
137
|
}
|
|
115
138
|
|
|
116
139
|
function findTagByRoute(routeId) {
|
|
@@ -118,82 +141,92 @@ const app = createApp({
|
|
|
118
141
|
store.state.leftPanel.tags.find((tag) =>
|
|
119
142
|
(tag.routes || []).some((route) => route.id === routeId)
|
|
120
143
|
)?.name || null
|
|
121
|
-
)
|
|
144
|
+
)
|
|
122
145
|
}
|
|
123
146
|
|
|
124
147
|
function syncSelectionToUrl() {
|
|
125
148
|
if (typeof window === "undefined") {
|
|
126
|
-
return
|
|
149
|
+
return
|
|
127
150
|
}
|
|
128
|
-
const params = new URLSearchParams(window.location.search)
|
|
151
|
+
const params = new URLSearchParams(window.location.search)
|
|
129
152
|
if (store.state.leftPanel.tag) {
|
|
130
|
-
params.set("tag", store.state.leftPanel.tag)
|
|
153
|
+
params.set("tag", store.state.leftPanel.tag)
|
|
131
154
|
} else {
|
|
132
|
-
params.delete("tag")
|
|
155
|
+
params.delete("tag")
|
|
133
156
|
}
|
|
134
157
|
if (store.state.leftPanel.routeId) {
|
|
135
|
-
params.set("route", store.state.leftPanel.routeId)
|
|
158
|
+
params.set("route", store.state.leftPanel.routeId)
|
|
136
159
|
} else {
|
|
137
|
-
params.delete("route")
|
|
160
|
+
params.delete("route")
|
|
138
161
|
}
|
|
139
|
-
const hash = window.location.hash || ""
|
|
140
|
-
const search = params.toString()
|
|
141
|
-
const base = window.location.pathname
|
|
142
|
-
const newUrl = search ? `${base}?${search}${hash}` : `${base}${hash}
|
|
143
|
-
window.history.replaceState({}, "", newUrl)
|
|
162
|
+
const hash = window.location.hash || ""
|
|
163
|
+
const search = params.toString()
|
|
164
|
+
const base = window.location.pathname
|
|
165
|
+
const newUrl = search ? `${base}?${search}${hash}` : `${base}${hash}`
|
|
166
|
+
window.history.replaceState({}, "", newUrl)
|
|
144
167
|
}
|
|
145
168
|
|
|
146
169
|
function applySelectionFromQuery(selection) {
|
|
147
|
-
let applied = false
|
|
148
|
-
if (
|
|
149
|
-
selection.tag
|
|
150
|
-
store.state.leftPanel.
|
|
151
|
-
|
|
152
|
-
store.state.leftPanel.tag = selection.tag;
|
|
153
|
-
store.state.leftPanel._tag = selection.tag;
|
|
154
|
-
applied = true;
|
|
170
|
+
let applied = false
|
|
171
|
+
if (selection.tag && store.state.leftPanel.tags.some((tag) => tag.name === selection.tag)) {
|
|
172
|
+
store.state.leftPanel.tag = selection.tag
|
|
173
|
+
store.state.leftPanel._tag = selection.tag
|
|
174
|
+
applied = true
|
|
155
175
|
}
|
|
156
176
|
if (selection.route && store.state.graph.routeItems?.[selection.route]) {
|
|
157
|
-
store.state.leftPanel.routeId = selection.route
|
|
158
|
-
applied = true
|
|
159
|
-
const inferredTag = findTagByRoute(selection.route)
|
|
177
|
+
store.state.leftPanel.routeId = selection.route
|
|
178
|
+
applied = true
|
|
179
|
+
const inferredTag = findTagByRoute(selection.route)
|
|
160
180
|
if (inferredTag) {
|
|
161
|
-
store.state.leftPanel.tag = inferredTag
|
|
162
|
-
store.state.leftPanel._tag = inferredTag
|
|
181
|
+
store.state.leftPanel.tag = inferredTag
|
|
182
|
+
store.state.leftPanel._tag = inferredTag
|
|
163
183
|
}
|
|
164
184
|
}
|
|
165
|
-
return applied
|
|
185
|
+
return applied
|
|
166
186
|
}
|
|
167
187
|
|
|
168
188
|
async function resetSearch() {
|
|
169
|
-
store.state.search.mode = false
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
if (
|
|
173
|
-
store.state.leftPanel.tag = store.state.previousTagRoute.tag
|
|
174
|
-
store.state.leftPanel._tag = store.state.previousTagRoute.tag
|
|
175
|
-
store.state.leftPanel.routeId = store.state.previousTagRoute.routeId
|
|
176
|
-
store.state.previousTagRoute.hasValue = false
|
|
189
|
+
store.state.search.mode = false
|
|
190
|
+
const hadPreviousValue = store.state.previousTagRoute.hasValue
|
|
191
|
+
|
|
192
|
+
if (hadPreviousValue) {
|
|
193
|
+
store.state.leftPanel.tag = store.state.previousTagRoute.tag
|
|
194
|
+
store.state.leftPanel._tag = store.state.previousTagRoute.tag
|
|
195
|
+
store.state.leftPanel.routeId = store.state.previousTagRoute.routeId
|
|
196
|
+
store.state.previousTagRoute.hasValue = false
|
|
177
197
|
} else {
|
|
178
|
-
store.state.leftPanel.tag = null
|
|
179
|
-
store.state.leftPanel._tag = null
|
|
180
|
-
store.state.leftPanel.routeId = null
|
|
198
|
+
store.state.leftPanel.tag = null
|
|
199
|
+
store.state.leftPanel._tag = null
|
|
200
|
+
store.state.leftPanel.routeId = null
|
|
181
201
|
}
|
|
182
202
|
|
|
183
203
|
syncSelectionToUrl()
|
|
184
|
-
|
|
185
|
-
|
|
204
|
+
|
|
205
|
+
// Load the full tags from cache (not search results) since we're resetting search
|
|
206
|
+
loadFullTags()
|
|
207
|
+
|
|
208
|
+
// If we restored a previous tag/route, generate with it
|
|
209
|
+
// Otherwise, fall back to initial policy
|
|
210
|
+
if (hadPreviousValue) {
|
|
211
|
+
onGenerate()
|
|
212
|
+
} else {
|
|
213
|
+
renderBasedOnInitialPolicy()
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function loadFullTags() {
|
|
218
|
+
// Restore from cache (set by loadInitial)
|
|
219
|
+
store.state.leftPanel.tags = store.state.leftPanel.fullTagsCache
|
|
186
220
|
}
|
|
187
221
|
|
|
188
222
|
async function onSearch() {
|
|
189
|
-
|
|
190
|
-
store.state.
|
|
191
|
-
store.state.leftPanel.
|
|
192
|
-
store.state.leftPanel.
|
|
193
|
-
store.state.leftPanel.routeId = null;
|
|
223
|
+
store.state.search.mode = true
|
|
224
|
+
store.state.leftPanel.tag = null
|
|
225
|
+
store.state.leftPanel._tag = null
|
|
226
|
+
store.state.leftPanel.routeId = null
|
|
194
227
|
syncSelectionToUrl()
|
|
195
|
-
await loadSearchedTags()
|
|
196
|
-
await onGenerate()
|
|
228
|
+
await loadSearchedTags()
|
|
229
|
+
await onGenerate()
|
|
197
230
|
}
|
|
198
231
|
async function loadSearchedTags() {
|
|
199
232
|
try {
|
|
@@ -204,56 +237,58 @@ const app = createApp({
|
|
|
204
237
|
brief: store.state.filter.brief,
|
|
205
238
|
hide_primitive_route: store.state.filter.hidePrimitiveRoute,
|
|
206
239
|
show_module: store.state.filter.showModule,
|
|
207
|
-
}
|
|
240
|
+
}
|
|
208
241
|
const res = await fetch("dot-search", {
|
|
209
242
|
method: "POST",
|
|
210
243
|
headers: { "Content-Type": "application/json" },
|
|
211
244
|
body: JSON.stringify(payload),
|
|
212
|
-
})
|
|
245
|
+
})
|
|
213
246
|
if (res.ok) {
|
|
214
|
-
const data = await res.json()
|
|
215
|
-
const tags = Array.isArray(data.tags) ? data.tags : []
|
|
216
|
-
store.state.leftPanel.tags = tags
|
|
247
|
+
const data = await res.json()
|
|
248
|
+
const tags = Array.isArray(data.tags) ? data.tags : []
|
|
249
|
+
store.state.leftPanel.tags = tags
|
|
217
250
|
}
|
|
218
251
|
} catch (err) {
|
|
219
|
-
console.error("dot-search failed", err)
|
|
252
|
+
console.error("dot-search failed", err)
|
|
220
253
|
}
|
|
221
254
|
}
|
|
222
255
|
|
|
223
256
|
async function loadInitial() {
|
|
224
|
-
store.state.initializing = true
|
|
257
|
+
store.state.initializing = true
|
|
225
258
|
try {
|
|
226
|
-
const res = await fetch("dot")
|
|
227
|
-
const data = await res.json()
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
259
|
+
const res = await fetch("dot")
|
|
260
|
+
const data = await res.json()
|
|
261
|
+
const tags = Array.isArray(data.tags) ? data.tags : []
|
|
262
|
+
store.state.leftPanel.tags = tags
|
|
263
|
+
// Cache the full tags for later use (e.g., resetSearch)
|
|
264
|
+
store.state.leftPanel.fullTagsCache = tags
|
|
265
|
+
|
|
266
|
+
const schemasArr = Array.isArray(data.schemas) ? data.schemas : []
|
|
231
267
|
// Build dict keyed by id for faster lookups and simpler prop passing
|
|
232
|
-
const schemaMap = Object.fromEntries(schemasArr.map((s) => [s.id, s]))
|
|
233
|
-
store.state.graph.schemaMap = schemaMap
|
|
234
|
-
store.state.graph.schemaKeys = new Set(Object.keys(schemaMap))
|
|
268
|
+
const schemaMap = Object.fromEntries(schemasArr.map((s) => [s.id, s]))
|
|
269
|
+
store.state.graph.schemaMap = schemaMap
|
|
270
|
+
store.state.graph.schemaKeys = new Set(Object.keys(schemaMap))
|
|
235
271
|
store.state.graph.routeItems = data.tags
|
|
236
272
|
.map((t) => t.routes)
|
|
237
273
|
.flat()
|
|
238
274
|
.reduce((acc, r) => {
|
|
239
|
-
acc[r.id] = r
|
|
240
|
-
return acc
|
|
241
|
-
}, {})
|
|
242
|
-
store.state.modeControl.briefModeEnabled =
|
|
243
|
-
|
|
244
|
-
store.state.
|
|
245
|
-
store.state.
|
|
246
|
-
store.state.config.
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
const
|
|
252
|
-
const restoredFromQuery = applySelectionFromQuery(querySelection);
|
|
275
|
+
acc[r.id] = r
|
|
276
|
+
return acc
|
|
277
|
+
}, {})
|
|
278
|
+
store.state.modeControl.briefModeEnabled = data.enable_brief_mode || false
|
|
279
|
+
store.state.version = data.version || ""
|
|
280
|
+
store.state.swagger.url = data.swagger_url || null
|
|
281
|
+
store.state.config.has_er_diagram = data.has_er_diagram || false
|
|
282
|
+
store.state.config.enable_pydantic_resolve_meta = data.enable_pydantic_resolve_meta || false
|
|
283
|
+
|
|
284
|
+
rebuildSchemaOptions()
|
|
285
|
+
|
|
286
|
+
const querySelection = readQuerySelection()
|
|
287
|
+
const restoredFromQuery = applySelectionFromQuery(querySelection)
|
|
253
288
|
if (restoredFromQuery) {
|
|
254
|
-
syncSelectionToUrl()
|
|
255
|
-
onGenerate()
|
|
256
|
-
return
|
|
289
|
+
syncSelectionToUrl()
|
|
290
|
+
onGenerate()
|
|
291
|
+
return
|
|
257
292
|
} else {
|
|
258
293
|
store.state.config.initial_page_policy = data.initial_page_policy
|
|
259
294
|
renderBasedOnInitialPolicy()
|
|
@@ -261,50 +296,44 @@ const app = createApp({
|
|
|
261
296
|
|
|
262
297
|
// default route options placeholder
|
|
263
298
|
} catch (e) {
|
|
264
|
-
console.error("Initial load failed", e)
|
|
299
|
+
console.error("Initial load failed", e)
|
|
265
300
|
} finally {
|
|
266
|
-
store.state.initializing = false
|
|
301
|
+
store.state.initializing = false
|
|
267
302
|
}
|
|
268
303
|
}
|
|
269
304
|
|
|
270
305
|
async function renderBasedOnInitialPolicy() {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
306
|
+
switch (store.state.config.initial_page_policy) {
|
|
307
|
+
case "full":
|
|
308
|
+
onGenerate()
|
|
309
|
+
return
|
|
310
|
+
case "empty":
|
|
311
|
+
return
|
|
312
|
+
case "first":
|
|
313
|
+
store.state.leftPanel.tag =
|
|
314
|
+
store.state.leftPanel.tags.length > 0 ? store.state.leftPanel.tags[0].name : null
|
|
315
|
+
store.state.leftPanel._tag = store.state.leftPanel.tag
|
|
316
|
+
syncSelectionToUrl()
|
|
317
|
+
onGenerate()
|
|
318
|
+
return
|
|
319
|
+
}
|
|
287
320
|
}
|
|
288
321
|
|
|
289
322
|
async function onGenerate(resetZoom = true) {
|
|
290
323
|
switch (store.state.mode) {
|
|
291
324
|
case "voyager":
|
|
292
|
-
await renderVoyager(resetZoom)
|
|
293
|
-
break
|
|
325
|
+
await renderVoyager(resetZoom)
|
|
326
|
+
break
|
|
294
327
|
case "er-diagram":
|
|
295
|
-
await renderErDiagram(resetZoom)
|
|
296
|
-
break
|
|
328
|
+
await renderErDiagram(resetZoom)
|
|
329
|
+
break
|
|
297
330
|
}
|
|
298
331
|
}
|
|
299
332
|
|
|
300
333
|
async function renderVoyager(resetZoom = true) {
|
|
301
|
-
const activeSchema = store.state.search.mode
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
const activeField = store.state.search.mode
|
|
305
|
-
? store.state.search.fieldName
|
|
306
|
-
: null;
|
|
307
|
-
store.state.generating = true;
|
|
334
|
+
const activeSchema = store.state.search.mode ? store.state.search.schemaName : null
|
|
335
|
+
const activeField = store.state.search.mode ? store.state.search.fieldName : null
|
|
336
|
+
store.state.generating = true
|
|
308
337
|
try {
|
|
309
338
|
const payload = {
|
|
310
339
|
tags: store.state.leftPanel.tag ? [store.state.leftPanel.tag] : null,
|
|
@@ -315,212 +344,248 @@ const app = createApp({
|
|
|
315
344
|
brief: store.state.filter.brief,
|
|
316
345
|
hide_primitive_route: store.state.filter.hidePrimitiveRoute,
|
|
317
346
|
show_module: store.state.filter.showModule,
|
|
318
|
-
show_pydantic_resolve_meta: store.state.modeControl.pydanticResolveMetaEnabled
|
|
319
|
-
}
|
|
320
|
-
initGraphUI()
|
|
347
|
+
show_pydantic_resolve_meta: store.state.modeControl.pydanticResolveMetaEnabled,
|
|
348
|
+
}
|
|
349
|
+
initGraphUI()
|
|
321
350
|
const res = await fetch("dot", {
|
|
322
351
|
method: "POST",
|
|
323
352
|
headers: { "Content-Type": "application/json" },
|
|
324
353
|
body: JSON.stringify(payload),
|
|
325
|
-
})
|
|
326
|
-
const dotText = await res.text()
|
|
354
|
+
})
|
|
355
|
+
const dotText = await res.text()
|
|
327
356
|
|
|
328
|
-
await graphUI.render(dotText, resetZoom)
|
|
357
|
+
await graphUI.render(dotText, resetZoom)
|
|
329
358
|
} catch (e) {
|
|
330
|
-
console.error("Generate failed", e)
|
|
359
|
+
console.error("Generate failed", e)
|
|
331
360
|
} finally {
|
|
332
|
-
store.state.generating = false
|
|
361
|
+
store.state.generating = false
|
|
333
362
|
}
|
|
334
363
|
}
|
|
335
364
|
|
|
336
365
|
function resetDetailPanels() {
|
|
337
|
-
store.state.rightDrawer.drawer = false
|
|
338
|
-
store.state.routeDetail.show = false
|
|
339
|
-
store.state.schemaDetail.schemaCodeName = ""
|
|
366
|
+
store.state.rightDrawer.drawer = false
|
|
367
|
+
store.state.routeDetail.show = false
|
|
368
|
+
store.state.schemaDetail.schemaCodeName = ""
|
|
340
369
|
}
|
|
341
370
|
|
|
342
371
|
async function onReset() {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
372
|
+
store.state.leftPanel.tag = null
|
|
373
|
+
store.state.leftPanel._tag = null
|
|
374
|
+
store.state.leftPanel.routeId = null
|
|
375
|
+
syncSelectionToUrl()
|
|
376
|
+
onGenerate()
|
|
348
377
|
}
|
|
349
378
|
|
|
350
379
|
async function togglePydanticResolveMeta(val) {
|
|
351
|
-
store.state.modeControl.pydanticResolveMetaEnabled = val
|
|
352
|
-
|
|
380
|
+
store.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()
|
|
353
387
|
}
|
|
354
388
|
|
|
355
389
|
async function renderErDiagram(resetZoom = true) {
|
|
356
|
-
initGraphUI()
|
|
357
|
-
erDiagramLoading.value = true
|
|
390
|
+
initGraphUI()
|
|
391
|
+
erDiagramLoading.value = true
|
|
358
392
|
const payload = {
|
|
359
393
|
show_fields: store.state.filter.showFields,
|
|
360
394
|
show_module: store.state.filter.showModule,
|
|
361
|
-
}
|
|
395
|
+
}
|
|
362
396
|
try {
|
|
363
|
-
const res = await fetch("er-diagram", {
|
|
397
|
+
const res = await fetch("er-diagram", {
|
|
364
398
|
method: "POST",
|
|
365
399
|
headers: { "Content-Type": "application/json" },
|
|
366
400
|
body: JSON.stringify(payload),
|
|
367
|
-
|
|
401
|
+
})
|
|
368
402
|
if (!res.ok) {
|
|
369
|
-
throw new Error(`failed with status ${res.status}`)
|
|
403
|
+
throw new Error(`failed with status ${res.status}`)
|
|
370
404
|
}
|
|
371
|
-
const dot = await res.text()
|
|
372
|
-
erDiagramCache.value = dot
|
|
373
|
-
await graphUI.render(dot, resetZoom)
|
|
405
|
+
const dot = await res.text()
|
|
406
|
+
erDiagramCache.value = dot
|
|
407
|
+
await graphUI.render(dot, resetZoom)
|
|
374
408
|
} catch (err) {
|
|
375
409
|
console.error(err)
|
|
376
410
|
} finally {
|
|
377
|
-
erDiagramLoading.value = false
|
|
411
|
+
erDiagramLoading.value = false
|
|
378
412
|
}
|
|
379
413
|
}
|
|
380
414
|
|
|
381
415
|
async function onModeChange(val) {
|
|
382
416
|
if (val === "er-diagram") {
|
|
383
|
-
// clear search
|
|
417
|
+
// clear search
|
|
384
418
|
store.state.search.schemaName = null
|
|
385
419
|
store.state.search.fieldName = null
|
|
386
420
|
store.state.search.invisible = true
|
|
387
421
|
|
|
388
422
|
if (store.state.leftPanel.width > 0) {
|
|
389
|
-
store.state.leftPanel.previousWidth = store.state.leftPanel.width
|
|
423
|
+
store.state.leftPanel.previousWidth = store.state.leftPanel.width
|
|
390
424
|
}
|
|
391
|
-
store.state.leftPanel.width = 0
|
|
392
|
-
await renderErDiagram()
|
|
425
|
+
store.state.leftPanel.width = 0
|
|
426
|
+
await renderErDiagram()
|
|
393
427
|
} else {
|
|
394
428
|
store.state.search.invisible = false
|
|
395
|
-
|
|
396
|
-
const fallbackWidth = store.state.leftPanel.previousWidth || 300
|
|
397
|
-
store.state.leftPanel.width = fallbackWidth
|
|
398
|
-
await onGenerate()
|
|
429
|
+
|
|
430
|
+
const fallbackWidth = store.state.leftPanel.previousWidth || 300
|
|
431
|
+
store.state.leftPanel.width = fallbackWidth
|
|
432
|
+
await onGenerate()
|
|
399
433
|
}
|
|
400
434
|
}
|
|
401
435
|
|
|
402
436
|
function toggleTag(tagName, expanded = null) {
|
|
403
437
|
if (expanded === true || store.state.search.mode === true) {
|
|
404
|
-
store.state.leftPanel._tag = tagName
|
|
405
|
-
store.state.leftPanel.tag = tagName
|
|
406
|
-
store.state.leftPanel.routeId = ""
|
|
438
|
+
store.state.leftPanel._tag = tagName
|
|
439
|
+
store.state.leftPanel.tag = tagName
|
|
440
|
+
store.state.leftPanel.routeId = ""
|
|
407
441
|
|
|
408
|
-
store.state.schemaDetail.schemaCodeName = ""
|
|
409
|
-
onGenerate()
|
|
442
|
+
store.state.schemaDetail.schemaCodeName = ""
|
|
443
|
+
onGenerate()
|
|
410
444
|
} else {
|
|
411
|
-
store.state.leftPanel._tag = null
|
|
445
|
+
store.state.leftPanel._tag = null
|
|
412
446
|
}
|
|
413
447
|
|
|
414
|
-
store.state.rightDrawer.drawer = false
|
|
415
|
-
store.state.routeDetail.show = false
|
|
416
|
-
syncSelectionToUrl()
|
|
448
|
+
store.state.rightDrawer.drawer = false
|
|
449
|
+
store.state.routeDetail.show = false
|
|
450
|
+
syncSelectionToUrl()
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function toggleTagNavigatorCollapse() {
|
|
454
|
+
if (store.state.leftPanel.collapsed) {
|
|
455
|
+
// Expand: restore previous width
|
|
456
|
+
const fallbackWidth = store.state.leftPanel.previousWidth || 300
|
|
457
|
+
store.state.leftPanel.width = fallbackWidth
|
|
458
|
+
store.state.leftPanel.collapsed = false
|
|
459
|
+
} else {
|
|
460
|
+
// Collapse: save current width and set to 0
|
|
461
|
+
if (store.state.leftPanel.width > 0) {
|
|
462
|
+
store.state.leftPanel.previousWidth = store.state.leftPanel.width
|
|
463
|
+
}
|
|
464
|
+
store.state.leftPanel.width = 0
|
|
465
|
+
store.state.leftPanel.collapsed = true
|
|
466
|
+
}
|
|
417
467
|
}
|
|
418
468
|
|
|
419
469
|
function selectRoute(routeId) {
|
|
420
470
|
// find belonging tag
|
|
421
|
-
const belongingTag = findTagByRoute(routeId)
|
|
471
|
+
const belongingTag = findTagByRoute(routeId)
|
|
422
472
|
if (belongingTag) {
|
|
423
|
-
store.state.leftPanel.tag = belongingTag
|
|
424
|
-
store.state.leftPanel._tag = belongingTag
|
|
473
|
+
store.state.leftPanel.tag = belongingTag
|
|
474
|
+
store.state.leftPanel._tag = belongingTag
|
|
425
475
|
}
|
|
426
476
|
|
|
427
477
|
if (store.state.leftPanel.routeId === routeId) {
|
|
428
|
-
store.state.leftPanel.routeId = ""
|
|
478
|
+
store.state.leftPanel.routeId = ""
|
|
429
479
|
} else {
|
|
430
|
-
store.state.leftPanel.routeId = routeId
|
|
480
|
+
store.state.leftPanel.routeId = routeId
|
|
431
481
|
}
|
|
432
482
|
|
|
433
|
-
store.state.rightDrawer.drawer = false
|
|
434
|
-
store.state.routeDetail.show = false
|
|
435
|
-
store.state.schemaDetail.schemaCodeName = ""
|
|
436
|
-
syncSelectionToUrl()
|
|
437
|
-
onGenerate()
|
|
483
|
+
store.state.rightDrawer.drawer = false
|
|
484
|
+
store.state.routeDetail.show = false
|
|
485
|
+
store.state.schemaDetail.schemaCodeName = ""
|
|
486
|
+
syncSelectionToUrl()
|
|
487
|
+
onGenerate()
|
|
438
488
|
}
|
|
439
489
|
|
|
440
490
|
function toggleShowModule(val) {
|
|
441
|
-
store.state.filter.showModule = val
|
|
442
|
-
|
|
491
|
+
store.state.filter.showModule = val
|
|
492
|
+
try {
|
|
493
|
+
localStorage.setItem("show_module_cluster", JSON.stringify(val))
|
|
494
|
+
} catch (e) {
|
|
495
|
+
console.warn("Failed to save show_module_cluster to localStorage", e)
|
|
496
|
+
}
|
|
497
|
+
onGenerate()
|
|
443
498
|
}
|
|
444
499
|
|
|
445
500
|
function toggleShowField(field) {
|
|
446
|
-
store.state.filter.showFields = field
|
|
447
|
-
onGenerate(false)
|
|
501
|
+
store.state.filter.showFields = field
|
|
502
|
+
onGenerate(false)
|
|
448
503
|
}
|
|
449
504
|
|
|
450
505
|
function toggleBrief(val) {
|
|
451
|
-
store.state.filter.brief = val
|
|
452
|
-
|
|
506
|
+
store.state.filter.brief = val
|
|
507
|
+
try {
|
|
508
|
+
localStorage.setItem("brief_mode", JSON.stringify(val))
|
|
509
|
+
} catch (e) {
|
|
510
|
+
console.warn("Failed to save brief_mode to localStorage", e)
|
|
511
|
+
}
|
|
512
|
+
onGenerate()
|
|
453
513
|
}
|
|
454
514
|
|
|
455
515
|
function toggleHidePrimitiveRoute(val) {
|
|
456
|
-
store.state.filter.hidePrimitiveRoute = val
|
|
457
|
-
|
|
516
|
+
store.state.filter.hidePrimitiveRoute = val
|
|
517
|
+
try {
|
|
518
|
+
localStorage.setItem("hide_primitive", JSON.stringify(val))
|
|
519
|
+
} catch (e) {
|
|
520
|
+
console.warn("Failed to save hide_primitive to localStorage", e)
|
|
521
|
+
}
|
|
522
|
+
onGenerate(false)
|
|
458
523
|
}
|
|
459
524
|
|
|
460
525
|
function startDragDrawer(e) {
|
|
461
|
-
const startX = e.clientX
|
|
462
|
-
const startWidth = store.state.rightDrawer.width
|
|
526
|
+
const startX = e.clientX
|
|
527
|
+
const startWidth = store.state.rightDrawer.width
|
|
463
528
|
|
|
464
529
|
function onMouseMove(moveEvent) {
|
|
465
|
-
const deltaX = startX - moveEvent.clientX
|
|
466
|
-
const newWidth = Math.max(300, Math.min(800, startWidth + deltaX))
|
|
467
|
-
store.state.rightDrawer.width = newWidth
|
|
530
|
+
const deltaX = startX - moveEvent.clientX
|
|
531
|
+
const newWidth = Math.max(300, Math.min(800, startWidth + deltaX))
|
|
532
|
+
store.state.rightDrawer.width = newWidth
|
|
468
533
|
}
|
|
469
534
|
|
|
470
535
|
function onMouseUp() {
|
|
471
|
-
document.removeEventListener("mousemove", onMouseMove)
|
|
472
|
-
document.removeEventListener("mouseup", onMouseUp)
|
|
473
|
-
document.body.style.cursor = ""
|
|
474
|
-
document.body.style.userSelect = ""
|
|
536
|
+
document.removeEventListener("mousemove", onMouseMove)
|
|
537
|
+
document.removeEventListener("mouseup", onMouseUp)
|
|
538
|
+
document.body.style.cursor = ""
|
|
539
|
+
document.body.style.userSelect = ""
|
|
475
540
|
}
|
|
476
541
|
|
|
477
|
-
document.addEventListener("mousemove", onMouseMove)
|
|
478
|
-
document.addEventListener("mouseup", onMouseUp)
|
|
479
|
-
document.body.style.cursor = "col-resize"
|
|
480
|
-
document.body.style.userSelect = "none"
|
|
481
|
-
e.preventDefault()
|
|
542
|
+
document.addEventListener("mousemove", onMouseMove)
|
|
543
|
+
document.addEventListener("mouseup", onMouseUp)
|
|
544
|
+
document.body.style.cursor = "col-resize"
|
|
545
|
+
document.body.style.userSelect = "none"
|
|
546
|
+
e.preventDefault()
|
|
482
547
|
}
|
|
483
548
|
|
|
484
549
|
watch(
|
|
485
550
|
() => store.state.graph.schemaMap,
|
|
486
551
|
() => {
|
|
487
|
-
rebuildSchemaOptions()
|
|
552
|
+
rebuildSchemaOptions()
|
|
488
553
|
},
|
|
489
554
|
{ deep: false }
|
|
490
|
-
)
|
|
555
|
+
)
|
|
491
556
|
|
|
492
557
|
watch(
|
|
493
558
|
() => store.state.leftPanel.width,
|
|
494
559
|
(val) => {
|
|
495
560
|
if (store.state.mode === "voyager" && typeof val === "number" && val > 0) {
|
|
496
|
-
store.state.leftPanel.previousWidth = val
|
|
561
|
+
store.state.leftPanel.previousWidth = val
|
|
497
562
|
}
|
|
498
563
|
}
|
|
499
|
-
)
|
|
564
|
+
)
|
|
500
565
|
|
|
501
566
|
watch(
|
|
502
567
|
() => store.state.mode,
|
|
503
568
|
(mode) => {
|
|
504
|
-
onModeChange(mode)
|
|
569
|
+
onModeChange(mode)
|
|
505
570
|
}
|
|
506
|
-
)
|
|
571
|
+
)
|
|
507
572
|
|
|
508
573
|
watch(
|
|
509
574
|
() => store.state.search.schemaName,
|
|
510
575
|
(schemaId) => {
|
|
511
|
-
store.state.search.schemaOptions = allSchemaOptions.value.slice()
|
|
512
|
-
populateFieldOptions(schemaId)
|
|
576
|
+
store.state.search.schemaOptions = allSchemaOptions.value.slice()
|
|
577
|
+
populateFieldOptions(schemaId)
|
|
513
578
|
if (!schemaId) {
|
|
514
|
-
store.state.search.mode = false
|
|
579
|
+
store.state.search.mode = false
|
|
515
580
|
}
|
|
516
581
|
}
|
|
517
|
-
)
|
|
582
|
+
)
|
|
518
583
|
|
|
519
584
|
onMounted(async () => {
|
|
520
|
-
document.body.classList.remove("app-loading")
|
|
521
|
-
await loadInitial()
|
|
585
|
+
document.body.classList.remove("app-loading")
|
|
586
|
+
await loadInitial()
|
|
522
587
|
// Reveal app content only after initial JS/data is ready
|
|
523
|
-
})
|
|
588
|
+
})
|
|
524
589
|
|
|
525
590
|
return {
|
|
526
591
|
store,
|
|
@@ -529,6 +594,7 @@ const app = createApp({
|
|
|
529
594
|
filterSearchSchemas,
|
|
530
595
|
onSearchSchemaChange,
|
|
531
596
|
toggleTag,
|
|
597
|
+
toggleTagNavigatorCollapse,
|
|
532
598
|
toggleBrief,
|
|
533
599
|
toggleHidePrimitiveRoute,
|
|
534
600
|
selectRoute,
|
|
@@ -539,21 +605,21 @@ const app = createApp({
|
|
|
539
605
|
toggleShowModule,
|
|
540
606
|
onModeChange,
|
|
541
607
|
renderErDiagram,
|
|
542
|
-
togglePydanticResolveMeta
|
|
543
|
-
}
|
|
608
|
+
togglePydanticResolveMeta,
|
|
609
|
+
}
|
|
544
610
|
},
|
|
545
|
-
})
|
|
611
|
+
})
|
|
546
612
|
|
|
547
|
-
app.use(window.Quasar)
|
|
613
|
+
app.use(window.Quasar)
|
|
548
614
|
|
|
549
615
|
// Set Quasar primary theme color to green
|
|
550
616
|
if (window.Quasar && typeof window.Quasar.setCssVar === "function") {
|
|
551
|
-
window.Quasar.setCssVar("primary", "#009485")
|
|
617
|
+
window.Quasar.setCssVar("primary", "#009485")
|
|
552
618
|
}
|
|
553
619
|
|
|
554
|
-
app.component("schema-code-display", SchemaCodeDisplay)
|
|
555
|
-
app.component("route-code-display", RouteCodeDisplay)
|
|
556
|
-
app.component("render-graph", RenderGraph)
|
|
557
|
-
app.component("demo-component", Demo)
|
|
620
|
+
app.component("schema-code-display", SchemaCodeDisplay) // double click to see node details
|
|
621
|
+
app.component("route-code-display", RouteCodeDisplay) // double click to see route details
|
|
622
|
+
app.component("render-graph", RenderGraph) // for debug, render pasted dot content
|
|
623
|
+
app.component("demo-component", Demo)
|
|
558
624
|
|
|
559
|
-
app.mount("#q-app")
|
|
625
|
+
app.mount("#q-app")
|