archsight 0.2.3 → 0.2.5
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.
- checksums.yaml +4 -4
- data/lib/archsight/analysis/sandbox.rb +1 -1
- data/lib/archsight/cli.rb +67 -0
- data/lib/archsight/graph.rb +10 -0
- data/lib/archsight/import/executor.rb +11 -3
- data/lib/archsight/import/handlers/cpp_grapher.rb +193 -0
- data/lib/archsight/import/handlers/crystal_grapher.rb +186 -0
- data/lib/archsight/import/handlers/elixir_grapher.rb +202 -0
- data/lib/archsight/import/handlers/go_grapher.rb +127 -0
- data/lib/archsight/import/handlers/grapher.rb +552 -0
- data/lib/archsight/import/handlers/java_grapher.rb +286 -0
- data/lib/archsight/import/handlers/javascript_grapher.rb +340 -0
- data/lib/archsight/import/handlers/python_grapher.rb +270 -0
- data/lib/archsight/import/handlers/repository.rb +41 -17
- data/lib/archsight/import/handlers/ruby_grapher.rb +203 -0
- data/lib/archsight/import/handlers/rust_grapher.rb +227 -0
- data/lib/archsight/import/registry.rb +23 -0
- data/lib/archsight/resources/import.rb +1 -0
- data/lib/archsight/resources/technology_artifact.rb +18 -1
- data/lib/archsight/version.rb +1 -1
- data/lib/archsight/web/api/json_helpers.rb +1 -1
- data/lib/archsight/web/public/vue/ApiDocsPage-C0y953v0.css +1 -0
- data/lib/archsight/web/public/vue/ApiDocsPage-DHSCaHEn.js +1 -0
- data/lib/archsight/web/public/vue/DocPage-DszOPlFy.js +1 -0
- data/lib/archsight/web/public/vue/EditorPage-CPZ0Ei4l.css +1 -0
- data/lib/archsight/web/public/vue/EditorPage-DsiuZ7fg.js +35 -0
- data/lib/archsight/web/public/vue/ErrorPage-C4JutrYc.js +2 -0
- data/lib/archsight/web/public/vue/ErrorPage-uMDnfY5_.css +1 -0
- data/lib/archsight/web/public/vue/GraphView-Bqlbt6dK.js +1 -0
- data/lib/archsight/web/public/vue/GraphView-Cj2V2stN.css +1 -0
- data/lib/archsight/web/public/vue/InstanceRouter-D8SEY2eu.js +2 -0
- data/lib/archsight/web/public/vue/InstanceRouter-D9hclKFt.css +1 -0
- data/lib/archsight/web/public/vue/KindList-CPDaNron.js +1 -0
- data/lib/archsight/web/public/vue/ResourceList-B5w9yiyS.js +1 -0
- data/lib/archsight/web/public/vue/ResourceList-DxZfNbOg.css +1 -0
- data/lib/archsight/web/public/vue/SearchResults-DSHpVO-c.css +1 -0
- data/lib/archsight/web/public/vue/SearchResults-FpkhdBFu.js +1 -0
- data/lib/archsight/web/public/vue/architecture-7EHR7CIX-DpNNjAIc.js +1 -0
- data/lib/archsight/web/public/vue/eventmodeling-FCH6USID-CiThxoWl.js +1 -0
- data/lib/archsight/web/public/vue/gitGraph-WXDBUCRP-BODMGpAm.js +1 -0
- data/lib/archsight/web/public/vue/graphviz-09t3o0af.js +13 -0
- data/lib/archsight/web/public/vue/index-BW0IzY6X.css +1 -0
- data/lib/archsight/web/public/vue/index-T1YqCmM1.js +2 -0
- data/lib/archsight/web/public/vue/info-J43DQDTF-fLq04sri.js +1 -0
- data/lib/archsight/web/public/vue/katex-5qHlIbPR.js +261 -0
- data/lib/archsight/web/public/vue/mermaid-DYyHQk7x.js +3093 -0
- data/lib/archsight/web/public/vue/packet-YPE3B663-DoY1fbqu.js +1 -0
- data/lib/archsight/web/public/vue/pie-LRSECV5Y-C7ZQVwRe.js +1 -0
- data/lib/archsight/web/public/vue/radar-GUYGQ44K-CRtY5oqf.js +1 -0
- data/lib/archsight/web/public/vue/rolldown-runtime-QTnfLwEv.js +1 -0
- data/lib/archsight/web/public/vue/treeView-BLDUP644-Csx2WLLh.js +1 -0
- data/lib/archsight/web/public/vue/treemap-LRROVOQU-CfEnRbTx.js +1 -0
- data/lib/archsight/web/public/vue/{useGraphviz-BN4iwLLN.js → useGraphviz-EKSrE4q_.js} +5 -4
- data/lib/archsight/web/public/vue/useHighlight-BcVbGyrK.js +10 -0
- data/lib/archsight/web/public/vue/useMermaid-CIZxhy_r.js +2 -0
- data/lib/archsight/web/public/vue/usePanZoom-C2slpyY9.js +11 -0
- data/lib/archsight/web/public/vue/wardley-L42UT6IY-97oUvxhz.js +1 -0
- data/lib/archsight/web/public/vue.html +4 -3
- metadata +51 -72
- data/lib/archsight/web/public/vue/ApiDocsPage-Cwn04X61.js +0 -1
- data/lib/archsight/web/public/vue/ApiDocsPage-DhNTOH4o.css +0 -1
- data/lib/archsight/web/public/vue/DocPage-Y83PCbYi.js +0 -1
- data/lib/archsight/web/public/vue/EditorPage-Dq0MuTnp.css +0 -1
- data/lib/archsight/web/public/vue/EditorPage-DqRMOBE6.js +0 -34
- data/lib/archsight/web/public/vue/ErrorPage-CwPT3JUr.css +0 -1
- data/lib/archsight/web/public/vue/ErrorPage-D0lKMCXA.js +0 -2
- data/lib/archsight/web/public/vue/GraphView-Byq-Nfd9.js +0 -1
- data/lib/archsight/web/public/vue/GraphView-DRcIqAiR.css +0 -1
- data/lib/archsight/web/public/vue/InstanceRouter-B3Q2fH0X.js +0 -2
- data/lib/archsight/web/public/vue/InstanceRouter-BJkDRXZY.css +0 -1
- data/lib/archsight/web/public/vue/KindList-DlDrvJDd.js +0 -1
- data/lib/archsight/web/public/vue/ResourceList-DP-z-j71.css +0 -1
- data/lib/archsight/web/public/vue/ResourceList-DwsfI85-.js +0 -1
- data/lib/archsight/web/public/vue/SearchResults-BGHbg48-.css +0 -1
- data/lib/archsight/web/public/vue/SearchResults-DlWGROho.js +0 -1
- data/lib/archsight/web/public/vue/_basePickBy-DXGWsL9H.js +0 -1
- data/lib/archsight/web/public/vue/_baseUniq-C8pAAASt.js +0 -1
- data/lib/archsight/web/public/vue/architectureDiagram-VXUJARFQ-Dg_wTk4u.js +0 -36
- data/lib/archsight/web/public/vue/blockDiagram-VD42YOAC-C8HXvtNT.js +0 -122
- data/lib/archsight/web/public/vue/c4Diagram-YG6GDRKO-QzXboDJ8.js +0 -10
- data/lib/archsight/web/public/vue/chunk-4BX2VUAB-DSPzEX5F.js +0 -1
- data/lib/archsight/web/public/vue/chunk-55IACEB6-Dd5Z8Bov.js +0 -1
- data/lib/archsight/web/public/vue/chunk-B4BG7PRW-B_hXD1nI.js +0 -165
- data/lib/archsight/web/public/vue/chunk-DI55MBZ5-C-2DUMJY.js +0 -220
- data/lib/archsight/web/public/vue/chunk-FMBD7UC4-BlBtfKnL.js +0 -15
- data/lib/archsight/web/public/vue/chunk-QN33PNHL-Db3REDIz.js +0 -1
- data/lib/archsight/web/public/vue/chunk-QZHKN3VN-BqVqGMTy.js +0 -1
- data/lib/archsight/web/public/vue/chunk-TZMSLE5B-DfX4VDWu.js +0 -1
- data/lib/archsight/web/public/vue/classDiagram-2ON5EDUG-C9Kk58xl.js +0 -1
- data/lib/archsight/web/public/vue/classDiagram-v2-WZHVMYZB-C9Kk58xl.js +0 -1
- data/lib/archsight/web/public/vue/clone-B6uzD5eH.js +0 -1
- data/lib/archsight/web/public/vue/cose-bilkent-S5V4N54A-CfkQxn-a.js +0 -1
- data/lib/archsight/web/public/vue/cytoscape.esm-5J0xJHOV.js +0 -321
- data/lib/archsight/web/public/vue/dagre-6UL2VRFP-D13da1qu.js +0 -4
- data/lib/archsight/web/public/vue/diagram-PSM6KHXK-BwzbeHPK.js +0 -24
- data/lib/archsight/web/public/vue/diagram-QEK2KX5R-COjSoDC8.js +0 -43
- data/lib/archsight/web/public/vue/diagram-S2PKOQOG-FH65FafS.js +0 -24
- data/lib/archsight/web/public/vue/erDiagram-Q2GNP2WA-D1mxJWSp.js +0 -60
- data/lib/archsight/web/public/vue/flowDiagram-NV44I4VS-DpRd5cPP.js +0 -162
- data/lib/archsight/web/public/vue/ganttDiagram-JELNMOA3-D04Sdd3Q.js +0 -267
- data/lib/archsight/web/public/vue/gitGraphDiagram-V2S2FVAM-DgNNP2nj.js +0 -65
- data/lib/archsight/web/public/vue/graph-Cnoy0p_X.js +0 -1
- data/lib/archsight/web/public/vue/graphviz-CJms5bxZ.js +0 -13
- data/lib/archsight/web/public/vue/index-Tiu4C-Sb.css +0 -1
- data/lib/archsight/web/public/vue/index-Zr9MoxJi.js +0 -2
- data/lib/archsight/web/public/vue/infoDiagram-HS3SLOUP-D5asL_9P.js +0 -2
- data/lib/archsight/web/public/vue/journeyDiagram-XKPGCS4Q-D-SRalYk.js +0 -139
- data/lib/archsight/web/public/vue/kanban-definition-3W4ZIXB7-CuOjHa3p.js +0 -89
- data/lib/archsight/web/public/vue/katex-C-M49wc6.js +0 -261
- data/lib/archsight/web/public/vue/layout-CD8FBujT.js +0 -1
- data/lib/archsight/web/public/vue/mermaid-DUllW9QE.js +0 -250
- data/lib/archsight/web/public/vue/mindmap-definition-VGOIOE7T-BfbYXGBk.js +0 -68
- data/lib/archsight/web/public/vue/pieDiagram-ADFJNKIX-mb757Gpq.js +0 -30
- data/lib/archsight/web/public/vue/quadrantDiagram-AYHSOK5B-DMtvHJQW.js +0 -7
- data/lib/archsight/web/public/vue/requirementDiagram-UZGBJVZJ-CHguirsB.js +0 -64
- data/lib/archsight/web/public/vue/sankeyDiagram-TZEHDZUN-nblWMNF6.js +0 -10
- data/lib/archsight/web/public/vue/sequenceDiagram-WL72ISMW-B83ZoXls.js +0 -145
- data/lib/archsight/web/public/vue/stateDiagram-FKZM4ZOC-Ct0OgmPh.js +0 -1
- data/lib/archsight/web/public/vue/stateDiagram-v2-4FDKWEC3-CJZXQ6xd.js +0 -1
- data/lib/archsight/web/public/vue/timeline-definition-IT6M3QCI-D1Wd-DLb.js +0 -61
- data/lib/archsight/web/public/vue/treemap-GDKQZRPO-DFPZrNlp.js +0 -162
- data/lib/archsight/web/public/vue/useHighlight-DmGaxZxx.js +0 -10
- data/lib/archsight/web/public/vue/useMermaid-DSo5f1Jc.js +0 -1
- data/lib/archsight/web/public/vue/usePanZoom-BEXq_r0S.js +0 -11
- data/lib/archsight/web/public/vue/xychartDiagram-PRI3JC2R-i_eB4HAQ.js +0 -7
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "grapher"
|
|
4
|
+
require_relative "../registry"
|
|
5
|
+
|
|
6
|
+
# RustGrapher — analyses a Rust repository and generates a GraphViz DOT
|
|
7
|
+
# graph of its crate/module structure, stored as architecture/rust/modules
|
|
8
|
+
# on the TechnologyArtifact.
|
|
9
|
+
#
|
|
10
|
+
# Supports single-crate projects (Cargo.toml at root) and Cargo workspaces
|
|
11
|
+
# (root Cargo.toml with [workspace] members). Uses pure static regex analysis
|
|
12
|
+
# of `use crate::` and `use <workspace_member>::` statements — no Rust
|
|
13
|
+
# toolchain required.
|
|
14
|
+
#
|
|
15
|
+
# File-to-package mapping follows Rust conventions:
|
|
16
|
+
# src/lib.rs, src/main.rs → crate root package
|
|
17
|
+
# src/foo.rs → crate/foo
|
|
18
|
+
# src/foo/mod.rs → crate/foo (directory module)
|
|
19
|
+
# src/foo/bar.rs → crate/foo/bar → capped to crate/foo
|
|
20
|
+
#
|
|
21
|
+
# Configuration:
|
|
22
|
+
# import/config/path - Path to the Rust repository root
|
|
23
|
+
# import/config/ranksep - Horizontal gap between rank columns (default: 0.6)
|
|
24
|
+
# import/config/nodesep - Vertical gap between nodes in a column (default: 0.15)
|
|
25
|
+
class Archsight::Import::Handlers::RustGrapher < Archsight::Import::Handlers::Grapher
|
|
26
|
+
def self.language_name = "rust"
|
|
27
|
+
|
|
28
|
+
def self.applicable?(path)
|
|
29
|
+
File.exist?(File.join(path, "Cargo.toml")) ||
|
|
30
|
+
Dir.glob(File.join(path, "*/Cargo.toml")).any?
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def wrap_single_module?
|
|
34
|
+
true
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
SKIP_DIRS = %w[target .git tests test benches examples].freeze
|
|
38
|
+
|
|
39
|
+
# MAX_PKG_DEPTH = 2: crate/feature is the natural Rust module depth.
|
|
40
|
+
MAX_PKG_DEPTH = 2
|
|
41
|
+
|
|
42
|
+
# use crate::foo::bar::Baz — capture everything after crate::
|
|
43
|
+
USE_CRATE_RE = /^\s*use\s+crate::((?:\w+::)*\w+)/
|
|
44
|
+
|
|
45
|
+
# use some_crate::path — capture full path for workspace dep resolution
|
|
46
|
+
USE_PATH_RE = /^\s*use\s+((?:\w+::)*\w+)/
|
|
47
|
+
|
|
48
|
+
# Identifiers that are never workspace crate names
|
|
49
|
+
BUILTIN_PREFIXES = %w[crate super self].freeze
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
# ── Module discovery ─────────────────────────────────────────────────────
|
|
54
|
+
|
|
55
|
+
def discover_modules(repo_root)
|
|
56
|
+
return workspace_discover(repo_root) if workspace?(repo_root)
|
|
57
|
+
|
|
58
|
+
mod_name = parse_crate_name(File.join(repo_root, "Cargo.toml")) ||
|
|
59
|
+
File.basename(repo_root)
|
|
60
|
+
[[".", mod_name]]
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def workspace?(repo_root)
|
|
64
|
+
content = File.read(File.join(repo_root, "Cargo.toml"), encoding: "utf-8")
|
|
65
|
+
content.match?(/^\[workspace\]/)
|
|
66
|
+
rescue StandardError
|
|
67
|
+
false
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def workspace_discover(repo_root)
|
|
71
|
+
members = workspace_members(repo_root)
|
|
72
|
+
|
|
73
|
+
if members.empty?
|
|
74
|
+
# Workspace with no members — fall back to treating root as a single crate
|
|
75
|
+
mod_name = parse_crate_name(File.join(repo_root, "Cargo.toml")) ||
|
|
76
|
+
File.basename(repo_root)
|
|
77
|
+
return [[".", mod_name]]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
members.filter_map do |rel_dir|
|
|
81
|
+
mod_name = parse_crate_name(File.join(repo_root, rel_dir, "Cargo.toml")) ||
|
|
82
|
+
File.basename(rel_dir)
|
|
83
|
+
[rel_dir, mod_name]
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def workspace_members(repo_root)
|
|
88
|
+
content = File.read(File.join(repo_root, "Cargo.toml"), encoding: "utf-8")
|
|
89
|
+
ws_section = content[/^\[workspace\].*?(?=^\[|\z)/m] || ""
|
|
90
|
+
members_block = ws_section[/\bmembers\s*=\s*\[(.*?)\]/m, 1] || ""
|
|
91
|
+
raw = members_block.scan(/"([^"]+)"/).flatten
|
|
92
|
+
|
|
93
|
+
raw.flat_map { |pat| Dir.glob(File.join(repo_root, pat)) }
|
|
94
|
+
.map { |d| d.delete_prefix("#{repo_root}/") }
|
|
95
|
+
.reject { |rel| SKIP_DIRS.any? { |d| rel.split("/").include?(d) } }
|
|
96
|
+
.select { |rel| File.exist?(File.join(repo_root, rel, "Cargo.toml")) }
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def parse_crate_name(cargo_toml_path)
|
|
100
|
+
content = File.read(cargo_toml_path, encoding: "utf-8")
|
|
101
|
+
# Extract from [package] only — avoid matching workspace.package or other sections
|
|
102
|
+
pkg_section = content[/^\[package\].*?(?=^\[|\z)/m]
|
|
103
|
+
pkg_section&.match(/^name\s*=\s*"([^"]+)"/)&.[](1)
|
|
104
|
+
rescue StandardError
|
|
105
|
+
nil
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# ── Package collection ────────────────────────────────────────────────────
|
|
109
|
+
|
|
110
|
+
def collect_packages(repo_root, modules, _prefix)
|
|
111
|
+
# Rust normalises Cargo.toml hyphens to underscores in `use` statements
|
|
112
|
+
known_crates = modules.each_with_object({}) do |(_, mod_name), h|
|
|
113
|
+
h[mod_name.gsub("-", "_")] = mod_name
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
all_pkgs = {}
|
|
117
|
+
|
|
118
|
+
modules.each do |rel_dir, mod_name|
|
|
119
|
+
mod_dir = rel_dir == "." ? repo_root : File.join(repo_root, rel_dir)
|
|
120
|
+
src_dir = Dir.exist?(File.join(mod_dir, "src")) ? File.join(mod_dir, "src") : mod_dir
|
|
121
|
+
scan_src_dir(src_dir, mod_name, known_crates, all_pkgs)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
pkg_set = all_pkgs.keys.to_set
|
|
125
|
+
all_pkgs.each_value { |deps| deps.select! { |d| pkg_set.include?(d) } }
|
|
126
|
+
|
|
127
|
+
all_pkgs
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# ── Scanning ─────────────────────────────────────────────────────────────
|
|
131
|
+
|
|
132
|
+
def scan_src_dir(src_dir, mod_name, known_crates, all_pkgs)
|
|
133
|
+
safe_glob(File.join(src_dir, "**", "*.rs")).each do |rs_file|
|
|
134
|
+
rel = rs_file.delete_prefix("#{src_dir}/")
|
|
135
|
+
rel_parts = rel.split("/")
|
|
136
|
+
next if rel_parts.any? { |p| SKIP_DIRS.include?(p) }
|
|
137
|
+
|
|
138
|
+
pkg = cap_depth(file_to_pkg(rs_file, src_dir, mod_name), mod_name)
|
|
139
|
+
all_pkgs[pkg] ||= []
|
|
140
|
+
|
|
141
|
+
# lib.rs and main.rs are crate entry points — they wire binaries and
|
|
142
|
+
# re-export the public API but don't express module architecture.
|
|
143
|
+
# Extracting their deps would produce hub-spoke edges from the invisible
|
|
144
|
+
# root node to every child, which matches no other language grapher's style.
|
|
145
|
+
next if %w[lib.rs main.rs].include?(rel)
|
|
146
|
+
|
|
147
|
+
extract_deps(rs_file, mod_name, known_crates).each do |dep|
|
|
148
|
+
dep = cap_depth(dep, mod_name)
|
|
149
|
+
next if dep == pkg || all_pkgs[pkg].include?(dep)
|
|
150
|
+
|
|
151
|
+
all_pkgs[pkg] << dep
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# ── Dependency extraction ─────────────────────────────────────────────────
|
|
157
|
+
|
|
158
|
+
def extract_deps(rs_file, mod_name, known_crates)
|
|
159
|
+
content = File.read(rs_file, encoding: "utf-8")
|
|
160
|
+
deps = []
|
|
161
|
+
|
|
162
|
+
content.each_line do |line|
|
|
163
|
+
# Intra-crate: use crate::foo::bar::Baz
|
|
164
|
+
if (m = line.match(USE_CRATE_RE))
|
|
165
|
+
path = m[1].gsub("::", "/")
|
|
166
|
+
deps << "#{mod_name}/#{path}"
|
|
167
|
+
next
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Cross-crate workspace: use other_crate::path::to::Type
|
|
171
|
+
# Check workspace membership before any stdlib filtering so that
|
|
172
|
+
# workspace crates named "core" or "alloc" are resolved correctly.
|
|
173
|
+
next unless (m = line.match(USE_PATH_RE))
|
|
174
|
+
|
|
175
|
+
parts = m[1].split("::")
|
|
176
|
+
next if BUILTIN_PREFIXES.include?(parts.first)
|
|
177
|
+
|
|
178
|
+
target_mod = known_crates[parts.first]
|
|
179
|
+
next unless target_mod
|
|
180
|
+
|
|
181
|
+
# Cap the sub-path to MAX_PKG_DEPTH-1 levels so the dep lands on a
|
|
182
|
+
# real package rather than a type name (e.g. ::User → drop it).
|
|
183
|
+
if parts.length > 1
|
|
184
|
+
sub_parts = parts[1..].first(MAX_PKG_DEPTH - 1)
|
|
185
|
+
deps << "#{target_mod}/#{sub_parts.join("/")}"
|
|
186
|
+
else
|
|
187
|
+
deps << target_mod
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
deps.uniq
|
|
192
|
+
rescue Encoding::InvalidByteSequenceError, Encoding::UndefinedConversionError
|
|
193
|
+
[]
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# ── Package path helpers ──────────────────────────────────────────────────
|
|
197
|
+
|
|
198
|
+
# Maps a .rs file to its package path.
|
|
199
|
+
# src/lib.rs, src/main.rs → crate root (mod_name)
|
|
200
|
+
# src/foo/mod.rs → mod_name/foo
|
|
201
|
+
# src/foo.rs → mod_name/foo
|
|
202
|
+
# src/foo/bar.rs → mod_name/foo/bar (capped later)
|
|
203
|
+
def file_to_pkg(abs_path, src_dir, mod_name)
|
|
204
|
+
rel = abs_path.delete_prefix("#{src_dir}/").delete_suffix(".rs")
|
|
205
|
+
return mod_name if %w[lib main].include?(rel)
|
|
206
|
+
|
|
207
|
+
rel = rel.delete_suffix("/mod")
|
|
208
|
+
return mod_name if rel == mod_name
|
|
209
|
+
return rel if rel.start_with?("#{mod_name}/")
|
|
210
|
+
|
|
211
|
+
"#{mod_name}/#{rel}"
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def cap_depth(pkg, mod_name)
|
|
215
|
+
return pkg if pkg != mod_name && !pkg.start_with?("#{mod_name}/")
|
|
216
|
+
|
|
217
|
+
suffix = pkg.delete_prefix("#{mod_name}/")
|
|
218
|
+
return mod_name if suffix == pkg
|
|
219
|
+
|
|
220
|
+
parts = suffix.split("/")
|
|
221
|
+
return pkg if parts.length <= MAX_PKG_DEPTH - 1
|
|
222
|
+
|
|
223
|
+
"#{mod_name}/#{parts.first(MAX_PKG_DEPTH - 1).join("/")}"
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
Archsight::Import::Registry.register("rust-grapher", Archsight::Import::Handlers::RustGrapher)
|
|
@@ -37,6 +37,29 @@ module Archsight::Import::Registry
|
|
|
37
37
|
handler_class
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
+
# Return all grapher handler classes that can handle the given path.
|
|
41
|
+
# Each class opts in by implementing `self.applicable?(path)` returning true/false.
|
|
42
|
+
def handlers_for(path)
|
|
43
|
+
@handlers.values.select do |h|
|
|
44
|
+
h.respond_to?(:applicable?) && begin
|
|
45
|
+
h.applicable?(path)
|
|
46
|
+
rescue Errno::ELOOP, Errno::ENOTDIR
|
|
47
|
+
false
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Look up a grapher handler by its declared language name.
|
|
53
|
+
# Handler classes opt in by implementing `self.language_name`.
|
|
54
|
+
def handler_for_language(lang)
|
|
55
|
+
@handlers.values.find { |h| h.respond_to?(:language_name) && h.language_name == lang }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Reverse lookup: return the registered name for a handler class, or nil.
|
|
59
|
+
def name_for(klass)
|
|
60
|
+
@handlers.key(klass)
|
|
61
|
+
end
|
|
62
|
+
|
|
40
63
|
# List all registered handler names
|
|
41
64
|
# @return [Array<String>] Handler names
|
|
42
65
|
def handlers
|
|
@@ -179,7 +179,7 @@ class Archsight::Resources::TechnologyArtifact < Archsight::Resources::Base
|
|
|
179
179
|
description: "License category",
|
|
180
180
|
title: "License Category",
|
|
181
181
|
filter: :word,
|
|
182
|
-
enum: %w[permissive copyleft weak-copyleft proprietary unknown]
|
|
182
|
+
enum: %w[permissive copyleft weak-copyleft source-available proprietary unknown]
|
|
183
183
|
annotation "license/dependencies/count",
|
|
184
184
|
description: "Total dependencies",
|
|
185
185
|
title: "Dependencies",
|
|
@@ -220,6 +220,23 @@ class Archsight::Resources::TechnologyArtifact < Archsight::Resources::Base
|
|
|
220
220
|
description: "Total unique contributors",
|
|
221
221
|
type: Integer
|
|
222
222
|
|
|
223
|
+
annotation "architecture/modules",
|
|
224
|
+
description: "GraphViz DOT graph of module/package structure (legacy single-language key; superseded by architecture/<lang>/modules)",
|
|
225
|
+
title: "Module Structure",
|
|
226
|
+
sidebar: false
|
|
227
|
+
|
|
228
|
+
{
|
|
229
|
+
"go" => "Go", "python" => "Python", "java" => "Java",
|
|
230
|
+
"typescript" => "TypeScript", "javascript" => "JavaScript",
|
|
231
|
+
"rust" => "Rust", "ruby" => "Ruby", "crystal" => "Crystal",
|
|
232
|
+
"csharp" => "C#", "zig" => "Zig", "elixir" => "Elixir"
|
|
233
|
+
}.each do |lang, label|
|
|
234
|
+
annotation "architecture/#{lang}/modules",
|
|
235
|
+
description: "GraphViz DOT — #{label} module/package structure",
|
|
236
|
+
title: "#{label} Module Structure",
|
|
237
|
+
sidebar: false
|
|
238
|
+
end
|
|
239
|
+
|
|
223
240
|
relation :servedBy, :technologyComponents, :TechnologyArtifact
|
|
224
241
|
relation :suppliedBy, :technologyComponents, :TechnologyService
|
|
225
242
|
relation :maintainedBy, :businessActors, :BusinessActor
|
data/lib/archsight/version.rb
CHANGED
|
@@ -198,7 +198,7 @@ module Archsight::Web::API::JsonHelpers
|
|
|
198
198
|
when :heading
|
|
199
199
|
{ type: "heading", level: section[:level], text: section[:text] }
|
|
200
200
|
when :text
|
|
201
|
-
{ type: "text", content: section[:content] }
|
|
201
|
+
{ type: "text", content: markdown(section[:content]) }
|
|
202
202
|
when :message
|
|
203
203
|
{ type: "message", level: section[:level].to_s, message: section[:message] }
|
|
204
204
|
when :table
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.back-link[data-v-0e03906d]{z-index:100;background:#fff;border:1px solid #ddd;border-radius:4px;padding:6px 14px;font-size:.875rem;text-decoration:none;position:fixed;top:12px;right:12px;box-shadow:0 1px 4px #0000001a}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{B as e,N as t,S as n,T as r,U as i,g as a,j as o,t as s,w as c,y as l,z as u}from"./index-T1YqCmM1.js";var d={class:`api-docs-page`},f={key:0,class:`pico-color-red-500`},p=s({__name:`ApiDocsPage`,setup(s){let p=a(),m=u(null),h=u(null);function g(e){return new Promise((t,n)=>{if(window.Redoc)return t();let r=document.createElement(`script`);r.src=e,r.onload=t,r.onerror=()=>n(Error(`Failed to load ReDoc`)),document.head.appendChild(r)})}return o(async()=>{try{await g(`https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js`),window.Redoc.init(`/api/v1/openapi.yaml`,{},m.value)}catch(e){h.value=e.message}}),(a,o)=>(t(),r(`div`,d,[n(`a`,{href:`#`,class:`back-link`,onClick:o[0]||=l(t=>e(p).back(),[`prevent`])},`← Back`),h.value?(t(),r(`div`,f,i(h.value),1)):c(``,!0),n(`div`,{ref_key:`container`,ref:m},null,512)]))}},[[`__scopeId`,`data-v-0e03906d`]]);export{p as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{I as e,N as t,S as n,T as r,a as i,k as a,z as o}from"./index-T1YqCmM1.js";import{t as s}from"./useHighlight-BcVbGyrK.js";import{t as c}from"./useMermaid-CIZxhy_r.js";var l=[`innerHTML`],u={key:1},d={__name:`DocPage`,props:{filename:String},setup(d){let f=d,p=o(``),m=o(!0),h=o(null);async function g(){m.value=!0,p.value=await i(f.filename)||`<p>Documentation not found.</p>`,m.value=!1,await a(),h.value&&(s(h.value),c(h.value))}return e(()=>f.filename,g,{immediate:!0}),(e,i)=>m.value?(t(),r(`article`,u,[...i[0]||=[n(`p`,null,`Loading...`,-1)]])):(t(),r(`div`,{key:0,ref_key:`articleEl`,ref:h,innerHTML:p.value},null,8,l))}};export{d as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.field-group[data-v-6dfd17ff]{margin-bottom:0}.field-group label[data-v-6dfd17ff]{margin-bottom:.25rem}.field-group label .required[data-v-6dfd17ff]{color:var(--del-color);margin-left:.15rem}.field-group input[data-v-6dfd17ff],.field-group select[data-v-6dfd17ff],.field-group textarea[data-v-6dfd17ff]{margin-bottom:0}.field-description[data-v-6dfd17ff]{color:var(--muted-color);margin-top:.25rem;font-size:.8rem}.field-error[data-v-6dfd17ff]{color:var(--del-color);margin-top:.25rem;font-size:.8rem}.field-full-width[data-v-6dfd17ff]{grid-column:1/-1}.markdown-field textarea[data-v-6dfd17ff]{font-family:var(--font-family-monospace);resize:vertical;min-height:200px}.code-field textarea[data-v-6dfd17ff]{resize:vertical;tab-size:2;white-space:pre;overflow-wrap:normal;min-height:600px;font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;font-size:.9em;line-height:1.5;overflow-x:auto}.future-hint[data-v-6dfd17ff]{color:var(--muted-color);align-items:center;gap:.35rem;margin-top:.5rem;font-size:.8rem;display:flex}.label-row[data-v-6dfd17ff]{justify-content:space-between;align-items:center;margin-bottom:.25rem;display:flex}.label-row label[data-v-6dfd17ff]{margin-bottom:0}.btn-edit-markdown[data-v-6dfd17ff]{color:var(--muted-color);border:1px solid var(--muted-border-color);border-radius:var(--border-radius);cursor:pointer;background:0 0;align-items:center;gap:.35rem;padding:.25rem .5rem;font-size:.85rem;transition:all .15s;display:inline-flex}.btn-edit-markdown[data-v-6dfd17ff]:hover{color:var(--primary);border-color:var(--primary);background-color:#0000}.btn-edit-markdown i[data-v-6dfd17ff]{font-size:.9rem}@media (prefers-color-scheme:dark){.btn-edit-markdown[data-v-6dfd17ff]{color:#8b949e;border-color:#ffffff26}.btn-edit-markdown[data-v-6dfd17ff]:hover{color:#58a6ff;border-color:#58a6ff}}.relations-editor[data-v-e27dc1c0]{flex-direction:column;gap:.75rem;display:flex}#relations-list[data-v-e27dc1c0]{background-color:var(--background-color);border:1px solid var(--muted-border-color);border-radius:4px;flex-direction:column;display:flex}#relations-list[data-v-e27dc1c0]:empty{display:none}.relation-item[data-v-e27dc1c0]{border-bottom:1px solid var(--muted-border-color);justify-content:space-between;align-items:center;gap:.5rem;padding:.35rem .5rem;font-size:.9rem;display:flex}.relation-item[data-v-e27dc1c0]:last-child{border-bottom:none}.relation-text[data-v-e27dc1c0]{white-space:nowrap;text-overflow:ellipsis;flex:1;overflow:hidden}.relation-text strong[data-v-e27dc1c0]{font-weight:600}.relation-text em[data-v-e27dc1c0]{color:var(--muted-color);font-style:normal}.btn-remove[data-v-e27dc1c0]{width:1.5rem;height:1.5rem;color:var(--muted-color);cursor:pointer;background:0 0;border:none;border-radius:3px;flex-shrink:0;justify-content:center;align-items:center;margin:0;padding:0;display:inline-flex}.btn-remove[data-v-e27dc1c0]:hover{background-color:var(--del-color);color:#fff}.btn-remove i[data-v-e27dc1c0]{font-size:.85rem}.add-relation-row[data-v-e27dc1c0]{align-items:center;gap:.5rem;display:flex}.add-relation-row select[data-v-e27dc1c0]{flex:1;margin:0}.add-relation-row select[data-v-e27dc1c0]:first-child{flex:1.2}.btn-add[data-v-e27dc1c0]{white-space:nowrap;flex-shrink:0;margin:0}.no-relations[data-v-e27dc1c0]{color:var(--muted-color);margin:0;font-style:italic}@media (width<=768px){.add-relation-row[data-v-e27dc1c0]{flex-direction:column}.add-relation-row select[data-v-e27dc1c0],.add-relation-row button[data-v-e27dc1c0]{width:100%}}.yaml-output-container[data-v-f7e6a550]{width:100%}.yaml-success[data-v-f7e6a550]{color:var(--ins-color);align-items:center;gap:.5rem;margin:0 0 1rem;font-weight:500;display:flex}#yaml-content[data-v-f7e6a550]{margin:0 calc(var(--block-spacing-horizontal) * -1);padding:var(--block-spacing-horizontal);background-color:var(--code-background-color);border-radius:0;overflow-x:auto}#yaml-content code[data-v-f7e6a550]{white-space:pre;padding:0;font-size:.9em;line-height:1.5}footer[data-v-f7e6a550]{justify-content:space-between;align-items:center;gap:1rem;display:flex}.yaml-file-info[data-v-f7e6a550],.yaml-instructions[data-v-f7e6a550]{color:var(--muted-color);flex:1;gap:.5rem;min-width:0;margin:0;font-size:.9em;line-height:1.5;display:flex}.yaml-file-info[data-v-f7e6a550]{align-items:center}.yaml-instructions[data-v-f7e6a550]{align-items:flex-start}.yaml-file-info>span[data-v-f7e6a550],.yaml-instructions>span[data-v-f7e6a550]{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.yaml-file-info>i[data-v-f7e6a550],.yaml-instructions>i[data-v-f7e6a550]{flex-shrink:0}.yaml-actions[data-v-f7e6a550]{flex-shrink:0;gap:.5rem;display:flex}.yaml-actions button[data-v-f7e6a550],.yaml-actions [role=button][data-v-f7e6a550]{width:auto;margin:0}.conflict-error[data-v-f7e6a550]{background-color:var(--pico-mark-background-color);color:var(--pico-del-color);border-radius:var(--pico-border-radius);align-items:center;gap:.5rem;margin-bottom:1rem;padding:1rem;display:flex}.conflict-error i[data-v-f7e6a550]{flex-shrink:0}@media (width<=768px){footer[data-v-f7e6a550]{flex-direction:column;align-items:stretch}.yaml-actions[data-v-f7e6a550]{flex-direction:column}.yaml-actions button[data-v-f7e6a550],.yaml-actions [role=button][data-v-f7e6a550]{width:100%}}#markdown-editor-overlay[data-v-9ca3d00d]{z-index:1000;position:fixed;inset:0}.markdown-editor-backdrop[data-v-9ca3d00d]{background-color:#00000080;position:absolute;inset:0}.markdown-editor-panel[data-v-9ca3d00d]{background-color:var(--card-background-color,#fff);border-radius:12px;flex-direction:column;display:flex;position:absolute;inset:2rem;overflow:hidden;box-shadow:0 8px 32px #0000004d,0 2px 8px #0003}.lexical-toolbar[data-v-9ca3d00d]{background-color:var(--card-background-color,#fff);z-index:1;flex-wrap:wrap;align-items:center;gap:.25rem;padding:.5rem 1rem;display:flex;box-shadow:0 2px 4px #0000001a}.toolbar-btn[data-v-9ca3d00d]{border-radius:var(--border-radius);min-width:2rem;height:2rem;color:var(--color);cursor:pointer;background:0 0;border:1px solid #0000;justify-content:center;align-items:center;margin:0;padding:0 .5rem;font-size:.85rem;font-weight:600;transition:all .15s;display:inline-flex}.toolbar-btn[data-v-9ca3d00d]:hover{background-color:var(--muted-border-color);border-color:var(--muted-border-color)}.toolbar-btn[data-v-9ca3d00d]:active{background-color:var(--primary);border-color:var(--primary);color:var(--primary-inverse)}.toolbar-btn i[data-v-9ca3d00d]{font-size:1.1rem}.toolbar-divider[data-v-9ca3d00d]{background-color:var(--muted-border-color);width:1px;height:1.5rem;margin:0 .5rem}.toolbar-spacer[data-v-9ca3d00d]{flex:1}.toolbar-action[data-v-9ca3d00d]{margin:0;padding:.4rem .75rem;font-size:.85rem}.toolbar-select[data-v-9ca3d00d]{border:1px solid var(--pico-muted-border-color,#e4e4e7);border-radius:var(--pico-border-radius,.375rem);width:auto;min-width:130px;max-width:180px;height:2rem;color:var(--pico-color,inherit);cursor:pointer;background-color:#0000;flex-shrink:0;margin:0;padding:0 .75rem;font-size:.85rem}.toolbar-select[data-v-9ca3d00d]:hover{border-color:var(--pico-primary,#1e88e5)}.toolbar-select[data-v-9ca3d00d]:focus{border-color:var(--pico-primary,#1e88e5);outline:none}.lexical-editor-container[data-v-9ca3d00d]{flex-direction:column;flex:1;min-height:0;display:flex;position:relative}#lexical-editor-root[data-v-9ca3d00d]{min-height:0;font-family:var(--font-family);flex:1;padding:1rem 1.5rem;line-height:1.6;position:relative;overflow-y:auto}#lexical-editor-root[data-v-9ca3d00d]:focus{outline:none}#lexical-editor-root[data-v-9ca3d00d] p{margin:0 0 .75rem}#lexical-editor-root[data-v-9ca3d00d] p:last-child{margin-bottom:0}#lexical-editor-root[data-v-9ca3d00d] h1,#lexical-editor-root[data-v-9ca3d00d] h2,#lexical-editor-root[data-v-9ca3d00d] h3,#lexical-editor-root[data-v-9ca3d00d] h4{margin:1rem 0 .5rem;font-weight:600}#lexical-editor-root[data-v-9ca3d00d] h1:first-child,#lexical-editor-root[data-v-9ca3d00d] h2:first-child,#lexical-editor-root[data-v-9ca3d00d] h3:first-child{margin-top:0}#lexical-editor-root[data-v-9ca3d00d] ul,#lexical-editor-root[data-v-9ca3d00d] ol{margin:.5rem 0;padding-left:1.5rem}#lexical-editor-root[data-v-9ca3d00d] li{margin:.25rem 0}#lexical-editor-root[data-v-9ca3d00d] blockquote{border-left:3px solid var(--pico-muted-border-color,#e4e4e7);color:var(--pico-muted-color,#71717a);margin:.75rem 0;padding:.5rem 1rem;font-style:italic}#lexical-editor-root[data-v-9ca3d00d] code{background-color:var(--code-background-color,#f4f4f5);border:1px solid var(--pico-muted-border-color,#e4e4e7);font-family:var(--pico-font-family-monospace,monospace);border-radius:3px;padding:.15rem .35rem;font-size:.9em}#lexical-editor-root[data-v-9ca3d00d] pre{background-color:var(--code-background-color);border-radius:var(--border-radius);margin:.75rem 0;padding:1rem;position:relative;overflow-x:auto}#lexical-editor-root[data-v-9ca3d00d] pre code{background:0 0;padding:0}#lexical-editor-root[data-v-9ca3d00d] .lexical-code{background-color:var(--code-background-color,#f4f4f5);border-top:1px solid var(--pico-muted-border-color,#e4e4e7);border-bottom:1px solid var(--pico-muted-border-color,#e4e4e7);min-width:calc(100% + 3rem);font-family:var(--pico-font-family-monospace,monospace);white-space:pre;tab-size:2;box-sizing:border-box;border-radius:0;margin:.75rem -1.5rem;padding:1rem 1.5rem;font-size:.9em;line-height:1.5;display:block;position:relative;overflow-x:auto}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenComment{color:#6a737d}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenPunctuation{color:#24292e}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenProperty{color:#005cc5}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenSelector{color:#032f62}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenOperator{color:#005cc5}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenAttr{color:#d73a49}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenVariable{color:#e36209}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenFunction{color:#6f42c1}#lexical-editor-root[data-v-9ca3d00d] a{color:var(--primary);text-decoration:underline}#lexical-editor-root[data-v-9ca3d00d] .lexical-link{color:var(--pico-primary,#1e88e5);cursor:pointer;text-decoration:underline}#lexical-editor-root[data-v-9ca3d00d] .lexical-link:hover{text-decoration:none}#lexical-editor-root[data-v-9ca3d00d] strong,#lexical-editor-root[data-v-9ca3d00d] .lexical-bold{font-weight:700}#lexical-editor-root[data-v-9ca3d00d] em,#lexical-editor-root[data-v-9ca3d00d] .lexical-italic{font-style:italic}#lexical-editor-root[data-v-9ca3d00d] u,#lexical-editor-root[data-v-9ca3d00d] .lexical-underline{text-decoration:underline}#lexical-editor-root[data-v-9ca3d00d] s,#lexical-editor-root[data-v-9ca3d00d] .lexical-strikethrough{text-decoration:line-through}@media (prefers-color-scheme:dark){.markdown-editor-panel[data-v-9ca3d00d]{background-color:var(--card-background-color,#1e1e1e);box-shadow:0 8px 32px #00000080,0 2px 8px #0000004d}.markdown-editor-backdrop[data-v-9ca3d00d]{background-color:#000000b3}.lexical-toolbar[data-v-9ca3d00d]{background-color:var(--card-background-color,#1e1e1e);box-shadow:0 2px 4px #0000004d}.toolbar-btn[data-v-9ca3d00d]{color:var(--color,#c9d1d9)}.toolbar-btn[data-v-9ca3d00d]:hover{background-color:#ffffff1a;border-color:#ffffff1a}.toolbar-btn[data-v-9ca3d00d]:active{background-color:var(--primary);border-color:var(--primary)}.toolbar-divider[data-v-9ca3d00d]{background-color:#ffffff26}.toolbar-select[data-v-9ca3d00d]{color:var(--color,#c9d1d9);background-color:#0000;border-color:#fff3}.toolbar-select[data-v-9ca3d00d]:hover,.toolbar-select[data-v-9ca3d00d]:focus{border-color:var(--primary,#58a6ff)}#lexical-editor-root[data-v-9ca3d00d]{color:var(--color,#c9d1d9);background-color:var(--card-background-color,#1e1e1e)}#lexical-editor-root[data-v-9ca3d00d] blockquote{color:#8b949e;border-left-color:#fff3}#lexical-editor-root[data-v-9ca3d00d] code{color:#c9d1d9;background-color:#6e768166;border-color:#ffffff1a}#lexical-editor-root[data-v-9ca3d00d] .lexical-code{color:#c9d1d9;background-color:#0d1117;border-color:#ffffff1a}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenComment{color:#8b949e}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenPunctuation{color:#c9d1d9}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenProperty{color:#79c0ff}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenSelector{color:#a5d6ff}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenOperator{color:#79c0ff}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenAttr{color:#ff7b72}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenVariable{color:#ffa657}#lexical-editor-root[data-v-9ca3d00d] .lexical-code .tokenFunction{color:#d2a8ff}#lexical-editor-root[data-v-9ca3d00d] .lexical-link,#lexical-editor-root[data-v-9ca3d00d] a{color:#58a6ff}}.editor-form[data-v-998cb080]{max-width:100%}.annotations-grid[data-v-998cb080]{grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:1rem;display:grid}.annotations-grid .field-full-width[data-v-998cb080]{grid-column:1/-1}.form-actions[data-v-998cb080]{gap:.75rem;margin-top:1rem;display:flex}.form-actions button[data-v-998cb080]{width:auto;margin:0;padding:.5rem 1.25rem}.btn-back[data-v-998cb080]{align-items:center;gap:.25rem;text-decoration:none;display:inline-flex}.relations-editor .relation-item[data-v-998cb080]{align-items:center;gap:.5rem;padding:.25rem 0;display:flex}.relations-editor .relation-text[data-v-998cb080]{flex:1}.relations-editor .btn-remove[data-v-998cb080]{cursor:pointer;color:var(--pico-del-color,#c62828);background:0 0;border:none;width:auto;margin:0;padding:.25rem}.relations-editor .add-relation-row[data-v-998cb080]{align-items:flex-start;gap:.5rem;margin-top:.5rem;display:flex}.relations-editor .add-relation-row select[data-v-998cb080]{flex:1;margin-bottom:0}.relations-editor .btn-add[data-v-998cb080]{white-space:nowrap;margin-bottom:0}.no-relations[data-v-998cb080]{color:var(--pico-muted-color);font-style:italic}.field-group[data-v-998cb080]{margin-bottom:.5rem}.field-group .label-row[data-v-998cb080]{align-items:center;gap:.5rem;margin-bottom:.25rem;display:flex}.field-group .required[data-v-998cb080]{color:var(--pico-del-color,#c62828)}.field-group .field-description[data-v-998cb080]{color:var(--pico-muted-color);margin-top:.125rem;display:block}.field-group .field-error[data-v-998cb080]{color:var(--pico-del-color,#c62828);margin-top:.125rem;font-size:.875rem}.field-group .btn-edit-markdown[data-v-998cb080]{border:1px solid var(--pico-primary);color:var(--pico-primary);cursor:pointer;background:0 0;border-radius:4px;width:auto;margin:0;padding:.125rem .5rem;font-size:.75rem}.field-group .future-hint[data-v-998cb080]{color:var(--pico-muted-color);margin-top:.125rem;display:block}.field-group textarea[data-v-998cb080]{min-height:8rem;font-family:inherit}.field-group .code-field textarea[data-v-998cb080]{font-family:monospace}.yaml-output-container .yaml-success[data-v-998cb080]{color:var(--pico-ins-color,#2e7d32)}.yaml-output-container pre[data-v-998cb080]{max-height:60vh;overflow:auto}.yaml-output-container .yaml-actions[data-v-998cb080]{gap:.5rem;margin-top:.5rem;display:flex}.yaml-output-container .yaml-file-info[data-v-998cb080],.yaml-output-container .yaml-instructions[data-v-998cb080]{color:var(--pico-muted-color)}.yaml-output-container .copied[data-v-998cb080],.yaml-output-container .saved[data-v-998cb080]{opacity:.8}.conflict-error[data-v-998cb080]{background:var(--pico-del-color,#c62828);color:#fff;border-radius:4px;align-items:center;gap:.5rem;margin-bottom:1rem;padding:.75rem 1rem;display:flex}.editor-container[data-v-998cb080]{width:100%}.editor-header[data-v-998cb080]{border-bottom:1px solid var(--muted-border-color);justify-content:space-between;align-items:center;margin-bottom:1.5rem;padding-bottom:1rem;display:flex}.editor-header h2[data-v-998cb080]{align-items:center;gap:.75rem;margin:0;display:flex}.header-actions[data-v-998cb080]{align-items:center;gap:.5rem;display:flex}.btn-header[data-v-998cb080]{color:var(--muted-color);border-radius:var(--border-radius);cursor:pointer;background:0 0;border:1px solid #0000;align-items:center;gap:.35rem;padding:.4rem .75rem;font-size:.9rem;text-decoration:none;transition:all .15s;display:inline-flex}.btn-header[data-v-998cb080]:hover{color:var(--primary);border-color:var(--primary)}.yaml-output>header[data-v-998cb080]{justify-content:space-between;align-items:center;display:flex}.yaml-output>header h3[data-v-998cb080]{align-items:center;gap:.5rem;margin:0;display:flex}@media (width<=768px){.editor-header[data-v-998cb080]{flex-direction:column;align-items:flex-start;gap:1rem}.annotations-grid[data-v-998cb080]{grid-template-columns:1fr}}
|