add D-055 Claude internal tabs, readable session name slugs
D-055: multiple Claude sessions share the workspace as internal tabs inside the Claude pane header. Primary has no close; secondaries show ×. Tab row hidden when only primary exists. + button and double-click-empty spawn new secondaries. Session names now use a readable path slug (strip $HOME, replace / with -) with fallback to FNV-1a hash when the slug exceeds 80 chars. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,15 +1,18 @@
|
||||
/// Derive deterministic tmux session names for Claude panes (D-041).
|
||||
///
|
||||
/// The primary session name for a repo is `clide-claude-<hash>` where
|
||||
/// `<hash>` is an 8-char FNV-1a-ish hex hash of the canonical
|
||||
/// (absolute, symlink-resolved) repo path. Not cryptographic — we just
|
||||
/// need short, stable, collision-resistant-enough strings. Secondary
|
||||
/// sessions append `-N` for monotonically increasing `N`.
|
||||
/// The primary session name encodes the repo path in a human-readable
|
||||
/// form: `clide-claude-<path-slug>`. For example:
|
||||
/// ~/projects/clide → clide-claude-projects-clide
|
||||
/// /var/mnt/data/myapp → clide-claude-var-mnt-data-myapp
|
||||
///
|
||||
/// Secondary sessions append `-N`.
|
||||
library;
|
||||
|
||||
import 'dart:io' show Platform;
|
||||
|
||||
/// Stable session name for the primary Claude pane of [repoRoot].
|
||||
String primarySessionName(String repoRoot) {
|
||||
return 'clide-claude-${_hash(repoRoot)}';
|
||||
return 'clide-claude-${_slugify(repoRoot)}';
|
||||
}
|
||||
|
||||
/// Nth secondary session name. [n] starts at 1.
|
||||
@@ -17,11 +20,29 @@ String secondarySessionName(String repoRoot, int n) {
|
||||
return '${primarySessionName(repoRoot)}-$n';
|
||||
}
|
||||
|
||||
/// 8-char hex hash. FNV-1a over UTF-16 code units; independent of
|
||||
/// platform endianness. Collision rate at N=1000 repos is still
|
||||
/// vanishingly small (≈0.0001%).
|
||||
// tmux session names max out at 256 chars; keep ours well under.
|
||||
const _maxSlugLen = 80;
|
||||
|
||||
String _slugify(String path) {
|
||||
final home = Platform.environment['HOME'] ?? '';
|
||||
var p = path;
|
||||
if (home.isNotEmpty && p.startsWith(home)) {
|
||||
p = p.substring(home.length);
|
||||
}
|
||||
p = p.replaceAll('/', '-').replaceAll('.', '');
|
||||
while (p.startsWith('-')) {
|
||||
p = p.substring(1);
|
||||
}
|
||||
while (p.endsWith('-')) {
|
||||
p = p.substring(0, p.length - 1);
|
||||
}
|
||||
if (p.isEmpty) p = 'root';
|
||||
if (p.length > _maxSlugLen) return _hash(path);
|
||||
return p;
|
||||
}
|
||||
|
||||
String _hash(String s) {
|
||||
var h = 2166136261; // FNV offset basis (32-bit)
|
||||
var h = 2166136261;
|
||||
for (var i = 0; i < s.length; i++) {
|
||||
h ^= s.codeUnitAt(i);
|
||||
h = (h * 16777619) & 0xffffffff;
|
||||
|
||||
Reference in New Issue
Block a user