add format engine packages: jovial_svg, markdown, html_core (D-058)

Three format engines adopted per the new D-058 rule (format
engines clear the dependency bar; UI chrome stays in-house):

- jovial_svg 1.1.26 (BSD-3) — SVG renderer via CustomPaint
- markdown 7.2.2 (BSD-3) — GFM parser; renderer is ours
- flutter_widget_from_html_core 0.17.2 (MIT) — HTML renderer

Welcome screen now loads logo.svg via jovial_svg instead of a
raster PNG. POLICY.md updated with format engine rule. All three
documented in licenses.yaml.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-23 10:28:46 +02:00
co-authored by Claude Opus 4.6
parent fa0032d26d
commit a44a2efae7
6 changed files with 131 additions and 1 deletions
+1
View File
@@ -63,6 +63,7 @@ This is not a preference. It is the project's architectural stance, documented i
- **Prefer inlining over depending.** If a package is small and does one thing we need, copy the relevant code (with attribution) rather than taking on the dependency. License permitting, this is usually the right call for utilities under a few hundred lines.
- **Prefer vendoring over fetching.** When a dependency is unavoidable and small enough to own, fork it into the repo and maintain it ourselves. The dep graph stops at our fence.
- **Reject deep trees.** A direct dependency that itself has fifteen transitive dependencies is fifteen dependencies we are taking on, not one. Evaluate the whole subtree, not just the top.
- **Format engines clear the bar.** Packages that parse or render external file formats (SVG, markdown, HTML, terminal escapes, tree-sitter grammars) are adoptable — they are not shortcuts for lazy coding but maintained renderers for specs we didn't write. The distinction: UI chrome (panels, tabs, canvas, layout) we own; someone else's file format we adopt and sandbox. See D-058.
### Every dependency is audited
+33
View File
@@ -142,6 +142,39 @@ dependencies:
wasmtime engine. Source repos are tree-sitter/tree-sitter-<lang>
on GitHub; sqlite is dhcmrlchtdj/tree-sitter-sqlite (MIT).
- name: jovial_svg
kind: dart-package
version: "1.1.26"
homepage: https://pub.dev/packages/jovial_svg
license: BSD-3-Clause
purpose: >-
SVG renderer. CustomPaint-based — parses SVG at runtime into
a DAG of drawing commands and renders via Canvas. Used for
workspace SVG files and the app icon. Format engine, not UI
chrome.
- name: markdown
kind: dart-package
version: "7.2.2"
homepage: https://pub.dev/packages/markdown
license: BSD-3-Clause
purpose: >-
Markdown parser (AST only). Parses GFM (tables, task lists,
fenced code blocks) into a node tree. Clide owns the renderer
— walks the AST to produce Flutter widgets with tree-sitter
syntax highlighting in code blocks.
- name: flutter_widget_from_html_core
kind: dart-package
version: "0.17.2"
homepage: https://pub.dev/packages/flutter_widget_from_html_core
license: MIT
purpose: >-
HTML-to-widget renderer. Parses HTML into Flutter widgets
with no JavaScript execution. Used for rendering HTML content
in the viewer. Sandboxed — tag/attribute whitelist enforced
at usage site.
# Build-time-only dependencies — test runners, mocks, lints. Tracked
# here for audit completeness; NOT rendered in the About screen.
dev_dependencies:
+10
View File
@@ -32,4 +32,14 @@ Toolchain, supply chain, CI, ignore strategy.
- **Cost:** Script names have a convention to follow.
- **Raised by:** 2026-04-21 planning (caught during commit rehearsal).
### D-058: Format engines are adoptable dependencies
- **Date:** 2026-04-23
- **Decision:** The "own the rendering stack" guardrail applies to **UI chrome** — panels, tabs, panes, canvas, terminal, layout primitives. **Format engines** — packages that parse or render external file formats (SVG, markdown, HTML, terminal escape sequences, tree-sitter grammars) — are adoptable like any other dependency: vet, exact-pin, CVE-lock, document in `licenses.yaml`. They are not shortcuts for lazy coding; they are well-maintained renderers for formats we didn't invent. The distinction: if it renders *our* UI, we own it; if it renders *someone else's file format*, we adopt a parser/renderer and sandbox it.
- **Adopted under this rule:** `jovial_svg` (SVG renderer), `markdown` (MD parser; renderer is ours), `flutter_widget_from_html_core` (HTML renderer; sandboxed), `xterm` (terminal emulator), tree-sitter (syntax highlighting). Canvas (`CustomPaint` + `InteractiveViewer`) stays in-house — UI chrome, not a format engine.
- **Amendment to D-031 (prefer-zero-deps):** D-031's "prefer-zero-deps" still applies — every new dependency needs justification. This record clarifies that format engines clear the justification bar by default. The supply-chain gate (exact-pin, advisory review, `licenses.yaml`) still applies.
- **Rationale:** Reimplementing SVG, markdown, or VT100 parsing adds months of work for no fidelity gain. tree-sitter already set this precedent. The key is sandboxing: HTML rendering must whitelist tags/attributes; SVG must not execute scripts; markdown rendering goes through our own widget builder so we control the output.
- **Cost:** Each adopted engine adds transitive dependencies and supply-chain surface. Mitigated by exact-pinning and `make security`.
- **Cross-reference:** [D-031](#d-031-prefer-zero-deps-exact-pin), [D-042](#d-042-dependencies-documented-in-licensesyaml).
- **Raised by:** 2026-04-23 format engine evaluation.
---
+9 -1
View File
@@ -2,7 +2,9 @@ import 'dart:async';
import 'package:clide/kernel/kernel.dart';
import 'package:clide/widgets/widgets.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:flutter/widgets.dart';
import 'package:jovial_svg/jovial_svg.dart';
class WelcomeView extends StatelessWidget {
const WelcomeView({super.key});
@@ -54,7 +56,13 @@ class _Header extends StatelessWidget {
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Image.asset('assets/logo/logo-192.png', width: 72, height: 72),
SizedBox(
width: 72,
height: 72,
child: ScalableImageWidget.fromSISource(
si: ScalableImageSource.fromSvg(rootBundle, 'assets/logo/logo.svg'),
),
),
const SizedBox(width: 24),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
+72
View File
@@ -105,6 +105,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.7"
csslib:
dependency: transitive
description:
name: csslib
sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
equatable:
dependency: transitive
description:
@@ -160,6 +168,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_widget_from_html_core:
dependency: "direct main"
description:
name: flutter_widget_from_html_core
sha256: "7ff010b116f6abc16429923e616fbc727f3f65ef4cee12ffdb280aeecbc21e7f"
url: "https://pub.dev"
source: hosted
version: "0.17.2"
frontend_server_client:
dependency: transitive
description:
@@ -181,6 +197,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.3"
html:
dependency: transitive
description:
name: html
sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602"
url: "https://pub.dev"
source: hosted
version: "0.15.6"
http:
dependency: transitive
description:
name: http
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
url: "https://pub.dev"
source: hosted
version: "1.6.0"
http_multi_server:
dependency: transitive
description:
@@ -210,6 +242,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.5"
jovial_misc:
dependency: transitive
description:
name: jovial_misc
sha256: "4301011027d87b8b919cb862db84071a34448eadbb32cc8d40fe505424dfe69a"
url: "https://pub.dev"
source: hosted
version: "0.9.2"
jovial_svg:
dependency: "direct main"
description:
name: jovial_svg
sha256: a3cab3d46dc8df80cf0792e5e4246cbe13287542d9a357007114b1057da2fe89
url: "https://pub.dev"
source: hosted
version: "1.1.26"
leak_tracker:
dependency: transitive
description:
@@ -250,6 +298,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.0"
markdown:
dependency: "direct main"
description:
name: markdown
sha256: ef2a1298144e3f985cc736b22e0ccdaf188b5b3970648f2d9dc13efd1d9df051
url: "https://pub.dev"
source: hosted
version: "7.2.2"
matcher:
dependency: transitive
description:
@@ -314,6 +370,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.9.1"
petitparser:
dependency: transitive
description:
name: petitparser
sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675"
url: "https://pub.dev"
source: hosted
version: "7.0.2"
platform:
dependency: transitive
description:
@@ -551,6 +615,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.2.1"
xml:
dependency: transitive
description:
name: xml
sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025"
url: "https://pub.dev"
source: hosted
version: "6.6.1"
xterm:
dependency: "direct main"
description:
+6
View File
@@ -41,6 +41,12 @@ dependencies:
ffi: 2.1.3
jovial_svg: 1.1.26
markdown: 7.2.2
flutter_widget_from_html_core: 0.17.2
dev_dependencies:
flutter_test:
sdk: flutter