archsight 0.2.4 → 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/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 +17 -0
- 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-C5lv_BWF.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-DHOFUCYc.js +0 -1
- data/lib/archsight/web/public/vue/ApiDocsPage-DhNTOH4o.css +0 -1
- data/lib/archsight/web/public/vue/DocPage-CV66qgTr.js +0 -1
- data/lib/archsight/web/public/vue/EditorPage-Dq0MuTnp.css +0 -1
- data/lib/archsight/web/public/vue/EditorPage-KqBivY-B.js +0 -34
- data/lib/archsight/web/public/vue/ErrorPage-CwPT3JUr.css +0 -1
- data/lib/archsight/web/public/vue/ErrorPage-DcbC8Kf1.js +0 -2
- data/lib/archsight/web/public/vue/GraphView-Bg_l-F-Q.js +0 -1
- data/lib/archsight/web/public/vue/GraphView-DRcIqAiR.css +0 -1
- data/lib/archsight/web/public/vue/InstanceRouter-4VEtZM7n.css +0 -1
- data/lib/archsight/web/public/vue/InstanceRouter-D-twdyZY.js +0 -2
- data/lib/archsight/web/public/vue/KindList-CPImTKNb.js +0 -1
- data/lib/archsight/web/public/vue/ResourceList-DMxm0cGh.js +0 -1
- data/lib/archsight/web/public/vue/ResourceList-DP-z-j71.css +0 -1
- data/lib/archsight/web/public/vue/SearchResults-BGHbg48-.css +0 -1
- data/lib/archsight/web/public/vue/SearchResults-BqUHEWHE.js +0 -1
- data/lib/archsight/web/public/vue/_baseUniq-BjkdEi26.js +0 -1
- data/lib/archsight/web/public/vue/architectureDiagram-VXUJARFQ-JN7CxdtP.js +0 -36
- data/lib/archsight/web/public/vue/blockDiagram-VD42YOAC-teziPFHX.js +0 -122
- data/lib/archsight/web/public/vue/c4Diagram-YG6GDRKO-CVNrzCCc.js +0 -10
- data/lib/archsight/web/public/vue/chunk-4BX2VUAB-BQUARyyA.js +0 -1
- data/lib/archsight/web/public/vue/chunk-55IACEB6-BgJn0Waa.js +0 -1
- data/lib/archsight/web/public/vue/chunk-B4BG7PRW-0ghAfB1t.js +0 -165
- data/lib/archsight/web/public/vue/chunk-DI55MBZ5-HJo1DW2B.js +0 -220
- data/lib/archsight/web/public/vue/chunk-FMBD7UC4-C1GwGKgX.js +0 -15
- data/lib/archsight/web/public/vue/chunk-QN33PNHL-BOoA1KfJ.js +0 -1
- data/lib/archsight/web/public/vue/chunk-QZHKN3VN-BcTNH3IX.js +0 -1
- data/lib/archsight/web/public/vue/chunk-TZMSLE5B-58bQF3J5.js +0 -1
- data/lib/archsight/web/public/vue/classDiagram-2ON5EDUG-DAA8tpbN.js +0 -1
- data/lib/archsight/web/public/vue/classDiagram-v2-WZHVMYZB-DAA8tpbN.js +0 -1
- data/lib/archsight/web/public/vue/clone-BPcOyh7U.js +0 -1
- data/lib/archsight/web/public/vue/cose-bilkent-S5V4N54A-Bv8iR4rF.js +0 -1
- data/lib/archsight/web/public/vue/cytoscape.esm-5J0xJHOV.js +0 -321
- data/lib/archsight/web/public/vue/dagre-6UL2VRFP-B8ZPRhgU.js +0 -4
- data/lib/archsight/web/public/vue/diagram-PSM6KHXK-Dp1bZnNq.js +0 -24
- data/lib/archsight/web/public/vue/diagram-QEK2KX5R-DZBsDWP6.js +0 -43
- data/lib/archsight/web/public/vue/diagram-S2PKOQOG-BKOnLWNk.js +0 -24
- data/lib/archsight/web/public/vue/erDiagram-Q2GNP2WA-V6FqHPc9.js +0 -60
- data/lib/archsight/web/public/vue/flowDiagram-NV44I4VS-BipXjPVT.js +0 -162
- data/lib/archsight/web/public/vue/ganttDiagram-JELNMOA3-DUdVPVK1.js +0 -267
- data/lib/archsight/web/public/vue/gitGraphDiagram-V2S2FVAM-Bcf_apTG.js +0 -65
- data/lib/archsight/web/public/vue/graph-C4e9XILZ.js +0 -1
- data/lib/archsight/web/public/vue/graphviz-CJms5bxZ.js +0 -13
- data/lib/archsight/web/public/vue/index-BUI400cn.js +0 -2
- data/lib/archsight/web/public/vue/index-Tiu4C-Sb.css +0 -1
- data/lib/archsight/web/public/vue/infoDiagram-HS3SLOUP-DBwnExXO.js +0 -2
- data/lib/archsight/web/public/vue/journeyDiagram-XKPGCS4Q-D4b8PEzB.js +0 -139
- data/lib/archsight/web/public/vue/kanban-definition-3W4ZIXB7-R6r4NPxJ.js +0 -89
- data/lib/archsight/web/public/vue/katex-C-M49wc6.js +0 -261
- data/lib/archsight/web/public/vue/layout-CXL8NrCi.js +0 -1
- data/lib/archsight/web/public/vue/mermaid-MmHzOPPB.js +0 -250
- data/lib/archsight/web/public/vue/min-B4MPxNTL.js +0 -1
- data/lib/archsight/web/public/vue/mindmap-definition-VGOIOE7T-BypPT67y.js +0 -68
- data/lib/archsight/web/public/vue/pieDiagram-ADFJNKIX-BJ4nb15u.js +0 -30
- data/lib/archsight/web/public/vue/quadrantDiagram-AYHSOK5B-D4Ee5XRs.js +0 -7
- data/lib/archsight/web/public/vue/requirementDiagram-UZGBJVZJ-DuxpUmt2.js +0 -64
- data/lib/archsight/web/public/vue/sankeyDiagram-TZEHDZUN-CgWlWmza.js +0 -10
- data/lib/archsight/web/public/vue/sequenceDiagram-WL72ISMW-v2l2CbI0.js +0 -145
- data/lib/archsight/web/public/vue/stateDiagram-FKZM4ZOC-B5ROi2_1.js +0 -1
- data/lib/archsight/web/public/vue/stateDiagram-v2-4FDKWEC3-CQhha5H7.js +0 -1
- data/lib/archsight/web/public/vue/timeline-definition-IT6M3QCI-Bnx2HvxP.js +0 -61
- data/lib/archsight/web/public/vue/treemap-GDKQZRPO-BVho_qBy.js +0 -162
- data/lib/archsight/web/public/vue/useHighlight-yg_u-WUA.js +0 -10
- data/lib/archsight/web/public/vue/useMermaid-BPBwn39g.js +0 -1
- data/lib/archsight/web/public/vue/usePanZoom-CgSmFLId.js +0 -11
- data/lib/archsight/web/public/vue/xychartDiagram-PRI3JC2R-CDkeZXF1.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
|
|
@@ -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}}
|