Eight unresolved doc references and broken README-rewritten links that surfaced under `dart doc --validate-links`: - Library-scope refs `[spawn]`, `[openProject]` qualified to `[Backend.spawn]` / `[Backend.openProject]`; same treatment for `[resolvePaths]` / `[applyResolved]` on Toolchain. - `[D-41]` was a decision ID, not a Dart symbol — drop the brackets. - `[from]` from I18n.interpolated qualified to `[I18nReplacer.from]`. - `[DefaultSurfaceMap]` was a stale name (private `_defaultSurfaceMap` in resolver.dart); switch to backticked path reference since dartdoc can't link private members. - `[icons/]` was a directory, not a symbol; backticked path. - README links to `legacy/`, `docs/initial-plan.md`, `decisions/`, `LICENSE` rewritten as absolute github.com/postmeridiem/clide URLs so dartdoc stops re-rooting them into the doc tree. Co-Authored-By: Claude <noreply@anthropic.com>
83 lines
2.2 KiB
Dart
83 lines
2.2 KiB
Dart
import 'package:clide/widgets/widgets.dart';
|
||
import 'package:flutter/widgets.dart';
|
||
|
||
import 'claude_pane.dart';
|
||
|
||
/// Hosts the primary Claude pane plus N user-spawned secondary
|
||
/// sessions per D-41. Uses [MultitabPane] for the tab strip
|
||
/// (drag-reorder, close ×, + button) and [IndexedStack]-mode
|
||
/// keep-alive so switching tabs doesn't tear down the underlying
|
||
/// PTY-backed terminal.
|
||
class ClaudeSessionHost extends StatefulWidget {
|
||
const ClaudeSessionHost({super.key});
|
||
|
||
@override
|
||
State<ClaudeSessionHost> createState() => ClaudeSessionHostState();
|
||
}
|
||
|
||
class ClaudeSessionHostState extends State<ClaudeSessionHost> {
|
||
static const _primaryId = 'primary';
|
||
|
||
late final MultitabController<_Session> _controller;
|
||
int _nextSecondary = 1;
|
||
|
||
@override
|
||
void initState() {
|
||
super.initState();
|
||
_controller = MultitabController<_Session>(
|
||
initial: [
|
||
MultitabEntry<_Session>(
|
||
id: _primaryId,
|
||
title: 'primary',
|
||
payload: const _Session(isPrimary: true),
|
||
// Primary persists across clide restarts and never gets a
|
||
// close affordance (D-41).
|
||
closeable: false,
|
||
reorderable: false,
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
@override
|
||
void dispose() {
|
||
_controller.dispose();
|
||
super.dispose();
|
||
}
|
||
|
||
/// Public entry point used by the `claude.new-secondary` command.
|
||
void addSecondary() {
|
||
final index = _nextSecondary++;
|
||
_controller.add(MultitabEntry<_Session>(
|
||
id: 'secondary-$index',
|
||
title: 'session $index',
|
||
payload: _Session(isPrimary: false, secondaryIndex: index),
|
||
));
|
||
}
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return MultitabPane<_Session>(
|
||
controller: _controller,
|
||
keepAlive: true,
|
||
onAddRequested: addSecondary,
|
||
bodyBuilder: (ctx, entry) {
|
||
final s = entry.payload;
|
||
return ClaudePane(
|
||
isPrimary: s.isPrimary,
|
||
secondaryIndex: s.secondaryIndex,
|
||
// The MultitabPane already provides the tab strip header;
|
||
// suppressing the ClaudePane's own chrome avoids a double row.
|
||
showChrome: false,
|
||
);
|
||
},
|
||
);
|
||
}
|
||
}
|
||
|
||
class _Session {
|
||
const _Session({required this.isPrimary, this.secondaryIndex});
|
||
final bool isPrimary;
|
||
final int? secondaryIndex;
|
||
}
|