lvkit 0.8.2__tar.gz → 0.8.3__tar.gz

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.
Files changed (515) hide show
  1. {lvkit-0.8.2 → lvkit-0.8.3}/PKG-INFO +1 -1
  2. {lvkit-0.8.2 → lvkit-0.8.3}/pyproject.toml +1 -1
  3. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/__init__.py +1 -1
  4. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/graph/builders/operations.py +6 -2
  5. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/graph/loading.py +132 -17
  6. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/graph/models.py +9 -8
  7. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/parser/metadata.py +24 -7
  8. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/parser/node_types.py +30 -24
  9. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/parser/vi.py +32 -15
  10. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/base.py +88 -7
  11. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/invoke_node.py +20 -19
  12. lvkit-0.8.3/src/lvkit/render/glyphs/nodes/property_node.py +78 -0
  13. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/nodes.py +111 -32
  14. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/scene.py +15 -6
  15. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/style.py +26 -2
  16. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/theme_web.py +1 -0
  17. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/structure.py +61 -9
  18. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_class_parent_linkinfo.py +22 -0
  19. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_class_wire_appearance.py +54 -0
  20. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_constant_decoding.py +45 -0
  21. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_parser.py +144 -0
  22. lvkit-0.8.3/tests/test_python_structure_plan.py +55 -0
  23. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_render.py +316 -12
  24. lvkit-0.8.2/src/lvkit/render/glyphs/nodes/property_node.py +0 -115
  25. {lvkit-0.8.2 → lvkit-0.8.3}/.gitignore +0 -0
  26. {lvkit-0.8.2 → lvkit-0.8.3}/.pre-commit-config.yaml +0 -0
  27. {lvkit-0.8.2 → lvkit-0.8.3}/LICENSE +0 -0
  28. {lvkit-0.8.2 → lvkit-0.8.3}/NOTICE +0 -0
  29. {lvkit-0.8.2 → lvkit-0.8.3}/README.md +0 -0
  30. {lvkit-0.8.2 → lvkit-0.8.3}/deploy/cloudrun/README.md +0 -0
  31. {lvkit-0.8.2 → lvkit-0.8.3}/docs/_internal/xml-schema/README.md +0 -0
  32. {lvkit-0.8.2 → lvkit-0.8.3}/editors/vscode/LICENSE +0 -0
  33. {lvkit-0.8.2 → lvkit-0.8.3}/editors/vscode/README.md +0 -0
  34. {lvkit-0.8.2 → lvkit-0.8.3}/mcpb/README.md +0 -0
  35. {lvkit-0.8.2 → lvkit-0.8.3}/plugin/README.md +0 -0
  36. {lvkit-0.8.2 → lvkit-0.8.3}/scripts/audit_primitive_consistency.py +0 -0
  37. {lvkit-0.8.2 → lvkit-0.8.3}/scripts/audit_terminal_order.py +0 -0
  38. {lvkit-0.8.2 → lvkit-0.8.3}/scripts/audit_unresolved.py +0 -0
  39. {lvkit-0.8.2 → lvkit-0.8.3}/scripts/build_example_pages.sh +0 -0
  40. {lvkit-0.8.2 → lvkit-0.8.3}/scripts/build_hero.py +0 -0
  41. {lvkit-0.8.2 → lvkit-0.8.3}/scripts/build_ni_function_catalog.py +0 -0
  42. {lvkit-0.8.2 → lvkit-0.8.3}/scripts/connector_geometry_audit.py +0 -0
  43. {lvkit-0.8.2 → lvkit-0.8.3}/scripts/dump_primitive_context.py +0 -0
  44. {lvkit-0.8.2 → lvkit-0.8.3}/scripts/extract_corpus.py +0 -0
  45. {lvkit-0.8.2 → lvkit-0.8.3}/scripts/gen_semantics_probe.py +0 -0
  46. {lvkit-0.8.2 → lvkit-0.8.3}/scripts/generate_docs.py +0 -0
  47. {lvkit-0.8.2 → lvkit-0.8.3}/scripts/generate_driver_data.py +0 -0
  48. {lvkit-0.8.2 → lvkit-0.8.3}/scripts/generate_python.py +0 -0
  49. {lvkit-0.8.2 → lvkit-0.8.3}/scripts/import_nodes_primitives.py +0 -0
  50. {lvkit-0.8.2 → lvkit-0.8.3}/scripts/list_unknown_primitives.py +0 -0
  51. {lvkit-0.8.2 → lvkit-0.8.3}/scripts/populate_vilib.py +0 -0
  52. {lvkit-0.8.2 → lvkit-0.8.3}/scripts/pull_samples.sh +0 -0
  53. {lvkit-0.8.2 → lvkit-0.8.3}/scripts/recategorize_vilib.py +0 -0
  54. {lvkit-0.8.2 → lvkit-0.8.3}/scripts/render_gallery.py +0 -0
  55. {lvkit-0.8.2 → lvkit-0.8.3}/scripts/reproduce_openg_corpus.py +0 -0
  56. {lvkit-0.8.2 → lvkit-0.8.3}/scripts/study_unwired_direction.py +0 -0
  57. {lvkit-0.8.2 → lvkit-0.8.3}/scripts/wire_decode_probe.py +0 -0
  58. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/__main__.py +0 -0
  59. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/_data.py +0 -0
  60. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/_pylabview_patches.py +0 -0
  61. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/cache_paths.py +0 -0
  62. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/cli.py +0 -0
  63. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/README.md +0 -0
  64. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/__init__.py +0 -0
  65. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/ast_optimizer.py +0 -0
  66. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/ast_utils.py +0 -0
  67. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/builder.py +0 -0
  68. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/class_builder/__init__.py +0 -0
  69. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/class_builder/_shared.py +0 -0
  70. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/class_builder/config.py +0 -0
  71. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/class_builder/core.py +0 -0
  72. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/class_builder/init_builder.py +0 -0
  73. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/class_builder/methods.py +0 -0
  74. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/class_builder/naming.py +0 -0
  75. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/class_builder/properties.py +0 -0
  76. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/class_builder/terminals.py +0 -0
  77. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/condition_builder.py +0 -0
  78. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/context.py +0 -0
  79. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/dataflow.py +0 -0
  80. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/elementwise.py +0 -0
  81. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/error_handler.py +0 -0
  82. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/expressions.py +0 -0
  83. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/fragment.py +0 -0
  84. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/function.py +0 -0
  85. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/imports.py +0 -0
  86. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/nodes/__init__.py +0 -0
  87. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/nodes/base.py +0 -0
  88. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/nodes/case.py +0 -0
  89. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/nodes/compound.py +0 -0
  90. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/nodes/constant.py +0 -0
  91. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/nodes/event.py +0 -0
  92. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/nodes/first_call.py +0 -0
  93. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/nodes/formula.py +0 -0
  94. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/nodes/in_place.py +0 -0
  95. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/nodes/invoke_node.py +0 -0
  96. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/nodes/loop.py +0 -0
  97. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/nodes/nmux.py +0 -0
  98. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/nodes/primitive.py +0 -0
  99. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/nodes/printf.py +0 -0
  100. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/nodes/property_node.py +0 -0
  101. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/nodes/queue_ops.py +0 -0
  102. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/nodes/sequence.py +0 -0
  103. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/nodes/subvi.py +0 -0
  104. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/stubs.py +0 -0
  105. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/codegen/unresolved.py +0 -0
  106. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/connector_geometry.py +0 -0
  107. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/connector_pane_geometry.py +0 -0
  108. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/builtin_types.json +0 -0
  109. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/connector_pane_patterns.json +0 -0
  110. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/drivers/_index.json +0 -0
  111. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/drivers/daqmx.json +0 -0
  112. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/drivers/nidcpower.json +0 -0
  113. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/drivers/nidigital.json +0 -0
  114. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/drivers/nidmm.json +0 -0
  115. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/drivers/nifgen.json +0 -0
  116. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/drivers/niscope.json +0 -0
  117. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/drivers/niswitch.json +0 -0
  118. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/drivers/serial.json +0 -0
  119. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/drivers/visa.json +0 -0
  120. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/labview_error_codes.json +0 -0
  121. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/measure_data.json +0 -0
  122. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/openg/_index.json +0 -0
  123. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/openg/array.json +0 -0
  124. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/openg/file.json +0 -0
  125. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/openg/string.json +0 -0
  126. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/openg/time.json +0 -0
  127. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/openg/variant.json +0 -0
  128. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/primitives.json +0 -0
  129. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/vilib/_index.json +0 -0
  130. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/vilib/_pending_terminals.json +0 -0
  131. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/vilib/_types.json +0 -0
  132. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/vilib/addons.json +0 -0
  133. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/vilib/application-control.json +0 -0
  134. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/vilib/array.json +0 -0
  135. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/vilib/boolean.json +0 -0
  136. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/vilib/cluster-class-variant.json +0 -0
  137. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/vilib/collection.json +0 -0
  138. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/vilib/comparison.json +0 -0
  139. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/vilib/connectivity.json +0 -0
  140. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/vilib/control-simulation.json +0 -0
  141. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/vilib/data-communication.json +0 -0
  142. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/vilib/dialog-user-interface.json +0 -0
  143. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/vilib/express.json +0 -0
  144. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/vilib/file-io.json +0 -0
  145. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/vilib/graphics-sound.json +0 -0
  146. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/vilib/instrument-io.json +0 -0
  147. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/vilib/mathematics.json +0 -0
  148. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/vilib/measurement-io.json +0 -0
  149. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/vilib/numeric.json +0 -0
  150. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/vilib/report-generation.json +0 -0
  151. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/vilib/signal-processing.json +0 -0
  152. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/vilib/string.json +0 -0
  153. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/vilib/structures.json +0 -0
  154. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/vilib/synchronization.json +0 -0
  155. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/vilib/timing.json +0 -0
  156. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/data/vilib/waveform.json +0 -0
  157. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/docs/__init__.py +0 -0
  158. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/docs/generate.py +0 -0
  159. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/docs/html_generator/__init__.py +0 -0
  160. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/docs/html_generator/assets.py +0 -0
  161. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/docs/html_generator/class_page.py +0 -0
  162. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/docs/html_generator/core.py +0 -0
  163. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/docs/html_generator/index_page.py +0 -0
  164. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/docs/html_generator/naming.py +0 -0
  165. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/docs/html_generator/vi_page.py +0 -0
  166. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/docs/template.css +0 -0
  167. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/docs/utils.py +0 -0
  168. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/extractor.py +0 -0
  169. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/flattened_typedesc.py +0 -0
  170. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/formula/__init__.py +0 -0
  171. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/formula/cparser.py +0 -0
  172. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/formula/emit.py +0 -0
  173. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/formula/lexer.py +0 -0
  174. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/formula/nodes.py +0 -0
  175. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/graph/__init__.py +0 -0
  176. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/graph/analysis.py +0 -0
  177. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/graph/builders/__init__.py +0 -0
  178. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/graph/builders/context.py +0 -0
  179. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/graph/builders/refs.py +0 -0
  180. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/graph/builders/structures.py +0 -0
  181. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/graph/construction.py +0 -0
  182. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/graph/core.py +0 -0
  183. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/graph/describe.py +0 -0
  184. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/graph/diff.py +0 -0
  185. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/graph/interface_order.py +0 -0
  186. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/graph/lvnet_grammar.py +0 -0
  187. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/graph/lvnet_parse.py +0 -0
  188. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/graph/lvnet_reconstruct.py +0 -0
  189. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/graph/netlist.py +0 -0
  190. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/graph/netlist_build.py +0 -0
  191. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/graph/netlist_diff_helpers.py +0 -0
  192. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/graph/netlist_json.py +0 -0
  193. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/graph/netlist_models.py +0 -0
  194. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/graph/op_walk.py +0 -0
  195. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/graph/operations.py +0 -0
  196. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/graph/parallel_parse.py +0 -0
  197. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/graph/queries.py +0 -0
  198. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/graph/render_lvnet.py +0 -0
  199. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/index/__init__.py +0 -0
  200. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/index/build.py +0 -0
  201. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/index/model.py +0 -0
  202. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/index/project.py +0 -0
  203. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/index/query.py +0 -0
  204. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/index/sql.py +0 -0
  205. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/index/store.py +0 -0
  206. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/labview_error.py +0 -0
  207. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/labview_error_codes.py +0 -0
  208. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/labview_queue.py +0 -0
  209. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/load_mode.py +0 -0
  210. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/lv_detect.py +0 -0
  211. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/mcp/__init__.py +0 -0
  212. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/mcp/schemas.py +0 -0
  213. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/mcp/server/__init__.py +0 -0
  214. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/mcp/server/__main__.py +0 -0
  215. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/mcp/server/_compat.py +0 -0
  216. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/mcp/server/app.py +0 -0
  217. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/mcp/server/index_tools.py +0 -0
  218. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/mcp/server/resolvers.py +0 -0
  219. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/mcp/server/roots.py +0 -0
  220. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/mcp/server/vi_tools.py +0 -0
  221. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/measure_data.py +0 -0
  222. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/models.py +0 -0
  223. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/num_format.py +0 -0
  224. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/output_cache.py +0 -0
  225. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/parser/__init__.py +0 -0
  226. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/parser/conp_types.py +0 -0
  227. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/parser/constants.py +0 -0
  228. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/parser/defaults.py +0 -0
  229. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/parser/flags.py +0 -0
  230. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/parser/fp_heap_type.py +0 -0
  231. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/parser/front_panel.py +0 -0
  232. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/parser/image_resources.py +0 -0
  233. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/parser/layout.py +0 -0
  234. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/parser/models.py +0 -0
  235. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/parser/naming.py +0 -0
  236. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/parser/nodes/__init__.py +0 -0
  237. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/parser/nodes/base.py +0 -0
  238. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/parser/nodes/case.py +0 -0
  239. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/parser/nodes/constant.py +0 -0
  240. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/parser/nodes/decompose.py +0 -0
  241. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/parser/nodes/disable.py +0 -0
  242. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/parser/nodes/event.py +0 -0
  243. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/parser/nodes/free_label.py +0 -0
  244. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/parser/nodes/loop.py +0 -0
  245. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/parser/nodes/sequence.py +0 -0
  246. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/parser/type_mapping.py +0 -0
  247. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/parser/type_resolution.py +0 -0
  248. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/parser/utils.py +0 -0
  249. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/parser/wire_table.py +0 -0
  250. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/pipeline.py +0 -0
  251. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/primitive_resolver/__init__.py +0 -0
  252. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/primitive_resolver/exceptions.py +0 -0
  253. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/primitive_resolver/models.py +0 -0
  254. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/primitive_resolver/resolver.py +0 -0
  255. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/project_store.py +0 -0
  256. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/py.typed +0 -0
  257. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/ARCHITECTURE.md +0 -0
  258. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/__init__.py +0 -0
  259. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/backend.py +0 -0
  260. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/composite.py +0 -0
  261. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/connector_pane.py +0 -0
  262. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/connector_pane_panel.py +0 -0
  263. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/diff_viewer.py +0 -0
  264. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/draw.py +0 -0
  265. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyph.py +0 -0
  266. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/__init__.py +0 -0
  267. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/decorations/__init__.py +0 -0
  268. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/decorations/arrow.py +0 -0
  269. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/decorations/base.py +0 -0
  270. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/decorations/factory.py +0 -0
  271. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/decorations/fallback.py +0 -0
  272. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/decorations/frame.py +0 -0
  273. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/decorations/line.py +0 -0
  274. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/decorations/picture.py +0 -0
  275. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/__init__.py +0 -0
  276. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/arith.py +0 -0
  277. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/array_build.py +0 -0
  278. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/array_constant.py +0 -0
  279. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/array_reverse.py +0 -0
  280. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/array_search.py +0 -0
  281. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/array_size.py +0 -0
  282. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/array_sort.py +0 -0
  283. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/array_split.py +0 -0
  284. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/boolean_constant.py +0 -0
  285. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/boolean_gate.py +0 -0
  286. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/bundle.py +0 -0
  287. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/bundle_by_name.py +0 -0
  288. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/centered_svg.py +0 -0
  289. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/class_glyph.py +0 -0
  290. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/cluster_constant.py +0 -0
  291. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/compound_arith.py +0 -0
  292. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/constant.py +0 -0
  293. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/control_ref_const.py +0 -0
  294. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/convert.py +0 -0
  295. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/error_cluster.py +0 -0
  296. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/event_data.py +0 -0
  297. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/event_reg_node.py +0 -0
  298. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/formula_node.py +0 -0
  299. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/icon_image.py +0 -0
  300. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/in_place_element.py +0 -0
  301. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/inline_svg.py +0 -0
  302. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/label.py +0 -0
  303. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/local_variable.py +0 -0
  304. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/path_glyph.py +0 -0
  305. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/refnum_glyph.py +0 -0
  306. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/unbundle.py +0 -0
  307. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/variant.py +0 -0
  308. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/nodes/wrapped_box.py +0 -0
  309. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/structures/__init__.py +0 -0
  310. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/structures/base.py +0 -0
  311. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/structures/case.py +0 -0
  312. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/structures/disable.py +0 -0
  313. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/structures/event.py +0 -0
  314. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/structures/factory.py +0 -0
  315. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/structures/flat_sequence.py +0 -0
  316. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/structures/for_loop.py +0 -0
  317. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/structures/generic.py +0 -0
  318. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/structures/in_place.py +0 -0
  319. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/structures/selectable.py +0 -0
  320. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/structures/stacked_sequence.py +0 -0
  321. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/structures/while_loop.py +0 -0
  322. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/terminals/__init__.py +0 -0
  323. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/terminals/autoindex.py +0 -0
  324. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/terminals/base.py +0 -0
  325. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/terminals/concatenate.py +0 -0
  326. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/terminals/conditional.py +0 -0
  327. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/terminals/event_dyn.py +0 -0
  328. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/terminals/event_timeout.py +0 -0
  329. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/terminals/factory.py +0 -0
  330. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/terminals/generic.py +0 -0
  331. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/terminals/loop_count.py +0 -0
  332. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/terminals/selector.py +0 -0
  333. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/terminals/shift_register.py +0 -0
  334. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/glyphs/terminals/tunnel.py +0 -0
  335. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/help_tip.py +0 -0
  336. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/icons.py +0 -0
  337. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/lane_pass.py +0 -0
  338. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/properties_panel.py +0 -0
  339. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/render_viewer.py +0 -0
  340. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/templates/diff_viewer.html +0 -0
  341. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/templates/render_viewer.html +0 -0
  342. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/theme_control.py +0 -0
  343. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/render/wire_router.py +0 -0
  344. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/runtime/__init__.py +0 -0
  345. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/runtime/lv.py +0 -0
  346. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/skill_templates/__init__.py +0 -0
  347. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/skill_templates/lvkit/SKILL.md +0 -0
  348. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/skill_templates/lvkit-convert/SKILL.md +0 -0
  349. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/skill_templates/lvkit-describe/SKILL.md +0 -0
  350. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/skill_templates/lvkit-document/SKILL.md +0 -0
  351. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/skill_templates/lvkit-query/SKILL.md +0 -0
  352. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/skill_templates/lvkit-resolve/SKILL.md +0 -0
  353. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/skill_templates/lvkit-review/SKILL.md +0 -0
  354. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/terminal_collector.py +0 -0
  355. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/text_encoding.py +0 -0
  356. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/tools/__init__.py +0 -0
  357. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/tools/connector_geometry_profile.py +0 -0
  358. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/type_defaults.py +0 -0
  359. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/unresolved.py +0 -0
  360. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/vi_diff.py +0 -0
  361. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/vilib_resolver/__init__.py +0 -0
  362. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/vilib_resolver/exceptions.py +0 -0
  363. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/vilib_resolver/loader.py +0 -0
  364. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/vilib_resolver/lookup.py +0 -0
  365. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/vilib_resolver/models.py +0 -0
  366. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/vilib_resolver/naming.py +0 -0
  367. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/vilib_resolver/resolver.py +0 -0
  368. {lvkit-0.8.2 → lvkit-0.8.3}/src/lvkit/vilib_resolver/variants.py +0 -0
  369. {lvkit-0.8.2 → lvkit-0.8.3}/tests/__init__.py +0 -0
  370. {lvkit-0.8.2 → lvkit-0.8.3}/tests/conftest.py +0 -0
  371. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/29/PROVENANCE.md +0 -0
  372. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/29/Test LVKit/Lib1/Class/Class.lvclass +0 -0
  373. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/29/Test LVKit/Lib1/Class/Do.vi +0 -0
  374. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/29/Test LVKit/Lib1/Lib1.lvlib +0 -0
  375. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/29/Test LVKit/Lib2/Class/Class.lvclass +0 -0
  376. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/29/Test LVKit/Lib2/Class/Do.vi +0 -0
  377. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/29/Test LVKit/Lib2/Class/Test.vi +0 -0
  378. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/29/Test LVKit/Lib2/Lib2.lvlib +0 -0
  379. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/29/Test LVKit/Test LVKit.aliases +0 -0
  380. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/29/Test LVKit/Test LVKit.lvlps +0 -0
  381. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/29/Test LVKit/Test LVKit.lvproj +0 -0
  382. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/30/PROVENANCE.md +0 -0
  383. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/30/visible-frame-case-sequence.vi +0 -0
  384. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/30/visible-frame-conditional-disable.vi +0 -0
  385. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/31/PROVENANCE.md +0 -0
  386. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/31/disable-structure-frame-names.vi +0 -0
  387. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/32/PROVENANCE.md +0 -0
  388. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/32/comments-and-decorations.vi +0 -0
  389. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/33/PROVENANCE.md +0 -0
  390. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/33/coercion-dot-consistency.vi +0 -0
  391. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/34/PROVENANCE.md +0 -0
  392. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/34/hidden-iteration-terminal.vi +0 -0
  393. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/35/PROVENANCE.md +0 -0
  394. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/35/objects-hidden-by-structures.vi +0 -0
  395. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/36/PROVENANCE.md +0 -0
  396. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/36/bundle-unbundle-names.vi +0 -0
  397. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/37/PROVENANCE.md +0 -0
  398. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/37/structure-tunnel-wire-bends.vi +0 -0
  399. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/38/PROVENANCE.md +0 -0
  400. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/38/auto-concatenating-tunnel.vi +0 -0
  401. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/39/PROVENANCE.md +0 -0
  402. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/39/zorder-not-respected.vi +0 -0
  403. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/82/Pictures And Decorations.vi +0 -0
  404. {lvkit-0.8.2 → lvkit-0.8.3}/tests/corpus/issues/README.md +0 -0
  405. {lvkit-0.8.2 → lvkit-0.8.3}/tests/helpers.py +0 -0
  406. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_array_ops_codegen.py +0 -0
  407. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_ast_builder.py +0 -0
  408. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_ast_optimizer.py +0 -0
  409. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_case_codegen.py +0 -0
  410. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_case_parser.py +0 -0
  411. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_class_field_walkup.py +0 -0
  412. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_class_properties.py +0 -0
  413. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_cli.py +0 -0
  414. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_cli_cache.py +0 -0
  415. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_cli_describe_lvnet.py +0 -0
  416. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_cli_graph_ops.py +0 -0
  417. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_cli_query.py +0 -0
  418. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_cluster_wire_color.py +0 -0
  419. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_codegen_context.py +0 -0
  420. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_compound_codegen.py +0 -0
  421. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_condition_builder.py +0 -0
  422. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_connector_geometry.py +0 -0
  423. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_connector_pane_label_recovery.py +0 -0
  424. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_connector_pane_patterns.py +0 -0
  425. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_connector_pane_render.py +0 -0
  426. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_conp_types.py +0 -0
  427. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_dependency_path_refs.py +0 -0
  428. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_determinism.py +0 -0
  429. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_diff.py +0 -0
  430. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_diff_properties.py +0 -0
  431. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_diff_viewer.py +0 -0
  432. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_disable_graph.py +0 -0
  433. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_disable_parser.py +0 -0
  434. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_docs_class_hierarchy.py +0 -0
  435. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_docs_render.py +0 -0
  436. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_driver_codegen.py +0 -0
  437. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_dynamic_dispatch.py +0 -0
  438. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_e2e_codegen.py +0 -0
  439. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_elementwise.py +0 -0
  440. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_error_codegen.py +0 -0
  441. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_error_handling.py +0 -0
  442. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_error_terminal_label_fallback.py +0 -0
  443. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_event_codegen.py +0 -0
  444. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_event_parser.py +0 -0
  445. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_extraction_cache.py +0 -0
  446. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_extractor_concurrency.py +0 -0
  447. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_extractor_long_path.py +0 -0
  448. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_first_call_codegen.py +0 -0
  449. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_formula_codegen.py +0 -0
  450. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_formula_runtime.py +0 -0
  451. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_formula_semantics.py +0 -0
  452. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_formula_transpile.py +0 -0
  453. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_fp_heap_type.py +0 -0
  454. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_frame_attribution.py +0 -0
  455. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_frame_zplane.py +0 -0
  456. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_glyphs.py +0 -0
  457. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_graceful_degradation.py +0 -0
  458. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_help_tip.py +0 -0
  459. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_import_startup.py +0 -0
  460. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_in_place_codegen.py +0 -0
  461. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_index.py +0 -0
  462. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_index_sql.py +0 -0
  463. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_index_warm_no_downgrade.py +0 -0
  464. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_interface_order.py +0 -0
  465. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_issue_corpus.py +0 -0
  466. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_label_extraction.py +0 -0
  467. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_labview_queue.py +0 -0
  468. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_livi_vipi.py +0 -0
  469. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_load_mode.py +0 -0
  470. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_load_upgrade.py +0 -0
  471. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_loop_codegen.py +0 -0
  472. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_lv_detect.py +0 -0
  473. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_lvnet_identity.py +0 -0
  474. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_lvnet_reconstruct.py +0 -0
  475. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_lvnet_roundtrip.py +0 -0
  476. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_lvpy.py +0 -0
  477. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_mcp_cache.py +0 -0
  478. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_mcp_evals.py +0 -0
  479. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_mcp_read_vi_lvnet.py +0 -0
  480. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_mcp_roots.py +0 -0
  481. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_naming.py +0 -0
  482. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_netlist_json.py +0 -0
  483. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_new_codegen.py +0 -0
  484. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_node_properties.py +0 -0
  485. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_output_cache.py +0 -0
  486. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_parallel_codegen.py +0 -0
  487. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_parallel_parse.py +0 -0
  488. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_parser_regression.py +0 -0
  489. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_primitive_name_uniqueness.py +0 -0
  490. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_primitive_positional_order.py +0 -0
  491. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_primitive_resolutions.py +0 -0
  492. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_progressive_index.py +0 -0
  493. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_progressive_warm.py +0 -0
  494. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_project_store.py +0 -0
  495. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_queue_codegen.py +0 -0
  496. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_render_emission_order.py +0 -0
  497. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_render_lvnet.py +0 -0
  498. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_render_title_identity.py +0 -0
  499. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_render_viewer.py +0 -0
  500. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_sequence.py +0 -0
  501. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_soft_codegen.py +0 -0
  502. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_structure_lvproj.py +0 -0
  503. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_subvi_nav.py +0 -0
  504. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_text_encoding.py +0 -0
  505. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_type_driven_fixes.py +0 -0
  506. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_type_fields.py +0 -0
  507. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_type_loading.py +0 -0
  508. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_type_mapping.py +0 -0
  509. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_unknown_nodes.py +0 -0
  510. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_unresolved.py +0 -0
  511. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_vi_graph.py +0 -0
  512. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_vi_properties.py +0 -0
  513. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_viewer_properties.py +0 -0
  514. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_vilib_docurls.py +0 -0
  515. {lvkit-0.8.2 → lvkit-0.8.3}/tests/test_web_progressive_staging.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: lvkit
