archsight 0.3.0 → 0.3.2
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/CONTRIBUTING.md +1 -1
- data/README.md +7 -2
- data/docs/computed_annotations.md +7 -5
- data/docs/icons.md +3 -2
- data/docs/index.md.erb +5 -4
- data/docs/licenses.md +1 -2
- data/docs/modeling.md +127 -6
- data/docs/pages.md +72 -6
- data/docs/search.md +26 -2
- data/docs/togaf.md +4 -0
- data/lib/archsight/annotations/annotation.rb +5 -4
- data/lib/archsight/annotations/computed.rb +5 -1
- data/lib/archsight/annotations/relation_resolver.rb +109 -83
- data/lib/archsight/cli.rb +9 -2
- data/lib/archsight/database.rb +59 -4
- data/lib/archsight/diagram.rb +7 -0
- data/lib/archsight/documentation.rb +9 -5
- data/lib/archsight/editor.rb +2 -2
- data/lib/archsight/export/confluence/exporter.rb +11 -2
- data/lib/archsight/export/confluence/storage.rb +99 -8
- data/lib/archsight/export/confluence/tables.rb +78 -0
- data/lib/archsight/helpers/fenced_blocks.rb +61 -0
- data/lib/archsight/helpers/requirements_blocks.rb +90 -0
- data/lib/archsight/helpers/resource_resolver.rb +1 -1
- data/lib/archsight/helpers/view_blocks.rb +108 -0
- data/lib/archsight/helpers/wiki_links.rb +50 -5
- data/lib/archsight/helpers.rb +3 -0
- data/lib/archsight/import/handlers/go_grapher.rb +4 -1
- data/lib/archsight/import/handlers/go_module_parser.rb +4 -1
- data/lib/archsight/linter.rb +31 -3
- data/lib/archsight/mcp/analyze_resource_tool.rb +2 -2
- data/lib/archsight/mcp/base.rb +38 -0
- data/lib/archsight/mcp/resource_doc_tool.rb +2 -2
- data/lib/archsight/query/ast.rb +2 -1
- data/lib/archsight/query/evaluator.rb +2 -2
- data/lib/archsight/references.rb +129 -0
- data/lib/archsight/requirements.rb +70 -0
- data/lib/archsight/resources/analysis.rb +2 -1
- data/lib/archsight/resources/application_component.rb +8 -3
- data/lib/archsight/resources/application_interface.rb +5 -3
- data/lib/archsight/resources/application_service.rb +8 -7
- data/lib/archsight/resources/base.rb +67 -17
- data/lib/archsight/resources/business_actor.rb +5 -3
- data/lib/archsight/resources/business_control.rb +80 -0
- data/lib/archsight/resources/business_process.rb +3 -2
- data/lib/archsight/resources/business_product.rb +6 -5
- data/lib/archsight/resources/compliance_evidence.rb +40 -3
- data/lib/archsight/resources/data_object.rb +3 -2
- data/lib/archsight/resources/import.rb +5 -2
- data/lib/archsight/resources/{business_constraint.rb → motivation_constraint.rb} +4 -4
- data/lib/archsight/resources/motivation_goal.rb +1 -1
- data/lib/archsight/resources/{business_requirement.rb → motivation_requirement.rb} +19 -8
- data/lib/archsight/resources/motivation_stakeholder.rb +2 -2
- data/lib/archsight/resources/page.rb +4 -2
- data/lib/archsight/resources/strategy_capability.rb +2 -2
- data/lib/archsight/resources/technology_artifact.rb +4 -3
- data/lib/archsight/resources/technology_node.rb +2 -2
- data/lib/archsight/resources/technology_service.rb +4 -0
- data/lib/archsight/resources/technology_system_software.rb +4 -0
- data/lib/archsight/resources/view.rb +2 -1
- data/lib/archsight/resources.rb +34 -3
- data/lib/archsight/template.rb +2 -2
- data/lib/archsight/version.rb +1 -1
- data/lib/archsight/view_table.rb +102 -0
- data/lib/archsight/web/api/docs.rb +1 -1
- data/lib/archsight/web/api/json_helpers.rb +21 -15
- data/lib/archsight/web/api/openapi/spec.yaml +135 -2
- data/lib/archsight/web/api/page_helpers.rb +8 -7
- data/lib/archsight/web/api/requirements_helpers.rb +26 -0
- data/lib/archsight/web/api/routes.rb +19 -0
- data/lib/archsight/web/application.rb +12 -2
- data/lib/archsight/web/public/vue/ApiDocsPage-DZ0fa5-h.css +1 -0
- data/lib/archsight/web/public/vue/ApiDocsPage-DoOxKjG0.js +1 -0
- data/lib/archsight/web/public/vue/{DocPage-uaT8CdFm.js → DocPage-CfsC3CeQ.js} +1 -1
- data/lib/archsight/web/public/vue/EditorPage-CbG4mc9T.css +1 -0
- data/lib/archsight/web/public/vue/EditorPage-CsJA0q8n.js +35 -0
- data/lib/archsight/web/public/vue/ErrorPage-Ck9izEUS.css +1 -0
- data/lib/archsight/web/public/vue/ErrorPage-OJpJz9df.js +2 -0
- data/lib/archsight/web/public/vue/GraphView-BvWAbUAl.css +1 -0
- data/lib/archsight/web/public/vue/GraphView-CBq6oFRV.js +1 -0
- data/lib/archsight/web/public/vue/HomePage-BwgMLgVC.js +2 -0
- data/lib/archsight/web/public/vue/InstanceRouter-DQz-SsQq.js +1 -0
- data/lib/archsight/web/public/vue/InstanceRouter-jeavM6PW.css +1 -0
- data/lib/archsight/web/public/vue/KindList-4q0K9Cll.js +1 -0
- data/lib/archsight/web/public/vue/PageView-DzeGnvoS.js +1 -0
- data/lib/archsight/web/public/vue/QueryError-3EqjpbyV.css +1 -0
- data/lib/archsight/web/public/vue/QueryError-kg6Yf-pW.js +1 -0
- data/lib/archsight/web/public/vue/ResourceList-B0FLaFR3.css +1 -0
- data/lib/archsight/web/public/vue/ResourceList-Df_0e0qt.js +2 -0
- data/lib/archsight/web/public/vue/SearchResults-CKgY6_wH.js +1 -0
- data/lib/archsight/web/public/vue/SearchResults-CyPUZZSC.css +1 -0
- data/lib/archsight/web/public/vue/WikiPage-DoFJ0dbW.css +1 -0
- data/lib/archsight/web/public/vue/WikiPage-DrvCHwJ7.js +13 -0
- data/lib/archsight/web/public/vue/architecture-7GRP2DOG-BfA1TPxQ.js +1 -0
- data/lib/archsight/web/public/vue/cynefin-OW5HDTMX-BmgdUKVD.js +1 -0
- data/lib/archsight/web/public/vue/eventmodeling-NTZA5JFV-DbXDvAWF.js +1 -0
- data/lib/archsight/web/public/vue/gitGraph-4MIJSDKK-BkVhrKS1.js +1 -0
- data/lib/archsight/web/public/vue/index-D0Q5GZRs.js +3 -0
- data/lib/archsight/web/public/vue/index-oF-iyZvk.css +1 -0
- data/lib/archsight/web/public/vue/info-A6RAGUB7-BIlCVuUY.js +1 -0
- data/lib/archsight/web/public/vue/mermaid-BjEi5URd.js +3334 -0
- data/lib/archsight/web/public/vue/packet-AYTQ26CC-eoQTjY3j.js +1 -0
- data/lib/archsight/web/public/vue/pie-WAS4IAKB-DEOotGhh.js +1 -0
- data/lib/archsight/web/public/vue/radar-RG4KPBEZ-C2yucuUN.js +1 -0
- data/lib/archsight/web/public/vue/railroad-74A4TZTK-CzONK5VY.js +1 -0
- data/lib/archsight/web/public/vue/railroad-abnf-HS5TGJTU-9zRPsDsx.js +1 -0
- data/lib/archsight/web/public/vue/railroad-ebnf-LZEXJU2U-BJ4dkz9l.js +1 -0
- data/lib/archsight/web/public/vue/railroad-peg-WCYAUIDC-DFy_rqAj.js +1 -0
- data/lib/archsight/web/public/vue/treeView-Q6P3EWNA-2cq5CyD2.js +1 -0
- data/lib/archsight/web/public/vue/treemap-WGGIJYW6-DK-e9Ez4.js +1 -0
- data/lib/archsight/web/public/vue/{useGraphviz-C71SdG-N.js → useGraphviz-DweKV7Kg.js} +1 -1
- data/lib/archsight/web/public/vue/wardley-WFR3VGLG-BQwqUqfB.js +1 -0
- data/lib/archsight/web/public/vue.html +3 -3
- data/lib/archsight.rb +2 -0
- metadata +53 -42
- data/lib/archsight/web/public/vue/ApiDocsPage-C0y953v0.css +0 -1
- data/lib/archsight/web/public/vue/ApiDocsPage-C_4tAWis.js +0 -1
- data/lib/archsight/web/public/vue/EditorPage-C557BJC-.js +0 -35
- data/lib/archsight/web/public/vue/EditorPage-df5N-p21.css +0 -1
- data/lib/archsight/web/public/vue/ErrorPage-Vdebmife.js +0 -2
- data/lib/archsight/web/public/vue/ErrorPage-uMDnfY5_.css +0 -1
- data/lib/archsight/web/public/vue/GraphView-BduUql2N.js +0 -1
- data/lib/archsight/web/public/vue/GraphView-Cj2V2stN.css +0 -1
- data/lib/archsight/web/public/vue/HomePage-BHUTg8Ap.js +0 -2
- data/lib/archsight/web/public/vue/InstanceRouter-1lSigA58.js +0 -1
- data/lib/archsight/web/public/vue/InstanceRouter-Di7f3Rya.css +0 -1
- data/lib/archsight/web/public/vue/KindList-BDZs0j6d.js +0 -1
- data/lib/archsight/web/public/vue/PageView-BYzJDwof.js +0 -1
- data/lib/archsight/web/public/vue/ResourceList-CnbhU9wm.js +0 -1
- data/lib/archsight/web/public/vue/ResourceList-xyBwu7fh.css +0 -1
- data/lib/archsight/web/public/vue/SearchResults-CNhf6VOx.js +0 -1
- data/lib/archsight/web/public/vue/SearchResults-DOHzqAy3.css +0 -1
- data/lib/archsight/web/public/vue/WikiPage-DbmWkM7W.js +0 -13
- data/lib/archsight/web/public/vue/WikiPage-GV67QmNJ.css +0 -1
- data/lib/archsight/web/public/vue/architecture-TIHT7OUA-Bf-MWFmn.js +0 -1
- data/lib/archsight/web/public/vue/cynefin-VYW2F7L2-CkKt8qqs.js +0 -1
- data/lib/archsight/web/public/vue/eventmodeling-45OFAUF4-NwSTYPHj.js +0 -1
- data/lib/archsight/web/public/vue/gitGraph-TEB2WS4Q-DLRegrRG.js +0 -1
- data/lib/archsight/web/public/vue/index-CyVWObLU.js +0 -3
- data/lib/archsight/web/public/vue/index-DtKeHT3S.css +0 -1
- data/lib/archsight/web/public/vue/info-DKCQHKI2-EV39NzoN.js +0 -1
- data/lib/archsight/web/public/vue/mermaid-BMkGfnhm.js +0 -3279
- data/lib/archsight/web/public/vue/packet-7NZHBO7P-USljV3MZ.js +0 -1
- data/lib/archsight/web/public/vue/pie-RZYD4A2V-DChciBW2.js +0 -1
- data/lib/archsight/web/public/vue/radar-I7S5WNFK-CgGJdW1t.js +0 -1
- data/lib/archsight/web/public/vue/railroad-3IZDKUUU-Dy43vF5c.js +0 -1
- data/lib/archsight/web/public/vue/railroad-abnf-AHOZXSZD-BhsTH-sE.js +0 -1
- data/lib/archsight/web/public/vue/railroad-ebnf-EBAXGLYW-BBTd_u0u.js +0 -1
- data/lib/archsight/web/public/vue/railroad-peg-LSFZ7HO6-BiNTEzf0.js +0 -1
- data/lib/archsight/web/public/vue/treeView-QDETBFTQ-BfnCcf-q.js +0 -1
- data/lib/archsight/web/public/vue/treemap-6X3UGDF4-BsAORhvk.js +0 -1
- data/lib/archsight/web/public/vue/wardley-OPB4EBWU-DQvnEhRj.js +0 -1
|
@@ -18,7 +18,7 @@ module Archsight::Web::API::Docs
|
|
|
18
18
|
# GET /api/v1/docs/resources/:filename — auto-generated resource docs
|
|
19
19
|
get "/api/v1/docs/resources/:filename" do
|
|
20
20
|
filename = params["filename"].gsub(/[^a-zA-Z0-9_-]/, "")
|
|
21
|
-
kind_name = filename.split("_").map(&:capitalize).join
|
|
21
|
+
kind_name = Archsight::Resources.canonical(filename.split("_").map(&:capitalize).join) # an old help link shows the renamed kind
|
|
22
22
|
|
|
23
23
|
begin
|
|
24
24
|
content = Archsight::Documentation.generate(kind_name)
|
|
@@ -74,7 +74,7 @@ module Archsight::Web::API::JsonHelpers
|
|
|
74
74
|
|
|
75
75
|
def build_list_response(kind, pagination, instances)
|
|
76
76
|
{
|
|
77
|
-
kind: kind,
|
|
77
|
+
kind: Archsight::Resources.canonical(kind),
|
|
78
78
|
total: pagination[:total],
|
|
79
79
|
limit: pagination[:limit],
|
|
80
80
|
offset: pagination[:offset],
|
|
@@ -88,9 +88,10 @@ module Archsight::Web::API::JsonHelpers
|
|
|
88
88
|
|
|
89
89
|
def build_instance_response(kind, instance)
|
|
90
90
|
{
|
|
91
|
-
kind: kind,
|
|
91
|
+
kind: Archsight::Resources.canonical(kind),
|
|
92
92
|
name: instance.name,
|
|
93
|
-
metadata: { annotations: render_annotations(instance.annotations, base: Archsight::Assets.base_dir_for(instance, resources_dir: Archsight.resources_dir)) },
|
|
93
|
+
metadata: { annotations: render_annotations(instance.annotations, base: Archsight::Assets.base_dir_for(instance, resources_dir: Archsight.resources_dir), kind: kind) },
|
|
94
|
+
annotation_formats: Archsight::Resources[kind]&.annotation_formats(instance.annotations.keys) || {},
|
|
94
95
|
spec: serialize_spec(instance.spec),
|
|
95
96
|
relations: extract_relations(instance),
|
|
96
97
|
references: extract_references(instance),
|
|
@@ -106,10 +107,14 @@ module Archsight::Web::API::JsonHelpers
|
|
|
106
107
|
Archsight::Helpers::DiagramBlocks.render_diagram(source, resolver: Archsight::Helpers::ResourceResolver.new(db))
|
|
107
108
|
end
|
|
108
109
|
|
|
110
|
+
# The values of markdown annotations are rendered to HTML: architecture/description and every annotation the
|
|
111
|
+
# kind defines with `format: :markdown`.
|
|
109
112
|
# @param base [String, nil] directory of the file the resource comes from, for the images of its markdown
|
|
110
|
-
|
|
113
|
+
# @param kind [String, nil] the kind of the resource, to find which of its annotations are markdown
|
|
114
|
+
def render_annotations(annotations, base: nil, kind: nil)
|
|
115
|
+
klass = kind && Archsight::Resources[kind]
|
|
111
116
|
annotations.each_with_object({}) do |(key, value), result|
|
|
112
|
-
result[key] = if
|
|
117
|
+
result[key] = if value.is_a?(String) && (MARKDOWN_ANNOTATION_KEYS.include?(key) || klass&.annotation_format(key) == :markdown)
|
|
113
118
|
markdown(value, base: base)
|
|
114
119
|
else
|
|
115
120
|
value
|
|
@@ -130,13 +135,11 @@ module Archsight::Web::API::JsonHelpers
|
|
|
130
135
|
end
|
|
131
136
|
|
|
132
137
|
def build_count_response(query, results, query_time_ms)
|
|
133
|
-
by_kind = results.group_by { |r| r.class.to_s.split("::").last }
|
|
134
|
-
.transform_values(&:length)
|
|
135
138
|
{
|
|
136
139
|
query: query,
|
|
137
140
|
total: results.length,
|
|
138
141
|
query_time_ms: query_time_ms,
|
|
139
|
-
by_kind:
|
|
142
|
+
by_kind: Archsight::MCP.count_by_kind(results)
|
|
140
143
|
}
|
|
141
144
|
end
|
|
142
145
|
|
|
@@ -144,10 +147,13 @@ module Archsight::Web::API::JsonHelpers
|
|
|
144
147
|
limit, offset = parse_pagination_params
|
|
145
148
|
output = parse_output_param
|
|
146
149
|
sorted = results.sort_by(&:name)
|
|
147
|
-
|
|
150
|
+
# `kind` narrows the hits to one kind; by_kind always counts all hits so the facets stay complete
|
|
151
|
+
kind = params[:kind].to_s
|
|
152
|
+
visible = kind.empty? ? sorted : sorted.select { |r| Archsight::MCP.kind_of(r) == kind }
|
|
153
|
+
pagination = paginate(visible, limit: limit, offset: offset)
|
|
148
154
|
|
|
149
155
|
instances = pagination[:items].map do |r|
|
|
150
|
-
resource_summary(r, output: output, omit_kind: false)
|
|
156
|
+
resource_summary(r, output: output, omit_kind: false).merge(highlights: Archsight::MCP.highlights(r))
|
|
151
157
|
end
|
|
152
158
|
|
|
153
159
|
{
|
|
@@ -157,6 +163,7 @@ module Archsight::Web::API::JsonHelpers
|
|
|
157
163
|
offset: pagination[:offset],
|
|
158
164
|
count: instances.length,
|
|
159
165
|
query_time_ms: query_time_ms,
|
|
166
|
+
by_kind: Archsight::MCP.count_by_kind(results),
|
|
160
167
|
instances: instances
|
|
161
168
|
}
|
|
162
169
|
end
|
|
@@ -165,11 +172,10 @@ module Archsight::Web::API::JsonHelpers
|
|
|
165
172
|
relations = {}
|
|
166
173
|
|
|
167
174
|
instance.class.relations.each do |verb, spec_key, kind|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
relations[verb] ||= {}
|
|
172
|
-
relations[verb][kind] = rels
|
|
175
|
+
found = instance.relations(verb, spec_key)
|
|
176
|
+
# derived relations hold resources of any kind: grouped by the kind each one is
|
|
177
|
+
groups = Archsight::Resources::DERIVED_VERBS.include?(verb) ? found.group_by(&:klass) : { kind => found }
|
|
178
|
+
groups.each { |target_kind, rels| (relations[verb] ||= {})[target_kind] = rels.map(&:name) unless rels.empty? }
|
|
173
179
|
end
|
|
174
180
|
|
|
175
181
|
relations
|
|
@@ -650,7 +650,12 @@ paths:
|
|
|
650
650
|
See the API description for full query language documentation.
|
|
651
651
|
|
|
652
652
|
Response includes: kind, name, metadata, spec (no relations/references).
|
|
653
|
-
Additional fields: query, query_time_ms, by_kind
|
|
653
|
+
Additional fields: query, query_time_ms, by_kind.
|
|
654
|
+
|
|
655
|
+
Every hit carries `highlights`: the values of the kind's summary attributes (annotations marked
|
|
656
|
+
`summary: true` in the kind definition, at most 3 per kind, computed ones included) that are set.
|
|
657
|
+
`by_kind` counts all hits of the query per kind, also when `kind` narrows the list, so a client can show
|
|
658
|
+
facets and let the user pick one.
|
|
654
659
|
operationId: search
|
|
655
660
|
tags:
|
|
656
661
|
- Search
|
|
@@ -674,6 +679,13 @@ paths:
|
|
|
674
679
|
orphan:
|
|
675
680
|
value: '<- none'
|
|
676
681
|
summary: Find orphan resources
|
|
682
|
+
- name: kind
|
|
683
|
+
in: query
|
|
684
|
+
required: false
|
|
685
|
+
description: Only return hits of this kind (`total` and paging follow; `by_kind` still counts all hits)
|
|
686
|
+
schema:
|
|
687
|
+
type: string
|
|
688
|
+
example: TechnologyArtifact
|
|
677
689
|
- $ref: '#/components/parameters/limit'
|
|
678
690
|
- $ref: '#/components/parameters/offset'
|
|
679
691
|
- $ref: '#/components/parameters/output'
|
|
@@ -691,6 +703,88 @@ paths:
|
|
|
691
703
|
schema:
|
|
692
704
|
$ref: '#/components/schemas/QueryError'
|
|
693
705
|
|
|
706
|
+
/api/v1/requirements:
|
|
707
|
+
get:
|
|
708
|
+
summary: Requirements of a selection of resources
|
|
709
|
+
description: |
|
|
710
|
+
The requirements that the resources selected by `of` realize (`implemented`), partially realize
|
|
711
|
+
(`partial`) or plan (`planned`), merged over all of them: one entry per requirement with the best status and
|
|
712
|
+
the resources it comes from (`by`). Sorted by priority (must, should, may, none), then name. `story` is
|
|
713
|
+
rendered HTML.
|
|
714
|
+
operationId: getRequirements
|
|
715
|
+
tags:
|
|
716
|
+
- Search
|
|
717
|
+
parameters:
|
|
718
|
+
- name: of
|
|
719
|
+
in: query
|
|
720
|
+
required: true
|
|
721
|
+
description: Query selecting the resources whose requirements are listed
|
|
722
|
+
schema:
|
|
723
|
+
type: string
|
|
724
|
+
example: 'ApplicationService: name =~ "Backup"'
|
|
725
|
+
- name: priority
|
|
726
|
+
in: query
|
|
727
|
+
required: false
|
|
728
|
+
description: Comma separated list, only requirements with one of these priorities
|
|
729
|
+
schema:
|
|
730
|
+
type: string
|
|
731
|
+
example: must,should
|
|
732
|
+
- name: status
|
|
733
|
+
in: query
|
|
734
|
+
required: false
|
|
735
|
+
description: Comma separated list, only requirements with one of these statuses
|
|
736
|
+
schema:
|
|
737
|
+
type: string
|
|
738
|
+
example: implemented,partial
|
|
739
|
+
responses:
|
|
740
|
+
'200':
|
|
741
|
+
description: The requirements
|
|
742
|
+
content:
|
|
743
|
+
application/json:
|
|
744
|
+
schema:
|
|
745
|
+
type: object
|
|
746
|
+
properties:
|
|
747
|
+
query:
|
|
748
|
+
type: string
|
|
749
|
+
total:
|
|
750
|
+
type: integer
|
|
751
|
+
query_time_ms:
|
|
752
|
+
type: number
|
|
753
|
+
requirements:
|
|
754
|
+
type: array
|
|
755
|
+
items:
|
|
756
|
+
type: object
|
|
757
|
+
properties:
|
|
758
|
+
name:
|
|
759
|
+
type: string
|
|
760
|
+
status:
|
|
761
|
+
type: string
|
|
762
|
+
enum: [implemented, partial, planned]
|
|
763
|
+
priority:
|
|
764
|
+
type: string
|
|
765
|
+
nullable: true
|
|
766
|
+
story:
|
|
767
|
+
type: string
|
|
768
|
+
nullable: true
|
|
769
|
+
by:
|
|
770
|
+
type: array
|
|
771
|
+
items:
|
|
772
|
+
type: object
|
|
773
|
+
properties:
|
|
774
|
+
kind:
|
|
775
|
+
type: string
|
|
776
|
+
name:
|
|
777
|
+
type: string
|
|
778
|
+
status:
|
|
779
|
+
type: string
|
|
780
|
+
enum: [implemented, partial, planned]
|
|
781
|
+
'400':
|
|
782
|
+
description: Invalid query, or an unknown priority or status
|
|
783
|
+
content:
|
|
784
|
+
application/json:
|
|
785
|
+
schema:
|
|
786
|
+
$ref: '#/components/schemas/QueryError'
|
|
787
|
+
|
|
694
788
|
/dot:
|
|
695
789
|
get:
|
|
696
790
|
summary: Get full architecture graph as DOT
|
|
@@ -1225,6 +1319,40 @@ components:
|
|
|
1225
1319
|
items:
|
|
1226
1320
|
$ref: '#/components/schemas/Kind'
|
|
1227
1321
|
|
|
1322
|
+
Highlight:
|
|
1323
|
+
type: object
|
|
1324
|
+
required:
|
|
1325
|
+
- key
|
|
1326
|
+
- title
|
|
1327
|
+
- value
|
|
1328
|
+
properties:
|
|
1329
|
+
key:
|
|
1330
|
+
type: string
|
|
1331
|
+
description: Annotation key, exactly as the query language takes it
|
|
1332
|
+
example: activity/status
|
|
1333
|
+
title:
|
|
1334
|
+
type: string
|
|
1335
|
+
description: Display title of the annotation
|
|
1336
|
+
example: Activity Status
|
|
1337
|
+
value:
|
|
1338
|
+
description: The value; an array for list annotations, a number for Integer/Float typed ones
|
|
1339
|
+
oneOf:
|
|
1340
|
+
- type: string
|
|
1341
|
+
- type: number
|
|
1342
|
+
- type: array
|
|
1343
|
+
items:
|
|
1344
|
+
type: string
|
|
1345
|
+
format:
|
|
1346
|
+
type: string
|
|
1347
|
+
nullable: true
|
|
1348
|
+
description: Rendering hint of the annotation (tag_word, tag_list, markdown, link, ...)
|
|
1349
|
+
example: tag_word
|
|
1350
|
+
type:
|
|
1351
|
+
type: string
|
|
1352
|
+
nullable: true
|
|
1353
|
+
description: Short name of the value type when the annotation declares one (Integer, Float, Time, Person, ...)
|
|
1354
|
+
example: Integer
|
|
1355
|
+
|
|
1228
1356
|
Instance:
|
|
1229
1357
|
type: object
|
|
1230
1358
|
required:
|
|
@@ -1247,6 +1375,11 @@ components:
|
|
|
1247
1375
|
type: object
|
|
1248
1376
|
additionalProperties: true
|
|
1249
1377
|
description: Instance specification
|
|
1378
|
+
highlights:
|
|
1379
|
+
type: array
|
|
1380
|
+
description: Values of the kind's summary attributes that are set (search results only)
|
|
1381
|
+
items:
|
|
1382
|
+
$ref: '#/components/schemas/Highlight'
|
|
1250
1383
|
relations:
|
|
1251
1384
|
type: object
|
|
1252
1385
|
additionalProperties:
|
|
@@ -1307,7 +1440,7 @@ components:
|
|
|
1307
1440
|
type: object
|
|
1308
1441
|
additionalProperties:
|
|
1309
1442
|
type: integer
|
|
1310
|
-
description: Result counts by kind (count mode only)
|
|
1443
|
+
description: Result counts by kind (search and count mode; all hits of the query, not only the current page)
|
|
1311
1444
|
instances:
|
|
1312
1445
|
type: array
|
|
1313
1446
|
items:
|
|
@@ -67,13 +67,14 @@ module Archsight::Web::API::PageHelpers
|
|
|
67
67
|
rest.to_s.lstrip
|
|
68
68
|
end
|
|
69
69
|
|
|
70
|
-
#
|
|
70
|
+
# The other pages that link to this page with [[name]] or [[title]] (optionally with |label): the pages that mention it
|
|
71
|
+
# (see Archsight::References), so a link in code does not count and a page is not listed twice.
|
|
71
72
|
def page_backlinks(page)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
73
|
+
page.references
|
|
74
|
+
.select { |ref| ref[:verb] == :mentions && ref[:instance].klass == "Page" && !ref[:instance].equal?(page) }
|
|
75
|
+
.map { |ref| ref[:instance] }
|
|
76
|
+
.uniq
|
|
77
|
+
.sort_by(&:name)
|
|
78
|
+
.map { |other| { name: other.name, title: other.title } }
|
|
78
79
|
end
|
|
79
80
|
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Archsight; end
|
|
4
|
+
module Archsight::Web; end
|
|
5
|
+
module Archsight::Web::API; end
|
|
6
|
+
|
|
7
|
+
# Parameters and JSON of `GET /api/v1/requirements` (see Archsight::Requirements)
|
|
8
|
+
module Archsight::Web::API::RequirementsHelpers
|
|
9
|
+
# @param name [Symbol] a comma separated list parameter, every value of which must be one of `allowed`
|
|
10
|
+
def csv_param(name, allowed)
|
|
11
|
+
values = params[name].to_s.split(",").map(&:strip).reject(&:empty?)
|
|
12
|
+
invalid = values - allowed
|
|
13
|
+
json_error("Parameter '#{name}' must be #{allowed.join(", ")}, not '#{invalid.first}'", status: 400, error_type: "BadRequest") unless invalid.empty?
|
|
14
|
+
|
|
15
|
+
values
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def build_requirements_response(query, requirements, query_time_ms)
|
|
19
|
+
{
|
|
20
|
+
query: query,
|
|
21
|
+
total: requirements.length,
|
|
22
|
+
query_time_ms: query_time_ms,
|
|
23
|
+
requirements: requirements.map { |r| r.merge(story: r[:story] && markdown(r[:story])) }
|
|
24
|
+
}
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -4,6 +4,7 @@ require "sinatra/base"
|
|
|
4
4
|
require "sinatra/extension"
|
|
5
5
|
require_relative "json_helpers"
|
|
6
6
|
require_relative "page_helpers"
|
|
7
|
+
require_relative "requirements_helpers"
|
|
7
8
|
require_relative "../../assets"
|
|
8
9
|
|
|
9
10
|
module Archsight; end
|
|
@@ -16,6 +17,7 @@ module Archsight::Web::API::Routes
|
|
|
16
17
|
|
|
17
18
|
helpers Archsight::Web::API::JsonHelpers
|
|
18
19
|
helpers Archsight::Web::API::PageHelpers
|
|
20
|
+
helpers Archsight::Web::API::RequirementsHelpers
|
|
19
21
|
|
|
20
22
|
# Rendering is CPU work, previews are typed, not pasted
|
|
21
23
|
MAX_DIAGRAM_SOURCE = 100_000
|
|
@@ -110,6 +112,23 @@ module Archsight::Web::API::Routes
|
|
|
110
112
|
end
|
|
111
113
|
end
|
|
112
114
|
|
|
115
|
+
# GET /api/v1/requirements - The requirements of the resources a query selects (see Archsight::Requirements)
|
|
116
|
+
get "/api/v1/requirements" do
|
|
117
|
+
query = params[:of]
|
|
118
|
+
json_error("Query parameter 'of' is required", status: 400, error_type: "BadRequest") unless query
|
|
119
|
+
|
|
120
|
+
priority = csv_param(:priority, Archsight::Requirements::PRIORITIES)
|
|
121
|
+
status = csv_param(:status, Archsight::Requirements::STATUSES.values)
|
|
122
|
+
start_time = Time.now
|
|
123
|
+
|
|
124
|
+
begin
|
|
125
|
+
requirements = Archsight::Requirements.collect(db, of: query, priority: priority, status: status)
|
|
126
|
+
json_response(build_requirements_response(query, requirements, ((Time.now - start_time) * 1000).round(2)))
|
|
127
|
+
rescue Archsight::Query::QueryError => e
|
|
128
|
+
json_error(e.message, status: 400, error_type: "QueryError", query: query)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
113
132
|
# GET /api/v1/assets/*path - Images and draw.io diagrams that markdown embeds. The path is relative to the
|
|
114
133
|
# resources directory; only files of the allowed types inside it are ever served, never anything outside of
|
|
115
134
|
# it and never resource definitions (see Archsight::Assets). Outside, missing and not-served-type all
|
|
@@ -87,9 +87,14 @@ class Archsight::Web::Application < Sinatra::Base
|
|
|
87
87
|
mcp_server.register_tool(Archsight::MCP::ResourceDocTool)
|
|
88
88
|
mcp_server.register_tool(Archsight::MCP::ExecuteAnalysisTool)
|
|
89
89
|
|
|
90
|
+
# The server is meant to be deployed (behind an ingress under its own hostname), and the web UI and API do not
|
|
91
|
+
# restrict who may reach them either. FastMcp's default would only accept the Origin / Host `localhost`
|
|
92
|
+
# (DNS-rebinding protection) and answer every other hostname with 403, so the check is switched off: an empty
|
|
93
|
+
# list of allowed origins disables it.
|
|
90
94
|
use FastMcp::Transports::RackTransport, mcp_server,
|
|
91
95
|
path_prefix: "/mcp",
|
|
92
|
-
localhost_only: false
|
|
96
|
+
localhost_only: false,
|
|
97
|
+
allowed_origins: []
|
|
93
98
|
end
|
|
94
99
|
|
|
95
100
|
helpers Archsight::GraphvisRenderer, Archsight::Helpers
|
|
@@ -140,6 +145,9 @@ class Archsight::Web::Application < Sinatra::Base
|
|
|
140
145
|
html = Archsight::Helpers::AssetImages.rewrite(html, base_dir: base, resources_dir: Archsight.resources_dir) if base
|
|
141
146
|
# ```asd blocks become placeholders until the text passes below are done (see DiagramBlocks)
|
|
142
147
|
html, diagrams = Archsight::Helpers::DiagramBlocks.extract(html, resolver: Archsight::Helpers::ResourceResolver.new(db))
|
|
148
|
+
# so are ```view and ```requirements blocks (see ViewBlocks, RequirementsBlocks)
|
|
149
|
+
html, views = Archsight::Helpers::ViewBlocks.extract(html)
|
|
150
|
+
html, requirements = Archsight::Helpers::RequirementsBlocks.extract(html)
|
|
143
151
|
|
|
144
152
|
# Resolve relative URLs if we have a git URL (for repository READMEs)
|
|
145
153
|
if git_url && (base_url = github_raw_base_url(git_url))
|
|
@@ -157,7 +165,9 @@ class Archsight::Web::Application < Sinatra::Base
|
|
|
157
165
|
# Convert [[Target]] / [[Target|label]] wiki-style links to page and resource links
|
|
158
166
|
html = Archsight::Helpers::Embeds.new(db).render(html)
|
|
159
167
|
html = Archsight::Helpers::WikiLinks.new(db).render(Archsight::Helpers::Macros.render(html, context: Archsight::Helpers::Macros::Context.new(db, page)))
|
|
160
|
-
Archsight::Helpers::DiagramBlocks.restore(html, diagrams)
|
|
168
|
+
html = Archsight::Helpers::DiagramBlocks.restore(html, diagrams)
|
|
169
|
+
html = Archsight::Helpers::ViewBlocks.restore(html, views)
|
|
170
|
+
Archsight::Helpers::RequirementsBlocks.restore(html, requirements)
|
|
161
171
|
end
|
|
162
172
|
|
|
163
173
|
# Generate asset path with cache-busting query string based on file mtime
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.back-link[data-v-4823b89b]{z-index:100;font-size:var(--fs-sm);background:#fff;border:1px solid #ddd;border-radius:4px;padding:6px 14px;text-decoration:none;position:fixed;top:12px;right:12px;box-shadow:0 1px 4px #0000001a}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{$ as e,B as t,H as n,M as r,T as i,X as a,Y as o,j as s,k as c,t as l,x as u}from"./index-D0Q5GZRs.js";var d={class:`api-docs-page`},f={key:0,class:`pico-color-red-500`},p=l({__name:`ApiDocsPage`,setup(l){let p=u(),m=o(null),h=o(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 t(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}}),(t,o)=>(n(),r(`div`,d,[c(`a`,{href:`#`,class:`back-link`,onClick:o[0]||=i(e=>a(p).back(),[`prevent`])},`← Back`),h.value?(n(),r(`div`,f,e(h.value),1)):s(``,!0),c(`div`,{ref_key:`container`,ref:m},null,512)]))}},[[`__scopeId`,`data-v-4823b89b`]]);export{p as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{A as e,H as t,K as n,M as r,Y as i,k as a,s as o}from"./index-D0Q5GZRs.js";import{t as s}from"./WikiPage-DrvCHwJ7.js";var c={key:1},l={__name:`DocPage`,props:{filename:String},setup(l){let u=l,d=i(null);function f(e,t){let n=new DOMParser().parseFromString(e,`text/html`).body,r=n.querySelector(`:scope > article`)||n,i=r.querySelector(`h1`),a=i?.textContent.trim()||t.split(`/`).pop().replace(/[-_]/g,` `);i?.remove();let o=[...r.querySelectorAll(`h2[id], h3[id], h4[id]`)].map(e=>({level:Number(e.tagName[1]),id:e.id,text:e.textContent.trim()})),s=[{title:`Documentation`}];return t.startsWith(`resources/`)&&s.push({title:`Resource types`}),{title:a,html:r.innerHTML,toc:o.length>=2?o:[],breadcrumb:s}}async function p(){let e=await o(u.filename);d.value=e?f(e,u.filename):{title:`Not found`,html:`<p>Documentation not found.</p>`,toc:[],breadcrumb:[{title:`Documentation`}]},document.title=`${d.value.title} - Archsight`}return n(()=>u.filename,p,{immediate:!0}),(n,i)=>d.value?(t(),e(s,{key:0,title:d.value.title,html:d.value.html,breadcrumb:d.value.breadcrumb,toc:d.value.toc},null,8,[`title`,`html`,`breadcrumb`,`toc`])):(t(),r(`article`,c,[...i[0]||=[a(`p`,null,`Loading...`,-1)]]))}};export{l as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.diagram-preview[data-v-11568657]{flex-direction:column;gap:.5rem;min-height:0;display:flex}.preview-head[data-v-11568657]{color:var(--pico-muted-color);font-size:var(--fs-sm);text-transform:uppercase;letter-spacing:.04em;justify-content:space-between;align-items:center;display:flex}.preview-tools[data-v-11568657]{align-items:center;gap:.75rem;display:inline-flex}.preview-state[data-v-11568657]{text-transform:none;letter-spacing:0}.preview-layout[data-v-11568657]{gap:.15rem;display:inline-flex}.preview-layout button[data-v-11568657]{width:1.4rem;height:1.4rem;font-size:var(--fs-xs);color:var(--pico-muted-color);border-radius:var(--pico-border-radius);cursor:pointer;background:0 0;border:1px solid #0000;justify-content:center;align-items:center;margin:0;padding:0;display:inline-flex}.preview-layout button[data-v-11568657]:hover,.preview-layout button[data-v-11568657]:focus-visible{color:var(--pico-primary);border-color:var(--pico-muted-border-color)}.preview-layout button.active[data-v-11568657]{color:var(--pico-primary);border-color:var(--pico-primary)}.preview-error[data-v-11568657]{border-left:4px solid var(--pico-del-color);background:var(--pico-mark-background-color);font-size:var(--fs-sm);overflow-wrap:anywhere;padding:.35rem .6rem}.preview-body[data-v-11568657]{min-height:0;overflow:auto}.preview-body.stale[data-v-11568657]{opacity:.45}.preview-body[data-v-11568657] .asd-diagram{margin:0}.preview-empty[data-v-11568657]{color:var(--pico-muted-color);font-size:var(--fs-xs);margin:0}.field-group[data-v-fe1221de]{margin-bottom:0}.field-group label[data-v-fe1221de]{margin-bottom:.25rem}.field-group label .required[data-v-fe1221de]{color:var(--pico-del-color);margin-left:.15rem}.field-group input[data-v-fe1221de],.field-group select[data-v-fe1221de],.field-group textarea[data-v-fe1221de]{margin-bottom:0}.field-description[data-v-fe1221de]{font-size:var(--fs-xs);color:var(--pico-muted-color);margin-top:.25rem}.field-error[data-v-fe1221de]{color:var(--pico-del-color);font-size:var(--fs-xs);margin-top:.25rem}.field-full-width[data-v-fe1221de]{grid-column:1/-1}.markdown-field textarea[data-v-fe1221de]{font-family:var(--font-mono);font-size:var(--fs-sm);resize:vertical;tab-size:2;min-height:24rem;line-height:1.5}.markdown-field.page-body textarea[data-v-fe1221de]{min-height:65vh}.code-field textarea[data-v-fe1221de]{font-family:var(--font-mono);font-size:var(--fs-sm);resize:vertical;tab-size:2;white-space:pre;overflow-wrap:normal;min-height:600px;line-height:1.5;overflow-x:auto}.field-diagram-preview[data-v-fe1221de]{margin-top:.75rem}.code-field.with-preview.layout-right[data-v-fe1221de]{grid-template-columns:minmax(0,1fr) minmax(0,1fr);align-items:start;gap:1.5rem;display:grid}.code-field.with-preview.layout-right .field-diagram-preview[data-v-fe1221de]{max-height:calc(100vh - 2rem);margin-top:0;position:sticky;top:1rem}.code-field.with-preview.layout-full .code-source[data-v-fe1221de]{display:none}.code-field.with-preview.layout-full .field-diagram-preview[data-v-fe1221de]{margin-top:0}.future-hint[data-v-fe1221de]{color:var(--pico-muted-color);font-size:var(--fs-xs);align-items:center;gap:.35rem;margin-top:.5rem;display:flex}.label-row[data-v-fe1221de]{justify-content:space-between;align-items:center;margin-bottom:.25rem;display:flex}.label-row label[data-v-fe1221de]{margin-bottom:0}.btn-edit-markdown[data-v-fe1221de]{font-size:var(--fs-xs);color:var(--pico-muted-color);border:1px solid var(--pico-muted-border-color);border-radius:var(--pico-border-radius);cursor:pointer;background:0 0;align-items:center;gap:.35rem;padding:.25rem .5rem;transition:all .15s;display:inline-flex}.btn-edit-markdown[data-v-fe1221de]:hover{color:var(--pico-primary);border-color:var(--pico-primary);background-color:#0000}.btn-edit-markdown i[data-v-fe1221de]{font-size:var(--fs-sm)}@media (prefers-color-scheme:dark){.btn-edit-markdown[data-v-fe1221de]{color:#8b949e;border-color:#ffffff26}.btn-edit-markdown[data-v-fe1221de]:hover{color:#58a6ff;border-color:#58a6ff}}.relations-editor[data-v-a987a6fe]{flex-direction:column;gap:.75rem;display:flex}#relations-list[data-v-a987a6fe]{background-color:var(--pico-background-color);border:1px solid var(--pico-muted-border-color);border-radius:4px;flex-direction:column;display:flex}#relations-list[data-v-a987a6fe]:empty{display:none}.relation-item[data-v-a987a6fe]{font-size:var(--fs-sm);border-bottom:1px solid var(--pico-muted-border-color);justify-content:space-between;align-items:center;gap:.5rem;padding:.35rem .5rem;display:flex}.relation-item[data-v-a987a6fe]:last-child{border-bottom:none}.relation-text[data-v-a987a6fe]{white-space:nowrap;text-overflow:ellipsis;flex:1;overflow:hidden}.relation-text strong[data-v-a987a6fe]{font-weight:600}.relation-text em[data-v-a987a6fe]{color:var(--pico-muted-color);font-style:normal}.btn-remove[data-v-a987a6fe]{width:1.5rem;height:1.5rem;color:var(--pico-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-a987a6fe]:hover{background-color:var(--pico-del-color);color:#fff}.btn-remove i[data-v-a987a6fe]{font-size:var(--fs-xs)}.add-relation-row[data-v-a987a6fe]{align-items:center;gap:.5rem;display:flex}.add-relation-row select[data-v-a987a6fe]{flex:1;margin:0}.add-relation-row select[data-v-a987a6fe]:first-child{flex:1.2}.btn-add[data-v-a987a6fe]{white-space:nowrap;flex-shrink:0;margin:0}.no-relations[data-v-a987a6fe]{color:var(--pico-muted-color);margin:0;font-style:italic}@media (width<=768px){.add-relation-row[data-v-a987a6fe]{flex-direction:column}.add-relation-row select[data-v-a987a6fe],.add-relation-row button[data-v-a987a6fe]{width:100%}}.yaml-output-container[data-v-b5ea938f]{width:100%}.yaml-success[data-v-b5ea938f]{color:var(--pico-ins-color);align-items:center;gap:.5rem;margin:0 0 1rem;font-weight:500;display:flex}#yaml-content[data-v-b5ea938f]{margin:0 calc(var(--pico-block-spacing-horizontal) * -1);padding:var(--pico-block-spacing-horizontal);background-color:var(--pico-code-background-color);border-radius:0;overflow-x:auto}#yaml-content code[data-v-b5ea938f]{font-size:var(--fs-sm);white-space:pre;padding:0;line-height:1.5}footer[data-v-b5ea938f]{justify-content:space-between;align-items:center;gap:1rem;display:flex}.yaml-file-info[data-v-b5ea938f],.yaml-instructions[data-v-b5ea938f]{font-size:var(--fs-sm);color:var(--pico-muted-color);flex:1;gap:.5rem;min-width:0;margin:0;line-height:1.5;display:flex}.yaml-file-info[data-v-b5ea938f]{align-items:center}.yaml-instructions[data-v-b5ea938f]{align-items:flex-start}.yaml-file-info>span[data-v-b5ea938f],.yaml-instructions>span[data-v-b5ea938f]{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.yaml-file-info>i[data-v-b5ea938f],.yaml-instructions>i[data-v-b5ea938f]{flex-shrink:0}.yaml-actions[data-v-b5ea938f]{flex-shrink:0;gap:.5rem;display:flex}.yaml-actions button[data-v-b5ea938f],.yaml-actions [role=button][data-v-b5ea938f]{width:auto;margin:0}.conflict-error[data-v-b5ea938f]{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-b5ea938f]{flex-shrink:0}@media (width<=768px){footer[data-v-b5ea938f]{flex-direction:column;align-items:stretch}.yaml-actions[data-v-b5ea938f]{flex-direction:column}.yaml-actions button[data-v-b5ea938f],.yaml-actions [role=button][data-v-b5ea938f]{width:100%}}#markdown-editor-overlay[data-v-e8d0cc10]{z-index:1000;position:fixed;inset:0}.markdown-editor-backdrop[data-v-e8d0cc10]{background-color:#00000080;position:absolute;inset:0}.markdown-editor-panel[data-v-e8d0cc10]{background-color:var(--pico-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-e8d0cc10]{background-color:var(--pico-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-e8d0cc10]{border-radius:var(--pico-border-radius);min-width:2rem;height:2rem;color:var(--pico-color);font-size:var(--fs-xs);cursor:pointer;background:0 0;border:1px solid #0000;justify-content:center;align-items:center;margin:0;padding:0 .5rem;font-weight:600;transition:all .15s;display:inline-flex}.toolbar-btn[data-v-e8d0cc10]:hover{background-color:var(--pico-muted-border-color);border-color:var(--pico-muted-border-color)}.toolbar-btn[data-v-e8d0cc10]:active{background-color:var(--pico-primary);border-color:var(--pico-primary);color:var(--pico-primary-inverse)}.toolbar-btn i[data-v-e8d0cc10]{font-size:var(--fs-md)}.toolbar-divider[data-v-e8d0cc10]{background-color:var(--pico-muted-border-color);width:1px;height:1.5rem;margin:0 .5rem}.toolbar-spacer[data-v-e8d0cc10]{flex:1}.toolbar-action[data-v-e8d0cc10]{font-size:var(--fs-xs);margin:0;padding:.4rem .75rem}.toolbar-select[data-v-e8d0cc10]{width:auto;min-width:11rem;max-width:15rem;height:2rem;font-size:var(--fs-xs);border:1px solid var(--pico-muted-border-color,#e4e4e7);border-radius:var(--pico-border-radius,.375rem);color:var(--pico-color,inherit);cursor:pointer;background-color:#0000;flex-shrink:0;margin:0;padding:0 .75rem}.toolbar-select[data-v-e8d0cc10]:hover{border-color:var(--pico-primary,#1e88e5)}.toolbar-select[data-v-e8d0cc10]:focus{border-color:var(--pico-primary,#1e88e5);outline:none}.editor-split[data-v-e8d0cc10]{flex-direction:column;flex:1;min-height:0;display:flex}.editor-diagram-preview[data-v-e8d0cc10]{border-top:1px solid var(--pico-muted-border-color,#e4e4e7);flex:0 45%;padding:.5rem 1.5rem 1rem;overflow:hidden}.editor-split.layout-right[data-v-e8d0cc10]{flex-direction:row}.editor-split.layout-right .lexical-editor-container[data-v-e8d0cc10]{flex:50%;min-width:0}.editor-split.layout-right .editor-diagram-preview[data-v-e8d0cc10]{border-top:none;border-left:1px solid var(--pico-muted-border-color,#e4e4e7);flex:50%;min-width:0;padding:1rem 1.5rem;overflow-y:auto}.editor-split.layout-full .lexical-editor-container[data-v-e8d0cc10]{display:none}.editor-split.layout-full .editor-diagram-preview[data-v-e8d0cc10]{border-top:none;flex:auto;overflow-y:auto}.editor-split.layout-hidden .editor-diagram-preview[data-v-e8d0cc10]{flex:none;padding-bottom:.5rem}.lexical-editor-container[data-v-e8d0cc10]{flex-direction:column;flex:1;min-height:0;display:flex;position:relative}#lexical-editor-root[data-v-e8d0cc10]{min-height:0;font-family:var(--pico-font-family);font-size:var(--fs-sm);flex:1;padding:1rem 1.5rem;line-height:1.55;position:relative;overflow-y:auto}#lexical-editor-root[data-v-e8d0cc10]:focus{outline:none}#lexical-editor-root[data-v-e8d0cc10] p{margin:0 0 .75rem}#lexical-editor-root[data-v-e8d0cc10] p:last-child{margin-bottom:0}#lexical-editor-root[data-v-e8d0cc10] h1,#lexical-editor-root[data-v-e8d0cc10] h2,#lexical-editor-root[data-v-e8d0cc10] h3,#lexical-editor-root[data-v-e8d0cc10] h4{margin:1rem 0 .5rem;font-weight:600}#lexical-editor-root[data-v-e8d0cc10] h1{font-size:var(--fs-xl)}#lexical-editor-root[data-v-e8d0cc10] h2{font-size:var(--fs-lg)}#lexical-editor-root[data-v-e8d0cc10] h3{font-size:var(--fs-md)}#lexical-editor-root[data-v-e8d0cc10] h4{font-size:1em}#lexical-editor-root[data-v-e8d0cc10] h1:first-child,#lexical-editor-root[data-v-e8d0cc10] h2:first-child,#lexical-editor-root[data-v-e8d0cc10] h3:first-child{margin-top:0}#lexical-editor-root[data-v-e8d0cc10] ul,#lexical-editor-root[data-v-e8d0cc10] ol{margin:.5rem 0;padding-left:1.5rem}#lexical-editor-root[data-v-e8d0cc10] li{margin:.25rem 0}#lexical-editor-root[data-v-e8d0cc10] 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-e8d0cc10] code{background-color:var(--pico-code-background-color,#f4f4f5);border:1px solid var(--pico-muted-border-color,#e4e4e7);font-family:var(--pico-font-family-monospace,monospace);font-size:var(--fs-sm);border-radius:3px;padding:.15rem .35rem}#lexical-editor-root[data-v-e8d0cc10] pre{background-color:var(--pico-code-background-color);border-radius:var(--pico-border-radius);margin:.75rem 0;padding:1rem;position:relative;overflow-x:auto}#lexical-editor-root[data-v-e8d0cc10] pre code{background:0 0;padding:0}#lexical-editor-root[data-v-e8d0cc10] .lexical-code{background-color:var(--pico-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);font-size:var(--fs-sm);white-space:pre;tab-size:2;box-sizing:border-box;border-radius:0;margin:.75rem -1.5rem;padding:1rem 1.5rem;line-height:1.5;display:block;position:relative;overflow-x:auto}#lexical-editor-root[data-v-e8d0cc10] .lexical-code .tokenComment{color:#6a737d}#lexical-editor-root[data-v-e8d0cc10] .lexical-code .tokenPunctuation{color:#24292e}#lexical-editor-root[data-v-e8d0cc10] .lexical-code .tokenProperty{color:#005cc5}#lexical-editor-root[data-v-e8d0cc10] .lexical-code .tokenSelector{color:#032f62}#lexical-editor-root[data-v-e8d0cc10] .lexical-code .tokenOperator{color:#005cc5}#lexical-editor-root[data-v-e8d0cc10] .lexical-code .tokenAttr{color:#d73a49}#lexical-editor-root[data-v-e8d0cc10] .lexical-code .tokenVariable{color:#e36209}#lexical-editor-root[data-v-e8d0cc10] .lexical-code .tokenFunction{color:#6f42c1}#lexical-editor-root[data-v-e8d0cc10] a{color:var(--pico-primary);text-decoration:underline}#lexical-editor-root[data-v-e8d0cc10] .lexical-link{color:var(--pico-primary,#1e88e5);cursor:pointer;text-decoration:underline}#lexical-editor-root[data-v-e8d0cc10] .lexical-link:hover{text-decoration:none}#lexical-editor-root[data-v-e8d0cc10] strong,#lexical-editor-root[data-v-e8d0cc10] .lexical-bold{font-weight:700}#lexical-editor-root[data-v-e8d0cc10] em,#lexical-editor-root[data-v-e8d0cc10] .lexical-italic{font-style:italic}#lexical-editor-root[data-v-e8d0cc10] u,#lexical-editor-root[data-v-e8d0cc10] .lexical-underline{text-decoration:underline}#lexical-editor-root[data-v-e8d0cc10] s,#lexical-editor-root[data-v-e8d0cc10] .lexical-strikethrough{text-decoration:line-through}@media (prefers-color-scheme:dark){.markdown-editor-panel[data-v-e8d0cc10]{background-color:var(--pico-card-background-color,#1e1e1e);box-shadow:0 8px 32px #00000080,0 2px 8px #0000004d}.markdown-editor-backdrop[data-v-e8d0cc10]{background-color:#000000b3}.lexical-toolbar[data-v-e8d0cc10]{background-color:var(--pico-card-background-color,#1e1e1e);box-shadow:0 2px 4px #0000004d}.toolbar-btn[data-v-e8d0cc10]{color:var(--pico-color,#c9d1d9)}.toolbar-btn[data-v-e8d0cc10]:hover{background-color:#ffffff1a;border-color:#ffffff1a}.toolbar-btn[data-v-e8d0cc10]:active{background-color:var(--pico-primary);border-color:var(--pico-primary)}.toolbar-divider[data-v-e8d0cc10]{background-color:#ffffff26}.toolbar-select[data-v-e8d0cc10]{color:var(--pico-color,#c9d1d9);background-color:#0000;border-color:#fff3}.toolbar-select[data-v-e8d0cc10]:hover,.toolbar-select[data-v-e8d0cc10]:focus{border-color:var(--pico-primary,#58a6ff)}#lexical-editor-root[data-v-e8d0cc10]{color:var(--pico-color,#c9d1d9);background-color:var(--pico-card-background-color,#1e1e1e)}#lexical-editor-root[data-v-e8d0cc10] blockquote{color:#8b949e;border-left-color:#fff3}#lexical-editor-root[data-v-e8d0cc10] code{color:#c9d1d9;background-color:#6e768166;border-color:#ffffff1a}#lexical-editor-root[data-v-e8d0cc10] .lexical-code{color:#c9d1d9;background-color:#0d1117;border-color:#ffffff1a}#lexical-editor-root[data-v-e8d0cc10] .lexical-code .tokenComment{color:#8b949e}#lexical-editor-root[data-v-e8d0cc10] .lexical-code .tokenPunctuation{color:#c9d1d9}#lexical-editor-root[data-v-e8d0cc10] .lexical-code .tokenProperty{color:#79c0ff}#lexical-editor-root[data-v-e8d0cc10] .lexical-code .tokenSelector{color:#a5d6ff}#lexical-editor-root[data-v-e8d0cc10] .lexical-code .tokenOperator{color:#79c0ff}#lexical-editor-root[data-v-e8d0cc10] .lexical-code .tokenAttr{color:#ff7b72}#lexical-editor-root[data-v-e8d0cc10] .lexical-code .tokenVariable{color:#ffa657}#lexical-editor-root[data-v-e8d0cc10] .lexical-code .tokenFunction{color:#d2a8ff}#lexical-editor-root[data-v-e8d0cc10] .lexical-link,#lexical-editor-root[data-v-e8d0cc10] a{color:#58a6ff}}.editor-form[data-v-822cad5e]{max-width:100%}.annotations-grid[data-v-822cad5e]{grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:1rem;display:grid}.annotations-grid .field-full-width[data-v-822cad5e]{grid-column:1/-1}.form-actions[data-v-822cad5e]{gap:.75rem;margin-top:1rem;display:flex}.form-actions button[data-v-822cad5e]{width:auto;margin:0;padding:.5rem 1.25rem}.btn-back[data-v-822cad5e]{align-items:center;gap:.25rem;text-decoration:none;display:inline-flex}.relations-editor .relation-item[data-v-822cad5e]{align-items:center;gap:.5rem;padding:.25rem 0;display:flex}.relations-editor .relation-text[data-v-822cad5e]{flex:1}.relations-editor .btn-remove[data-v-822cad5e]{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-822cad5e]{align-items:flex-start;gap:.5rem;margin-top:.5rem;display:flex}.relations-editor .add-relation-row select[data-v-822cad5e]{flex:1;margin-bottom:0}.relations-editor .btn-add[data-v-822cad5e]{white-space:nowrap;margin-bottom:0}.no-relations[data-v-822cad5e]{color:var(--pico-muted-color);font-style:italic}.field-group[data-v-822cad5e]{margin-bottom:.5rem}.field-group .label-row[data-v-822cad5e]{align-items:center;gap:.5rem;margin-bottom:.25rem;display:flex}.field-group .required[data-v-822cad5e]{color:var(--pico-del-color,#c62828)}.field-group .field-description[data-v-822cad5e]{color:var(--pico-muted-color);margin-top:.125rem;display:block}.field-group .field-error[data-v-822cad5e]{color:var(--pico-del-color,#c62828);font-size:var(--fs-sm);margin-top:.125rem}.field-group .btn-edit-markdown[data-v-822cad5e]{border:1px solid var(--pico-primary);color:var(--pico-primary);cursor:pointer;font-size:var(--fs-sm);background:0 0;border-radius:4px;width:auto;margin:0;padding:.125rem .5rem}.field-group .future-hint[data-v-822cad5e]{color:var(--pico-muted-color);margin-top:.125rem;display:block}.field-group textarea[data-v-822cad5e]{min-height:8rem;font-family:inherit}.field-group .code-field textarea[data-v-822cad5e]{font-family:var(--font-mono)}.yaml-output-container .yaml-success[data-v-822cad5e]{color:var(--pico-ins-color,#2e7d32)}.yaml-output-container pre[data-v-822cad5e]{max-height:60vh;overflow:auto}.yaml-output-container .yaml-actions[data-v-822cad5e]{gap:.5rem;margin-top:.5rem;display:flex}.yaml-output-container .yaml-file-info[data-v-822cad5e],.yaml-output-container .yaml-instructions[data-v-822cad5e]{color:var(--pico-muted-color)}.yaml-output-container .copied[data-v-822cad5e],.yaml-output-container .saved[data-v-822cad5e]{opacity:.8}.conflict-error[data-v-822cad5e]{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-822cad5e]{width:100%}.editor-header[data-v-822cad5e]{border-bottom:1px solid var(--pico-muted-border-color);justify-content:space-between;align-items:center;margin-bottom:1.5rem;padding-bottom:1rem;display:flex}.editor-header h2[data-v-822cad5e]{align-items:center;gap:.75rem;margin:0;display:flex}.header-actions[data-v-822cad5e]{align-items:center;gap:.5rem;display:flex}.btn-header[data-v-822cad5e]{font-size:var(--fs-sm);color:var(--pico-muted-color);border-radius:var(--pico-border-radius);cursor:pointer;background:0 0;border:1px solid #0000;align-items:center;gap:.35rem;padding:.4rem .75rem;text-decoration:none;transition:all .15s;display:inline-flex}.btn-header[data-v-822cad5e]:hover{color:var(--pico-primary);border-color:var(--pico-primary)}.yaml-output>header[data-v-822cad5e]{justify-content:space-between;align-items:center;display:flex}.yaml-output>header h3[data-v-822cad5e]{align-items:center;gap:.5rem;margin:0;display:flex}@media (width<=768px){.editor-header[data-v-822cad5e]{flex-direction:column;align-items:flex-start;gap:1rem}.annotations-grid[data-v-822cad5e]{grid-template-columns:1fr}}
|