fastapi-voyager 0.15.5__py3-none-any.whl → 0.15.6__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.
@@ -1,2 +1,2 @@
1
1
  __all__ = ["__version__"]
2
- __version__ = "0.15.5"
2
+ __version__ = "0.15.6"
@@ -1,4 +1,15 @@
1
1
  export class GraphUI {
2
+ // ====================
3
+ // Constants
4
+ // ====================
5
+
6
+ static HIGHLIGHT_COLOR = "#822dba"
7
+ static HIGHLIGHT_STROKE_WIDTH = "3.0"
8
+
9
+ // ====================
10
+ // Constructor
11
+ // ====================
12
+
2
13
  constructor(selector = "#graph", options = {}) {
3
14
  this.selector = selector
4
15
  this.options = options // e.g. { onSchemaClick: (name) => {} }
@@ -9,6 +20,10 @@ export class GraphUI {
9
20
  this._init()
10
21
  }
11
22
 
23
+ // ====================
24
+ // Highlight Methods
25
+ // ====================
26
+
12
27
  _highlight(mode = "bidirectional") {
13
28
  let highlightedNodes = $()
14
29
  for (const selection of this.currentSelection) {
@@ -68,49 +83,79 @@ export class GraphUI {
68
83
  return $result
69
84
  }
70
85
 
86
+ // ====================
87
+ // Schema Banner Methods
88
+ // ====================
89
+
71
90
  highlightSchemaBanner(node) {
72
- // Get all polygons in the node
73
91
  const polygons = node.querySelectorAll("polygon")
74
-
75
- // The first polygon is typically the outer frame of the entire node
76
92
  const outerFrame = polygons[0]
77
- // The second polygon is typically the title background
78
93
  const titleBg = polygons[1]
79
94
 
80
95
  if (outerFrame) {
81
- // Save original attributes for potential restoration
82
- if (!outerFrame.hasAttribute("data-original-stroke")) {
83
- outerFrame.setAttribute("data-original-stroke", outerFrame.getAttribute("stroke") || "")
84
- outerFrame.setAttribute(
85
- "data-original-stroke-width",
86
- outerFrame.getAttribute("stroke-width") || "1"
87
- )
88
- outerFrame.setAttribute("data-original-fill", outerFrame.getAttribute("fill") || "")
89
- }
90
-
91
- // Apply bold purple border to the outer frame
92
- outerFrame.setAttribute("stroke", "#822dba")
93
- outerFrame.setAttribute("stroke-width", "3.0")
96
+ this._saveOriginalAttributes(outerFrame)
97
+ outerFrame.setAttribute("stroke", GraphUI.HIGHLIGHT_COLOR)
98
+ outerFrame.setAttribute("stroke-width", GraphUI.HIGHLIGHT_STROKE_WIDTH)
94
99
  }
95
100
 
96
101
  if (titleBg) {
97
- // Save original attributes
98
- if (!titleBg.hasAttribute("data-original-stroke")) {
99
- titleBg.setAttribute("data-original-stroke", titleBg.getAttribute("stroke") || "")
100
- titleBg.setAttribute(
101
- "data-original-stroke-width",
102
- titleBg.getAttribute("stroke-width") || "1"
103
- )
104
- titleBg.setAttribute("data-original-fill", titleBg.getAttribute("fill") || "")
105
- }
102
+ this._saveOriginalAttributes(titleBg)
103
+ titleBg.setAttribute("fill", GraphUI.HIGHLIGHT_COLOR)
104
+ titleBg.setAttribute("stroke", GraphUI.HIGHLIGHT_COLOR)
105
+ }
106
+ }
106
107
 
107
- // Apply purple background to title
108
- titleBg.setAttribute("fill", "#822dba")
109
- // Also update the stroke to match
110
- titleBg.setAttribute("stroke", "#822dba")
108
+ clearSchemaBanners() {
109
+ if (this.gv) {
110
+ this.gv.highlight()
111
+ }
112
+
113
+ const allPolygons = document.querySelectorAll("polygon[data-original-stroke]")
114
+ allPolygons.forEach((polygon) => {
115
+ polygon.removeAttribute("data-original-stroke")
116
+ polygon.removeAttribute("data-original-stroke-width")
117
+ polygon.removeAttribute("data-original-fill")
118
+ })
119
+ }
120
+
121
+ _saveOriginalAttributes(element) {
122
+ if (!element.hasAttribute("data-original-stroke")) {
123
+ element.setAttribute("data-original-stroke", element.getAttribute("stroke") || "")
124
+ element.setAttribute(
125
+ "data-original-stroke-width",
126
+ element.getAttribute("stroke-width") || "1"
127
+ )
128
+ element.setAttribute("data-original-fill", element.getAttribute("fill") || "")
111
129
  }
112
130
  }
113
131
 
132
+ _applyNodeHighlight(node) {
133
+ const set = $()
134
+ set.push(node)
135
+ const obj = { set, direction: "bidirectional" }
136
+
137
+ this.clearSchemaBanners()
138
+ this.currentSelection = [obj]
139
+ this._highlight()
140
+
141
+ return obj
142
+ }
143
+
144
+ _triggerCallback(callbackName, schemaName) {
145
+ const callback = this.options[callbackName]
146
+ if (callback && schemaName) {
147
+ try {
148
+ callback(schemaName)
149
+ } catch (e) {
150
+ console.warn(`${callbackName} callback failed`, e)
151
+ }
152
+ }
153
+ }
154
+
155
+ // ====================
156
+ // Initialization & Events
157
+ // ====================
158
+
114
159
  _init() {
115
160
  const self = this
116
161
  $(this.selector).graphviz({
@@ -127,80 +172,64 @@ export class GraphUI {
127
172
 
128
173
  nodes.on("dblclick.graphui", function (event) {
129
174
  event.stopPropagation()
175
+
176
+ self._applyNodeHighlight(this)
177
+
130
178
  try {
131
179
  self.highlightSchemaBanner(this)
132
180
  } catch (e) {
133
181
  console.log(e)
134
182
  }
135
- const set = $()
136
- set.push(this)
137
- const schemaName = event.currentTarget.dataset.name
138
- if (schemaName) {
139
- try {
140
- self.options.onSchemaClick(schemaName)
141
- } catch (e) {
142
- console.warn("onSchemaClick callback failed", e)
143
- }
144
- }
183
+
184
+ self._triggerCallback("onSchemaClick", event.currentTarget.dataset.name)
145
185
  })
146
186
 
147
187
  edges.on("click.graphui", function (event) {
188
+ const [upStreamNode, downStreamNode] = event.currentTarget.dataset.name.split("->")
189
+ const nodes = self.gv.nodesByName()
190
+
148
191
  const up = $()
149
192
  const down = $()
150
193
  const edge = $()
151
- const [upStreamNode, downStreamNode] = event.currentTarget.dataset.name.split("->")
152
- const nodes = self.gv.nodesByName()
194
+
153
195
  up.push(nodes[upStreamNode])
154
196
  down.push(nodes[downStreamNode])
155
197
  edge.push(this)
156
- const upObj = { set: up, direction: "upstream" }
157
- const downObj = { set: down, direction: "downstream" }
158
- const edgeOjb = { set: edge, direction: "single" }
159
- self.currentSelection = [upObj, downObj, edgeOjb]
198
+
199
+ self.currentSelection = [
200
+ { set: up, direction: "upstream" },
201
+ { set: down, direction: "downstream" },
202
+ { set: edge, direction: "single" },
203
+ ]
160
204
 
161
205
  self._highlightEdgeNodes()
162
206
  })
163
207
 
164
208
  nodes.on("click.graphui", function (event) {
165
- const set = $()
166
- set.push(this)
167
- const obj = { set, direction: "bidirectional" }
168
-
169
- const schemaName = event.currentTarget.dataset.name
170
- console.log("shift click detected")
171
- if (event.shiftKey && self.options.onSchemaShiftClick) {
172
- if (schemaName) {
173
- try {
174
- self.options.onSchemaShiftClick(schemaName)
175
- } catch (e) {
176
- console.warn("onSchemaShiftClick callback failed", e)
177
- }
178
- }
209
+ if (event.shiftKey) {
210
+ self._triggerCallback("onSchemaShiftClick", event.currentTarget.dataset.name)
179
211
  } else {
180
- self.currentSelection = [obj]
181
- self._highlight()
212
+ self._applyNodeHighlight(this)
182
213
  }
183
214
  })
184
215
 
185
216
  $(document)
186
217
  .off("click.graphui")
187
218
  .on("click.graphui", function (evt) {
188
- // if outside container, do nothing
189
219
  const graphContainer = $(self.selector)[0]
190
220
  if (!graphContainer || !evt.target || !graphContainer.contains(evt.target)) {
191
221
  return
192
222
  }
193
223
 
194
- let isNode = false
195
224
  const $everything = self.gv.$nodes.add(self.gv.$edges).add(self.gv.$clusters)
196
225
  const node = evt.target.parentNode
197
- $everything.each(function () {
198
- if (this === node) {
199
- isNode = true
200
- }
226
+ const isNode = $everything.is(function () {
227
+ return this === node
201
228
  })
229
+
202
230
  if (!isNode && self.gv) {
203
- self.gv.highlight()
231
+ self.clearSchemaBanners()
232
+
204
233
  if (self.options.resetCb) {
205
234
  self.options.resetCb()
206
235
  }
@@ -210,6 +239,10 @@ export class GraphUI {
210
239
  })
211
240
  }
212
241
 
242
+ // ====================
243
+ // Render Method
244
+ // ====================
245
+
213
246
  async render(dotSrc, resetZoom = true) {
214
247
  const height = this.options.height || "100%"
215
248
  return new Promise((resolve, reject) => {
@@ -17,6 +17,13 @@
17
17
 
18
18
  GraphvizSvg.GVPT_2_PX = 32.5 // used to ease removal of extra space
19
19
 
20
+ // SVG element selectors for color manipulation
21
+ // NOTE: If you need to add more element types for highlighting/dimming,
22
+ // update SHAPE_ELEMENTS and the code will automatically handle them
23
+ GraphvizSvg.SHAPE_ELEMENTS = "polygon, ellipse, path, polyline"
24
+ GraphvizSvg.TEXT_ELEMENTS = "text"
25
+ GraphvizSvg.ALL_COLOR_ELEMENTS = GraphvizSvg.SHAPE_ELEMENTS + ", " + GraphvizSvg.TEXT_ELEMENTS
26
+
20
27
  GraphvizSvg.DEFAULTS = {
21
28
  url: null,
22
29
  svg: null,
@@ -178,8 +185,8 @@
178
185
  this.setInteractiveCursor($el, type === "edge")
179
186
  }
180
187
 
181
- // save the colors of the paths, ellipses and polygons
182
- $el.find("polygon, ellipse, path").each(function () {
188
+ // Save the colors of shape elements (polygon, ellipse, path, polyline)
189
+ $el.find(GraphvizSvg.SHAPE_ELEMENTS).each(function () {
183
190
  var $this = $(this)
184
191
  if ($this.attr("data-graphviz-hitbox") === "true") {
185
192
  return
@@ -196,6 +203,20 @@
196
203
  }
197
204
  })
198
205
 
206
+ // Save the colors of text elements
207
+ $el.find(GraphvizSvg.TEXT_ELEMENTS).each(function () {
208
+ var $this = $(this)
209
+ // text elements might not have explicit fill attribute, use black as default
210
+ var fill = $this.attr("fill")
211
+ if (!fill || fill === "none") {
212
+ fill = "#000000" // default black color for text
213
+ }
214
+ $this.data("graphviz.svg.color", {
215
+ fill: fill,
216
+ stroke: $this.attr("stroke"),
217
+ })
218
+ })
219
+
199
220
  // save the node name and check if theres a comment above; save it
200
221
  var $title = $el.children("title")
201
222
  if ($title[0]) {
@@ -404,6 +425,57 @@
404
425
  return retval
405
426
  }
406
427
 
428
+ // Helper function to apply color transformation to elements
429
+ GraphvizSvg.prototype._applyColorToElements = function (
430
+ $elements,
431
+ colorTransformer,
432
+ bgColor,
433
+ setStrokeWidth
434
+ ) {
435
+ var that = this
436
+ $elements.each(function () {
437
+ var $this = $(this)
438
+ if ($this.attr("data-graphviz-hitbox") === "true") {
439
+ return
440
+ }
441
+ var color = $this.data("graphviz.svg.color")
442
+ if (color) {
443
+ if (color.fill && color.fill != "none") {
444
+ $this.attr("fill", colorTransformer(color.fill, bgColor))
445
+ }
446
+ if (color.stroke && color.stroke != "none") {
447
+ $this.attr("stroke", colorTransformer(color.stroke, bgColor))
448
+ }
449
+ if (setStrokeWidth !== undefined) {
450
+ $this.attr("stroke-width", setStrokeWidth)
451
+ }
452
+ }
453
+ })
454
+ }
455
+
456
+ // Helper function to restore original colors
457
+ GraphvizSvg.prototype._restoreElementColors = function ($elements, setStrokeWidth) {
458
+ var that = this
459
+ $elements.each(function () {
460
+ var $this = $(this)
461
+ if ($this.attr("data-graphviz-hitbox") === "true") {
462
+ return
463
+ }
464
+ var color = $this.data("graphviz.svg.color")
465
+ if (color) {
466
+ if (color.fill && color.fill != "none") {
467
+ $this.attr("fill", color.fill)
468
+ }
469
+ if (color.stroke && color.stroke != "none") {
470
+ $this.attr("stroke", color.stroke)
471
+ }
472
+ if (setStrokeWidth !== undefined) {
473
+ $this.attr("stroke-width", setStrokeWidth)
474
+ }
475
+ }
476
+ })
477
+ }
478
+
407
479
  GraphvizSvg.prototype.findEdge = function (nodeName, testEdge, $retval) {
408
480
  var retval = []
409
481
  for (var name in this._edgesByName) {
@@ -439,37 +511,14 @@
439
511
 
440
512
  GraphvizSvg.prototype.colorElement = function ($el, getColor) {
441
513
  var bg = this.$element.css("background")
442
- $el.find("polygon, ellipse, path").each(function () {
443
- var $this = $(this)
444
- if ($this.attr("data-graphviz-hitbox") === "true") {
445
- return
446
- }
447
- var color = $this.data("graphviz.svg.color")
448
- if (color.fill && color.fill != "none") {
449
- $this.attr("fill", getColor(color.fill, bg)) // don't set fill if it's a path
450
- }
451
- if (color.stroke && color.stroke != "none") {
452
- $this.attr("stroke", getColor(color.stroke, bg))
453
- }
454
- $this.attr("stroke-width", 1.6)
455
- })
514
+
515
+ // Apply color transformation to all elements (shapes + text)
516
+ this._applyColorToElements($el.find(GraphvizSvg.ALL_COLOR_ELEMENTS), getColor, bg)
456
517
  }
457
518
 
458
519
  GraphvizSvg.prototype.restoreElement = function ($el) {
459
- $el.find("polygon, ellipse, path").each(function () {
460
- var $this = $(this)
461
- if ($this.attr("data-graphviz-hitbox") === "true") {
462
- return
463
- }
464
- var color = $this.data("graphviz.svg.color")
465
- if (color.fill && color.fill != "none") {
466
- $this.attr("fill", color.fill) // don't set fill if it's a path
467
- }
468
- if (color.stroke && color.stroke != "none") {
469
- $this.attr("stroke", color.stroke)
470
- }
471
- $this.attr("stroke-width", 1)
472
- })
520
+ // Restore original colors for all elements (shapes + text)
521
+ this._restoreElementColors($el.find(GraphvizSvg.ALL_COLOR_ELEMENTS), 1)
473
522
  }
474
523
 
475
524
  // methods users can actually call
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-voyager
3
- Version: 0.15.5
3
+ Version: 0.15.6
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=E0gGU7D1pBvnV7gFLBUvnkwtiBFbU1PbxEXHHSIA1oA,9115
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=7F_t4pHiI_38oJ-3IRvYl5xfn2qV8LfVfFPlM9BacI0,49
12
+ fastapi_voyager/version.py,sha256=ILB2INUiq4EGupYfMT2L0gsR_3dHm9fPDuoUnU5BSf0,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
@@ -24,9 +24,9 @@ fastapi_voyager/templates/html/pydantic_meta.j2,sha256=_tsSqjucs_QrAlPIVRy9u6I2-
24
24
  fastapi_voyager/templates/html/schema_field_row.j2,sha256=KfKexHO_QJV-OIJS0eiY_7fqA8031fWpD2g2wTv4BuE,111
25
25
  fastapi_voyager/templates/html/schema_header.j2,sha256=9WpuHLy3Zbv5GHG08qqaj5Xf-gaR-79ErBYuANZp7iA,179
26
26
  fastapi_voyager/templates/html/schema_table.j2,sha256=rzphiGk1il7uv4Gr2p_HLPHqyLZk63vLrGAmIduTdSE,117
27
- fastapi_voyager/web/graph-ui.js,sha256=4gEkXTgbA6CouD4IDMW5yKYfJTxHN2vL9G0CAr6w4qA,7662
27
+ fastapi_voyager/web/graph-ui.js,sha256=9b2auyGWEpUcF65YV231GNojQ9Uk6FsT1SlRR3lSYnc,7664
28
28
  fastapi_voyager/web/graphviz.svg.css,sha256=K218ov_mdSe3ga4KwhiBB92ynVvm5zaAk9_D9a3d8hE,1546
29
- fastapi_voyager/web/graphviz.svg.js,sha256=deI815RgxpZ3_MpELeV-TBYy2MVuUvZtQOHfS3aeXHY,18203
29
+ fastapi_voyager/web/graphviz.svg.js,sha256=VokgCghvP4zm3SFiFVPIikdW6XzjkZJXQkBbCrEitug,19885
30
30
  fastapi_voyager/web/index.html,sha256=wM9vJ_UfHR8p98F6SEMCKKjJcBEl0EyosWuPqVZYXvA,23496
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
@@ -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.5.dist-info/METADATA,sha256=nvrvye9JChzW2WQLNq2S3wzPrDU67ZEFoOMYkNcXtuA,9870
47
- fastapi_voyager-0.15.5.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
48
- fastapi_voyager-0.15.5.dist-info/entry_points.txt,sha256=pEIKoUnIDXEtdMBq8EmXm70m16vELIu1VPz9-TBUFWM,53
49
- fastapi_voyager-0.15.5.dist-info/licenses/LICENSE,sha256=lNVRR3y_bFVoFKuK2JM8N4sFaj3m-7j29kvL3olFi5Y,1067
50
- fastapi_voyager-0.15.5.dist-info/RECORD,,
46
+ fastapi_voyager-0.15.6.dist-info/METADATA,sha256=D-09okYHB5uaatoDc5SqmOVr_jTbw18o4_76pmxO4_g,9870
47
+ fastapi_voyager-0.15.6.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
48
+ fastapi_voyager-0.15.6.dist-info/entry_points.txt,sha256=pEIKoUnIDXEtdMBq8EmXm70m16vELIu1VPz9-TBUFWM,53
49
+ fastapi_voyager-0.15.6.dist-info/licenses/LICENSE,sha256=lNVRR3y_bFVoFKuK2JM8N4sFaj3m-7j29kvL3olFi5Y,1067
50
+ fastapi_voyager-0.15.6.dist-info/RECORD,,