3
- Version: 0.8.2
3
+ Version: 0.8.3
4
4
  Summary: Understand and convert LabVIEW VIs to Python without a LabVIEW license
5
5
  Project-URL: Repository, https://github.com/pragmatest-dev/lvkit
6
6
  Project-URL: Issues, https://github.com/pragmatest-dev/lvkit/issues
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "lvkit"
7
- version = "0.8.2"
7
+ version = "0.8.3"
8
8
  description = "Understand and convert LabVIEW VIs to Python without a LabVIEW license"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -4,7 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  from typing import TYPE_CHECKING
6
6
 
7
- __version__ = "0.8.2"
7
+ __version__ = "0.8.3"
8
8
 
9
9
  # The public API is exposed LAZILY (PEP 562 module ``__getattr__``): the graph /
10
10
  # parser / structure stacks (networkx, pydantic, pylabview, PIL — ~230 ms) load
@@ -99,6 +99,12 @@ class PrimitiveBuildHandler(NodeBuildHandler):
99
99
  if isinstance(node, (PropertyNode, InvokeNode, EventRegNode)):
100
100
  prim_kwargs["object_name"] = node.object_name
101
101
  prim_kwargs["object_method_id"] = node.object_method_id
102
+ # Property/Invoke nodes share the SAME implicit-binding fields
103
+ # (task #51/#55) -- EventRegNode has neither, so it's excluded
104
+ # here rather than duplicating this pair per elif branch below.
105
+ if isinstance(node, (PropertyNode, InvokeNode)):
106
+ prim_kwargs["bound_control_uid"] = node.bound_control_uid
107
+ prim_kwargs["bound_control_type"] = node.bound_control_type
102
108
  if isinstance(node, PropertyNode):
