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/er_diagram.py
CHANGED
|
@@ -15,138 +15,86 @@ from fastapi_voyager.type import (
|
|
|
15
15
|
ModuleNode,
|
|
16
16
|
SchemaNode,
|
|
17
17
|
)
|
|
18
|
+
from fastapi_voyager.render import Renderer
|
|
19
|
+
from fastapi_voyager.render_style import RenderConfig
|
|
18
20
|
from pydantic import BaseModel
|
|
19
21
|
from pydantic_resolve import ErDiagram, Entity, Relationship, MultipleRelationship
|
|
20
22
|
from logging import getLogger
|
|
21
|
-
from fastapi_voyager.module import build_module_schema_tree
|
|
22
23
|
|
|
23
24
|
logger = getLogger(__name__)
|
|
24
25
|
|
|
25
26
|
|
|
26
|
-
class DiagramRenderer:
|
|
27
|
+
class DiagramRenderer(Renderer):
|
|
28
|
+
"""
|
|
29
|
+
Renderer for Entity-Relationship diagrams.
|
|
30
|
+
|
|
31
|
+
Inherits from Renderer to reuse template system and styling.
|
|
32
|
+
ER diagrams have simpler structure (no tags/routes), so we only
|
|
33
|
+
need to customize the top-level DOT structure.
|
|
34
|
+
"""
|
|
35
|
+
|
|
27
36
|
def __init__(
|
|
28
37
|
self,
|
|
29
38
|
*,
|
|
30
39
|
show_fields: FieldType = 'single',
|
|
31
40
|
show_module: bool = True
|
|
32
41
|
) -> None:
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
42
|
+
# Initialize parent Renderer with shared config
|
|
43
|
+
super().__init__(
|
|
44
|
+
show_fields=show_fields,
|
|
45
|
+
show_module=show_module,
|
|
46
|
+
config=RenderConfig() # Use unified style configuration
|
|
47
|
+
)
|
|
36
48
|
logger.info(f'show_module: {self.show_module}')
|
|
37
49
|
|
|
38
|
-
def render_schema_label(self, node: SchemaNode, color: str | None=None) -> str:
|
|
39
|
-
has_base_fields = any(f.from_base for f in node.fields)
|
|
40
|
-
fields = [n for n in node.fields if n.from_base is False]
|
|
41
|
-
|
|
42
|
-
if self.show_fields == 'all':
|
|
43
|
-
_fields = fields
|
|
44
|
-
elif self.show_fields == 'object':
|
|
45
|
-
_fields = [f for f in fields if f.is_object is True]
|
|
46
|
-
else: # 'single'
|
|
47
|
-
_fields = []
|
|
48
|
-
|
|
49
|
-
fields_parts: list[str] = []
|
|
50
|
-
if self.show_fields == 'all' and has_base_fields:
|
|
51
|
-
fields_parts.append('<tr><td align="left" cellpadding="8"><font color="#999"> Inherited Fields ... </font></td></tr>')
|
|
52
|
-
|
|
53
|
-
for field in _fields:
|
|
54
|
-
type_name = field.type_name[:25] + '..' if len(field.type_name) > 25 else field.type_name
|
|
55
|
-
display_xml = f'<s align="left">{field.name}: {type_name}</s>' if field.is_exclude else f'{field.name}: {type_name}'
|
|
56
|
-
field_str = f"""<tr><td align="left" port="f{field.name}" cellpadding="8"><font> {display_xml} </font></td></tr>"""
|
|
57
|
-
fields_parts.append(field_str)
|
|
58
|
-
|
|
59
|
-
header_color = '#009485' if color is None else color
|
|
60
|
-
header = f"""<tr><td cellpadding="6" bgcolor="{header_color}" align="center" colspan="1" port="{PK}"> <font color="white"> {node.name} </font></td> </tr>"""
|
|
61
|
-
field_content = ''.join(fields_parts) if fields_parts else ''
|
|
62
|
-
return f"""<<table border="0" cellborder="1" cellpadding="0" cellspacing="0" bgcolor="white"> {header} {field_content} </table>>"""
|
|
63
|
-
|
|
64
|
-
def _handle_schema_anchor(self, source: str) -> str:
|
|
65
|
-
if '::' in source:
|
|
66
|
-
a, b = source.split('::', 1)
|
|
67
|
-
return f'"{a}":{b}'
|
|
68
|
-
return f'"{source}"'
|
|
69
|
-
|
|
70
50
|
def render_link(self, link: Link) -> str:
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
51
|
+
"""Override to increase link length by 40% for ER diagrams."""
|
|
52
|
+
source = self._handle_schema_anchor(link.source)
|
|
53
|
+
target = self._handle_schema_anchor(link.target)
|
|
54
|
+
|
|
55
|
+
# Build link attributes
|
|
56
|
+
if link.style is not None:
|
|
57
|
+
attrs = {'style': link.style}
|
|
58
|
+
if link.label:
|
|
59
|
+
attrs['label'] = link.label
|
|
60
|
+
# Increase minlen by 40% (3 * 1.4 = 4.2, round to 4)
|
|
61
|
+
attrs['minlen'] = 4
|
|
74
62
|
else:
|
|
75
|
-
|
|
63
|
+
attrs = self.style.get_link_attributes(link.type)
|
|
64
|
+
if link.label:
|
|
65
|
+
attrs['label'] = link.label
|
|
76
66
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
margin="0.5,0.1"
|
|
84
|
-
];'''
|
|
85
|
-
|
|
86
|
-
def render_module_schema(mod: ModuleNode, show_cluster:bool=True) -> str:
|
|
87
|
-
inner_nodes = [ render_node(node) for node in mod.schema_nodes ]
|
|
88
|
-
inner_nodes_str = '\n'.join(inner_nodes)
|
|
89
|
-
child_str = '\n'.join(render_module_schema(mod=m, show_cluster=show_cluster) for m in mod.modules)
|
|
90
|
-
|
|
91
|
-
if show_cluster:
|
|
92
|
-
return f'''
|
|
93
|
-
subgraph cluster_module_{mod.fullname.replace('.', '_')} {{
|
|
94
|
-
tooltip="{mod.fullname}"
|
|
95
|
-
color = "#666"
|
|
96
|
-
style="rounded"
|
|
97
|
-
label = " {mod.name}"
|
|
98
|
-
labeljust = "l"
|
|
99
|
-
pencolor="#ccc"
|
|
100
|
-
penwidth=""
|
|
101
|
-
{inner_nodes_str}
|
|
102
|
-
{child_str}
|
|
103
|
-
}}'''
|
|
104
|
-
else:
|
|
105
|
-
return f'''
|
|
106
|
-
{inner_nodes_str}
|
|
107
|
-
{child_str}
|
|
108
|
-
'''
|
|
109
|
-
|
|
110
|
-
# if self.show_module:
|
|
111
|
-
module_schemas = build_module_schema_tree(nodes)
|
|
112
|
-
return '\n'.join(render_module_schema(mod=m, show_cluster=self.show_module) for m in module_schemas)
|
|
67
|
+
return self.template_renderer.render_template(
|
|
68
|
+
'dot/link.j2',
|
|
69
|
+
source=source,
|
|
70
|
+
target=target,
|
|
71
|
+
attributes=self._format_link_attributes(attrs)
|
|
72
|
+
)
|
|
113
73
|
|
|
114
74
|
def render_dot(self, nodes: list[SchemaNode], links: list[Link], spline_line=False) -> str:
|
|
115
|
-
|
|
116
|
-
|
|
75
|
+
"""
|
|
76
|
+
Render ER diagram as DOT format.
|
|
117
77
|
|
|
118
|
-
|
|
119
|
-
digraph
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
fontname="Helvetica,Arial,sans-serif"
|
|
124
|
-
node [fontname="Helvetica,Arial,sans-serif"]
|
|
125
|
-
edge [
|
|
126
|
-
fontname="Helvetica,Arial,sans-serif"
|
|
127
|
-
color="gray"
|
|
128
|
-
]
|
|
129
|
-
graph [
|
|
130
|
-
rankdir = "LR"
|
|
131
|
-
];
|
|
132
|
-
node [
|
|
133
|
-
fontsize = "16"
|
|
134
|
-
];
|
|
78
|
+
Reuses parent's render_module_schema_content and render_link methods.
|
|
79
|
+
Only customizes the top-level digraph structure.
|
|
80
|
+
"""
|
|
81
|
+
# Reuse parent's module schema rendering
|
|
82
|
+
module_schemas_str = self.render_module_schema_content(nodes)
|
|
135
83
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
margin=18
|
|
139
|
-
style="dashed"
|
|
140
|
-
label=" ER Diagram"
|
|
141
|
-
labeljust="l"
|
|
142
|
-
fontsize="20"
|
|
143
|
-
{module_schemas_str}
|
|
144
|
-
}}
|
|
84
|
+
# Reuse parent's link rendering
|
|
85
|
+
link_str = '\n'.join(self.render_link(link) for link in links)
|
|
145
86
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
87
|
+
# Render using ER diagram template
|
|
88
|
+
return self.template_renderer.render_template(
|
|
89
|
+
'dot/er_diagram.j2',
|
|
90
|
+
pad=self.style.pad,
|
|
91
|
+
nodesep=self.style.nodesep,
|
|
92
|
+
font=self.style.font,
|
|
93
|
+
node_fontsize=self.style.node_fontsize,
|
|
94
|
+
spline='line' if spline_line else None,
|
|
95
|
+
er_cluster=module_schemas_str,
|
|
96
|
+
links=link_str
|
|
97
|
+
)
|
|
150
98
|
|
|
151
99
|
|
|
152
100
|
class VoyagerErDiagram:
|
fastapi_voyager/render.py
CHANGED
|
@@ -262,8 +262,18 @@ class Renderer:
|
|
|
262
262
|
source = self._handle_schema_anchor(link.source)
|
|
263
263
|
target = self._handle_schema_anchor(link.target)
|
|
264
264
|
|
|
265
|
-
#
|
|
266
|
-
|
|
265
|
+
# Build link attributes
|
|
266
|
+
# If link.style is explicitly set (e.g., 'solid, dashed' for ER diagrams), use it
|
|
267
|
+
# Otherwise, get default style from configuration based on link.type
|
|
268
|
+
if link.style is not None:
|
|
269
|
+
attrs = {'style': link.style}
|
|
270
|
+
if link.label:
|
|
271
|
+
attrs['label'] = link.label
|
|
272
|
+
# attrs['minlen'] = 3
|
|
273
|
+
else:
|
|
274
|
+
attrs = self.style.get_link_attributes(link.type)
|
|
275
|
+
if link.label:
|
|
276
|
+
attrs['label'] = link.label
|
|
267
277
|
|
|
268
278
|
return self.template_renderer.render_template(
|
|
269
279
|
'dot/link.j2',
|
fastapi_voyager/server.py
CHANGED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
digraph world {
|
|
2
|
+
pad="{{ pad }}"
|
|
3
|
+
nodesep={{ nodesep }}
|
|
4
|
+
{% if spline %}splines={{ spline }}{% endif %}
|
|
5
|
+
fontname="{{ font }}"
|
|
6
|
+
node [fontname="{{ font }}"]
|
|
7
|
+
edge [
|
|
8
|
+
fontname="{{ font }}"
|
|
9
|
+
color="gray"
|
|
10
|
+
]
|
|
11
|
+
graph [
|
|
12
|
+
rankdir = "LR"
|
|
13
|
+
];
|
|
14
|
+
node [
|
|
15
|
+
fontsize = {{ node_fontsize }}
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
subgraph cluster_schema {
|
|
19
|
+
color = "#aaa"
|
|
20
|
+
margin=18
|
|
21
|
+
style="dashed"
|
|
22
|
+
label=" ER Diagram"
|
|
23
|
+
labeljust="l"
|
|
24
|
+
fontsize="20"
|
|
25
|
+
{{ er_cluster }}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
{{ links }}
|
|
29
|
+
}
|
fastapi_voyager/version.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
__all__ = ["__version__"]
|
|
2
|
-
__version__ = "0.15.
|
|
2
|
+
__version__ = "0.15.2"
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
const { defineComponent, computed } = window.Vue
|
|
1
|
+
const { defineComponent, computed } = window.Vue
|
|
2
2
|
|
|
3
|
-
import { store } from
|
|
3
|
+
import { store } from "../store.js"
|
|
4
4
|
|
|
5
5
|
export default defineComponent({
|
|
6
6
|
name: "Demo",
|
|
7
7
|
emits: ["close"],
|
|
8
8
|
setup() {
|
|
9
|
-
return { store }
|
|
9
|
+
return { store }
|
|
10
10
|
},
|
|
11
11
|
template: `
|
|
12
12
|
<div>
|
|
13
13
|
<p>Count: {{ store.state.item.count }}</p>
|
|
14
14
|
<button @click="store.mutations.increment()">Add</button>
|
|
15
15
|
</div>
|
|
16
|
-
|
|
17
|
-
})
|
|
16
|
+
`,
|
|
17
|
+
})
|
|
@@ -1,71 +1,71 @@
|
|
|
1
|
-
import { GraphUI } from "../graph-ui.js"
|
|
2
|
-
const { defineComponent, ref, onMounted, nextTick } = window.Vue
|
|
1
|
+
import { GraphUI } from "../graph-ui.js"
|
|
2
|
+
const { defineComponent, ref, onMounted, nextTick } = window.Vue
|
|
3
3
|
|
|
4
4
|
export default defineComponent({
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
5
|
+
name: "RenderGraph",
|
|
6
|
+
props: {
|
|
7
|
+
coreData: { type: [Object, Array], required: false, default: null },
|
|
8
|
+
},
|
|
9
|
+
emits: ["close"],
|
|
10
|
+
setup(props, { emit }) {
|
|
11
|
+
const containerId = `graph-render-${Math.random().toString(36).slice(2, 9)}`
|
|
12
|
+
const hasRendered = ref(false)
|
|
13
|
+
const loading = ref(false)
|
|
14
|
+
let graphInstance = null
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
async function ensureGraph() {
|
|
17
|
+
await nextTick()
|
|
18
|
+
if (!graphInstance) {
|
|
19
|
+
graphInstance = new GraphUI(`#${containerId}`)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
async function renderFromDot(dotText) {
|
|
24
|
+
if (!dotText) return
|
|
25
|
+
await ensureGraph()
|
|
26
|
+
await graphInstance.render(dotText)
|
|
27
|
+
hasRendered.value = true
|
|
28
|
+
}
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
30
|
+
async function renderFromCoreData() {
|
|
31
|
+
if (!props.coreData) return
|
|
32
|
+
loading.value = true
|
|
33
|
+
try {
|
|
34
|
+
const res = await fetch("dot-render-core-data", {
|
|
35
|
+
method: "POST",
|
|
36
|
+
headers: { "Content-Type": "application/json" },
|
|
37
|
+
body: JSON.stringify(props.coreData),
|
|
38
|
+
})
|
|
39
|
+
const dotText = await res.text()
|
|
40
|
+
await renderFromDot(dotText)
|
|
41
|
+
if (window.Quasar?.Notify) {
|
|
42
|
+
window.Quasar.Notify.create({ type: "positive", message: "Rendered" })
|
|
43
|
+
}
|
|
44
|
+
} catch (e) {
|
|
45
|
+
console.error("Render from core data failed", e)
|
|
46
|
+
if (window.Quasar?.Notify) {
|
|
47
|
+
window.Quasar.Notify.create({ type: "negative", message: "Render failed" })
|
|
48
|
+
}
|
|
49
|
+
} finally {
|
|
50
|
+
loading.value = false
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
async function reload() {
|
|
55
|
+
await renderFromCoreData()
|
|
56
|
+
}
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
onMounted(async () => {
|
|
59
|
+
await reload()
|
|
60
|
+
})
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
function close() {
|
|
63
|
+
emit("close")
|
|
64
|
+
}
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
return { containerId, close, hasRendered, reload, loading }
|
|
67
|
+
},
|
|
68
|
+
template: `
|
|
69
69
|
<div style="height:100%; position:relative; background:#fff;">
|
|
70
70
|
<q-btn
|
|
71
71
|
flat dense round icon="close"
|
|
@@ -83,5 +83,4 @@ export default defineComponent({
|
|
|
83
83
|
<div :id="containerId" style="width:100%; height:100%; overflow:auto; background:#fafafa"></div>
|
|
84
84
|
</div>
|
|
85
85
|
`,
|
|
86
|
-
})
|
|
87
|
-
|
|
86
|
+
})
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { defineComponent, ref, watch, onMounted } = window.Vue
|
|
1
|
+
const { defineComponent, ref, watch, onMounted } = window.Vue
|
|
2
2
|
|
|
3
3
|
// Component: RouteCodeDisplay
|
|
4
4
|
// Props:
|
|
@@ -10,45 +10,43 @@ export default defineComponent({
|
|
|
10
10
|
},
|
|
11
11
|
emits: ["close"],
|
|
12
12
|
setup(props, { emit }) {
|
|
13
|
-
const loading = ref(false)
|
|
14
|
-
const code = ref("")
|
|
15
|
-
const error = ref("")
|
|
16
|
-
const link = ref("")
|
|
13
|
+
const loading = ref(false)
|
|
14
|
+
const code = ref("")
|
|
15
|
+
const error = ref("")
|
|
16
|
+
const link = ref("")
|
|
17
17
|
|
|
18
18
|
function close() {
|
|
19
|
-
emit("close")
|
|
19
|
+
emit("close")
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
function highlightLater() {
|
|
23
23
|
requestAnimationFrame(() => {
|
|
24
24
|
try {
|
|
25
25
|
if (window.hljs) {
|
|
26
|
-
const block = document.querySelector(
|
|
27
|
-
".frv-route-code-display pre code.language-python"
|
|
28
|
-
);
|
|
26
|
+
const block = document.querySelector(".frv-route-code-display pre code.language-python")
|
|
29
27
|
if (block) {
|
|
30
|
-
window.hljs.highlightElement(block)
|
|
28
|
+
window.hljs.highlightElement(block)
|
|
31
29
|
}
|
|
32
30
|
}
|
|
33
31
|
} catch (e) {
|
|
34
|
-
console.warn("highlight failed", e)
|
|
32
|
+
console.warn("highlight failed", e)
|
|
35
33
|
}
|
|
36
|
-
})
|
|
34
|
+
})
|
|
37
35
|
}
|
|
38
36
|
|
|
39
37
|
async function load() {
|
|
40
38
|
if (!props.routeId) {
|
|
41
|
-
code.value = ""
|
|
42
|
-
return
|
|
39
|
+
code.value = ""
|
|
40
|
+
return
|
|
43
41
|
}
|
|
44
42
|
|
|
45
|
-
loading.value = true
|
|
46
|
-
error.value = null
|
|
47
|
-
code.value = ""
|
|
48
|
-
link.value = ""
|
|
43
|
+
loading.value = true
|
|
44
|
+
error.value = null
|
|
45
|
+
code.value = ""
|
|
46
|
+
link.value = ""
|
|
49
47
|
|
|
50
48
|
// try to fetch from server: POST /source with { schema_name: routeId }
|
|
51
|
-
const payload = { schema_name: props.routeId }
|
|
49
|
+
const payload = { schema_name: props.routeId }
|
|
52
50
|
try {
|
|
53
51
|
const resp = await fetch(`source`, {
|
|
54
52
|
method: "POST",
|
|
@@ -57,18 +55,18 @@ export default defineComponent({
|
|
|
57
55
|
"Content-Type": "application/json",
|
|
58
56
|
},
|
|
59
57
|
body: JSON.stringify(payload),
|
|
60
|
-
})
|
|
58
|
+
})
|
|
61
59
|
|
|
62
|
-
const data = await resp.json().catch(() => ({}))
|
|
60
|
+
const data = await resp.json().catch(() => ({}))
|
|
63
61
|
if (resp.ok) {
|
|
64
|
-
code.value = data.source_code || "// no source code available"
|
|
62
|
+
code.value = data.source_code || "// no source code available"
|
|
65
63
|
} else {
|
|
66
|
-
error.value = (data && data.error) || "Failed to load source"
|
|
64
|
+
error.value = (data && data.error) || "Failed to load source"
|
|
67
65
|
}
|
|
68
66
|
} catch (e) {
|
|
69
|
-
error.value = e && e.message ? e.message : "Failed to load source"
|
|
67
|
+
error.value = e && e.message ? e.message : "Failed to load source"
|
|
70
68
|
} finally {
|
|
71
|
-
loading.value = false
|
|
69
|
+
loading.value = false
|
|
72
70
|
}
|
|
73
71
|
|
|
74
72
|
try {
|
|
@@ -79,36 +77,36 @@ export default defineComponent({
|
|
|
79
77
|
"Content-Type": "application/json",
|
|
80
78
|
},
|
|
81
79
|
body: JSON.stringify(payload),
|
|
82
|
-
})
|
|
80
|
+
})
|
|
83
81
|
|
|
84
|
-
const data = await resp.json().catch(() => ({}))
|
|
82
|
+
const data = await resp.json().catch(() => ({}))
|
|
85
83
|
if (resp.ok) {
|
|
86
|
-
link.value = data.link || "// no source code available"
|
|
84
|
+
link.value = data.link || "// no source code available"
|
|
87
85
|
} else {
|
|
88
|
-
error.value += (data && data.error) || "Failed to load vscode link"
|
|
86
|
+
error.value += (data && data.error) || "Failed to load vscode link"
|
|
89
87
|
}
|
|
90
88
|
} catch (e) {
|
|
91
89
|
} finally {
|
|
92
|
-
loading.value = false
|
|
90
|
+
loading.value = false
|
|
93
91
|
}
|
|
94
92
|
|
|
95
93
|
if (!error.value) {
|
|
96
|
-
highlightLater()
|
|
94
|
+
highlightLater()
|
|
97
95
|
}
|
|
98
96
|
}
|
|
99
97
|
|
|
100
98
|
watch(
|
|
101
99
|
() => props.routeId,
|
|
102
100
|
() => {
|
|
103
|
-
load()
|
|
101
|
+
load()
|
|
104
102
|
}
|
|
105
|
-
)
|
|
103
|
+
)
|
|
106
104
|
|
|
107
105
|
onMounted(() => {
|
|
108
|
-
load()
|
|
109
|
-
})
|
|
106
|
+
load()
|
|
107
|
+
})
|
|
110
108
|
|
|
111
|
-
return { loading, code, error, close, link }
|
|
109
|
+
return { loading, code, error, close, link }
|
|
112
110
|
},
|
|
113
111
|
template: `
|
|
114
112
|
<div class="frv-route-code-display" style="border:1px solid #ccc; position:relative; background:#fff;">
|
|
@@ -122,4 +120,4 @@ export default defineComponent({
|
|
|
122
120
|
<pre v-else style="margin:0;"><code class="language-python">{{ code }}</code></pre>
|
|
123
121
|
</div>
|
|
124
122
|
</div>`,
|
|
125
|
-
})
|
|
123
|
+
})
|