Files
clide/app/lib/extension/src/manifest.dart
T
jpmschweitzerandClaude b13483e2d5 scaffold Flutter app — kernel, extensions, widgets, Tier 0 built-ins
First real content under app/. Lays the whole Tier 0 foundation in one
commit because the pieces depend on each other circularly (kernel →
extension → widgets → built-ins all reference types from the layer
below); splitting would leave intermediate commits that don't compile.

Key shapes:

  * bare WidgetsApp root — no Material, no Cupertino, no Scaffold.
    ClideTheme InheritedWidget is the only source of tokens.
  * ClideKernel InheritedWidget aggregates 18 services (settings,
    project, extensions, theme, panels, events, ipc, commands +
    palette + keybindings, clipboard, files, notify, dialog, tray,
    secrets, os, net, focus, log, i18n). ExtensionContext exposes
    them through a stable interface.
  * ClideExtension + sealed ContributionPoint hierarchy (Tab,
    StatusItem, Toolbar, Command, TrayItem, LayoutPreset). One
    manifest ships N contributions into kernel slots.
  * Three-tier theme pipeline: palette (named colors) → semantic
    roles → ~60 surface tokens. Defaults at each layer so legacy
    palette-only themes produce a complete SurfaceTokens.
  * A11y baked in from day one — every interactive primitive wraps
    in Semantics(label:, hint:, button:); ensureSemantics() at boot;
    theme/contrast.dart helper exposes token pairs for the WCAG
    gate in the a11y test suite.
  * i18n ported from fframe's L10n pattern (text-driven, namespaced
    JSON catalogs, caller-supplied placeholders) with a proper
    locale fallback chain (exact → language → default → placeholder)
    fframe lacks.
  * Four Tier-0 built-ins live (default-layout, welcome, ipc-status,
    theme-picker) plus 17 id-reserving stubs so later tiers fill in
    without rename churn.

.gitignore extended to cover app/ sub-package artefacts and the
Playwright harness scratch dirs.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-21 15:39:41 +02:00

61 lines
1.8 KiB
Dart

import 'dart:io';
import 'package:yaml/yaml.dart';
/// A parsed third-party extension manifest.
///
/// Built-in extensions don't need a manifest file — they compile in as
/// Dart subclasses of [ClideExtension]. Third-party extensions ship a
/// `manifest.yaml` under `~/.clide/extensions/<id>/` alongside their
/// Lua entrypoint; this class parses and validates that file.
class ExtensionManifest {
const ExtensionManifest({
required this.id,
required this.title,
required this.version,
required this.dependsOn,
required this.entry,
required this.schemaVersion,
});
final String id;
final String title;
final String version;
final List<String> dependsOn;
final String entry; // relative path to lua entrypoint
final int schemaVersion;
factory ExtensionManifest.fromYamlString(String text) {
final doc = loadYaml(text);
if (doc is! Map) {
throw const FormatException('manifest root is not a map');
}
final id = doc['id'];
if (id is! String || id.isEmpty) {
throw const FormatException('manifest missing `id`');
}
final title = (doc['title'] as String?) ?? id;
final version = (doc['version'] as String?) ?? '0.0.0';
final entry = (doc['entry'] as String?) ?? 'extension.lua';
final schemaVersion = (doc['schema_version'] as int?) ?? 1;
final depsYaml = doc['depends_on'];
final deps = <String>[];
if (depsYaml is YamlList) {
for (final d in depsYaml) {
if (d is String) deps.add(d);
}
}
return ExtensionManifest(
id: id,
title: title,
version: version,
dependsOn: deps,
entry: entry,
schemaVersion: schemaVersion,
);
}
static Future<ExtensionManifest> fromFile(File f) async =>
ExtensionManifest.fromYamlString(await f.readAsString());
}