lvkit 0.6.2__tar.gz → 0.6.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.
- {lvkit-0.6.2 → lvkit-0.6.3}/.gitignore +4 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/PKG-INFO +1 -1
- {lvkit-0.6.2 → lvkit-0.6.3}/pyproject.toml +1 -1
- lvkit-0.6.3/scripts/build_example_pages.sh +56 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/__init__.py +1 -1
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/cache_paths.py +55 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/connector_pane_patterns.json +8 -8
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/primitives.json +17 -16
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/docs/generate.py +3 -4
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/graph/core.py +3 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/graph/loading.py +5 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/graph/queries.py +40 -7
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/index/store.py +7 -54
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/output_cache.py +10 -3
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/render/__init__.py +17 -6
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/render/draw.py +2 -2
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/render/icons.py +18 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/render/nodes.py +9 -3
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/render/scene.py +8 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/vi_diff.py +17 -2
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_output_cache.py +16 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_primitive_name_uniqueness.py +4 -1
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_subvi_nav.py +38 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/.pre-commit-config.yaml +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/LICENSE +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/NOTICE +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/README.md +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/deploy/cloudrun/README.md +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/docs/_internal/xml-schema/README.md +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/editors/vscode/LICENSE +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/editors/vscode/README.md +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/mcpb/README.md +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/plugin/README.md +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/scripts/audit_primitive_consistency.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/scripts/audit_terminal_order.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/scripts/audit_unresolved.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/scripts/build_hero.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/scripts/build_ni_function_catalog.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/scripts/connector_geometry_audit.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/scripts/dump_primitive_context.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/scripts/extract_corpus.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/scripts/gen_semantics_probe.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/scripts/generate_docs.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/scripts/generate_driver_data.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/scripts/generate_python.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/scripts/list_unknown_primitives.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/scripts/populate_vilib.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/scripts/pull_samples.sh +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/scripts/recategorize_vilib.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/scripts/render_gallery.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/scripts/reproduce_openg_corpus.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/scripts/study_unwired_direction.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/scripts/wire_decode_probe.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/__main__.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/_data.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/_pylabview_patches.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/cli.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/README.md +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/__init__.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/ast_optimizer.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/ast_utils.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/builder.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/class_builder.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/condition_builder.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/context.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/dataflow.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/elementwise.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/error_handler.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/expressions.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/fragment.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/function.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/imports.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/nodes/__init__.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/nodes/base.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/nodes/case.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/nodes/compound.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/nodes/constant.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/nodes/event.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/nodes/first_call.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/nodes/formula.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/nodes/in_place.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/nodes/invoke_node.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/nodes/loop.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/nodes/nmux.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/nodes/primitive.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/nodes/printf.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/nodes/property_node.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/nodes/queue_ops.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/nodes/sequence.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/nodes/subvi.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/stubs.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/codegen/unresolved.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/connector_geometry.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/connector_pane_geometry.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/drivers/_index.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/drivers/daqmx.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/drivers/nidcpower.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/drivers/nidigital.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/drivers/nidmm.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/drivers/nifgen.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/drivers/niscope.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/drivers/niswitch.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/drivers/serial.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/drivers/visa.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/labview_error_codes.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/openg/_index.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/openg/array.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/openg/file.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/openg/string.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/openg/time.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/openg/variant.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/vilib/_index.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/vilib/_pending_terminals.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/vilib/_types.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/vilib/addons.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/vilib/application-control.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/vilib/array.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/vilib/boolean.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/vilib/cluster-class-variant.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/vilib/collection.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/vilib/comparison.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/vilib/connectivity.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/vilib/control-simulation.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/vilib/data-communication.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/vilib/dialog-user-interface.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/vilib/express.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/vilib/file-io.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/vilib/graphics-sound.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/vilib/instrument-io.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/vilib/mathematics.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/vilib/measurement-io.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/vilib/numeric.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/vilib/report-generation.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/vilib/signal-processing.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/vilib/string.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/vilib/structures.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/vilib/synchronization.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/vilib/timing.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/data/vilib/waveform.json +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/docs/__init__.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/docs/html_generator.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/docs/template.css +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/docs/utils.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/extractor.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/flattened_typedesc.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/formula/__init__.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/formula/cparser.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/formula/emit.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/formula/lexer.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/formula/nodes.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/graph/__init__.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/graph/analysis.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/graph/builders/__init__.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/graph/builders/context.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/graph/builders/operations.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/graph/builders/refs.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/graph/builders/structures.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/graph/construction.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/graph/describe.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/graph/diff.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/graph/interface_order.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/graph/models.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/graph/netlist.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/graph/op_walk.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/graph/operations.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/graph/parallel_parse.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/index/__init__.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/index/build.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/index/model.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/index/project.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/index/query.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/index/sql.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/labview_error.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/labview_error_codes.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/labview_queue.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/load_mode.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/lv_detect.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/mcp/__init__.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/mcp/schemas.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/mcp/server.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/models.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/num_format.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/parser/__init__.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/parser/conp_types.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/parser/constants.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/parser/defaults.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/parser/flags.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/parser/fp_heap_type.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/parser/front_panel.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/parser/layout.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/parser/metadata.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/parser/models.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/parser/naming.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/parser/node_types.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/parser/nodes/__init__.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/parser/nodes/base.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/parser/nodes/case.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/parser/nodes/constant.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/parser/nodes/decompose.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/parser/nodes/disable.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/parser/nodes/event.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/parser/nodes/loop.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/parser/nodes/sequence.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/parser/type_mapping.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/parser/type_resolution.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/parser/utils.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/parser/vi.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/parser/wire_table.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/pipeline.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/primitive_resolver.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/project_store.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/py.typed +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/render/backend.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/render/connector_pane.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/render/connector_pane_panel.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/render/diff_viewer.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/render/glyph.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/render/help_tip.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/render/lane_pass.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/render/properties_panel.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/render/render_viewer.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/render/style.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/render/templates/diff_viewer.html +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/render/templates/render_viewer.html +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/render/theme_control.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/render/theme_web.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/render/wire_router.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/runtime/__init__.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/runtime/lv.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/skill_templates/__init__.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/skill_templates/lvkit/SKILL.md +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/skill_templates/lvkit-convert/SKILL.md +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/skill_templates/lvkit-describe/SKILL.md +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/skill_templates/lvkit-document/SKILL.md +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/skill_templates/lvkit-query/SKILL.md +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/skill_templates/lvkit-resolve/SKILL.md +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/skill_templates/lvkit-review/SKILL.md +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/structure.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/terminal_collector.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/text_encoding.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/tools/__init__.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/tools/connector_geometry_profile.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/type_defaults.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/unresolved.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/src/lvkit/vilib_resolver.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/__init__.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/conftest.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/helpers.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_array_ops_codegen.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_ast_builder.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_ast_optimizer.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_case_codegen.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_case_parser.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_class_field_walkup.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_class_parent_linkinfo.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_class_properties.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_cli.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_cli_cache.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_cli_graph_ops.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_cli_query.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_codegen_context.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_compound_codegen.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_condition_builder.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_connector_geometry.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_connector_pane_label_recovery.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_connector_pane_patterns.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_connector_pane_render.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_conp_types.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_constant_decoding.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_dependency_path_refs.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_determinism.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_diff.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_diff_properties.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_diff_viewer.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_disable_graph.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_disable_parser.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_docs_class_hierarchy.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_docs_render.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_driver_codegen.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_dynamic_dispatch.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_e2e_codegen.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_elementwise.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_error_codegen.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_error_handling.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_error_terminal_label_fallback.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_event_codegen.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_event_parser.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_extraction_cache.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_extractor_concurrency.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_extractor_long_path.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_first_call_codegen.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_formula_codegen.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_formula_runtime.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_formula_semantics.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_formula_transpile.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_fp_heap_type.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_frame_attribution.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_frame_zplane.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_glyphs.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_graceful_degradation.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_help_tip.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_import_startup.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_in_place_codegen.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_index.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_index_sql.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_index_warm_no_downgrade.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_interface_order.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_label_extraction.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_labview_queue.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_load_mode.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_load_upgrade.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_loop_codegen.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_lv_detect.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_lvpy.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_mcp_cache.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_mcp_evals.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_mcp_roots.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_naming.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_netlist.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_netlist_json.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_new_codegen.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_node_properties.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_parallel_codegen.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_parallel_parse.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_parser.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_parser_regression.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_primitive_positional_order.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_primitive_resolutions.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_progressive_index.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_progressive_warm.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_project_store.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_queue_codegen.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_render.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_render_viewer.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_sequence.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_soft_codegen.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_structure_lvproj.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_text_encoding.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_type_driven_fixes.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_type_fields.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_type_loading.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_type_mapping.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_unknown_nodes.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_unresolved.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_vi_graph.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_vi_properties.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_viewer_properties.py +0 -0
- {lvkit-0.6.2 → lvkit-0.6.3}/tests/test_vilib_docurls.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: lvkit
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.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
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Regenerate the pragmatest.com lvkit example pages from PUBLIC sources cloned at
|
|
3
|
+
# build time — no local corpus, no hand-staged fixtures. Every input is a public
|
|
4
|
+
# git ref, so this runs identically on a fresh CI runner and on a dev box.
|
|
5
|
+
#
|
|
6
|
+
# scripts/build_example_pages.sh <output-dir>
|
|
7
|
+
#
|
|
8
|
+
# Produces, under <output-dir>:
|
|
9
|
+
# render/testcase.html — JKI VI Tester TestCase/run.vi @ tag 3.0.0
|
|
10
|
+
# diff/testcase.html — that VI 3.0.0 (base) -> 3.0.1 (head)
|
|
11
|
+
# render/md5.html — OpenG MD5 Message Digest (Binary String)__ogtk.vi
|
|
12
|
+
#
|
|
13
|
+
# Sources (both OSI-approved, redistributable):
|
|
14
|
+
# JKI-VI-Tester BSD-3-Clause github.com/JKISoftware/JKI-VI-Tester
|
|
15
|
+
# OpenG Toolkit BSD-3-Clause reproduced from upstream .vip via scripts/
|
|
16
|
+
set -euo pipefail
|
|
17
|
+
|
|
18
|
+
OUT="${1:?usage: build_example_pages.sh <output-dir>}"
|
|
19
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
20
|
+
mkdir -p "$OUT/render" "$OUT/diff"
|
|
21
|
+
OUT="$(cd "$OUT" && pwd)" # absolute — lvk() renders from $ROOT, OUT must not be relative to it
|
|
22
|
+
WORK="$(mktemp -d)"
|
|
23
|
+
trap 'rm -rf "$WORK"' EXIT
|
|
24
|
+
|
|
25
|
+
lvk() { ( cd "$ROOT" && uv run lvkit "$@" ); }
|
|
26
|
+
|
|
27
|
+
# --- JKI VI Tester: TestCase/run.vi at 3.0.0 (base) vs 3.0.1 (head) -----------
|
|
28
|
+
# run_base = the committed run.vi at tag 3.0.0; run_head = the same file at 3.0.1
|
|
29
|
+
# (JKI wrapped several nodes in a new Case between those releases). Both are real
|
|
30
|
+
# public commits — extracted with `git show`, never hand-edited.
|
|
31
|
+
JKI="$WORK/JKI-VI-Tester"
|
|
32
|
+
RUNVI="source/Classes/TestCase/run.vi"
|
|
33
|
+
echo "clone JKI-VI-Tester (BSD-3-Clause)"
|
|
34
|
+
git clone --quiet https://github.com/JKISoftware/JKI-VI-Tester.git "$JKI"
|
|
35
|
+
git -C "$JKI" show 3.0.0:"$RUNVI" > "$WORK/run_base.vi"
|
|
36
|
+
git -C "$JKI" show 3.0.1:"$RUNVI" > "$WORK/run_head.vi"
|
|
37
|
+
|
|
38
|
+
echo "→ render: testcase ($OUT/render/testcase.html)"
|
|
39
|
+
lvk render "$WORK/run_base.vi" --format html --search-path "$JKI/source" \
|
|
40
|
+
--no-cache -o "$OUT/render/testcase.html"
|
|
41
|
+
|
|
42
|
+
echo "→ diff: testcase ($OUT/diff/testcase.html)"
|
|
43
|
+
lvk diff "$WORK/run_base.vi" "$WORK/run_head.vi" --format html \
|
|
44
|
+
--search-path "$JKI/source" --no-cache -o "$OUT/diff/testcase.html"
|
|
45
|
+
|
|
46
|
+
# --- OpenG MD5 (BSD-3-Clause), reproduced from upstream .vip packages ---------
|
|
47
|
+
OPENG="$WORK/openg"
|
|
48
|
+
echo "reproduce OpenG corpus (BSD-3-Clause; fetches upstream .vip)"
|
|
49
|
+
uv run python "$ROOT/scripts/reproduce_openg_corpus.py" "$OPENG"
|
|
50
|
+
MD5="$OPENG/File Group 0/user.lib/_OpenG.lib/md5/md5.llb/MD5 Message Digest (Binary String)__ogtk.vi"
|
|
51
|
+
|
|
52
|
+
echo "→ render: md5 ($OUT/render/md5.html)"
|
|
53
|
+
lvk render "$MD5" --format html --search-path "$OPENG" \
|
|
54
|
+
--no-cache -o "$OUT/render/md5.html"
|
|
55
|
+
|
|
56
|
+
echo "done → $OUT"
|
|
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from typing import TYPE_CHECKING
|
|
6
6
|
|
|
7
|
-
__version__ = "0.6.
|
|
7
|
+
__version__ = "0.6.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
|
|
@@ -33,6 +33,7 @@ legibly separated at the top.
|
|
|
33
33
|
|
|
34
34
|
from __future__ import annotations
|
|
35
35
|
|
|
36
|
+
import functools
|
|
36
37
|
import hashlib
|
|
37
38
|
import json
|
|
38
39
|
import os
|
|
@@ -201,6 +202,60 @@ def sha256_file(path: Path) -> str:
|
|
|
201
202
|
return hashlib.sha256(Path(path).read_bytes()).hexdigest()
|
|
202
203
|
|
|
203
204
|
|
|
205
|
+
# Top-level package subdirs that DEMONSTRABLY feed NO cached artifact — verified
|
|
206
|
+
# absent from the import closure of the fact/render producers and pinned by
|
|
207
|
+
# ``test_facts_fingerprint_skips_only_non_facts_dirs``. Skipping them means
|
|
208
|
+
# editing codegen / docs / the formula transpiler / the MCP transport layer
|
|
209
|
+
# doesn't force a needless rebuild. (Verified: ``render/`` imports none of these
|
|
210
|
+
# — Formula NODES are drawn from graph models, not the ``formula/`` transpiler.)
|
|
211
|
+
#
|
|
212
|
+
# This is an EXCLUDE list on purpose: the default for any file — including a
|
|
213
|
+
# brand-new module or ``data/`` table — is to be hashed, so a new dependency can
|
|
214
|
+
# never silently escape the fingerprint. The only failure mode (a dep ADDED under
|
|
215
|
+
# one of these dirs) trips the guard test, which fails loudly rather than letting
|
|
216
|
+
# staleness back in.
|
|
217
|
+
_FINGERPRINT_SKIP_DIRS = frozenset({"codegen", "docs", "formula", "mcp"})
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
@functools.lru_cache(maxsize=1)
|
|
221
|
+
def source_fingerprint() -> str:
|
|
222
|
+
"""A hash of lvkit's own SOURCE + bundled data, so EVERY derived cache (the
|
|
223
|
+
SQLite index AND the render/diff output cache) self-invalidates the moment
|
|
224
|
+
that code changes — with no manual version to bump. This is the SINGLE shared
|
|
225
|
+
invalidation used by all caches; do not reintroduce a per-cache version.
|
|
226
|
+
|
|
227
|
+
A cache keyed only on input VI bytes cannot see a change to lvkit's own logic
|
|
228
|
+
(the VI bytes are identical) — so an edit to the parser/graph/render/index
|
|
229
|
+
code, or to a ``data/`` primitive/vilib table, would otherwise keep serving
|
|
230
|
+
output built by the OLD code. That is silent staleness. A hand-bumped version
|
|
231
|
+
can't defend against it because in active development the bump is forgotten
|
|
232
|
+
far more often than not.
|
|
233
|
+
|
|
234
|
+
Hashes every file in the package EXCEPT the ``_FINGERPRINT_SKIP_DIRS`` known
|
|
235
|
+
to feed no cache. Skipping is done by directory (a coarse, verifiable unit),
|
|
236
|
+
never by hand-picking modules — an *include* list is a guess that can miss a
|
|
237
|
+
transitive dep and reintroduce staleness, whereas defaulting to "hash it"
|
|
238
|
+
over-invalidates at worst (one extra cold rebuild). Memoized: the source
|
|
239
|
+
cannot change within a live process (Python does not hot-reload), and a dev
|
|
240
|
+
editing lvkit restarts it before the next run anyway.
|
|
241
|
+
"""
|
|
242
|
+
import lvkit
|
|
243
|
+
|
|
244
|
+
pkg = Path(lvkit.__file__).resolve().parent
|
|
245
|
+
h = hashlib.sha256()
|
|
246
|
+
for f in sorted(pkg.rglob("*")):
|
|
247
|
+
if not f.is_file() or f.suffix == ".pyc" or "__pycache__" in f.parts:
|
|
248
|
+
continue
|
|
249
|
+
rel = f.relative_to(pkg)
|
|
250
|
+
if rel.parts[0] in _FINGERPRINT_SKIP_DIRS:
|
|
251
|
+
continue
|
|
252
|
+
h.update(rel.as_posix().encode())
|
|
253
|
+
h.update(b"\0")
|
|
254
|
+
h.update(f.read_bytes())
|
|
255
|
+
h.update(b"\0")
|
|
256
|
+
return h.hexdigest()
|
|
257
|
+
|
|
258
|
+
|
|
204
259
|
def meta_fresh(vi_path: Path, meta_path: Path, extra: dict | None = None) -> bool:
|
|
205
260
|
"""True if the cached artifact described by ``meta_path`` is still valid for
|
|
206
261
|
``vi_path``.
|
|
@@ -125,15 +125,15 @@
|
|
|
125
125
|
"name": "special-7",
|
|
126
126
|
"terminal_count": 7,
|
|
127
127
|
"corpus_files": 2,
|
|
128
|
-
"grid": {"cols": 3, "rows":
|
|
128
|
+
"grid": {"cols": 3, "rows": 6},
|
|
129
129
|
"cells": [
|
|
130
|
-
{"index": 0, "col": 0, "row": 0, "colspan": 1, "rowspan":
|
|
131
|
-
{"index": 1, "col": 1, "row": 0, "colspan": 1, "rowspan":
|
|
132
|
-
{"index": 5, "col": 2, "row": 0, "colspan": 1, "rowspan":
|
|
133
|
-
{"index": 2, "col": 0, "row":
|
|
134
|
-
{"index": 6, "col": 2, "row":
|
|
135
|
-
{"index": 3, "col": 0, "row":
|
|
136
|
-
{"index": 4, "col": 1, "row":
|
|
130
|
+
{"index": 0, "col": 0, "row": 0, "colspan": 1, "rowspan": 2},
|
|
131
|
+
{"index": 1, "col": 1, "row": 0, "colspan": 1, "rowspan": 2},
|
|
132
|
+
{"index": 5, "col": 2, "row": 0, "colspan": 1, "rowspan": 3},
|
|
133
|
+
{"index": 2, "col": 0, "row": 2, "colspan": 2, "rowspan": 2},
|
|
134
|
+
{"index": 6, "col": 2, "row": 3, "colspan": 1, "rowspan": 3},
|
|
135
|
+
{"index": 3, "col": 0, "row": 4, "colspan": 1, "rowspan": 2},
|
|
136
|
+
{"index": 4, "col": 1, "row": 4, "colspan": 1, "rowspan": 2}
|
|
137
137
|
]
|
|
138
138
|
},
|
|
139
139
|
"4818": {
|
|
@@ -305,40 +305,41 @@
|
|
|
305
305
|
"verified": true
|
|
306
306
|
},
|
|
307
307
|
"1540": {
|
|
308
|
-
"name": "
|
|
308
|
+
"name": "Array To Spreadsheet String",
|
|
309
309
|
"terminals": [
|
|
310
310
|
{
|
|
311
311
|
"index": 0,
|
|
312
312
|
"direction": "out",
|
|
313
|
-
"name": "
|
|
314
|
-
"type": "
|
|
313
|
+
"name": "spreadsheet_string",
|
|
314
|
+
"type": "String"
|
|
315
315
|
},
|
|
316
316
|
{
|
|
317
317
|
"index": 1,
|
|
318
318
|
"direction": "in",
|
|
319
|
-
"name": "
|
|
320
|
-
"type": "
|
|
319
|
+
"name": "delimiter",
|
|
320
|
+
"type": "String",
|
|
321
|
+
"default_value": "'\\t'"
|
|
321
322
|
},
|
|
322
323
|
{
|
|
323
324
|
"index": 2,
|
|
324
325
|
"direction": "in",
|
|
325
|
-
"name": "
|
|
326
|
-
"type": "
|
|
326
|
+
"name": "array",
|
|
327
|
+
"type": "Array"
|
|
327
328
|
},
|
|
328
329
|
{
|
|
329
330
|
"index": 3,
|
|
330
331
|
"direction": "in",
|
|
331
|
-
"name": "
|
|
332
|
-
"type": "
|
|
333
|
-
"default_value": "''"
|
|
332
|
+
"name": "format_string",
|
|
333
|
+
"type": "String"
|
|
334
334
|
}
|
|
335
335
|
],
|
|
336
336
|
"python_code": {
|
|
337
|
-
"
|
|
337
|
+
"spreadsheet_string": "(in_1 or '\\t').join(in_3 % x for x in in_2)"
|
|
338
338
|
},
|
|
339
339
|
"inline": true,
|
|
340
340
|
"verified": true,
|
|
341
|
-
"doc_url": "https://www.ni.com/docs/en-US/bundle/labview-api-ref/page/functions/
|
|
341
|
+
"doc_url": "https://www.ni.com/docs/en-US/bundle/labview-api-ref/page/functions/array-to-spreadsheet-string.html",
|
|
342
|
+
"note": "parmIndex from OBSERVED caller dataflow (OpenG '1D Array to String' control wiring: in_1<-delimiter, in_2<-array, in_3<-format '%s'): in_1=delimiter (default Tab), in_2=array, in_3=format string, out=spreadsheet string. python_code covers the common 1-D case; full N-D (Tab columns / EOL rows / page headers) would need an lvkit runtime helper, as with 1539."
|
|
342
343
|
},
|
|
343
344
|
"1050": {
|
|
344
345
|
"name": "Add",
|
|
@@ -2206,12 +2207,12 @@
|
|
|
2206
2207
|
"note": "Search 1D Array -- returns the index of element (searching from start_index) or -1 if not found. Delete From Array (class='aDelete') is a separate node_type entry. Was mislabeled 'Delete From Array'."
|
|
2207
2208
|
},
|
|
2208
2209
|
"1902": {
|
|
2209
|
-
"name": "
|
|
2210
|
+
"name": "Transpose 2D Array",
|
|
2210
2211
|
"terminals": [
|
|
2211
2212
|
{
|
|
2212
2213
|
"index": 0,
|
|
2213
2214
|
"direction": "out",
|
|
2214
|
-
"name": "
|
|
2215
|
+
"name": "transposed_array",
|
|
2215
2216
|
"type": "Array"
|
|
2216
2217
|
},
|
|
2217
2218
|
{
|
|
@@ -2222,11 +2223,11 @@
|
|
|
2222
2223
|
}
|
|
2223
2224
|
],
|
|
2224
2225
|
"python_code": {
|
|
2225
|
-
"
|
|
2226
|
+
"transposed_array": "[list(r) for r in zip(*in_1)]"
|
|
2226
2227
|
},
|
|
2227
2228
|
"inline": true,
|
|
2228
2229
|
"verified": true,
|
|
2229
|
-
"doc_url": "https://www.ni.com/docs/en-US/bundle/labview-api-ref/page/functions/
|
|
2230
|
+
"doc_url": "https://www.ni.com/docs/en-US/bundle/labview-api-ref/page/functions/transpose-2d-array.html"
|
|
2230
2231
|
},
|
|
2231
2232
|
"1128": {
|
|
2232
2233
|
"name": "Not A Number/Path/Refnum?",
|
|
@@ -17,6 +17,7 @@ from lvkit.graph import InMemoryVIGraph
|
|
|
17
17
|
from lvkit.graph.loading import LoadMode
|
|
18
18
|
from lvkit.models import CaseOperation, SequenceOperation
|
|
19
19
|
from lvkit.render import render_vi_with_subvis
|
|
20
|
+
from lvkit.render.icons import resolve_icon_png
|
|
20
21
|
from lvkit.structure import parse_lvclass, parse_lvlib
|
|
21
22
|
|
|
22
23
|
logger = logging.getLogger(__name__)
|
|
@@ -106,11 +107,9 @@ def _collect_icons(graph: InMemoryVIGraph, output_dir: Path) -> dict[str, str]:
|
|
|
106
107
|
|
|
107
108
|
vi_path = Path(vi_path)
|
|
108
109
|
|
|
109
|
-
icon_source = vi_path.parent
|
|
110
|
-
if not icon_source.exists():
|
|
111
|
-
icon_source = vi_path.parent / f"{vi_path.stem}_icl8.png"
|
|
110
|
+
icon_source = resolve_icon_png(vi_path.parent, vi_path.stem)
|
|
112
111
|
|
|
113
|
-
if icon_source
|
|
112
|
+
if icon_source is not None:
|
|
114
113
|
safe_name = vi_name.replace(":", "_").replace("/", "_").replace("\\", "_")
|
|
115
114
|
safe_name = safe_name.replace(" ", "_").replace(".", "_")
|
|
116
115
|
icon_dest = icons_dir / f"{safe_name}.png"
|
|
@@ -193,6 +193,9 @@ class InMemoryVIGraph(
|
|
|
193
193
|
self._dep_load_mode: dict[str, LoadMode] = {}
|
|
194
194
|
# Source file paths: vi_name -> Path to original .vi file
|
|
195
195
|
self._source_paths: dict[str, Path] = {}
|
|
196
|
+
# vi_key -> qualified DISPLAY name (qname/basename). Display only, never a
|
|
197
|
+
# lookup key — resolve_vi_name still returns the vi_key (path) identity.
|
|
198
|
+
self._vi_display_names: dict[str, str] = {}
|
|
196
199
|
# Search paths the graph was loaded with, retained so decoration-only
|
|
197
200
|
# lookups (a SubVI's own _ICON.png) can locate a project-local .vi by
|
|
198
201
|
# name even under a MINIMAL load that never walks the SubVI tree.
|
|
@@ -190,6 +190,7 @@ class LoadingMixin:
|
|
|
190
190
|
_loaded_vis: set[str]
|
|
191
191
|
_dep_load_mode: dict[str, LoadMode]
|
|
192
192
|
_source_paths: dict[str, Path]
|
|
193
|
+
_vi_display_names: dict[str, str]
|
|
193
194
|
_vi_metadata: dict[str, VIMetadata]
|
|
194
195
|
_vi_properties: dict[str, VIProperties]
|
|
195
196
|
_vi_health: dict[str, VIHealth]
|
|
@@ -814,6 +815,10 @@ class LoadingMixin:
|
|
|
814
815
|
visited.add(vi_key)
|
|
815
816
|
|
|
816
817
|
self._source_paths[vi_key] = source_file
|
|
818
|
+
# vi_key -> its qualified DISPLAY name (qname, else bare filename). Never a
|
|
819
|
+
# lookup key (vi_key/path is the identity); this is the human-facing title
|
|
820
|
+
# only, so the render/viewer shows "Class.lvclass:vi.vi" not the abspath.
|
|
821
|
+
self._vi_display_names[vi_key] = own_qname
|
|
817
822
|
# Reverse indexes: display-name / qname -> [vi_key], for resolve_vi_name.
|
|
818
823
|
# List-valued: on-disk duplicates share a name/qname across distinct keys.
|
|
819
824
|
if vi_key not in self._name_to_keys.setdefault(unqualified_name, []):
|
|
@@ -65,6 +65,7 @@ class QueryMixin:
|
|
|
65
65
|
_qname_to_keys: dict[str, list[str]]
|
|
66
66
|
_loaded_vis: set[str]
|
|
67
67
|
_source_paths: dict[str, Path]
|
|
68
|
+
_vi_display_names: dict[str, str]
|
|
68
69
|
_vi_metadata: dict[str, VIMetadata]
|
|
69
70
|
_vi_properties: dict[str, VIProperties]
|
|
70
71
|
_vi_health: dict[str, VIHealth]
|
|
@@ -119,6 +120,19 @@ class QueryMixin:
|
|
|
119
120
|
return self._pick_vi_key(keys)
|
|
120
121
|
return vi_name
|
|
121
122
|
|
|
123
|
+
def vi_display_name(self, vi_name: str) -> str:
|
|
124
|
+
"""The VI's human-facing qualified name (``Class.lvclass:vi.vi``, else the
|
|
125
|
+
bare filename) for titles/headers. DISPLAY ONLY — never a lookup key. The
|
|
126
|
+
identity remains the ``vi_key`` (path) from :meth:`resolve_vi_name`; this
|
|
127
|
+
maps that key to its qname so the viewer shows the qualified name instead
|
|
128
|
+
of the absolute source path. Falls back to the resolved key's basename,
|
|
129
|
+
then the input, when no display name was recorded."""
|
|
130
|
+
key = self.resolve_vi_name(vi_name)
|
|
131
|
+
display = self._vi_display_names.get(key)
|
|
132
|
+
if display:
|
|
133
|
+
return display
|
|
134
|
+
return Path(key).name or vi_name
|
|
135
|
+
|
|
122
136
|
def _pick_vi_key(self, keys: list[str]) -> str:
|
|
123
137
|
"""Deterministically choose among duplicate ``vi_key``s that share a
|
|
124
138
|
name/qname. Prefer the richest copy -- a stripped built copy loses
|
|
@@ -154,13 +168,32 @@ class QueryMixin:
|
|
|
154
168
|
"""Get the source file path for a VI (accepts a vi_key, qname, or name)."""
|
|
155
169
|
return self._source_paths.get(self.resolve_vi_name(vi_name))
|
|
156
170
|
|
|
157
|
-
def locate_vi_file(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
171
|
+
def locate_vi_file(
|
|
172
|
+
self, vi_name: str, qualified_path: str | None = None
|
|
173
|
+
) -> Path | None:
|
|
174
|
+
"""Best-effort on-disk ``.vi`` for a SubVI. Decoration-only (SubVI icons /
|
|
175
|
+
click-nav) — never forces a load.
|
|
176
|
+
|
|
177
|
+
``qualified_path`` (the caller's project-relative token, e.g.
|
|
178
|
+
``/Lib1/Do.vi``) resolves to the EXACT file by PATH: joined to each search
|
|
179
|
+
root, the file AT that path IS the SubVI. This is what disambiguates two
|
|
180
|
+
SubVIs that share a bare filename across libraries (``Lib1/Do.vi`` vs
|
|
181
|
+
``Lib2/Do.vi``) — a bare-name lookup collides, the path does not. A
|
|
182
|
+
``<vilib>``/``<userlib>`` token is skipped here (resolved separately).
|
|
183
|
+
|
|
184
|
+
Falls back to an already-loaded source, then a bare-filename search of the
|
|
185
|
+
retained search paths (the last-resort path, which CAN collide on
|
|
186
|
+
duplicate names — callers with a ``qualified_path`` never reach it). Under
|
|
187
|
+
a MINIMAL load the SubVIs aren't in ``_source_paths``, so the filename
|
|
188
|
+
search is what lets a project-local SubVI's own ``_ICON.png`` resolve; its
|
|
189
|
+
index is built once, lazily, and cached."""
|
|
190
|
+
if qualified_path:
|
|
191
|
+
rel = qualified_path.replace("\\", "/").lstrip("/")
|
|
192
|
+
if rel and not rel.startswith("<"):
|
|
193
|
+
for root in self._search_paths:
|
|
194
|
+
candidate = root / rel
|
|
195
|
+
if candidate.is_file():
|
|
196
|
+
return candidate
|
|
164
197
|
loaded = self._source_paths.get(self.resolve_vi_name(vi_name))
|
|
165
198
|
if loaded is not None:
|
|
166
199
|
return loaded
|
|
@@ -29,8 +29,6 @@ version bump to forget. See ``_ensure_facts_version``.
|
|
|
29
29
|
|
|
30
30
|
from __future__ import annotations
|
|
31
31
|
|
|
32
|
-
import functools
|
|
33
|
-
import hashlib
|
|
34
32
|
import json
|
|
35
33
|
import sqlite3
|
|
36
34
|
from collections.abc import Iterable
|
|
@@ -289,58 +287,13 @@ _FLAT_PROPERTY_COLUMNS: tuple[tuple[str, bool], ...] = (
|
|
|
289
287
|
)
|
|
290
288
|
|
|
291
289
|
|
|
292
|
-
#
|
|
293
|
-
#
|
|
294
|
-
#
|
|
295
|
-
#
|
|
296
|
-
#
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
# This is an EXCLUDE list on purpose: the default for any file — including a
|
|
300
|
-
# brand-new module or ``data/`` table — is to be hashed, so a new facts
|
|
301
|
-
# dependency can never silently escape the fingerprint. The only failure mode
|
|
302
|
-
# (a facts dep ADDED under one of these dirs) trips the guard test, which fails
|
|
303
|
-
# loudly rather than letting staleness back in.
|
|
304
|
-
_FINGERPRINT_SKIP_DIRS = frozenset({"codegen", "docs", "formula", "mcp"})
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
@functools.lru_cache(maxsize=1)
|
|
308
|
-
def _facts_fingerprint() -> str:
|
|
309
|
-
"""A hash of lvkit's own facts-producing SOURCE + bundled data, so the index
|
|
310
|
-
self-invalidates the moment that changes — with no manual version to bump.
|
|
311
|
-
|
|
312
|
-
The index is a CACHE of what lvkit derives from each VI. Per-VI freshness is
|
|
313
|
-
keyed on VI bytes (``meta.content_sha``), but that cannot see a change to
|
|
314
|
-
lvkit's *extraction logic* (the VI bytes are identical) — so an edit to the
|
|
315
|
-
parser/graph/index code, or to a ``data/`` primitive/vilib table, would
|
|
316
|
-
otherwise keep serving facts built by the OLD code. That is silent staleness,
|
|
317
|
-
and it corrupts everything reading the index: queries, the MCP server, and
|
|
318
|
-
evals. A hand-bumped ``SCHEMA_VERSION`` can't defend against it because in
|
|
319
|
-
active development the bump is forgotten far more often than not.
|
|
320
|
-
|
|
321
|
-
Hashes every file in the package EXCEPT the ``_FINGERPRINT_SKIP_DIRS`` known
|
|
322
|
-
not to feed facts. Skipping is done by directory (a coarse, verifiable unit),
|
|
323
|
-
never by hand-picking the facts modules — an *include* list is a guess that
|
|
324
|
-
can miss a transitive dep and reintroduce staleness, whereas defaulting to
|
|
325
|
-
"hash it" over-invalidates at worst (one extra cold rebuild). Memoized: the
|
|
326
|
-
source cannot change within a live process (Python does not hot-reload), and
|
|
327
|
-
a dev editing lvkit restarts it before the next run anyway.
|
|
328
|
-
"""
|
|
329
|
-
import lvkit
|
|
330
|
-
|
|
331
|
-
pkg = Path(lvkit.__file__).resolve().parent
|
|
332
|
-
h = hashlib.sha256()
|
|
333
|
-
for f in sorted(pkg.rglob("*")):
|
|
334
|
-
if not f.is_file() or f.suffix == ".pyc" or "__pycache__" in f.parts:
|
|
335
|
-
continue
|
|
336
|
-
rel = f.relative_to(pkg)
|
|
337
|
-
if rel.parts[0] in _FINGERPRINT_SKIP_DIRS:
|
|
338
|
-
continue
|
|
339
|
-
h.update(rel.as_posix().encode())
|
|
340
|
-
h.update(b"\0")
|
|
341
|
-
h.update(f.read_bytes())
|
|
342
|
-
h.update(b"\0")
|
|
343
|
-
return h.hexdigest()
|
|
290
|
+
# The source fingerprint that invalidates the index now lives in ``cache_paths``
|
|
291
|
+
# (the light, dependency-free module) so EVERY cache — this SQLite index and the
|
|
292
|
+
# render/diff output cache — shares the exact SAME invalidation. Re-exported here
|
|
293
|
+
# under the historical names the index code + guard test reference. See
|
|
294
|
+
# ``cache_paths.source_fingerprint`` / ``_FINGERPRINT_SKIP_DIRS``.
|
|
295
|
+
_FINGERPRINT_SKIP_DIRS = cache_paths._FINGERPRINT_SKIP_DIRS
|
|
296
|
+
_facts_fingerprint = cache_paths.source_fingerprint
|
|
344
297
|
|
|
345
298
|
|
|
346
299
|
def db_path(project_root: Path) -> Path:
|
|
@@ -17,9 +17,12 @@ Addressing mirrors extraction (see :mod:`lvkit.cache_paths`):
|
|
|
17
17
|
addressed** in a flat ``<cache>/adhoc/render/<sha>.<ext>`` and swept by TTL.
|
|
18
18
|
|
|
19
19
|
Freshness = the VI-content signal ``cache_paths.meta_fresh`` already uses PLUS
|
|
20
|
-
the
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
the SHARED ``cache_paths.source_fingerprint()`` (the same hash-of-lvkit-source
|
|
21
|
+
that invalidates the SQLite index — so a renderer/graph/parser/data edit busts
|
|
22
|
+
this cache too, with no version bump), the lvkit ``version`` (kept for readable
|
|
23
|
+
meta), text encoding, and an ``options`` tag — so a VI edit, an lvkit CODE
|
|
24
|
+
change, a system-code-page change, or different ``--format``/``--theme`` is a
|
|
25
|
+
miss.
|
|
23
26
|
"""
|
|
24
27
|
|
|
25
28
|
from __future__ import annotations
|
|
@@ -154,6 +157,7 @@ def lookup_render(input_path: Path, fmt: str, options: str, version: str) -> str
|
|
|
154
157
|
meta_path,
|
|
155
158
|
{
|
|
156
159
|
"lvkit_version": version,
|
|
160
|
+
"source_fingerprint": cache_paths.source_fingerprint(),
|
|
157
161
|
"options": options,
|
|
158
162
|
"text_encoding": labview_text_encoding(),
|
|
159
163
|
},
|
|
@@ -172,6 +176,7 @@ def store_render(
|
|
|
172
176
|
body,
|
|
173
177
|
{
|
|
174
178
|
"lvkit_version": version,
|
|
179
|
+
"source_fingerprint": cache_paths.source_fingerprint(),
|
|
175
180
|
"options": options,
|
|
176
181
|
"kind": "render",
|
|
177
182
|
"text_encoding": labview_text_encoding(),
|
|
@@ -191,6 +196,7 @@ def lookup_diff(
|
|
|
191
196
|
meta_path,
|
|
192
197
|
{
|
|
193
198
|
"lvkit_version": version,
|
|
199
|
+
"source_fingerprint": cache_paths.source_fingerprint(),
|
|
194
200
|
"options": options,
|
|
195
201
|
"before_sha": before_sha,
|
|
196
202
|
"text_encoding": labview_text_encoding(),
|
|
@@ -215,6 +221,7 @@ def store_diff(
|
|
|
215
221
|
body,
|
|
216
222
|
{
|
|
217
223
|
"lvkit_version": version,
|
|
224
|
+
"source_fingerprint": cache_paths.source_fingerprint(),
|
|
218
225
|
"options": options,
|
|
219
226
|
"before_sha": before_sha,
|
|
220
227
|
"kind": "diff",
|
|
@@ -344,6 +344,7 @@ def render_vi(
|
|
|
344
344
|
interactive=interactive,
|
|
345
345
|
extra_css=extra_css,
|
|
346
346
|
extra_attrs=extra_attrs,
|
|
347
|
+
display_name=graph.vi_display_name(vi_name),
|
|
347
348
|
)
|
|
348
349
|
aside = _vi_aside_svg(graph, vi_name, scene, theme)
|
|
349
350
|
if aside:
|
|
@@ -377,7 +378,7 @@ def _vi_aside_svg(
|
|
|
377
378
|
panel = render_connector_pane_help(
|
|
378
379
|
ctx.connector_pattern_id,
|
|
379
380
|
pane_terminals((*ctx.inputs, *ctx.outputs)),
|
|
380
|
-
title=vi_name.split(":")[-1],
|
|
381
|
+
title=graph.vi_display_name(vi_name).split(":")[-1],
|
|
381
382
|
description=ctx.description,
|
|
382
383
|
icon_uri=icon_uri,
|
|
383
384
|
theme=theme,
|
|
@@ -438,6 +439,7 @@ def _render_scene_svg(
|
|
|
438
439
|
interactive: bool = True,
|
|
439
440
|
extra_css: str = "",
|
|
440
441
|
extra_attrs: dict[str, str] | None = None,
|
|
442
|
+
display_name: str | None = None,
|
|
441
443
|
) -> str:
|
|
442
444
|
"""Draw an already-built ``Scene`` to a self-contained interactive SVG.
|
|
443
445
|
|
|
@@ -445,7 +447,11 @@ def _render_scene_svg(
|
|
|
445
447
|
dark ``--lv-*`` palette block for ``dark``/``auto`` theme modes. Empty keeps
|
|
446
448
|
the ``<style>`` byte-identical to the legacy light output. ``extra_attrs``
|
|
447
449
|
(``None`` by default) becomes root-``<svg>`` ``data-*`` attributes — e.g.
|
|
448
|
-
``render_vi``'s ``data-lv-properties``/``data-lv-health`` (task #19).
|
|
450
|
+
``render_vi``'s ``data-lv-properties``/``data-lv-health`` (task #19).
|
|
451
|
+
``display_name`` (defaults to ``vi_name``) is the DISPLAY-only accessible
|
|
452
|
+
name (aria-label): the qualified name, so the SVG never exposes the abspath
|
|
453
|
+
``vi_name`` key. ``vi_name`` still drives the scoping ``root_id``."""
|
|
454
|
+
title = display_name if display_name is not None else vi_name
|
|
449
455
|
backend = SvgBackend()
|
|
450
456
|
draw_scene(scene, backend, theme)
|
|
451
457
|
style = _BASE_CSS + extra_css
|
|
@@ -467,7 +473,7 @@ def _render_scene_svg(
|
|
|
467
473
|
script = "\n".join(scripts).replace("__ROOT_ID__", json.dumps(root_id))
|
|
468
474
|
return backend.render(
|
|
469
475
|
scene.bounds,
|
|
470
|
-
title=
|
|
476
|
+
title=title,
|
|
471
477
|
script=script,
|
|
472
478
|
root_id=root_id,
|
|
473
479
|
style=style,
|
|
@@ -475,7 +481,7 @@ def _render_scene_svg(
|
|
|
475
481
|
)
|
|
476
482
|
return backend.render(
|
|
477
483
|
scene.bounds,
|
|
478
|
-
title=
|
|
484
|
+
title=title,
|
|
479
485
|
style=style,
|
|
480
486
|
extra_attrs=extra_attrs,
|
|
481
487
|
)
|
|
@@ -505,7 +511,10 @@ def render_vi_with_subvis(
|
|
|
505
511
|
if isinstance(rn.node, VINode):
|
|
506
512
|
target = rn.node.qualified_name or rn.node.name
|
|
507
513
|
if target:
|
|
508
|
-
|
|
514
|
+
# Key by the DOCUMENT-LOCAL dom_id — the same value emitted as
|
|
515
|
+
# ``data-node`` — so the docs NAV map matches the SVG (and neither
|
|
516
|
+
# carries the abspath vi_key).
|
|
517
|
+
subvis[rn.dom_id] = target
|
|
509
518
|
return _render_scene_svg(scene, vi_name, theme), subvis
|
|
510
519
|
|
|
511
520
|
|
|
@@ -586,4 +595,6 @@ def render_vi_file_titled(
|
|
|
586
595
|
|
|
587
596
|
warm_all_loaded(graph)
|
|
588
597
|
svg = render_vi(graph, name, theme=theme, theme_mode=theme_mode)
|
|
589
|
-
|
|
598
|
+
# `name` is the vi_key (canonical source path) — the render identity. The
|
|
599
|
+
# TITLE is display-only: the qualified name, never the abspath key.
|
|
600
|
+
return svg, graph.vi_display_name(name)
|
|
@@ -362,7 +362,7 @@ def draw_node(node: RenderNode, backend: Backend, theme: Theme = DEFAULT_THEME)
|
|
|
362
362
|
# the node's group to open even when it otherwise carries no tooltip.
|
|
363
363
|
group_needed = bool(tooltip) or node.subvi_rel is not None
|
|
364
364
|
if group_needed:
|
|
365
|
-
data = {"node": node.
|
|
365
|
+
data = {"node": node.dom_id}
|
|
366
366
|
if node.subvi_rel is not None:
|
|
367
367
|
data["lv-vi-rel"] = node.subvi_rel
|
|
368
368
|
# A resolvable node (primitive / vi.lib VI) links to its NI docs page
|
|
@@ -936,7 +936,7 @@ def _draw_connector_panel(node: RenderNode, backend: Backend, theme: Theme) -> N
|
|
|
936
936
|
# All panel styling (hidden-until-hover, no pointer capture, drop shadow)
|
|
937
937
|
# lives in the .lv-help CSS rule (render/__init__.py _BASE_CSS) — the single
|
|
938
938
|
# source both the in-place reveal and a host's cloned overlay inherit.
|
|
939
|
-
backend.begin_group(cls="lv-help", data={"node": node.
|
|
939
|
+
backend.begin_group(cls="lv-help", data={"node": node.dom_id})
|
|
940
940
|
backend.rect(
|
|
941
941
|
0.0,
|
|
942
942
|
0.0,
|
|
@@ -16,6 +16,24 @@ import base64
|
|
|
16
16
|
from collections import defaultdict
|
|
17
17
|
from pathlib import Path
|
|
18
18
|
|
|
19
|
+
# LabVIEW dumps a VI's icon in several depth layers next to its heap XML. The
|
|
20
|
+
# block diagram shows the COLOR icon, so prefer the 256-color layer (icl8), then
|
|
21
|
+
# the 16-color layer (icl4), and only fall back to the 1-bit black-and-white
|
|
22
|
+
# ``_ICON.png`` when no color layer exists — otherwise a class/library banner
|
|
23
|
+
# color is silently dropped (e.g. Lib1/Lib2 members render mono).
|
|
24
|
+
_ICON_LAYER_SUFFIXES = ("_icl8.png", "_icl4.png", "_ICON.png")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def resolve_icon_png(directory: Path, stem: str) -> Path | None:
|
|
28
|
+
"""Best available icon PNG for ``<stem>`` under ``directory``: 256-color,
|
|
29
|
+
else 16-color, else the 1-bit B&W fallback; ``None`` if none exist."""
|
|
30
|
+
for suffix in _ICON_LAYER_SUFFIXES:
|
|
31
|
+
candidate = directory / f"{stem}{suffix}"
|
|
32
|
+
if candidate.is_file():
|
|
33
|
+
return candidate
|
|
34
|
+
return None
|
|
35
|
+
|
|
36
|
+
|
|
19
37
|
_WHITE_CUTOFF = 238 # channel value at/above which a pixel counts as background
|
|
20
38
|
|
|
21
39
|
|