Files
clide/lib/extension/src/contribution.dart
T
jpmschweitzerandClaude Fable 5 1674d3021a tint the Claude sidebar tab icon with the Claude accent (T-418)
TabContribution gains an optional iconColor honoured by the icon rail:
full-strength when active/hovered, dimmed (70%) when idle, so the tint
reads as identity without outshouting the active-state border. The
Claude Activity tab sets claudeAccent (#D97757) — nominative use per the
licenses.yaml trademark note (it marks Claude's own panel).

Filed and closed as a try-it-out (user request); trivially revertible if
the accent doesn't land visually.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 13:30:22 +02:00

141 lines
5.0 KiB
Dart

import 'package:clide/clide.dart';
import 'package:clide/kernel/src/panels/slot_id.dart';
import 'package:flutter/widgets.dart';
/// One atom contributed by a [ClideExtension]. Extensions declare N of
/// these in a manifest; the kernel and slot hosts render them.
///
/// Adding a new contribution type: add a case to this sealed hierarchy,
/// extend the host dispatch in the default-layout extension, and bump
/// the extension manifest schema version.
sealed class ContributionPoint {
const ContributionPoint({required this.id});
/// Stable id for this contribution, unique within its extension.
final String id;
/// The slot this contribution targets, or `null` for non-slot
/// contributions (commands, events, grammars).
SlotId? get slot => null;
}
/// A tab in a slot that hosts tabs (sidebar / workspace / context).
class TabContribution extends ContributionPoint {
const TabContribution({
required super.id,
required this.slot,
required this.title,
required this.build,
this.icon,
this.iconColor,
this.priority = 0,
this.fileGlobs = const [],
this.listenable,
this.titleKey,
this.i18nNamespace,
});
@override
final SlotId slot;
final String title;
final WidgetBuilder build;
final Object? icon;
/// Optional identity tint for the icon-rail glyph (T-418).
final Color? iconColor;
final int priority;
final List<String> fileGlobs;
final Listenable? listenable;
/// When set, the slot host resolves the display title via
/// `i18n.string(titleKey, namespace: i18nNamespace, placeholder: title)`.
/// [title] stays as the English fallback (also used in tests/logs).
final String? titleKey;
/// The i18n namespace to look up [titleKey] in. Extensions usually
/// pass their own `id`. Required when [titleKey] is set.
final String? i18nNamespace;
}
/// A status-bar item. Order is determined by [priority] within each
/// alignment group; negative priorities float left, positive right.
class StatusItemContribution extends ContributionPoint {
const StatusItemContribution({required super.id, required this.build, this.priority = 0, this.listenable, this.flex = 0});
@override
SlotId get slot => Slots.statusbar;
final WidgetBuilder build;
final int priority;
final Listenable? listenable;
/// When > 0, the status bar wraps this item in
/// `Flexible(flex: flex, fit: FlexFit.loose)` so it yields width when the
/// bar is tight and any contained [ClideMarquee] can scroll (T-160).
/// Defaults to 0 (intrinsic/non-flex). Only meaningful for left-side items
/// (priority < 100); right-side items are always intrinsic-width.
final int flex;
}
/// A button in the main toolbar.
class ToolbarButtonContribution extends ContributionPoint {
const ToolbarButtonContribution({required super.id, required this.label, required this.onPressed, this.icon, this.tooltip, this.priority = 0});
@override
SlotId get slot => Slots.toolbar;
final String label;
final Object? icon;
final String? tooltip;
final int priority;
final VoidCallback onPressed;
}
/// A command extensions register with [CommandRegistry]. Surfaced by the
/// command palette, the keybinding resolver, and `clide` CLI subcommands.
class CommandContribution extends ContributionPoint {
const CommandContribution({required super.id, required this.command, required this.run, this.title, this.defaultBinding, this.bindingWhen});
final String command; // e.g. "git.commit"
final String? title; // "Git: Commit staged"
final String? defaultBinding; // e.g. "ctrl+shift+g"
/// Optional when-clause guarding [defaultBinding] (same grammar as keymap
/// YAML `when:`). Lets a global binding yield to a higher-context one — e.g.
/// `panel.focusMode.exit`'s `escape` stands down in Vim insert/visual mode so
/// the preset's `vim.mode.normal` wins (T-257). Null → binding always active.
final String? bindingWhen;
final Future<IpcResponse> Function(List<String> args) run;
}
/// Registers an item in the OS tray / menu-bar.
class TrayItemContribution extends ContributionPoint {
const TrayItemContribution({required super.id, required this.label, required this.onSelected, this.priority = 0});
@override
SlotId get slot => Slots.tray;
final String label;
final int priority;
final VoidCallback onSelected;
}
/// A named layout arrangement. One "classic" preset ships with
/// `builtin.default-layout`; other presets can be contributed.
class LayoutPresetContribution extends ContributionPoint {
const LayoutPresetContribution({required super.id, required this.displayName, required this.slots});
final String displayName;
final List<LayoutSlot> slots;
}
/// One slot in a [LayoutPresetContribution]. Describes where the slot
/// appears and its initial size/visibility.
class LayoutSlot {
const LayoutSlot({required this.slot, required this.position, this.defaultSize, this.minSize, this.maxSize, this.visible = true});
final SlotId slot;
final SlotPosition position;
final double? defaultSize;
final double? minSize;
final double? maxSize;
final bool visible;
}