103
109
  prim_kwargs["properties"] = [
104
110
  PropertyDef(name=p.get("name", "")) if isinstance(p, dict) else p
@@ -107,8 +113,6 @@ class PrimitiveBuildHandler(NodeBuildHandler):
107
113
  prim_kwargs["property_value_terminal_ids"] = [
108
114
  ctx.qid(uid) for uid in node.dco_terminal_uids
109
115
  ]
110
- prim_kwargs["bound_control_uid"] = node.bound_control_uid
111
- prim_kwargs["bound_control_type"] = node.bound_control_type
112
116
  elif isinstance(node, InvokeNode):
113
117
  prim_kwargs["method_name"] = node.method_name
114
118
  prim_kwargs["method_code"] = node.method_code
@@ -647,13 +647,14 @@ class LoadingMixin:
647
647
  search_paths: list[Path] | None = None,
648
648
  owner_chain: list[str] | None = None,
649
649
  ) -> str:
650
- """Load a .lvclass file. Returns the dep_graph qname it was
651
- registered under (``cls_qname``, built from the ON-DISK file's own
652
- stem casing) — the CALLER may have referenced this class under a
653
- different casing (a type reference's recorded casing can differ
654
- from the file's actual casing), so callers that add dep_graph edges
655
- or aliases must use this return value, never re-derive/assume the
656
- qname themselves (see ``_load_dependency``'s ``.lvclass`` branch).
650
+ """Load a .lvclass file. Returns the dep-graph KEY (the resolved
651
+ ``.lvclass`` path) it was registered under — mirrors ``load_lvlib``/
652
+ ``load_typedef``: callers add ownership/reference edges to THIS key,
653
+ never to the qname (``cls_qname``, built from the ON-DISK file's own
654
+ stem casing — the CALLER may have referenced this class under a
655
+ different casing, e.g. a type reference's recorded casing can differ
656
+ from the file's actual casing, so the qname alone isn't a safe edge
657
+ target; see ``_load_dependency``'s ``.lvclass`` branch).
657
658
 
658
659
  ``MINIMAL`` is an INTERFACE load: add the class's private-data fields
659
660
  (and its parent chain's, via walk-up — nMux field indices run into the
@@ -686,7 +687,9 @@ class LoadingMixin:
686
687
  # walks follow it by IDENTITY (a name can hit duplicates; a path
687
688
  # can't). None when the parent isn't on disk.
688
689
  parent = cls.parent_class
689
- parent_file, parent_intended = self._resolve_parent(cls, lvclass_path)
690
+ parent_file, parent_intended = self._resolve_parent(
691
+ cls, lvclass_path, search_paths
692
+ )
690
693
  # The parent's KEY is its recorded path whether or not the file is staged
691
694
  # yet — so progressive (web) staging can NAME + fetch a not-yet-present
692
695
  # parent (an ``.exists()`` gate would drop the whole inherited surface).
@@ -832,6 +835,18 @@ class LoadingMixin:
832
835
  try:
833
836
  pd_fields = private_data_from_lvclass_xml(lvclass_path)
834
837
  except Exception:
838
+ # Intentional hard best-effort boundary, not a narrowable
839
+ # set: private_data_from_lvclass_xml decodes a possibly
840
+ # malformed/legacy embedded RSRC container through
841
+ # pylabview's OWN VI-resource parser (parse_flattened_
842
+ # private_data -> lv_rsrc.VI(...).parseData()), whose
843
+ # exception surface on untrusted/old-format binary data
844
+ # isn't a documented closed set (struct/index/value/key
845
+ # errors and more have all been observed from that parser
846
+ # elsewhere) -- this docstring's own contract is "a
847
+ # malformed/older embedded control must never break class
848
+ # loading", so any failure here degrades to "no fields
849
+ # recovered", never propagates.
835
850
  logger.debug(
836
851
  "flattened private-data recovery failed for %s",
837
852
  lvclass_path,
@@ -841,23 +856,119 @@ class LoadingMixin:
841
856
  fields = [private_data_field_to_cluster_field(f) for f in pd_fields]
842
857
  return fields
843
858
 
859
+ def _resolve_vilib_class_ref(
860
+ self,
861
+ parent_name: str,
862
+ path_tokens: list[str],
863
+ lvclass_path: Path,
864
+ search_paths: list[Path],
865
+ ) -> tuple[Path | None, Path | None]:
866
+ """Resolve a ``<vilib>``-rooted class reference's TOKENS (already
867
+ split — ``path_tokens[0] == "<vilib>"``) -> ``(parent_file,
868
+ parent_intended)``, via ``ParsedDependencyRef.resolve_against`` — the
869
+ EXACT SAME machinery a SubVI/type ``<vilib>`` dependency already
870
+ resolves through (``_resolve_dependency_path``), never a new
871
+ resolver. Shared by BOTH ``<vilib>``-rooted sources ``_resolve_parent``
872
+ can see (a plain-XML ``Parent`` Item's URL, and the binary
873
+ ``ParentClassLinkInfo``'s own recorded path — issue #84's #F1
874
+ correction: the URL case was previously naively joined, leaving a
875
+ literal ``<vilib>`` path component that never exists).
876
+
877
+ A configured ``vilib_root`` gives an exact, scan-free hit; otherwise
878
+ ``<vilib>`` maps onto ``search_paths`` (this loader's already-
879
+ configured SubVI roots) the same way, with ``_find_file``'s existing
880
+ bounded per-search-path lookup (used for every other unresolved
881
+ dependency) actually LOCATING the file when it's nested deeper than
882
+ a direct join reaches (e.g. actor-framework vendors ``Actor.lvclass``
883
+ under an extra ``Core/`` level the recorded path itself doesn't
884
+ record — LabVIEW's own vi.lib layout has no such prefix).
885
+ ``parent_intended`` is still produced via pure path math even when
886
+ no local file is found (one of ``vilib_root``/``search_paths[0]``),
887
+ so progressive/web staging can NAME + fetch it — never
888
+ ``.exists()``-gated away, same contract as the method-path code in
889
+ ``load_lvclass`` above."""
890
+ parent_file: Path | None = None
891
+ parent_intended: Path | None = None
892
+ dep_ref = ParsedDependencyRef(
893
+ name=parent_name + ".lvclass", path_tokens=path_tokens
894
+ )
895
+ candidate = dep_ref.resolve_against(
896
+ lvclass_path,
897
+ vilib_root=self._vilib_root,
898
+ userlib_root=self._userlib_root,
899
+ instrlib_root=self._instrlib_root,
900
+ )
901
+ if candidate is not None:
902
+ parent_intended = candidate
903
+ if candidate.exists():
904
+ parent_file = candidate
905
+ # Both the search_paths glob AND the search_paths[0]-as-vilib_root
906
+ # intended-path fallback are skipped once EITHER parent_file OR
907
+ # parent_intended is already known -- no point globbing when the
908
+ # exact vilib_root candidate already resolved, and no point
909
+ # recomputing parent_intended once parent_file has resolved (it's
910
+ # only ever consulted as parent_file's own fallback, never both).
911
+ if parent_file is None and parent_intended is None and search_paths:
912
+ parent_file = self._find_file(
913
+ parent_name + ".lvclass", search_paths, lvclass_path.parent
914
+ )
915
+ if parent_file is None and parent_intended is None and search_paths:
916
+ # No configured vilib_root — map <vilib> onto the first search
917
+ # root instead, so an absent parent still gets a deterministic
918
+ # (pure path math, no scan) intended path for web staging to
919
+ # name.
920
+ parent_intended = dep_ref.resolve_against(
921
+ lvclass_path, vilib_root=search_paths[0]
922
+ )
923
+ return parent_file, parent_intended
924
+
844
925
  def _resolve_parent(
845
- self, cls: LVClass, lvclass_path: Path
926
+ self, cls: LVClass, lvclass_path: Path, search_paths: list[Path]
846
927
  ) -> tuple[Path | None, Path | None]:
847
928
  """The parent class's file (when present) and its INTENDED path
848
929
  (present or not) -> ``(parent_file, parent_intended)``.
849
930
 
850
931
  PREFERS the parent's own recorded URL (a DIRECT relative path — the
851
932
  parent commonly lives in a sibling subtree an up-tree walk can't
852
- reach, e.g. ``../../Layer/Layer.lvclass``); falls back to a walk-up
853
- by name only when no URL was recorded. Both None when the class has
854
- no parent (or its parent is the root ``LabVIEW Object``).
933
+ reach, e.g. ``../../Layer/Layer.lvclass``) — UNLESS that URL is
934
+ itself ``<vilib>``-rooted (``cls.is_vilib_parent``, e.g.
935
+ ``"/<vilib>/ActorFramework/Actor/Actor.lvclass"``, verified against
936
+ Event Receiver Actor.lvclass's real ``Parent`` Item — a LEADING
937
+ SLASH, the marker, then forward-slash path components, NOT a normal
938
+ relative path), in which case it's tokenized and resolved via
939
+ ``_resolve_vilib_class_ref`` like any other vi.lib reference (issue
940
+ #84's #F1 correction — the naive join used to leave a literal
941
+ ``<vilib>`` path component that never exists).
942
+
943
+ When there's no URL but the parent's binary link record is
944
+ ``<vilib>``-rooted (``cls.parent_link_path``, issue #84 — e.g.
945
+ ``["<vilib>", "ActorFramework", "Actor", "Actor.lvclass"]``),
946
+ resolves it the SAME way via ``_resolve_vilib_class_ref``.
947
+
948
+ A NON-vilib link record (the "in-repo" PTH0 shape — no recorded
949
+ URL, parent lives elsewhere in the same tree) is deliberately left
950
+ to the existing bare-name walk-up below — ``resolve_against``'s
951
+ generic (non-``<vilib>``) branch reads leading EMPTY tokens as
952
+ ``..`` hops, a shape not verified against a real in-repo PTH0
953
+ record, so this stays scoped to what issue #84 actually asked for
954
+ rather than risking the already-working walk-up.
955
+
956
+ Falls back to a bare-name walk-up (``_walk_up_find`` —
957
+ filesystem-only, no search_paths, so it can't reach a cross-subtree
958
+ or vi.lib parent) when NEITHER a URL nor a resolved vilib link path
959
+ was found. Both None when the class has no parent (or its parent is
960
+ the root ``LabVIEW Object``).
855
961
  """
856
962
  parent = cls.parent_class
857
963
  parent_file: Path | None = None
858
964
  parent_intended: Path | None = None
859
965
  if parent and parent != "LabVIEW Object":
860
- if cls.parent_url:
966
+ if cls.parent_url and cls.is_vilib_parent:
967
+ tokens = [t for t in cls.parent_url.split("/") if t]
968
+ parent_file, parent_intended = self._resolve_vilib_class_ref(
969
+ parent, tokens, lvclass_path, search_paths
970
+ )
971
+ elif cls.parent_url:
861
972
  # LabVIEW ``.lvclass`` URLs are relative to the class FILE
862
973
  # treated as a directory (members read ``../X.vi``), so
863
974
  # resolve against ``lvclass_path`` itself — that absorbs the
@@ -865,6 +976,10 @@ class LoadingMixin:
865
976
  parent_intended = (lvclass_path / cls.parent_url).resolve()
866
977
  if parent_intended.exists():
867
978
  parent_file = parent_intended
979
+ elif cls.is_vilib_parent and cls.parent_link_path:
980
+ parent_file, parent_intended = self._resolve_vilib_class_ref(
981
+ parent, cls.parent_link_path, lvclass_path, search_paths
982
+ )
868
983
  if parent_file is None and parent_intended is None:
869
984
  parent_file = self._walk_up_find(
870
985
  lvclass_path.parent, parent + ".lvclass"
@@ -1639,19 +1754,19 @@ class LoadingMixin:
1639
1754
  owner_chain = parts[:-1] if len(parts) > 1 else None
1640
1755
  # MINIMAL: field-load the class (no method bodies) — enough for
1641
1756
  # by-name field names. FULL: load its whole method tree for codegen.
1642
- loaded_qname = self.load_lvclass(
1757
+ loaded_key = self.load_lvclass(
1643
1758
  resolved,
1644
1759
  mode,
1645
1760
  search_paths=search_paths,
1646
1761
  owner_chain=owner_chain,
1647
1762
  )
1648
1763
  if caller_qname:
1649
- self._dep_graph.add_edge(caller_qname, loaded_qname)
1764
+ self._dep_graph.add_edge(caller_qname, loaded_key)
1650
1765
  # See the walk-up branch above: alias rather than let a
1651
1766
  # differently-cased reference create a second, empty dep_graph
1652
1767
  # node that shadows the real (populated) one.
1653
- if qualified_name != loaded_qname:
1654
- self._qualified_aliases[qualified_name] = loaded_qname
1768
+ if qualified_name != loaded_key:
1769
+ self._qualified_aliases[qualified_name] = loaded_key
1655
1770
  elif leaf.endswith(".lvlib"):
1656
1771
  lib_key = self.load_lvlib(resolved, mode, search_paths=search_paths)
1657
1772
  if caller_qname:
@@ -158,17 +158,18 @@ class PrimitiveNode(GraphNode):
158
158
  # reference terminal, but this structural id list always can. See
159
159
  # ``op_walk.correlate_property_terminals``.
160
160
  property_value_terminal_ids: list[str] = []
161
- # Property node only: the BOUND front-panel control's own ddo uid (RAW,
162
- # unqualified -- not this node's own qualified id) when this property
163
- # node is IMPLICIT (permanently tied to a specific control, e.g. GTR's
164
- # "Abort" boolean property node) -- "" for an EXPLICIT one (identity from
165
- # a wired reference). See ``parser.node_types.PropertyNode``'s class
166
- # docstring for the full heap discriminator (a direct ``<ddo>`` child +
167
- # a ``<label>`` naming the control, both absent when explicit).
161
+ # Property/Invoke node only: the BOUND front-panel control's own ddo uid
162
+ # (RAW, unqualified -- not this node's own qualified id) when this node
163
+ # is IMPLICIT (permanently tied to a specific control, e.g. GTR's
164
+ # "Abort" boolean property node or its "Test Hierarchy Tree" invoke
165
+ # node) -- "" for an EXPLICIT one (identity from a wired reference). See
166
+ # ``parser.node_types.PropertyNode``/``InvokeNode``'s class docstrings
167
+ # for the full heap discriminator (a direct ``<ddo>`` child + a
168
+ # ``<label>`` naming the control, both absent when explicit).
168
169
  bound_control_uid: str = ""
169
170
  # The bound control's reconstructed LVType (``parser.fp_heap_type.
170
171
  # reconstruct_control_lvtype``, resolved at PARSE time against the
171
- # front-panel heap -- see ``parser.vi._resolve_property_node_bound_types``).
172
+ # front-panel heap -- see ``parser.vi._resolve_bound_control_types``).
172
173
  # None for an explicit node, or an implicit one whose control class isn't
173
174
  # modeled by that reconstructor.
174
175
  bound_control_type: LVType | None = None
@@ -406,7 +406,7 @@ def parse_polymorphic_info(root: ET.Element) -> dict[str, Any]:
406
406
 
407
407
  # Check for AllowPolyTypeAdapt flag - this indicates a polymorphic wrapper
408
408
  # VIs that call polymorphic VIs have PolyVI TypeDesc but AllowPolyTypeAdapt="0"
409
- exec2 = root.find(".//LVSR//Execution2")
409
+ exec2 = root.find(".//LVSR/Section/Execution2")
410
410
  if exec2 is None or exec2.get("AllowPolyTypeAdapt") != "1":
411
411
  # Also check for selector-based polymorphic (ShowPolySelector)
412
412
  if exec2 is None or exec2.get("ShowPolySelector") != "1":
@@ -515,11 +515,26 @@ def _decode_pth0_components(
515
515
  ``end`` bounds how far the decode may read (exclusive) -- either
516
516
  ``len(data)`` for a whole-file scan or a caller-defined record boundary.
517
517
  Reads the component count at ``pth0_offset+10:+12`` (guarded to
518
- ``0 < ncomp <= 64`` -- a sanity bound against a garbage length), then
519
- decodes each component as a pascal string (1-byte length prefix + native
520
- text). Returns ``([], pth0_offset)`` if the record is truncated (would read
521
- past ``end``) or the count is out of range -- a partial token list is never
522
- returned, so a truncated record reads as "no components" to every caller.
518
+ ``0 <= ncomp <= 64`` -- a sanity bound against a garbage length; ``0`` is
519
+ a REAL, legitimate value -- LabVIEW's own encoding for an empty/unset
520
+ Path, e.g. ``PTH0 00 00 00 04 00 00 00 00`` -- never rejected as
521
+ "truncated": see issue #91, where treating ncomp==0 as an error made
522
+ ``_walk_path`` return ``consumed=0`` for a genuinely empty path, which
523
+ left the CALLER's byte offset stuck. For a cluster field, every
524
+ subsequent field then decoded from that stale offset -- e.g. GTR's SMUI
525
+ cluster's ``TestMethods``/``TestProjectClasses`` arrays (variable-size,
526
+ confirmed by their type descriptor's ``FixedSize="0xFFFFFF"`` sentinel,
527
+ never a fixed bound) read a residual `"PTH0"` tag as their 4-byte
528
+ element count once an earlier empty ``Open Path``/``Test Project``
529
+ field desynced them -- a real leading count IS present and correct once
530
+ alignment holds), then decodes each component as a pascal string
531
+ (1-byte length prefix + native text). Returns ``([], pth0_offset)`` if
532
+ the record is truncated (would read past ``end``) or the count is out
533
+ of range -- a partial token list is never returned, so a truncated
534
+ record reads as "no components" to every caller. An empty path
535
+ (``ncomp==0``) is NOT truncated -- it returns ``([], pth0_offset + 12)``,
536
+ the byte offset just past its magic+length+ncomp fields (see the
537
+ 12-byte-record math above), so the caller's alignment stays correct.
523
538
  ``next_idx`` is the byte offset just past the last decoded component --
524
539
  the single source of both the decoded tokens AND the consumed-byte count,
525
540
  shared by every caller that needs to keep reading past this record (e.g.
@@ -529,8 +544,10 @@ def _decode_pth0_components(
529
544
  if pth0_offset + 12 > end:
530
545
  return [], pth0_offset
531
546
  ncomp = int.from_bytes(data[pth0_offset + 10 : pth0_offset + 12], "big")
532
- if not (0 < ncomp <= 64):
547
+ if not (0 <= ncomp <= 64):
533
548
  return [], pth0_offset
549
+ if ncomp == 0:
550
+ return [], pth0_offset + 12
534
551
  idx = pth0_offset + 12
535
552
  tokens: list[str] = []
536
553
  for _ in range(ncomp):
@@ -60,18 +60,6 @@ class ArrayBuildNode(ParsedNode):
60
60
  pass # No extra fields yet
61
61
 
62
62
 
63
- @dataclass
64
- class ArrayInitNode(ParsedNode):
65
- """Initialize Array node (class="aInit").
66
-
67
- Builds an N-dimensional array from an element value and one size input per
68
- dimension. Terminals (element + dimension sizes → array) carry on the base
69
- ParsedNode; no extra fields.
70
- """
71
-
72
- pass
73
-
74
-
75
63
  @dataclass
76
64
  class FormulaNode(ParsedNode):
77
65
  """A Formula Node (class="fBox") with an embedded C-like script.
@@ -498,17 +486,6 @@ class ArrayBuildHandler(NodeTypeHandler):
498
486
  return ArrayBuildNode(**common)
499
487
 
500
488
 
501
- class ArrayInitHandler(NodeTypeHandler):
502
- """Handler for Initialize Array nodes (class="aInit")."""
503
-
504
- xml_class = "aInit"
505
- display_name = "Initialize Array"
506
-
507
- def parse(self, elem: ET.Element) -> ArrayInitNode:
508
- common = self._extract_common(elem)
509
- return ArrayInitNode(**common)
510
-
511
-
512
489
  class WhileLoopHandler(NodeTypeHandler):
513
490
  """Handler for While Loop nodes (class="whileLoop")."""
514
491
 
@@ -687,6 +664,17 @@ class InvokeNode(ParsedNode):
687
664
  sides of a row, but only wireable sides get a real type. See the
688
665
  render layer (``render/nodes.py:_invoke_node_glyph``), which is where
689
666
  that Void check happens (type resolution isn't available at parse time).
667
+
668
+ Like ``PropertyNode``, an Invoke Node draws in one of two forms
669
+ depending on whether it is permanently bound to a specific front-panel
670
+ control (IMPLICIT) or takes its identity from a wired reference
671
+ (EXPLICIT) -- the SAME heap discriminator: an IMPLICIT node carries a
672
+ direct ``<ddo uid="...">`` CHILD (a sibling of its own ``<termList>``,
673
+ never a part of it) naming the bound control, plus a ``<label>``
674
+ showing that control's NAME instead of the generic node name (verified
675
+ on GTR's "Test Hierarchy Tree" invoke node, uid 11387, bound-control ddo
676
+ uid 12, ``<methName>"Custom Item Symbols.Revert Symbols"``). An EXPLICIT
677
+ node has neither.
690
678
  """
691
679
 
692
680
  object_name: str = ""
@@ -694,6 +682,18 @@ class InvokeNode(ParsedNode):
694
682
  method_name: str = ""
695
683
  method_code: int = 0
696
684
  row_terminal_uids: list[str] = field(default_factory=list)
685
+ # The BOUND control's own ddo uid (see class docstring) -- "" for an
686
+ # EXPLICIT invoke node (no such binding). The sole discriminator: never
687
+ # inferred from the label text or from wiring (those only corroborate).
688
+ bound_control_uid: str = ""
689
+ # The bound control's reconstructed LVType (via
690
+ # ``fp_heap_type.reconstruct_control_lvtype`` on ``bound_control_uid``'s
691
+ # ddo in the FRONT-PANEL heap -- resolved by ``_parse_block_diagram``,
692
+ # which has both heaps, never by ``render/`` reading heap XML itself).
693
+ # None for an explicit node, OR an implicit one whose control class this
694
+ # reconstructor doesn't model (see that function's own docstring) --
695
+ # never a guessed color in either case.
696
+ bound_control_type: LVType | None = None
697
697
 
698
698
 
699
699
  @dataclass
@@ -788,6 +788,12 @@ class InvokeNodeHandler(NodeTypeHandler):
788
788
  meth_code = int(meth_code_text)
789
789
  except ValueError:
790
790
  meth_code = 0
791
+ # An IMPLICIT invoke node's DIRECT <ddo> CHILD (a sibling of its own
792
+ # <termList>, never a part of it) names the front-panel control it's
793
+ # permanently bound to (see InvokeNode's class docstring) -- absent
794
+ # entirely for an EXPLICIT one.
795
+ bound_ddo = elem.find("ddo")
796
+ bound_control_uid = bound_ddo.get("uid", "") if bound_ddo is not None else ""
791
797
  return InvokeNode(
792
798
  **common,
793
799
  object_name=clean_labview_string(elem.findtext("nodeName")),
@@ -795,6 +801,7 @@ class InvokeNodeHandler(NodeTypeHandler):
795
801
  method_name=clean_labview_string(elem.findtext("methName")),
796
802
  method_code=meth_code,
797
803
  row_terminal_uids=_dco_list_terminal_uids(elem),
804
+ bound_control_uid=bound_control_uid,
798
805
  )
799
806
 
800
807
 
@@ -1319,7 +1326,6 @@ _HANDLERS: list[NodeTypeHandler] = [
1319
1326
  FeedbackSlaveHandler(),
1320
1327
  CpdArithHandler(),
1321
1328
  ArrayBuildHandler(),
1322
- ArrayInitHandler(),
1323
1329
  WhileLoopHandler(),
1324
1330
  ForLoopHandler(),
1325
1331
  SelectHandler(),
@@ -64,7 +64,7 @@ from .models import (
64
64
  ParsedWire,
65
65
  SelectorTable,
66
66
  )
67
- from .node_types import PropertyNode, parse_node
67
+ from .node_types import InvokeNode, PropertyNode, parse_node
68
68
  from .nodes import (
69
69
  extract_case_structures,
70
70
  extract_constants,
@@ -374,7 +374,7 @@ def _parse_block_diagram(
374
374
  root = tree.getroot()
375
375
 
376
376
  nodes = _extract_nodes(root)
377
- _resolve_property_node_bound_types(nodes, fp_xml)
377
+ _resolve_bound_control_types(nodes, fp_xml)
378
378
  constants = extract_constants(root)
379
379
  labels = extract_free_labels(root)
380
380
  wires = _extract_wires(root)
@@ -615,21 +615,25 @@ def _extract_nodes(root: ET.Element) -> list[ParsedNode]:
615
615
  return nodes
616
616
 
617
617
 
618
- def _resolve_property_node_bound_types(
618
+ def _resolve_bound_control_types(
619
619
  nodes: list[ParsedNode], fp_xml: Path | str | None
620
620
  ) -> None:
621
- """Resolve each IMPLICIT ``PropertyNode``'s ``bound_control_type`` from
622
- the FRONT-PANEL heap (see ``PropertyNode``'s class docstring for the
623
- ``bound_control_uid`` discriminator) -- in-place, mutating ``nodes``.
621
+ """Resolve each IMPLICIT ``PropertyNode``/``InvokeNode``'s
622
+ ``bound_control_type`` from the FRONT-PANEL heap (see each class's own
623
+ docstring for the ``bound_control_uid`` discriminator -- the SAME direct
624
+ ``<ddo>``-child convention for both node kinds) -- in-place, mutating
625
+ ``nodes``.
624
626
 
625
- This is the one place render's "implicit vs explicit" property-node
627
+ This is the one place render's "implicit vs explicit" node-identity
626
628
  distinction touches the front-panel heap: done here at PARSE time (this
627
629
  module owns both heaps), never in ``render/``, which only ever reads the
628
630
  already-decoded graph/``Layout`` (see ``layout.py``'s own module
629
631
  docstring). The FP root is parsed at most ONCE per VI, only when at
630
- least one property node actually needs it."""
631
- bound: list[PropertyNode] = [
632
- n for n in nodes if isinstance(n, PropertyNode) and n.bound_control_uid
632
+ least one node actually needs it."""
633
+ bound: list[PropertyNode | InvokeNode] = [
634
+ n
635
+ for n in nodes
636
+ if isinstance(n, (PropertyNode, InvokeNode)) and n.bound_control_uid
633
637
  ]
634
638
  if not bound or not fp_xml:
635
639
  return
@@ -637,10 +641,10 @@ def _resolve_property_node_bound_types(
637
641
  if not fp_path.exists():
638
642
  return
639
643
  fp_root = ET.parse(fp_path).getroot()
640
- for prop_node in bound:
641
- ddo = fp_root.find(f".//*[@uid='{prop_node.bound_control_uid}']")
644
+ for bound_node in bound:
645
+ ddo = fp_root.find(f".//*[@uid='{bound_node.bound_control_uid}']")
642
646
  if ddo is not None:
643
- prop_node.bound_control_type = reconstruct_control_lvtype(ddo)
647
+ bound_node.bound_control_type = reconstruct_control_lvtype(ddo)
644
648
 
645
649
 
646
650
  def _extract_wires(root: ET.Element) -> list[ParsedWire]:
@@ -1691,11 +1695,24 @@ def _decode_element(data: bytes, elem_type: LVType | None) -> tuple[str | None,
1691
1695
  if underlying in ("LVVariant", "Variant"):
1692
1696
  return "Variant()", len(data)
1693
1697
 
1694
- # MeasureData (timestamp): 16 bytes (8 int + 8 frac)
1698
+ # MeasureData (timestamp): 16 bytes -- LabVIEW's 128-bit timestamp is an
1699
+ # i64 whole-second count (data[:8]) plus a u64 FRACTION of a second
1700
+ # (data[8:16], fraction = u64/2**64 sec) -- both halves threaded through
1701
+ # so a real (non-epoch) timestamp shows its actual sub-second value
1702
+ # (task #66 follow-up) instead of always truncating to ".000". Emitted
1703
+ # as decimal microseconds (matching datetime.timedelta's own resolution
1704
+ # ceiling -- finer precision would be silently rounded away by the
1705
+ # renderer's timedelta(seconds=...) reconstruction anyway), carrying
1706
+ # into the whole-second count on the (rare) round-up-to-1.0 edge.
1695
1707
  if underlying == "MeasureData":
1696
1708
  if len(data) >= 16:
1697
1709
  secs = int.from_bytes(data[:8], "big", signed=True)
1698
- return f"Timestamp({secs})", 16
1710
+ frac_u64 = int.from_bytes(data[8:16], "big", signed=False)
1711
+ frac_micros = round(frac_u64 * 1_000_000 / 2**64)
1712
+ if frac_micros >= 1_000_000:
1713
+ secs += 1
1714
+ frac_micros = 0
1715
+ return f"Timestamp({secs}.{frac_micros:06d})", 16
1699
1716
  return "Timestamp(0)", len(data)
1700
1717
 
1701
1718
  return None, 0
@@ -410,17 +410,41 @@ def _draw_node_tile(
410
410
  # fixed left gutter wide enough for one arrow, so a row's label starts at the
411
411
  # same x whether or not that row actually draws a left arrow.
412
412
  _ROW_LPAD = 3.0
413
-
414
- # Shared drawer-row geometry for PropertyNodeGlyph and InvokeNodeGlyph: a
415
- # fixed left gutter wide enough for one arrow, so a row's label starts at the
416
- # same x whether or not that row actually draws a left arrow.
417
- _ROW_LPAD = 3.0
418
- _ROW_ARROW_W = 7.0
419
-
420
413
  _ROW_ARROW_W = 7.0
421
414
  _ROW_GUTTER = _ROW_ARROW_W + _ROW_LPAD
422
415
 
423
416
 
417
+ @dataclass(frozen=True)
418
+ class DrawerHeaderGlyphBase:
419
+ """Common implicit/explicit-binding fields shared by ``PropertyNodeGlyph``
420
+ and ``InvokeNodeGlyph`` (task #51/#55) -- both draw a HEADER band (see
421
+ ``draw_drawer_header``) above a DRAWER of named rows, keyed by whether
422
+ the node is permanently bound to a specific front-panel control:
423
+
424
+ - EXPLICIT (``is_implicit=False``): the header names the object CLASS
425
+ the wired reference is of (``⚙ <class_name>``); the reference/error
426
+ terminals thread the box edges at the header level, placed by the
427
+ scene, not drawn here.
428
+ - IMPLICIT (``is_implicit=True``): the header names the BOUND control
429
+ itself (``target_name``), and a TYPE-COLOR BAR (``bar_color``) draws
430
+ under the header instead. No reference terminals thread through.
431
+
432
+ Subclasses add only their own row tuple (the drawer's per-row shape
433
+ differs — a property row is ``(name, is_read)``, an invoke row is
434
+ ``(name, show_left, show_right)``) plus any glyph-specific fields
435
+ (``InvokeNodeGlyph``'s ``method``/``return_present``), and their own
436
+ ``cell_h`` denominator (one header cell + one per row for a property
437
+ node; header + method + one per param for an invoke node)."""
438
+
439
+ class_name: str = "" # object class shown in the header (e.g. "VI")
440
+ is_implicit: bool = False
441
+ target_name: str = "" # the bound control's own name (implicit only)
442
+ bar_color: str | None = None # the bound control's wire color (implicit only)
443
+ fill_attr: str = "prim_fill"
444
+ stroke_attr: str = "prim_stroke"
445
+ text_attr: str = "prim_text"
446
+
447
+
424
448
  def _draw_drawer_row(
425
449
  backend: Backend,
426
450
  x1: float,
@@ -466,3 +490,60 @@ def _draw_drawer_row(
466
490
  lsize,
467
491
  fill=text_fill,
468
492
  )
493
+
494
+
495
+ def draw_drawer_header(
496
+ backend: Backend,
497
+ x1: float,
498
+ x2: float,
499
+ y1: float,
500
+ cell_h: float,
501
+ *,
502
+ is_implicit: bool,
503
+ target_name: str,
504
+ class_name: str,
505
+ bar_color: str | None,
506
+ stroke: str,
507
+ text_fill: str,
508
+ lsize: float,
509
+ ) -> float:
510
+ """Shared header band for ``PropertyNodeGlyph``/``InvokeNodeGlyph``: the
511
+ object class (explicit) or the BOUND CONTROL's own name (implicit) —
512
+ see either glyph's class docstring for the full implicit/explicit
513
+ contract — with a divider beneath. For an EXPLICIT node this is the row
514
+ the reference + error terminals thread through (drawn by the scene, not
515
+ here); an implicit node has none to thread, and instead draws a
516
+ TYPE-COLOR BAR (``bar_color``) in a thin band under the header text.
517
+ Returns ``hy2``, the header band's bottom y (where the caller's own
518
+ method/property drawer rows start)."""
519
+ lpad = _ROW_LPAD
520
+ hy2 = y1 + cell_h
521
+ if is_implicit and target_name:
522
+ header = target_name
523
+ else:
524
+ header = f"⚙ {class_name}".strip() if class_name else "⚙ class"
525
+ bar_h = min(5.0, cell_h * 0.3) if is_implicit else 0.0
526
+ text_cy = y1 + (cell_h - bar_h) / 2
527
+ backend.text(
528
+ (x1 + x2) / 2,
529
+ text_cy + lsize * 0.34,
530
+ fit_label(header, (x2 - x1) - 2 * lpad, backend, lsize),
531
+ lsize,
532
+ fill=text_fill,
533
+ )
534
+ if bar_h >= 1.0 and bar_color:
535
+ # The TYPE-COLOR BAR: a thin band in the bound control's own wire
536
+ # color, filling most of the header's width — LabVIEW's own cue for
537
+ # "this node's identity is a specific control of THIS type",
538
+ # replacing the (absent) reference terminals. Guarded to bar_h>=1.0
539
+ # (not just >0): the rect's own top/bottom are hy2-bar_h/hy2-1.0, so
540
+ # a bar_h under 1px would draw with NEGATIVE height (top below
541
+ # bottom) — an inverted rect — for a very short cell (cell_h below
542
+ # ~3.3px, since bar_h = min(5.0, cell_h*0.3)).
543
+ bar_pad = (x2 - x1) * 0.12
544
+ backend.rect(
545
+ x1 + bar_pad, hy2 - bar_h, x2 - bar_pad, hy2 - 1.0,
546
+ fill=bar_color, stroke="none",
547
+ )
548
+ backend.line(x1, hy2, x2, hy2, stroke=stroke, stroke_width=1.0)
549
+ return hy2