@litfamily/lithermes 1.0.8 → 1.0.9
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.
- package/README.md +299 -13
- package/README_Ko-KR.md +300 -14
- package/assets/lithermes-plugin/README.md +17 -6
- package/assets/lithermes-plugin/__init__.py +96 -21
- package/assets/lithermes-plugin/core.py +15 -6
- package/assets/lithermes-plugin/core_commands.py +140 -28
- package/assets/lithermes-plugin/core_contexts.py +88 -15
- package/assets/lithermes-plugin/core_contract.py +118 -9
- package/assets/lithermes-plugin/core_routing.py +197 -8
- package/assets/lithermes-plugin/deliverable_hedge_guard.py +378 -357
- package/assets/lithermes-plugin/diagnostics.py +53 -2
- package/assets/lithermes-plugin/humanizer_detector.py +348 -0
- package/assets/lithermes-plugin/humanizer_ko_metrics.py +108 -0
- package/assets/lithermes-plugin/humanizer_office.py +18 -0
- package/assets/lithermes-plugin/lit_mark.py +1 -1
- package/assets/lithermes-plugin/motion_receipts.py +80 -0
- package/assets/lithermes-plugin/payload-version.json +2240 -64
- package/assets/lithermes-plugin/plugin.yaml +1 -1
- package/assets/lithermes-plugin/rules/bundled-rules/lit-humanizer.md +14 -0
- package/assets/lithermes-plugin/skills/autoconference/assets/report_template.md +2 -1
- package/assets/lithermes-plugin/skills/autoconference/modes/ship/SKILL.md +11 -8
- package/assets/lithermes-plugin/skills/autoresearch/modes/ship/type-checklists.md +2 -2
- package/assets/lithermes-plugin/skills/browser-drive/ORIGIN.json +17 -0
- package/assets/lithermes-plugin/skills/browser-drive/SKILL.md +41 -16
- package/assets/lithermes-plugin/skills/browser-drive/scripts/capability_probe.py +122 -10
- package/assets/lithermes-plugin/skills/frontend-ui-ux/SKILL.md +45 -13
- package/assets/lithermes-plugin/skills/frontend-ui-ux/examples/phase-3-readme-ab/README.md +51 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/README.md +44 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/adaptive-layout.md +34 -62
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/brand-and-imagery.md +22 -70
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/complete-contract.md +18 -4
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/composition.md +27 -78
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/craft-floor.md +74 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/creative-directions.md +25 -69
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/evidence-review.md +18 -97
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/implementation-platforms.md +23 -83
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/inclusive-interface.md +22 -70
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/interaction-motion.md +34 -68
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/motion-guide.md +62 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/operating-lanes.md +14 -78
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/performance-delivery.md +22 -71
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/product-direction.md +17 -70
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/production.md +19 -7
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/redesign-playbook.md +23 -76
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/slop-register.md +69 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/system-foundations.md +22 -67
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/visual-language.md +16 -72
- package/assets/lithermes-plugin/skills/frontend-ui-ux/references/visual-reconstruction.md +16 -81
- package/assets/lithermes-plugin/skills/frontend-ui-ux/resources/THIRD-PARTY-NOTICE.txt +15 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/scripts/probe-page.js +311 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/scripts/probe-rules.json +46 -0
- package/assets/lithermes-plugin/skills/frontend-ui-ux/scripts/probe.mjs +231 -0
- package/assets/lithermes-plugin/skills/lit-comprehend/SKILL.md +36 -25
- package/assets/lithermes-plugin/skills/lit-comprehend/references/artifact-template.md +45 -87
- package/assets/lithermes-plugin/skills/lit-comprehend/scripts/verify-explainer.py +3 -27
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/NOTICE +33 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/SKILL.md +308 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-architecture-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-architecture-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-architecture-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-bar-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-bar-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-bar-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-beeswarm-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-beeswarm-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-beeswarm-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-bubble-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-bubble-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-bubble-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-bump-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-bump-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-bump-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-data-flow-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-data-flow-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-data-flow-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-datalake-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-datalake-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-datalake-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-db-schema-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-db-schema-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-db-schema-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-dependency-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-dependency-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-dependency-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-deployment-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-deployment-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-deployment-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-dp-integration-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-dp-integration-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-dp-integration-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-dp-security-matrix-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-dp-security-matrix-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-dp-security-matrix-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-er-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-er-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-er-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-fishbone-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-fishbone-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-fishbone-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-flowchart-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-flowchart-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-flowchart-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-gantt-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-gantt-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-gantt-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-heatmap-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-heatmap-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-heatmap-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-high-level-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-high-level-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-high-level-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-high-level-vertical-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-high-level-vertical-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-high-level-vertical-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-import-drawio-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-import-drawio-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-import-drawio-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-import-excalidraw-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-import-excalidraw-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-import-excalidraw-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-import-mermaid-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-import-mermaid-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-import-mermaid-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-it-state-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-it-state-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-it-state-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-journey-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-journey-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-journey-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-kanban-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-kanban-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-kanban-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-layers-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-layers-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-layers-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-line-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-line-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-line-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-loop-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-loop-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-loop-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-loop-terminal-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-loop-terminal-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-loop-terminal-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-marimekko-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-marimekko-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-marimekko-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-medallion-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-medallion-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-medallion-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-nested-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-nested-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-nested-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-org-chart-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-org-chart-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-org-chart-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-paved-road-animated-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-paved-road-animated-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-paved-road-animated-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-polar-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-polar-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-polar-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-policy-trace-animated-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-policy-trace-animated-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-policy-trace-animated-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-process-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-process-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-process-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-pyramid-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-pyramid-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-pyramid-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-quadrant-consultant-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-quadrant-consultant-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-quadrant-consultant-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-quadrant-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-quadrant-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-quadrant-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-queue-animated-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-queue-animated-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-queue-animated-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-radar-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-radar-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-radar-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-ridgeline-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-ridgeline-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-ridgeline-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-sankey-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-sankey-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-sankey-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-scatter-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-scatter-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-scatter-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-sequence-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-sequence-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-sequence-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-sequence-oauth-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-sequence-oauth-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-sequence-oauth-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-slopegraph-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-slopegraph-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-slopegraph-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-state-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-state-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-state-lifecycle-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-state-lifecycle-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-state-lifecycle-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-state-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-story-map-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-story-map-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-story-map-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-streamgraph-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-streamgraph-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-streamgraph-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-swimlane-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-swimlane-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-swimlane-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-timeline-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-timeline-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-timeline-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-tree-block-decomposition-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-tree-block-decomposition-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-tree-block-decomposition-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-tree-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-tree-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-tree-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-treemap-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-treemap-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-treemap-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-uml-class-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-uml-class-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-uml-class-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-venn-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-venn-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-venn-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-wardley-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-wardley-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-wardley-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-waterfall-dark.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-waterfall-full.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/examples/type-waterfall-light.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/fonts/OFL.txt +94 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/fonts/PretendardVariable.woff2 +0 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/fonts/provenance.json +22 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/icons.html +231 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/index.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/licenses/devicon-MIT.txt +20 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/licenses/diagram-design-MIT.txt +21 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/licenses/log-z-MIT.txt +21 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/licenses/simple-icons-CC0.md +30 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/assets/licenses/tabler-MIT.txt +21 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/01-service-architecture/after.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/01-service-architecture/brief.md +33 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/02-release-decision/after.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/02-release-decision/brief.md +18 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/03-data-retention/after.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/03-data-retention/brief.md +19 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/04-oauth-sequence/after.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/04-oauth-sequence/brief.md +17 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/05-review-lanes/after.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/05-review-lanes/brief.md +17 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/06-rollout-gates/after.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/06-rollout-gates/brief.md +17 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/07-deployment-boundary/after.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/07-deployment-boundary/brief.md +19 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/08-policy-traces/after.html +1 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/examples/08-policy-traces/brief.md +20 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/accessibility.md +37 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/export.md +29 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/import-drawio.md +21 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/import-excalidraw.md +14 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/import-mermaid.md +18 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/import-schema.md +28 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/korean-typography.md +34 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/motion.md +29 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/office-pptx-docx.md +40 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/output-spec.md +48 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/primitive-annotation.md +12 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/primitive-icons.md +21 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/primitive-sketchy.md +11 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/primitive-terminal.md +11 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/profiles.md +24 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/semantic-patterns.md +27 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/style-guide.md +67 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-architecture.md +51 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-bar.md +51 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-beeswarm.md +49 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-bubble.md +49 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-bump.md +49 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-catalog.json +1433 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-data-flow.md +51 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-datalake.md +49 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-db-schema.md +49 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-dependency.md +49 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-deployment.md +49 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-dp-integration.md +49 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-dp-security-matrix.md +49 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-er.md +49 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-fishbone.md +49 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-flowchart.md +49 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-gantt.md +49 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-heatmap.md +53 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-high-level-vertical.md +53 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-high-level.md +53 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-import-drawio.md +53 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-import-excalidraw.md +53 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-import-mermaid.md +53 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-it-state.md +54 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-journey.md +54 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-kanban.md +54 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-layers.md +54 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-line.md +54 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-loop-terminal.md +53 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-loop.md +54 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-marimekko.md +54 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-medallion.md +54 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-nested.md +59 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-org-chart.md +58 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-paved-road-animated.md +72 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-polar.md +67 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-policy-trace-animated.md +69 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-process.md +58 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-pyramid.md +59 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-quadrant-consultant.md +60 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-quadrant.md +57 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-queue-animated.md +63 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-radar.md +57 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-ridgeline.md +63 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-sankey.md +68 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-scatter.md +60 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-sequence-oauth.md +66 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-sequence.md +63 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-slopegraph.md +65 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-state-lifecycle.md +53 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-state.md +59 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-story-map.md +52 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-streamgraph.md +53 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-swimlane.md +52 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-timeline.md +52 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-tree-block-decomposition.md +57 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-tree.md +54 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-treemap.md +54 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-uml-class.md +52 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-venn.md +51 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-wardley.md +52 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/type-waterfall.md +54 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/references/verifier-guide.md +31 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/block-registry.mjs +43 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/browser-version.mjs +12 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/check-visible-text.py +12 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/diagram_brief.py +192 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/diagram_catalog.py +135 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/diagram_checks.py +123 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/diagram_cli.py +94 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/diagram_geometry.py +130 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/diagram_metrics.py +166 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/diagram_quality.py +293 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/diagram_quality_geometry.py +200 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/diagram_records.py +205 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/diagram_sequence.py +55 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/doctor.mjs +24 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/drawio_extract.py +209 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/drawio_source.py +188 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/excalidraw_extract.py +233 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/excalidraw_support.py +64 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/export.mjs +125 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/mermaid_extract.py +240 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/mermaid_source.py +39 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/office-safety.mjs +54 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/verify-all.py +12 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/verify-brief.py +12 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/verify-diagram.py +12 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/verify-motion.py +12 -0
- package/assets/lithermes-plugin/skills/lit-diagram-drawer/scripts/verify-type.py +12 -0
- package/assets/lithermes-plugin/skills/lit-docx/NOTICE +1 -0
- package/assets/lithermes-plugin/skills/lit-docx/SKILL.md +97 -0
- package/assets/lithermes-plugin/skills/lit-docx/references/frontmatter_schema.md +42 -0
- package/assets/lithermes-plugin/skills/lit-docx/references/journal_style_spec.md +17 -0
- package/assets/lithermes-plugin/skills/lit-docx/references/markdown_quality_checklist.md +37 -0
- package/assets/lithermes-plugin/skills/lit-docx/references/slop_phrase_list.yaml +58 -0
- package/assets/lithermes-plugin/skills/lit-docx/references/slop_rules.md +35 -0
- package/assets/lithermes-plugin/skills/lit-docx/scripts/clean_markdown.py +95 -0
- package/assets/lithermes-plugin/skills/lit-docx/scripts/convert_md_to_docx.py +1041 -0
- package/assets/lithermes-plugin/skills/lit-docx/scripts/convert_md_to_pdf.py +281 -0
- package/assets/lithermes-plugin/skills/lit-docx/scripts/convert_pdf.py +130 -0
- package/assets/lithermes-plugin/skills/lit-docx/scripts/edit_docx.py +297 -0
- package/assets/lithermes-plugin/skills/lit-docx/scripts/embed_images.py +78 -0
- package/assets/lithermes-plugin/skills/lit-docx/scripts/generate_docx_templates.py +491 -0
- package/assets/lithermes-plugin/skills/lit-docx/scripts/slop_lint.py +536 -0
- package/assets/lithermes-plugin/skills/lit-docx/scripts/visual_audit.py +41 -0
- package/assets/lithermes-plugin/skills/lit-docx/templates/docx/acs.docx +0 -0
- package/assets/lithermes-plugin/skills/lit-docx/templates/docx/elsevier.docx +0 -0
- package/assets/lithermes-plugin/skills/lit-docx/templates/docx/ieee.docx +0 -0
- package/assets/lithermes-plugin/skills/lit-docx/templates/docx/korean-generic.docx +0 -0
- package/assets/lithermes-plugin/skills/lit-docx/templates/docx/nature.docx +0 -0
- package/assets/lithermes-plugin/skills/lit-docx/templates/latex/acs/README.md +5 -0
- package/assets/lithermes-plugin/skills/lit-docx/templates/latex/acs/template.tex +37 -0
- package/assets/lithermes-plugin/skills/lit-docx/templates/latex/elsevier/README.md +5 -0
- package/assets/lithermes-plugin/skills/lit-docx/templates/latex/elsevier/template.tex +38 -0
- package/assets/lithermes-plugin/skills/lit-docx/templates/latex/ieee/README.md +5 -0
- package/assets/lithermes-plugin/skills/lit-docx/templates/latex/ieee/template.tex +37 -0
- package/assets/lithermes-plugin/skills/lit-docx/templates/latex/korean-generic/README.md +5 -0
- package/assets/lithermes-plugin/skills/lit-docx/templates/latex/korean-generic/template.tex +37 -0
- package/assets/lithermes-plugin/skills/lit-docx/templates/latex/nature/README.md +5 -0
- package/assets/lithermes-plugin/skills/lit-docx/templates/latex/nature/template.tex +37 -0
- package/assets/lithermes-plugin/skills/lit-docx/templates/registry.yaml +1366 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/NOTICE +170 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/SKILL.md +125 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/assets/report-skeleton.md +26 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/assets/slide-text-skeleton.md +30 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/accessibility-note/after.md +7 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/accessibility-note/before.md +7 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/api-reference/after.md +9 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/api-reference/before.md +15 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/changelog/after.md +5 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/changelog/before.md +12 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/code-comments/after.md +10 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/code-comments/before.md +19 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/commit/after.md +5 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/commit/before.md +7 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/data-release/after.md +7 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/data-release/before.md +9 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/detector-validation/ko-metrics-golden.json +49 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/detector-validation/pos-real.txt +26 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/detector-validation/rule-cases.json +306 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/english-essay/after.md +5 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/english-essay/before.md +5 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/incident-update/after.md +7 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/incident-update/before.md +7 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/ko-essay/after.md +7 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/ko-essay/before.md +9 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/ko-report/after.md +9 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/ko-report/before.md +13 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/ko-slide/after.md +19 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/ko-slide/before.md +21 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/meeting-brief/after.md +9 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/meeting-brief/before.md +11 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/policy-faq/after.md +9 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/policy-faq/before.md +9 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/pr/after.md +15 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/pr/before.md +17 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/project-update/after.md +7 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/project-update/before.md +9 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/readme/after.md +12 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/readme/before.md +14 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/report/after.md +13 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/report/before.md +11 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/research-abstract/after.md +5 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/research-abstract/before.md +5 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/slides/after.md +19 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/slides/before.md +19 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/troubleshooting/after.md +11 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/examples/troubleshooting/before.md +11 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/references/README.md +16 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/references/always-on-rule.md +9 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/references/code-patterns.md +468 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/references/deliverable-channels.md +111 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/references/en-patterns-checklist.md +43 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/references/en-patterns-content.md +243 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/references/en-patterns-structure.md +375 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/references/en-patterns.md +57 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/references/ko-metrics.md +33 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/references/ko-patterns-a-d.md +659 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/references/ko-patterns-e-j.md +387 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/references/ko-patterns.md +31 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/references/rewrite-playbook.md +54 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/references/taxonomy.md +153 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/rules.json +884 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/scripts/detect.py +17 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/scripts/extract_office_text.py +114 -0
- package/assets/lithermes-plugin/skills/lit-humanizer/scripts/ko_metrics.py +35 -0
- package/assets/lithermes-plugin/skills/lit-pptx/NOTICE +1 -0
- package/assets/lithermes-plugin/skills/lit-pptx/SKILL.md +109 -0
- package/assets/lithermes-plugin/skills/lit-pptx/agents/analyst.md +49 -0
- package/assets/lithermes-plugin/skills/lit-pptx/agents/antithesis.md +65 -0
- package/assets/lithermes-plugin/skills/lit-pptx/agents/architect.md +44 -0
- package/assets/lithermes-plugin/skills/lit-pptx/agents/critic.md +42 -0
- package/assets/lithermes-plugin/skills/lit-pptx/agents/synthesizer.md +47 -0
- package/assets/lithermes-plugin/skills/lit-pptx/agents/thesis.md +33 -0
- package/assets/lithermes-plugin/skills/lit-pptx/assets/azure/circle-lg.png +0 -0
- package/assets/lithermes-plugin/skills/lit-pptx/assets/azure/circle-md.png +0 -0
- package/assets/lithermes-plugin/skills/lit-pptx/assets/azure/circle-sm.png +0 -0
- package/assets/lithermes-plugin/skills/lit-pptx/bin/office.mjs +77 -0
- package/assets/lithermes-plugin/skills/lit-pptx/fonts/PRETENDARD-OFL.txt +94 -0
- package/assets/lithermes-plugin/skills/lit-pptx/fonts/a2z-font/A2Z-Black.otf +0 -0
- package/assets/lithermes-plugin/skills/lit-pptx/fonts/a2z-font/A2Z-Bold.otf +0 -0
- package/assets/lithermes-plugin/skills/lit-pptx/fonts/a2z-font/A2Z-ExtraBold.otf +0 -0
- package/assets/lithermes-plugin/skills/lit-pptx/fonts/a2z-font/A2Z-ExtraLight.otf +0 -0
- package/assets/lithermes-plugin/skills/lit-pptx/fonts/a2z-font/A2Z-Light.otf +0 -0
- package/assets/lithermes-plugin/skills/lit-pptx/fonts/a2z-font/A2Z-Medium.otf +0 -0
- package/assets/lithermes-plugin/skills/lit-pptx/fonts/a2z-font/A2Z-Regular.otf +0 -0
- package/assets/lithermes-plugin/skills/lit-pptx/fonts/a2z-font/A2Z-SemiBold.otf +0 -0
- package/assets/lithermes-plugin/skills/lit-pptx/fonts/a2z-font/A2Z-Thin.otf +0 -0
- package/assets/lithermes-plugin/skills/lit-pptx/fonts/a2z-font/OFL.txt +94 -0
- package/assets/lithermes-plugin/skills/lit-pptx/fonts/a2z-font/SOURCE.md +29 -0
- package/assets/lithermes-plugin/skills/lit-pptx/pretendard-font/public/static/PretendardGOV-Bold.otf +0 -0
- package/assets/lithermes-plugin/skills/lit-pptx/pretendard-font/public/static/PretendardGOV-Regular.otf +0 -0
- package/assets/lithermes-plugin/skills/lit-pptx/references/anti-slop-checklist.md +71 -0
- package/assets/lithermes-plugin/skills/lit-pptx/references/authoring-guide.md +139 -0
- package/assets/lithermes-plugin/skills/lit-pptx/references/consensus-protocol.md +125 -0
- package/assets/lithermes-plugin/skills/lit-pptx/references/data-display-cookbook.md +45 -0
- package/assets/lithermes-plugin/skills/lit-pptx/references/design-system-azure.md +58 -0
- package/assets/lithermes-plugin/skills/lit-pptx/runtime/package-lock.json +750 -0
- package/assets/lithermes-plugin/skills/lit-pptx/runtime/package.json +15 -0
- package/assets/lithermes-plugin/skills/lit-pptx/runtime/requirements.in +7 -0
- package/assets/lithermes-plugin/skills/lit-pptx/runtime/requirements.lock +391 -0
- package/assets/lithermes-plugin/skills/lit-pptx/scripts/compile-deck.js +447 -0
- package/assets/lithermes-plugin/skills/lit-pptx/scripts/craft_extras.py +215 -0
- package/assets/lithermes-plugin/skills/lit-pptx/scripts/embed_fonts.py +199 -0
- package/assets/lithermes-plugin/skills/lit-pptx/scripts/integrity.py +60 -0
- package/assets/lithermes-plugin/skills/lit-pptx/scripts/inventory.py +64 -0
- package/assets/lithermes-plugin/skills/lit-pptx/scripts/learn_template.py +433 -0
- package/assets/lithermes-plugin/skills/lit-pptx/scripts/lib/derive-palette.js +176 -0
- package/assets/lithermes-plugin/skills/lit-pptx/scripts/lib/directive-parser.js +244 -0
- package/assets/lithermes-plugin/skills/lit-pptx/scripts/lib/font-map.js +79 -0
- package/assets/lithermes-plugin/skills/lit-pptx/scripts/lib/gen-bg.js +85 -0
- package/assets/lithermes-plugin/skills/lit-pptx/scripts/lib/gen-gradient-assets.js +36 -0
- package/assets/lithermes-plugin/skills/lit-pptx/scripts/lib/layout-resolver.js +736 -0
- package/assets/lithermes-plugin/skills/lit-pptx/scripts/lib/markdown-loader.js +136 -0
- package/assets/lithermes-plugin/skills/lit-pptx/scripts/lib/render-adapter-html.js +357 -0
- package/assets/lithermes-plugin/skills/lit-pptx/scripts/lib/render-adapter-pptx.js +1864 -0
- package/assets/lithermes-plugin/skills/lit-pptx/scripts/lib/slide-ast.js +539 -0
- package/assets/lithermes-plugin/skills/lit-pptx/scripts/lib/spec-validator.js +94 -0
- package/assets/lithermes-plugin/skills/lit-pptx/scripts/lib/template-registry.js +355 -0
- package/assets/lithermes-plugin/skills/lit-pptx/scripts/qa_deck.py +791 -0
- package/assets/lithermes-plugin/skills/lit-pptx/scripts/validate_pptx.py +206 -0
- package/assets/lithermes-plugin/skills/lit-pptx/slide-formats/markdown-slide-spec-v1.md +251 -0
- package/assets/lithermes-plugin/skills/lit-pptx/slide-formats/markdown-slide-spec-v2.md +221 -0
- package/assets/lithermes-plugin/skills/lit-pptx/slide-formats/slide-ast-v1.schema.json +229 -0
- package/assets/lithermes-plugin/skills/lit-pptx/slide-formats/slide-ast-v2.schema.json +144 -0
- package/assets/lithermes-plugin/skills/lit-pptx/slide-formats/template-enrollment-contract.md +228 -0
- package/assets/lithermes-plugin/skills/lit-pptx/templates/enrolled/AZURE-A2Z/capabilities.yaml +146 -0
- package/assets/lithermes-plugin/skills/lit-pptx/templates/enrolled/AZURE-A2Z/layout-mapping.yaml +384 -0
- package/assets/lithermes-plugin/skills/lit-pptx/templates/enrolled/AZURE-A2Z/template.yaml +37 -0
- package/assets/lithermes-plugin/skills/lit-pptx/templates/enrolled/AZURE-PRO/capabilities.yaml +154 -0
- package/assets/lithermes-plugin/skills/lit-pptx/templates/enrolled/AZURE-PRO/layout-mapping.yaml +405 -0
- package/assets/lithermes-plugin/skills/lit-pptx/templates/enrolled/AZURE-PRO/template.yaml +37 -0
- package/assets/lithermes-plugin/skills/lit-pptx/templates/enrolled/BOILERPLATE-A2Z/capabilities.yaml +179 -0
- package/assets/lithermes-plugin/skills/lit-pptx/templates/enrolled/BOILERPLATE-A2Z/layout-mapping.yaml +299 -0
- package/assets/lithermes-plugin/skills/lit-pptx/templates/enrolled/BOILERPLATE-A2Z/template.yaml +45 -0
- package/assets/lithermes-plugin/skills/lit-pptx/templates/enrolled/BOILERPLATE-PRETENDARD/capabilities.yaml +181 -0
- package/assets/lithermes-plugin/skills/lit-pptx/templates/enrolled/BOILERPLATE-PRETENDARD/layout-mapping.yaml +299 -0
- package/assets/lithermes-plugin/skills/lit-pptx/templates/enrolled/BOILERPLATE-PRETENDARD/template.yaml +44 -0
- package/assets/lithermes-plugin/skills/lit-scientific-visualization/SKILL.md +2 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/SKILL.md +71 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/bin/audio.py +45 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/bin/motion.mjs +690 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/bin/runtime.mjs +299 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/NOTICE +23 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/THIRD_PARTY_NOTICES +55 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/browser.mjs +169 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/completion.mjs +52 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/constants.mjs +162 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/director.mjs +33 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/encode.mjs +191 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/flash.mjs +163 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/frame.mjs +428 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/gate.mjs +483 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/look.mjs +223 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/page.js +622 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/presets.mjs +170 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/scenes.mjs +242 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/sound.mjs +382 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/stage-gate.mjs +97 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/stage-init.js +186 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/stage-kit.js +389 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/stage-qa.mjs +358 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/stage-worker.mjs +33 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/stage.mjs +691 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/stills.mjs +71 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/stroke.mjs +231 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/timeline.mjs +177 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/treatment.mjs +244 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/type.mjs +183 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/engine/util.mjs +152 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/references/craft-loop.md +175 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/references/engine-contract.md +166 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/references/runtime.md +122 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/references/stage.md +261 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/references/style-bibles.md +122 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/references/treatment.md +164 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/references/type-craft.md +92 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/runtime/fonts.json +154 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/runtime/licenses/CREDITS +20 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/runtime/licenses/EMS-OFL.txt +91 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/runtime/package-lock.json +88 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/runtime/package.json +12 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/runtime/requirements.in +1 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/runtime/requirements.lock +727 -0
- package/assets/lithermes-plugin/skills/lit-typographic-motion/runtime/word-timing.json +9 -0
- package/assets/lithermes-plugin/skills/litresearch/SKILL.md +19 -18
- package/assets/lithermes-plugin/skills/litwork/SKILL.md +28 -0
- package/assets/lithermes-plugin/skills/visual-qa/references/complete-contract.md +3 -3
- package/package.json +1 -1
- package/readme-assets/ab-simple/s11-ui-baseline-desktop.webp +0 -0
- package/readme-assets/ab-simple/s11-ui-lithermes-desktop.webp +0 -0
- package/readme-assets/ab-simple/s3-ui-baseline-desktop.webp +0 -0
- package/readme-assets/ab-simple/s3-ui-lithermes-desktop.webp +0 -0
- package/readme-assets/ab-simple/s4-ui-baseline-desktop.webp +0 -0
- package/readme-assets/ab-simple/s4-ui-lithermes-desktop.webp +0 -0
- package/readme-assets/ab-simple/s5-office-baseline-slides.webp +0 -0
- package/readme-assets/ab-simple/s5-office-lithermes-slides.webp +0 -0
- package/readme-assets/ab-simple/s7-lithermes-diagram.webp +0 -0
- package/readme-assets/ab-simple/s8-office-baseline-slides.webp +0 -0
- package/readme-assets/ab-simple/s8-office-lithermes-slides.webp +0 -0
- package/readme-assets/ab-simple/s9-office-lithermes-pages.webp +0 -0
- package/readme-assets/badge-version.svg +1 -1
- package/readme-assets/cover-motion-still.webp +0 -0
- package/readme-assets/cover-motion.webp +0 -0
- package/readme-assets/skills/autoconference.webp +0 -0
- package/readme-assets/skills/autoresearch.webp +0 -0
- package/readme-assets/skills/browser-drive.webp +0 -0
- package/readme-assets/skills/comment-checker.webp +0 -0
- package/readme-assets/skills/debugging.webp +0 -0
- package/readme-assets/skills/deep-interview.webp +0 -0
- package/readme-assets/skills/frontend-ui-ux.webp +0 -0
- package/readme-assets/skills/lit-burnoff-file.webp +0 -0
- package/readme-assets/skills/lit-burnoff.webp +0 -0
- package/readme-assets/skills/lit-code.webp +0 -0
- package/readme-assets/skills/lit-commit.webp +0 -0
- package/readme-assets/skills/lit-comprehend.webp +0 -0
- package/readme-assets/skills/lit-crucible.webp +0 -0
- package/readme-assets/skills/lit-diagram-drawer.webp +0 -0
- package/readme-assets/skills/lit-docx.webp +0 -0
- package/readme-assets/skills/lit-handoff.webp +0 -0
- package/readme-assets/skills/lit-humanizer.webp +0 -0
- package/readme-assets/skills/lit-init.webp +0 -0
- package/readme-assets/skills/lit-plan.webp +0 -0
- package/readme-assets/skills/lit-pptx.webp +0 -0
- package/readme-assets/skills/lit-recap.webp +0 -0
- package/readme-assets/skills/lit-scientific-visualization.webp +0 -0
- package/readme-assets/skills/lit-typographic-motion.webp +0 -0
- package/readme-assets/skills/litgoal.webp +0 -0
- package/readme-assets/skills/litresearch.webp +0 -0
- package/readme-assets/skills/litwork.webp +0 -0
- package/readme-assets/skills/lsp-setup.webp +0 -0
- package/readme-assets/skills/lsp.webp +0 -0
- package/readme-assets/skills/readme-studio.webp +0 -0
- package/readme-assets/skills/refactor.webp +0 -0
- package/readme-assets/skills/review-work.webp +0 -0
- package/readme-assets/skills/rules.webp +0 -0
- package/readme-assets/skills/start-work.webp +0 -0
- package/readme-assets/skills/structural-search.webp +0 -0
- package/readme-assets/skills/visual-qa.webp +0 -0
- package/readme-assets/skills/wikify.webp +0 -0
- package/src/cli.js +40 -1
- package/src/lib/check.js +21 -0
- package/src/lib/install.js +262 -7
- package/src/lib/skillPayload.js +3 -3
- package/assets/lithermes-plugin/skills/lit-korean/SKILL.md +0 -237
- package/assets/lithermes-plugin/skills/lit-korean/references/quick-rules.md +0 -24
- package/assets/lithermes-plugin/skills/lit-korean/references/safety-checklist.md +0 -23
- package/readme-assets/cover.webp +0 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""Standard-library parsing and geometry assembly for inline SVG diagrams."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
from math import isfinite
|
|
7
|
+
from types import MappingProxyType
|
|
8
|
+
from typing import assert_never
|
|
9
|
+
|
|
10
|
+
from diagram_records import (
|
|
11
|
+
Boundary, Marker, ParsedSvg, Rect, Route, Shape, TextBox,
|
|
12
|
+
_PATH_TOKEN_RE, _clean, _number, _path_segments, attributes, glyph_width,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
_SVG_RE = re.compile(r"<svg\b([^>]*)>([\s\S]*?)</svg\s*>", re.IGNORECASE)
|
|
16
|
+
_TAG_RE = re.compile(r"<(rect|circle|line|path|text|polygon)\b([^>]*)>([\s\S]*?)</\1\s*>|<(rect|circle|line|path|polygon)\b([^>]*)/?>", re.IGNORECASE)
|
|
17
|
+
|
|
18
|
+
def _parse_shape(kind: str, raw_attrs: str, body: str = "") -> Shape | TextBox | None:
|
|
19
|
+
attrs = attributes(raw_attrs)
|
|
20
|
+
if kind.lower() == "text":
|
|
21
|
+
label = _clean(body)
|
|
22
|
+
if not label:
|
|
23
|
+
return None
|
|
24
|
+
size = _number(attrs.get("font-size"), 16)
|
|
25
|
+
width = glyph_width(label, size)
|
|
26
|
+
anchor = attrs.get("text-anchor", "start")
|
|
27
|
+
x = _number(attrs.get("x")) - (width / 2 if anchor == "middle" else width if anchor == "end" else 0)
|
|
28
|
+
y = _number(attrs.get("y")) - size * 0.82
|
|
29
|
+
return TextBox(
|
|
30
|
+
label=label,
|
|
31
|
+
bounds=Rect(x, y, width, size * 1.22),
|
|
32
|
+
anchor=(_number(attrs.get("x")), _number(attrs.get("y"))),
|
|
33
|
+
font_size=size,
|
|
34
|
+
fill=attrs.get("fill", "#000000"),
|
|
35
|
+
role=attrs.get("data-role", ""),
|
|
36
|
+
edge_for=attrs.get("data-edge-for", ""),
|
|
37
|
+
boundary_for=attrs.get("data-boundary-for", attrs.get("data-group-for", "")),
|
|
38
|
+
)
|
|
39
|
+
kind = kind.lower()
|
|
40
|
+
if kind == "rect":
|
|
41
|
+
rect = Rect(_number(attrs.get("x")), _number(attrs.get("y")), _number(attrs.get("width")), _number(attrs.get("height")))
|
|
42
|
+
elif kind == "circle":
|
|
43
|
+
radius = _number(attrs.get("r"))
|
|
44
|
+
center_x, center_y = _number(attrs.get("cx")), _number(attrs.get("cy"))
|
|
45
|
+
rect = Rect(center_x - radius, center_y - radius, radius * 2, radius * 2)
|
|
46
|
+
else:
|
|
47
|
+
return None
|
|
48
|
+
if rect.w <= 2 or rect.h <= 2:
|
|
49
|
+
return None
|
|
50
|
+
return Shape(kind, rect, attrs.get("fill", ""), attrs)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _marker_points(body: str) -> tuple[tuple[float, float], ...]:
|
|
54
|
+
points: list[tuple[float, float]] = []
|
|
55
|
+
for match in re.finditer(r"<(path|polygon)\b([^>]*)>", body, re.IGNORECASE):
|
|
56
|
+
attrs = attributes(match.group(2))
|
|
57
|
+
if match.group(1).lower() == "polygon":
|
|
58
|
+
values = [float(value) for value in _PATH_TOKEN_RE.findall(attrs.get("points", "")) if not value.isalpha()]
|
|
59
|
+
points.extend((values[i], values[i + 1]) for i in range(0, len(values) - 1, 2))
|
|
60
|
+
else:
|
|
61
|
+
for segment in _path_segments(attrs.get("d", "")):
|
|
62
|
+
if not points:
|
|
63
|
+
points.append((segment[0], segment[1]))
|
|
64
|
+
points.append((segment[2], segment[3]))
|
|
65
|
+
return tuple(points)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def parse_svg(source: str) -> ParsedSvg:
|
|
69
|
+
"""Parse SVG tags and geometric metadata without invoking a renderer."""
|
|
70
|
+
matches = tuple(_SVG_RE.finditer(source))
|
|
71
|
+
svg = "\n".join(match.group(0) for match in matches)
|
|
72
|
+
first_attrs = attributes(matches[0].group(1)) if matches else MappingProxyType({})
|
|
73
|
+
try:
|
|
74
|
+
view_values = [float(value) for value in re.split(r"[\s,]+", first_attrs.get("viewBox", "").strip()) if value]
|
|
75
|
+
except ValueError:
|
|
76
|
+
view_values = []
|
|
77
|
+
view_box = Rect(*view_values) if len(view_values) == 4 else None
|
|
78
|
+
shapes: list[Shape] = []
|
|
79
|
+
texts: list[TextBox] = []
|
|
80
|
+
routes: list[Route] = []
|
|
81
|
+
line_segments: list[Segment] = []
|
|
82
|
+
boundaries: list[Boundary] = []
|
|
83
|
+
for match in _TAG_RE.finditer(svg):
|
|
84
|
+
kind = match.group(1) or match.group(4)
|
|
85
|
+
raw_attrs = match.group(2) if match.group(1) else match.group(5) or ""
|
|
86
|
+
body = match.group(3) or ""
|
|
87
|
+
if kind is None:
|
|
88
|
+
continue
|
|
89
|
+
attrs = attributes(raw_attrs)
|
|
90
|
+
parsed = _parse_shape(kind, raw_attrs, body)
|
|
91
|
+
match parsed:
|
|
92
|
+
case TextBox():
|
|
93
|
+
texts.append(parsed)
|
|
94
|
+
case Shape():
|
|
95
|
+
shapes.append(parsed)
|
|
96
|
+
case None:
|
|
97
|
+
pass
|
|
98
|
+
case unreachable:
|
|
99
|
+
assert_never(unreachable)
|
|
100
|
+
if kind.lower() == "line":
|
|
101
|
+
points = tuple(_number(attrs.get(key), float("nan")) for key in ("x1", "y1", "x2", "y2"))
|
|
102
|
+
if all(isfinite(value) for value in points):
|
|
103
|
+
segment = (points[0], points[1], points[2], points[3])
|
|
104
|
+
if "data-lifeline-for" not in attrs:
|
|
105
|
+
line_segments.append(segment)
|
|
106
|
+
if attrs.get("data-from") and attrs.get("data-to"):
|
|
107
|
+
routes.append(Route(attrs["data-from"], attrs["data-to"], attrs.get("data-label", ""), (segment,), attrs.get("marker-start", ""), attrs.get("marker-end", ""), _number(attrs.get("stroke-width"), 1)))
|
|
108
|
+
if kind.lower() == "rect" and "data-from" in attrs and "data-to" in attrs:
|
|
109
|
+
continue
|
|
110
|
+
if kind.lower() == "path" and attrs.get("data-from") and attrs.get("data-to"):
|
|
111
|
+
segments = _path_segments(attrs.get("d", ""), curve_steps=16)
|
|
112
|
+
if segments:
|
|
113
|
+
routes.append(Route(attrs["data-from"], attrs["data-to"], attrs.get("data-label", ""), segments, attrs.get("marker-start", ""), attrs.get("marker-end", ""), _number(attrs.get("stroke-width"), 1)))
|
|
114
|
+
for shape in shapes:
|
|
115
|
+
attrs = shape.attributes
|
|
116
|
+
marker_keys = ("data-trust-boundary", "data-boundary", "data-boundary-name", "data-group-boundary", "data-group-name", "data-group-label", "data-group-id")
|
|
117
|
+
class_marker = re.search(r"\b(?:boundary|group)(?:[-_][\w-]+)?\b", attrs.get("class", ""), re.IGNORECASE)
|
|
118
|
+
if shape.kind != "rect" or not any(key in attrs for key in marker_keys) and not class_marker:
|
|
119
|
+
continue
|
|
120
|
+
labels = tuple(attrs[key] for key in ("data-trust-boundary", "data-boundary-name", "data-group-boundary", "data-group-name", "data-group-label") if attrs.get(key) and attrs[key] != "true")
|
|
121
|
+
name = attrs.get("data-group-id", attrs.get("id", labels[0] if labels else "outline"))
|
|
122
|
+
boundaries.append(Boundary(name, labels, shape.rect, _number(attrs.get("stroke-width"), 1)))
|
|
123
|
+
markers: dict[str, Marker] = {}
|
|
124
|
+
for match in re.finditer(r"<marker\b([^>]*)>([\s\S]*?)</marker\s*>", svg, re.IGNORECASE):
|
|
125
|
+
attrs = attributes(match.group(1))
|
|
126
|
+
name = attrs.get("id", "")
|
|
127
|
+
if name:
|
|
128
|
+
body = match.group(2)
|
|
129
|
+
markers[name] = Marker(name, _number(attrs.get("refX")), _number(attrs.get("refY")), attrs.get("markerUnits", "strokeWidth"), _marker_points(body), bool(re.search(r"\bz\b|<polygon\b", body, re.IGNORECASE)))
|
|
130
|
+
return ParsedSvg(source, svg, first_attrs, view_box, tuple(shapes), tuple(texts), tuple(routes), tuple(line_segments), tuple(boundaries), MappingProxyType(markers))
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""Dependency-free SVG text, clipping, contrast, and accessibility metrics."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from html import unescape
|
|
6
|
+
import math
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
import re
|
|
9
|
+
from typing import TypedDict
|
|
10
|
+
|
|
11
|
+
from diagram_geometry import ParsedSvg, Rect, parse_svg
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ContrastFailure(TypedDict):
|
|
15
|
+
text: str
|
|
16
|
+
ratio: float
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class DiagramMetrics(TypedDict):
|
|
20
|
+
source: str
|
|
21
|
+
hasSvg: bool
|
|
22
|
+
viewBox: Rect | None
|
|
23
|
+
title: bool
|
|
24
|
+
desc: bool
|
|
25
|
+
roleImage: bool
|
|
26
|
+
ariaLinked: bool
|
|
27
|
+
textCount: int
|
|
28
|
+
textOverlaps: int
|
|
29
|
+
objectOverlaps: int
|
|
30
|
+
offCanvas: int
|
|
31
|
+
offCanvasLabels: list[str]
|
|
32
|
+
arrowCrossings: int
|
|
33
|
+
contrastFailures: list[ContrastFailure]
|
|
34
|
+
colors: int
|
|
35
|
+
largeRadius: int
|
|
36
|
+
hasShadow: bool
|
|
37
|
+
fontFamilyPresent: bool
|
|
38
|
+
localFont: bool
|
|
39
|
+
fontFileValid: bool
|
|
40
|
+
visibleText: str
|
|
41
|
+
type: str
|
|
42
|
+
sourceBytes: int
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _intersects(first: Rect, second: Rect) -> bool:
|
|
46
|
+
return first.x < second.right - 1 and first.right > second.x + 1 and first.y < second.bottom - 1 and first.bottom > second.y + 1
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _contains(first: Rect, second: Rect) -> bool:
|
|
50
|
+
return first.x <= second.x + 1 and first.y <= second.y + 1 and first.right >= second.right - 1 and first.bottom >= second.bottom - 1
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _luminance(color: str) -> float | None:
|
|
54
|
+
if not re.fullmatch(r"#[0-9a-f]{3}(?:[0-9a-f]{3})?", color, re.IGNORECASE):
|
|
55
|
+
return None
|
|
56
|
+
digits = color[1:]
|
|
57
|
+
if len(digits) == 3:
|
|
58
|
+
digits = "".join(character * 2 for character in digits)
|
|
59
|
+
channels = [int(digits[index:index + 2], 16) / 255 for index in (0, 2, 4)]
|
|
60
|
+
linear = [channel / 12.92 if channel <= 0.04045 else ((channel + 0.055) / 1.055) ** 2.4 for channel in channels]
|
|
61
|
+
return 0.2126 * linear[0] + 0.7152 * linear[1] + 0.0722 * linear[2]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def contrast_ratio(foreground: str, background: str) -> float | None:
|
|
65
|
+
"""Return WCAG contrast for literal hex colors; unresolved CSS stays unscored."""
|
|
66
|
+
first, second = _luminance(foreground), _luminance(background)
|
|
67
|
+
if first is None or second is None:
|
|
68
|
+
return None
|
|
69
|
+
return (max(first, second) + 0.05) / (min(first, second) + 0.05)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _visible_text(source: str, parsed: ParsedSvg) -> str:
|
|
73
|
+
from_svg = "\n".join(re.sub(r"\s+", " ", unescape(re.sub(r"<[^>]*>", " ", match.group(1)))).strip() for match in re.finditer(r"<(?:text|title|desc)\b[^>]*>([\s\S]*?)</(?:text|title|desc)\s*>", parsed.svg, re.IGNORECASE))
|
|
74
|
+
document = re.sub(r"<head\b[^>]*>[\s\S]*?</head\s*>|<style\b[^>]*>[\s\S]*?</style\s*>|<script\b[^>]*>[\s\S]*?</script\s*>|<svg\b[^>]*>[\s\S]*?</svg\s*>", " ", source, flags=re.IGNORECASE)
|
|
75
|
+
html_text = re.sub(r"<[^>]*>", " ", unescape(document))
|
|
76
|
+
html_text = re.sub(r"\s+", " ", html_text).strip()
|
|
77
|
+
return "\n".join(value for value in (from_svg, html_text) if value)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _line_crossings(parsed: ParsedSvg) -> int:
|
|
81
|
+
segments = list(dict.fromkeys([*parsed.line_segments, *(segment for route in parsed.routes for segment in route.segments)]))
|
|
82
|
+
crossings = 0
|
|
83
|
+
for index, first in enumerate(segments):
|
|
84
|
+
for second in segments[index + 1:]:
|
|
85
|
+
a = (first[0], first[1])
|
|
86
|
+
b = (first[2], first[3])
|
|
87
|
+
c = (second[0], second[1])
|
|
88
|
+
d = (second[2], second[3])
|
|
89
|
+
shared = any(math.hypot(point[0] - other[0], point[1] - other[1]) < 2 for point in (a, b) for other in (c, d))
|
|
90
|
+
if not shared and _orientation(a, b, c) * _orientation(a, b, d) < 0 and _orientation(c, d, a) * _orientation(c, d, b) < 0:
|
|
91
|
+
crossings += 1
|
|
92
|
+
return crossings
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _orientation(first: tuple[float, float], second: tuple[float, float], third: tuple[float, float]) -> float:
|
|
96
|
+
return (second[0] - first[0]) * (third[1] - first[1]) - (second[1] - first[1]) * (third[0] - first[0])
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def source_metrics(source: str, source_path: str = "<stdin>") -> DiagramMetrics:
|
|
100
|
+
"""Measure accessible text and geometric warning conditions in an SVG document."""
|
|
101
|
+
parsed = parse_svg(source)
|
|
102
|
+
view = parsed.view_box
|
|
103
|
+
geometry = [shape.rect for shape in parsed.shapes] + [text.bounds for text in parsed.texts]
|
|
104
|
+
off_canvas: list[str] = []
|
|
105
|
+
if view is not None:
|
|
106
|
+
for index, rect in enumerate(geometry):
|
|
107
|
+
if rect.x < view.x - 1 or rect.y < view.y - 1 or rect.right > view.right + 1 or rect.bottom > view.bottom + 1:
|
|
108
|
+
off_canvas.append(parsed.texts[index - len(parsed.shapes)].label if index >= len(parsed.shapes) else parsed.shapes[index].kind)
|
|
109
|
+
text_overlaps = sum(_intersects(first.bounds, second.bounds) for index, first in enumerate(parsed.texts) for second in parsed.texts[index + 1:])
|
|
110
|
+
visible_shapes = [shape for shape in parsed.shapes if not (view is not None and shape.rect.w >= view.w - 2 and shape.rect.h >= view.h - 2)]
|
|
111
|
+
venn = re.search(r"diagram-venn-(?:light|dark|full)", parsed.attributes.get("id", "")) is not None
|
|
112
|
+
object_overlaps = 0
|
|
113
|
+
for index, first in enumerate(visible_shapes):
|
|
114
|
+
for second in visible_shapes[index + 1:]:
|
|
115
|
+
venn_pair = venn and first.kind == "circle" and second.kind == "circle"
|
|
116
|
+
if not venn_pair and _intersects(first.rect, second.rect) and not _contains(first.rect, second.rect) and not _contains(second.rect, first.rect):
|
|
117
|
+
object_overlaps += 1
|
|
118
|
+
visible_rects = [shape for shape in parsed.shapes if shape.kind == "rect" and shape.fill.lower() not in {"none", "transparent"}]
|
|
119
|
+
paper = "#ffffff"
|
|
120
|
+
if visible_rects:
|
|
121
|
+
visible_rects.sort(key=lambda shape: (bool(view and shape.rect.x <= view.x + 1 and shape.rect.y <= view.y + 1 and shape.rect.w >= view.w - 2 and shape.rect.h >= view.h - 2), shape.rect.w * shape.rect.h), reverse=True)
|
|
122
|
+
paper = visible_rects[0].fill or paper
|
|
123
|
+
failures: list[ContrastFailure] = []
|
|
124
|
+
for label in parsed.texts:
|
|
125
|
+
ratio = contrast_ratio(label.fill, paper)
|
|
126
|
+
if ratio is not None and ratio < 4.5:
|
|
127
|
+
failures.append({"text": label.label, "ratio": round(ratio, 2)})
|
|
128
|
+
colors = {
|
|
129
|
+
match.group(1).lower()
|
|
130
|
+
for match in re.finditer(r"\b(?:fill|stroke)\s*=\s*['\"](#[0-9a-f]{3,8})['\"]", parsed.svg, re.IGNORECASE)
|
|
131
|
+
}
|
|
132
|
+
family_present = bool(re.search(r"Pretendard", parsed.svg, re.IGNORECASE) and re.search(r"Pretendard", source, re.IGNORECASE))
|
|
133
|
+
font_urls = [unescape(match.group(1).strip()) for match in re.finditer(r"@font-face\b[\s\S]{0,250}?url\(\s*['\"]?([^'\")]+)['\"]?\)", source, re.IGNORECASE)]
|
|
134
|
+
local_font = any(url.lower().startswith("data:font/") or not re.match(r"https?://", url, re.IGNORECASE) and re.search(r"\.(?:woff2?|ttf|otf)(?:[?#].*)?$", url, re.IGNORECASE) for url in font_urls)
|
|
135
|
+
font_file_valid = any(
|
|
136
|
+
url.lower().startswith("data:font/")
|
|
137
|
+
or not re.match(r"https?://", url, re.IGNORECASE)
|
|
138
|
+
and source_path != "<stdin>"
|
|
139
|
+
and (Path(source_path).resolve().parent / re.split(r"[?#]", url, maxsplit=1)[0]).is_file()
|
|
140
|
+
for url in font_urls
|
|
141
|
+
)
|
|
142
|
+
return {
|
|
143
|
+
"source": source_path,
|
|
144
|
+
"hasSvg": bool(parsed.svg),
|
|
145
|
+
"viewBox": view,
|
|
146
|
+
"title": bool(re.search(r"<title\b[^>]*>[\s\S]*?</title\s*>", parsed.svg, re.IGNORECASE)),
|
|
147
|
+
"desc": bool(re.search(r"<desc\b[^>]*>[\s\S]*?</desc\s*>", parsed.svg, re.IGNORECASE)),
|
|
148
|
+
"roleImage": bool(re.search(r"\brole\s*=\s*['\"]img['\"]", parsed.svg, re.IGNORECASE)),
|
|
149
|
+
"ariaLinked": bool(parsed.attributes.get("aria-labelledby")),
|
|
150
|
+
"textCount": len(parsed.texts),
|
|
151
|
+
"textOverlaps": text_overlaps,
|
|
152
|
+
"objectOverlaps": object_overlaps,
|
|
153
|
+
"offCanvas": len(off_canvas),
|
|
154
|
+
"offCanvasLabels": off_canvas[:12],
|
|
155
|
+
"arrowCrossings": _line_crossings(parsed),
|
|
156
|
+
"contrastFailures": failures,
|
|
157
|
+
"colors": len(colors),
|
|
158
|
+
"largeRadius": sum(float(shape.attributes.get("rx", "0") or 0) > 8 for shape in parsed.shapes if shape.kind == "rect"),
|
|
159
|
+
"hasShadow": bool(re.search(r"\bbox-shadow\s*:|filter\s*=\s*['\"]url\(#", source, re.IGNORECASE)),
|
|
160
|
+
"fontFamilyPresent": family_present,
|
|
161
|
+
"localFont": local_font,
|
|
162
|
+
"fontFileValid": font_file_valid,
|
|
163
|
+
"visibleText": _visible_text(source, parsed),
|
|
164
|
+
"type": parsed.attributes.get("id", ""),
|
|
165
|
+
"sourceBytes": len(source.encode("utf-8")),
|
|
166
|
+
}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
"""Accessibility, routing, spacing, boundary, and canvas checks for SVG."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
import re
|
|
7
|
+
import colorsys
|
|
8
|
+
|
|
9
|
+
from diagram_geometry import parse_svg
|
|
10
|
+
from diagram_metrics import DiagramMetrics, source_metrics
|
|
11
|
+
from diagram_quality_geometry import (
|
|
12
|
+
distance_to_rect_boundary, marker_length, marker_overlaps_rect, marker_scale,
|
|
13
|
+
marker_tip, rectangle_gap, route_bends, route_crosses_boundary,
|
|
14
|
+
route_crossings, route_distance, route_length, route_manhattan_distance,
|
|
15
|
+
routes_overlap_collinearly, segment_along_boundary, segment_hits_rect,
|
|
16
|
+
segment_hits_text, text_overlaps_node,
|
|
17
|
+
)
|
|
18
|
+
from diagram_records import Boundary, ParsedSvg, Route, Shape, TextBox
|
|
19
|
+
from diagram_sequence import sequence_issues
|
|
20
|
+
|
|
21
|
+
_CONTAMINATION = (
|
|
22
|
+
re.compile(r"\blitfamily\b", re.IGNORECASE),
|
|
23
|
+
re.compile(r"\b(?:generator|generated by|template|prototype)\b", re.IGNORECASE),
|
|
24
|
+
re.compile(r"\bsource\s*:", re.IGNORECASE),
|
|
25
|
+
re.compile(r"\bnotes?\s*:", re.IGNORECASE),
|
|
26
|
+
re.compile(r"\bstatic\s+first\b", re.IGNORECASE),
|
|
27
|
+
re.compile(r"\baccessible\s+labels\b", re.IGNORECASE),
|
|
28
|
+
re.compile(r"\bone\s+accent\b", re.IGNORECASE),
|
|
29
|
+
re.compile(r"\bfocal\b", re.IGNORECASE),
|
|
30
|
+
re.compile(r"\b(?:light|dark|full)\s*[·|/]\s*static\b", re.IGNORECASE),
|
|
31
|
+
re.compile(r"\bdesign\s+rules?\b", re.IGNORECASE),
|
|
32
|
+
re.compile(r"^\s*show\b", re.IGNORECASE),
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
OFFICE_RULES = {
|
|
36
|
+
"group_ratio": 2, "accent_saturation": .12, "accent_light_min": .12,
|
|
37
|
+
"accent_light_max": .93, "accent_hue_family_deg": 20,
|
|
38
|
+
"accent_medium_count": 3, "accent_high_count": 4,
|
|
39
|
+
"latin_share": .80, "title_case_share": .60,
|
|
40
|
+
"label_inset_px": 4,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _office_craft_issues(parsed: ParsedSvg, nodes: tuple[Shape, ...]) -> list[str]:
|
|
45
|
+
issues: list[str] = []
|
|
46
|
+
if len(parsed.boundaries) >= 2:
|
|
47
|
+
def group(shape: Shape) -> str:
|
|
48
|
+
center = (shape.rect.x + shape.rect.w / 2, shape.rect.y + shape.rect.h / 2)
|
|
49
|
+
for boundary in parsed.boundaries:
|
|
50
|
+
if boundary.rect.x <= center[0] <= boundary.rect.right and boundary.rect.y <= center[1] <= boundary.rect.bottom:
|
|
51
|
+
return boundary.name
|
|
52
|
+
return "unbounded"
|
|
53
|
+
|
|
54
|
+
within, between = [], []
|
|
55
|
+
for index, first in enumerate(nodes):
|
|
56
|
+
for second in nodes[index + 1:]:
|
|
57
|
+
gap = rectangle_gap(first.rect, second.rect)
|
|
58
|
+
(within if group(first) == group(second) else between).append(gap)
|
|
59
|
+
if within and between and min(between) < OFFICE_RULES["group_ratio"] * max(within):
|
|
60
|
+
issues.append(f"OF-201 group ratio {min(between) / max(within):.2f} below {OFFICE_RULES['group_ratio']}")
|
|
61
|
+
|
|
62
|
+
hues: list[float] = []
|
|
63
|
+
for node in nodes:
|
|
64
|
+
if node.attributes.get("display") == "none" or not re.fullmatch(r"#[0-9a-fA-F]{6}", node.fill):
|
|
65
|
+
continue
|
|
66
|
+
rgb = tuple(int(node.fill[index:index + 2], 16) / 255 for index in (1, 3, 5))
|
|
67
|
+
hue, lightness, saturation = colorsys.rgb_to_hls(*rgb)
|
|
68
|
+
hue *= 360
|
|
69
|
+
if saturation < OFFICE_RULES["accent_saturation"] or not OFFICE_RULES["accent_light_min"] <= lightness <= OFFICE_RULES["accent_light_max"]:
|
|
70
|
+
continue
|
|
71
|
+
if not any(min(abs(hue - prior), 360 - abs(hue - prior)) <= OFFICE_RULES["accent_hue_family_deg"] for prior in hues):
|
|
72
|
+
hues.append(hue)
|
|
73
|
+
if len(hues) >= OFFICE_RULES["accent_medium_count"]:
|
|
74
|
+
level = "HIGH" if len(hues) >= OFFICE_RULES["accent_high_count"] else "MEDIUM"
|
|
75
|
+
issues.append(f"OF-202 {level} accent families={len(hues)}")
|
|
76
|
+
|
|
77
|
+
for text in parsed.texts:
|
|
78
|
+
label = text.label.strip()
|
|
79
|
+
latin = sum(character.isascii() and character.isalnum() for character in label)
|
|
80
|
+
if label and latin / len(label) >= OFFICE_RULES["latin_share"] and label != label.upper():
|
|
81
|
+
words = re.findall(r"[A-Za-z]{4,}", label)
|
|
82
|
+
capitals = sum(bool(re.fullmatch(r"[A-Z][a-z]+", word)) for word in words)
|
|
83
|
+
function_word = re.search(r"\b(?:Of|And|The|To|For|In)\b", label)
|
|
84
|
+
if len(words) >= 2 and capitals / len(words) > OFFICE_RULES["title_case_share"] and function_word:
|
|
85
|
+
issues.append(f"OF-203 MEDIUM title-case label={label}")
|
|
86
|
+
if text.edge_for:
|
|
87
|
+
continue # Edge labels have no stable inner box in this parser.
|
|
88
|
+
box = next((boundary.rect for boundary in parsed.boundaries if boundary.name == text.boundary_for), None) if text.boundary_for else None
|
|
89
|
+
if box is None and text.role == "node":
|
|
90
|
+
box = next((node.rect for node in nodes if node.rect.x <= text.anchor[0] <= node.rect.right and
|
|
91
|
+
node.rect.y <= text.anchor[1] <= node.rect.bottom), None)
|
|
92
|
+
if box is not None and text.bounds.w > box.w - OFFICE_RULES["label_inset_px"]:
|
|
93
|
+
issues.append(f"OF-204 label {label} width={text.bounds.w:.1f}px exceeds {box.w - OFFICE_RULES['label_inset_px']:.1f}px")
|
|
94
|
+
return issues
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _node_shapes(parsed: ParsedSvg) -> tuple[Shape, ...]:
|
|
98
|
+
return tuple(shape for shape in parsed.shapes if shape.attributes.get("data-node-id"))
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _node_by_id(nodes: tuple[Shape, ...], name: str) -> Shape | None:
|
|
102
|
+
return next((node for node in nodes if node.attributes.get("data-node-id") == name), None)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _boundary_label(text: TextBox, outlines: tuple[Boundary, ...]) -> bool:
|
|
106
|
+
return text.role == "boundary" or bool(text.boundary_for) or any(text.label in boundary.labels or text.boundary_for == boundary.name for boundary in outlines)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _boundary_edges(boundary: Boundary) -> tuple[tuple[float, float, float, float], ...]:
|
|
110
|
+
rect = boundary.rect
|
|
111
|
+
return ((rect.x, rect.y, rect.right, rect.y), (rect.right, rect.y, rect.right, rect.bottom), (rect.right, rect.bottom, rect.x, rect.bottom), (rect.x, rect.bottom, rect.x, rect.y))
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _marker_id(value: str) -> str:
|
|
115
|
+
match = re.search(r"url\(\s*['\"]?#([^\s)'\"`]+)['\"]?\s*\)", value, re.IGNORECASE)
|
|
116
|
+
return match.group(1) if match else ""
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _aligned_route(route: Route, nodes: tuple[Shape, ...]) -> str:
|
|
120
|
+
origin, target = _node_by_id(nodes, route.from_id), _node_by_id(nodes, route.to_id)
|
|
121
|
+
if origin is None or target is None or not route.segments:
|
|
122
|
+
return ""
|
|
123
|
+
same_row = abs(origin.rect.y + origin.rect.h / 2 - target.rect.y - target.rect.h / 2) <= 1
|
|
124
|
+
same_column = abs(origin.rect.x + origin.rect.w / 2 - target.rect.x - target.rect.w / 2) <= 1
|
|
125
|
+
horizontal = all(abs(y1 - y2) <= 1 for _, y1, _, y2 in route.segments)
|
|
126
|
+
vertical = all(abs(x1 - x2) <= 1 for x1, _, x2, _ in route.segments)
|
|
127
|
+
origin_x, target_x = origin.rect.x + origin.rect.w / 2, target.rect.x + target.rect.w / 2
|
|
128
|
+
origin_y, target_y = origin.rect.y + origin.rect.h / 2, target.rect.y + target.rect.h / 2
|
|
129
|
+
row_clear = all(
|
|
130
|
+
node is origin or node is target
|
|
131
|
+
or abs(node.rect.y + node.rect.h / 2 - origin_y) > 1
|
|
132
|
+
or node.rect.x + node.rect.w / 2 <= min(origin_x, target_x)
|
|
133
|
+
or node.rect.x + node.rect.w / 2 >= max(origin_x, target_x)
|
|
134
|
+
for node in nodes
|
|
135
|
+
)
|
|
136
|
+
column_clear = all(
|
|
137
|
+
node is origin or node is target
|
|
138
|
+
or abs(node.rect.x + node.rect.w / 2 - origin_x) > 1
|
|
139
|
+
or node.rect.y + node.rect.h / 2 <= min(origin_y, target_y)
|
|
140
|
+
or node.rect.y + node.rect.h / 2 >= max(origin_y, target_y)
|
|
141
|
+
for node in nodes
|
|
142
|
+
)
|
|
143
|
+
if same_row and row_clear and not horizontal:
|
|
144
|
+
return "same-row"
|
|
145
|
+
if same_column and column_clear and not vertical:
|
|
146
|
+
return "same-column"
|
|
147
|
+
return ""
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _decision_issues(nodes: tuple[Shape, ...], routes: tuple[Route, ...]) -> list[str]:
|
|
151
|
+
issues: list[str] = []
|
|
152
|
+
for node in nodes:
|
|
153
|
+
if node.attributes.get("data-node-type") != "decision":
|
|
154
|
+
continue
|
|
155
|
+
name = node.attributes["data-node-id"]
|
|
156
|
+
outcomes = [value.strip() for value in node.attributes.get("data-outcomes", "").split("|") if value.strip()]
|
|
157
|
+
if len(outcomes) < 2:
|
|
158
|
+
issues.append(f"DECISION_OUTCOMES_MISSING {name} must name at least two outcomes")
|
|
159
|
+
continue
|
|
160
|
+
labels = {route.label for route in routes if route.from_id == name and route.label}
|
|
161
|
+
issues.extend(f"DECISION_OUTCOME_MISSING {name} {outcome}" for outcome in outcomes if outcome not in labels)
|
|
162
|
+
issues.extend(f"DECISION_OUTCOME_UNDECLARED {name} {label}" for label in labels if label not in outcomes)
|
|
163
|
+
return issues
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _route_issues(parsed: ParsedSvg, metrics: DiagramMetrics, nodes: tuple[Shape, ...]) -> list[str]:
|
|
167
|
+
issues: list[str] = []
|
|
168
|
+
sequence = bool(re.search(r"\bdata-type\s*=\s*['\"]sequence(?:-oauth)?['\"]", parsed.source, re.IGNORECASE))
|
|
169
|
+
for text in parsed.texts:
|
|
170
|
+
if text.role != "edge" and not text.edge_for:
|
|
171
|
+
continue
|
|
172
|
+
matches = [route for route in parsed.routes if f"{route.from_id}|{route.to_id}" == text.edge_for and (not route.label or route.label == text.label)]
|
|
173
|
+
if not text.edge_for or len(matches) != 1:
|
|
174
|
+
issues.append(f"EDGE_LABEL_UNBOUND {text.edge_for or text.label}")
|
|
175
|
+
continue
|
|
176
|
+
route = matches[0]
|
|
177
|
+
own = route_distance(text.anchor, route)
|
|
178
|
+
other = min((route_distance(text.anchor, item) for item in parsed.routes if item is not route), default=math.inf)
|
|
179
|
+
if own > 24:
|
|
180
|
+
issues.append(f"EDGE_LABEL_TOO_FAR {text.label} own={own:.1f}px max=24px")
|
|
181
|
+
if math.isfinite(other) and own >= other:
|
|
182
|
+
issues.append(f"EDGE_LABEL_NEAREST_PATH {text.label} own={own:.1f}px other={other:.1f}px")
|
|
183
|
+
for node in nodes:
|
|
184
|
+
if text_overlaps_node(text, node.rect):
|
|
185
|
+
issues.append(f"EDGE_LABEL_NODE_COLLISION {text.label} overlaps {node.attributes['data-node-id']}")
|
|
186
|
+
for route in parsed.routes:
|
|
187
|
+
distance = route_manhattan_distance(route)
|
|
188
|
+
ratio = route_length(route) / distance if distance else 1
|
|
189
|
+
if ratio > 1.4:
|
|
190
|
+
issues.append(f"ROUTE_DETOUR {route.from_id}->{route.to_id} ratio={ratio:.2f} max=1.40")
|
|
191
|
+
aligned = _aligned_route(route, nodes)
|
|
192
|
+
if aligned:
|
|
193
|
+
issues.append(f"ROUTE_ALIGNED_PATH {route.from_id}->{route.to_id} {aligned} neighbors require one straight segment")
|
|
194
|
+
crosses = any(route_crosses_boundary(route, boundary) for boundary in parsed.boundaries)
|
|
195
|
+
limit = 3 if crosses else 2
|
|
196
|
+
bends = route_bends(route)
|
|
197
|
+
if bends > limit:
|
|
198
|
+
issues.append(f"ROUTE_BENDS {route.from_id}->{route.to_id} bends={bends} max={limit}")
|
|
199
|
+
if route.marker_start and route.marker_end:
|
|
200
|
+
issues.append(f"ROUTE_OPPOSED_HEADS {route.from_id}->{route.to_id}")
|
|
201
|
+
for text in parsed.texts:
|
|
202
|
+
if any(segment_hits_text(segment, text) for segment in route.segments):
|
|
203
|
+
issues.append(f"EDGE_TEXT_COLLISION {route.from_id}->{route.to_id}: {text.label}")
|
|
204
|
+
break
|
|
205
|
+
for node in nodes:
|
|
206
|
+
if node.attributes.get("data-node-id") not in {route.from_id, route.to_id} and any(segment_hits_rect(segment, node.rect, 8) for segment in route.segments):
|
|
207
|
+
issues.append(f"ROUTE_THROUGH_NODE {route.from_id}->{route.to_id} via {node.attributes['data-node-id']}")
|
|
208
|
+
if any(segment_along_boundary(segment, boundary) for boundary in parsed.boundaries for segment in route.segments):
|
|
209
|
+
issues.append(f"ROUTE_ALONG_BOUNDARY {route.from_id}->{route.to_id}")
|
|
210
|
+
target = _node_by_id(nodes, route.to_id)
|
|
211
|
+
if route.marker_end:
|
|
212
|
+
marker = parsed.markers.get(_marker_id(route.marker_end))
|
|
213
|
+
minimum = max(5 * route.stroke_width, (metrics["viewBox"].w if metrics["viewBox"] else 1080) * 12 / 1080)
|
|
214
|
+
if marker is None:
|
|
215
|
+
issues.append(f"ARROWHEAD_GEOMETRY_MISSING {route.from_id}->{route.to_id} end")
|
|
216
|
+
elif marker_length(marker) * marker_scale(marker, route) + 0.01 < minimum:
|
|
217
|
+
issues.append(f"ARROWHEAD_SIZE_FLOOR {route.from_id}->{route.to_id} end min={minimum:.1f}px")
|
|
218
|
+
if marker is not None and target is not None and not sequence:
|
|
219
|
+
tip = marker_tip(route, marker, "end")
|
|
220
|
+
if tip is not None and distance_to_rect_boundary(tip, target.rect) > 2:
|
|
221
|
+
issues.append(f"ARROWHEAD_TIP_MISSED_TARGET {route.from_id}->{route.to_id}")
|
|
222
|
+
if marker is not None and target is not None and marker_overlaps_rect(route, target.rect, marker):
|
|
223
|
+
issues.append(f"ARROWHEAD_TARGET_OVERLAP {route.from_id}->{route.to_id} marker intersects target {route.to_id}")
|
|
224
|
+
for index, first in enumerate(parsed.routes):
|
|
225
|
+
for second in parsed.routes[index + 1:]:
|
|
226
|
+
if routes_overlap_collinearly(first, second):
|
|
227
|
+
issues.append(f"ROUTE_COLLINEAR_OVERLAP {first.from_id}->{first.to_id} / {second.from_id}->{second.to_id} shared within 12px")
|
|
228
|
+
for x, y in route_crossings(first, second):
|
|
229
|
+
issues.append(f"ROUTE_CROSSING {first.from_id}->{first.to_id} / {second.from_id}->{second.to_id} at {x:.1f},{y:.1f}")
|
|
230
|
+
return issues
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def quality_issues(source: str, *, enforce_canvas: bool = False, scale: float | None = None) -> list[str]:
|
|
234
|
+
"""Return stable issue codes for geometry, readability, and accessibility defects."""
|
|
235
|
+
parsed = parse_svg(source)
|
|
236
|
+
metrics = source_metrics(source)
|
|
237
|
+
nodes = _node_shapes(parsed)
|
|
238
|
+
issues: list[str] = []
|
|
239
|
+
contaminated = list(dict.fromkeys(text.label for text in parsed.texts if any(rule.search(text.label) for rule in _CONTAMINATION)))
|
|
240
|
+
if contaminated:
|
|
241
|
+
issues.append("PROCESS_TEXT " + " | ".join(contaminated))
|
|
242
|
+
if not metrics["hasSvg"]:
|
|
243
|
+
issues.append("SVG_MISSING")
|
|
244
|
+
return issues
|
|
245
|
+
for key, code in (("title", "SVG_TITLE_MISSING"), ("desc", "SVG_DESC_MISSING"), ("roleImage", "SVG_ROLE_IMAGE_MISSING"), ("ariaLinked", "SVG_ARIA_LINK_MISSING")):
|
|
246
|
+
if not metrics[key]:
|
|
247
|
+
issues.append(code)
|
|
248
|
+
if metrics["offCanvas"]:
|
|
249
|
+
issues.append("OFF_CANVAS " + " | ".join(metrics["offCanvasLabels"]))
|
|
250
|
+
if metrics["textOverlaps"]:
|
|
251
|
+
issues.append(f"TEXT_OVERLAP count={metrics['textOverlaps']}")
|
|
252
|
+
if metrics["objectOverlaps"]:
|
|
253
|
+
issues.append(f"OBJECT_OVERLAP count={metrics['objectOverlaps']}")
|
|
254
|
+
for failure in metrics["contrastFailures"]:
|
|
255
|
+
issues.append(f"CONTRAST_FLOOR {failure['text']}={failure['ratio']}:1")
|
|
256
|
+
issues.extend(_route_issues(parsed, metrics, nodes))
|
|
257
|
+
issues.extend(_decision_issues(nodes, parsed.routes))
|
|
258
|
+
issues.extend(sequence_issues(source, parsed, nodes))
|
|
259
|
+
for text in parsed.texts:
|
|
260
|
+
for boundary in parsed.boundaries:
|
|
261
|
+
if any(segment_hits_text(edge, text, boundary.stroke_width / 2 + 0.5) for edge in _boundary_edges(boundary)):
|
|
262
|
+
issues.append(f"OUTLINE_TEXT_COLLISION {text.label} crosses {boundary.name}")
|
|
263
|
+
break
|
|
264
|
+
boundary_failures = {failure["text"] for failure in metrics["contrastFailures"]}
|
|
265
|
+
for text in parsed.texts:
|
|
266
|
+
if _boundary_label(text, parsed.boundaries) and text.label in boundary_failures:
|
|
267
|
+
issues.append(f"BOUNDARY_CONTRAST_FLOOR {text.label} below 4.5:1")
|
|
268
|
+
for index, first in enumerate(nodes):
|
|
269
|
+
for second in nodes[index + 1:]:
|
|
270
|
+
if rectangle_gap(first.rect, second.rect) < 12:
|
|
271
|
+
issues.append(f"NODE_GAP {first.attributes['data-node-id']}/{second.attributes['data-node-id']} below 12px")
|
|
272
|
+
issues.extend(_office_craft_issues(parsed, nodes))
|
|
273
|
+
if enforce_canvas:
|
|
274
|
+
ratio = scale if scale is not None else min(metrics["viewBox"].w / 1080, metrics["viewBox"].h / 640) if metrics["viewBox"] else 1
|
|
275
|
+
if not any(text.role == "title" for text in parsed.texts):
|
|
276
|
+
issues.append('TITLE_ROLE_MISSING visible title must use data-role="title"')
|
|
277
|
+
for text in parsed.texts:
|
|
278
|
+
boundary_text = _boundary_label(text, parsed.boundaries)
|
|
279
|
+
minimum = 28 if text.role == "title" else 13 if text.role == "edge" or boundary_text else 15 if text.role == "node" else 0
|
|
280
|
+
if minimum and text.font_size + 0.01 < minimum * ratio:
|
|
281
|
+
detail = f"{text.role or 'boundary'}:{text.label}={text.font_size}px (min {minimum * ratio:.1f}px)"
|
|
282
|
+
issues.append(f"BOUNDARY_FONT_FLOOR {detail}" if boundary_text else "FONT_FLOOR " + detail)
|
|
283
|
+
points = [(node.rect.x, node.rect.y) for node in nodes] + [(node.rect.right, node.rect.bottom) for node in nodes]
|
|
284
|
+
points.extend(point for route in parsed.routes for segment in route.segments for point in ((segment[0], segment[1]), (segment[2], segment[3])))
|
|
285
|
+
if nodes and metrics["viewBox"] and points:
|
|
286
|
+
left, top = min(point[0] for point in points), min(point[1] for point in points)
|
|
287
|
+
right, bottom = max(point[0] for point in points), max(point[1] for point in points)
|
|
288
|
+
width, height = (right - left) / (metrics["viewBox"].w * 0.90), (bottom - top) / (metrics["viewBox"].h * 0.74)
|
|
289
|
+
if width < 0.75 or height < 0.54:
|
|
290
|
+
issues.append(f"CONTENT_FILL safe-area-width={width:.3f} safe-area-height={height:.3f} minimum=.75/.54")
|
|
291
|
+
if not nodes:
|
|
292
|
+
issues.append("CONTENT_NODES_MISSING")
|
|
293
|
+
return list(dict.fromkeys(issues))
|