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