feat(timeline): add pulse animation for in-flight drag mutations

Allocation bars that have active optimistic overrides (post-drag,
awaiting server confirmation) now pulse subtly via animate-pulse.
The pending set is derived from the existing optimisticAllocations
map keys, requiring no additional state.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-09 13:28:46 +02:00
co-authored by Claude Sonnet 4.6
parent 7a5e98e2e9
commit 1df208dbcc
386 changed files with 657 additions and 81650 deletions
-136
View File
@@ -1,136 +0,0 @@
#!/usr/bin/env node
/**
* Claude Flow Session Manager
* Handles session lifecycle: start, restore, end
*/
const fs = require('fs');
const path = require('path');
const WORKSPACE_ROOT = fs.realpathSync(process.cwd());
const SESSION_DIR = path.join(WORKSPACE_ROOT, '.claude-flow', 'sessions');
const SESSION_FILE = path.join(SESSION_DIR, 'current.json');
const commands = {
start: () => {
const sessionId = `session-${Date.now()}`;
const session = {
id: sessionId,
startedAt: new Date().toISOString(),
cwd: WORKSPACE_ROOT,
context: {},
metrics: {
edits: 0,
commands: 0,
tasks: 0,
errors: 0,
},
};
fs.mkdirSync(SESSION_DIR, { recursive: true });
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
console.log(`Session started: ${sessionId}`);
return session;
},
restore: () => {
if (!fs.existsSync(SESSION_FILE)) {
console.log('No session to restore');
return null;
}
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
session.restoredAt = new Date().toISOString();
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
console.log(`Session restored: ${session.id}`);
return session;
},
end: () => {
if (!fs.existsSync(SESSION_FILE)) {
console.log('No active session');
return null;
}
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
session.endedAt = new Date().toISOString();
session.duration = Date.now() - new Date(session.startedAt).getTime();
// Archive session
const archivePath = path.join(SESSION_DIR, `${session.id}.json`);
fs.writeFileSync(archivePath, JSON.stringify(session, null, 2));
fs.unlinkSync(SESSION_FILE);
console.log(`Session ended: ${session.id}`);
console.log(`Duration: ${Math.round(session.duration / 1000 / 60)} minutes`);
console.log(`Metrics: ${JSON.stringify(session.metrics)}`);
return session;
},
status: () => {
if (!fs.existsSync(SESSION_FILE)) {
console.log('No active session');
return null;
}
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
const duration = Date.now() - new Date(session.startedAt).getTime();
console.log(`Session: ${session.id}`);
console.log(`Started: ${session.startedAt}`);
console.log(`Duration: ${Math.round(duration / 1000 / 60)} minutes`);
console.log(`Metrics: ${JSON.stringify(session.metrics)}`);
return session;
},
update: (key, value) => {
if (!fs.existsSync(SESSION_FILE)) {
console.log('No active session');
return null;
}
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
session.context[key] = value;
session.updatedAt = new Date().toISOString();
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
return session;
},
get: (key) => {
if (!fs.existsSync(SESSION_FILE)) return null;
try {
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
return key ? (session.context || {})[key] : session.context;
} catch { return null; }
},
metric: (name) => {
if (!fs.existsSync(SESSION_FILE)) {
return null;
}
const session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf-8'));
if (session.metrics[name] !== undefined) {
session.metrics[name]++;
fs.writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
}
return session;
},
};
// CLI
const [,, command, ...args] = process.argv;
if (command && commands[command]) {
commands[command](...args);
} else {
console.log('Usage: session.js <start|restore|end|status|update|metric> [args]');
}
module.exports = commands;