From 96c26c240671da9d71e49c021272e015602a2ea5 Mon Sep 17 00:00:00 2001 From: OpenClaw Bot Date: Sat, 9 May 2026 19:16:30 +0200 Subject: [PATCH] feat: add functionalities overview --- src/App.tsx | 158 ++++++++++++++++++++++++++++++++++++++++++++- src/index.css | 134 ++++++++++++++++++++++++++++++++++++++ src/store/types.ts | 2 +- 3 files changed, 292 insertions(+), 2 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 5877173..342bd13 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -9,6 +9,7 @@ import type { AppState, Feature, FeatureColumn, ParkingLotItem, PulseEvent, Risk import { arrayToLines, formatDateTime, linesToArray, nowIso, slugify } from './utils/format' const TABS: Array<{ key: TabKey; label: string }> = [ + { key: 'functionalities', label: 'Functionalities' }, { key: 'feature-plan', label: 'Feature Plan' }, { key: 'parking-lot', label: 'Parking Lot' }, { key: 'pulse-log', label: 'Pulse Log' }, @@ -65,7 +66,7 @@ const downloadText = (filename: string, text: string, contentType = 'text/plain; function App() { const [appState, setAppState] = useState(() => loadAppState()) - const [activeTab, setActiveTab] = useState('feature-plan') + const [activeTab, setActiveTab] = useState('functionalities') const [statusMessage, setStatusMessage] = useState('Seeded with BuildPulse so you can dogfood it immediately.') const [selectedFeatureId, setSelectedFeatureId] = useState(null) const [selectedParkingId, setSelectedParkingId] = useState(null) @@ -611,6 +612,63 @@ function App() { const currentFeatureCount = groupedFeatures.now.length const recentPulsePreview = [...appState.pulses].sort((a, b) => b.timestamp.localeCompare(a.timestamp)).slice(0, 3) + const completedFeatureCount = groupedFeatures.done.length + const functionalityCards = [ + { + title: 'Project Cockpit', + status: 'live', + description: 'Single-project mission, goal, notes, and focus statistics stay visible before the board tries to swallow the room.', + signal: appState.project.current_goal ? 'Goal set' : 'Needs current goal', + metric: appState.project.name, + action: 'Edit summary', + tab: 'functionalities' as TabKey, + }, + { + title: 'Feature Plan', + status: currentFeatureCount ? 'active' : 'ready', + description: 'Now / Next / Later / Done columns keep work small, shaped, and movable without becoming Jira in a fake moustache.', + signal: `${appState.features.length} total · ${currentFeatureCount} now · ${completedFeatureCount} done`, + metric: `${currentFeatureCount} now`, + action: 'Open board', + tab: 'feature-plan' as TabKey, + }, + { + title: 'Parking Lot', + status: appState.parking_lot.length ? 'active' : 'ready', + description: 'Useful distractions get captured, risk-tagged, and converted into features only when they earn their keep.', + signal: `${appState.parking_lot.length} parked idea${appState.parking_lot.length === 1 ? '' : 's'}`, + metric: `${appState.parking_lot.length} parked`, + action: 'Review parked', + tab: 'parking-lot' as TabKey, + }, + { + title: 'Pulse Log', + status: appState.pulses.length ? 'active' : 'ready', + description: 'Intent, decisions, blockers, test results, and outcomes form a future-compatible trail for agents and humans.', + signal: recentPulsePreview[0] ? `Latest: ${recentPulsePreview[0].pulse_type}` : 'No pulses yet', + metric: `${appState.pulses.length} pulses`, + action: 'Open log', + tab: 'pulse-log' as TabKey, + }, + { + title: 'AI Handoff + Export', + status: 'live', + description: 'Generate JSON, JSONL, Markdown packages, and focused session prompts so coding agents get context without soup.', + signal: `${Object.keys(markdownPackage).length} markdown files ready`, + metric: 'handoff ready', + action: 'Export context', + tab: 'export' as TabKey, + }, + { + title: 'Appwrite Sync', + status: backendMode === 'appwrite' && syncStatus === 'synced' ? 'live' : syncStatus === 'degraded' ? 'degraded' : 'syncing', + description: 'State persists through the Appwrite runtime document, with explicit refresh and force-sync controls for operator recovery.', + signal: backendMode === 'appwrite' ? `Sync status: ${syncStatus}` : 'Local cache fallback active', + metric: backendMode === 'appwrite' ? 'Appwrite' : 'cache', + action: 'Refresh state', + tab: 'functionalities' as TabKey, + }, + ] const backendLabel = backendMode === 'appwrite' ? 'Appwrite backend · Unraid server' : backendMode === 'connecting' ? 'Connecting to Appwrite…' : 'Local cache fallback' const syncLabel = @@ -722,6 +780,9 @@ function App() {
+ @@ -736,6 +797,101 @@ function App() {
+ {activeTab === 'functionalities' && ( +
+
+
+

Functionalities

+

The living map of what BuildPulse actually does right now — no brochure fog, no phantom roadmap theatre.

+
+
+ NPM live + Appwrite backed + Unraid runtime +
+
+ +
+
+

Capability map

+

{appState.project.name} is a pulse-compatible feature cockpit.

+

+ It keeps the product thread visible: define the mission, shape features, park distractions, log movement, sync state, + and hand clean context to AI agents without turning the app into a bloated command bunker. +

+
+
+
+ Live functions + {functionalityCards.filter((card) => card.status === 'live' || card.status === 'active').length} +
+
+ Operator recovery + {backendMode === 'appwrite' ? 'Ready' : 'Cache'} +
+
+ Context packages + {Object.keys(markdownPackage).length} +
+
+
+ +
+ {functionalityCards.map((card) => ( +
+
+
+

{card.status}

+

{card.title}

+
+ {card.metric} +
+

{card.description}

+
+ {card.signal} + +
+
+ ))} +
+ +
+
+
+

What this is deliberately not yet

+

Guardrails matter. The small cockpit wins because it refuses to cosplay as a whole enterprise suite.

+
+
+
+
+ Not Jira +

No issue jungle, sprint ceremony, or fake certainty factory.

+
+
+ Not an autonomous agent framework +

Agent ingestion can come later; manual pulse truth comes first.

+
+
+ Not multi-project yet +

Single-project discipline keeps v0.1 sharp enough to dogfood.

+
+
+
+
+ )} + {activeTab === 'feature-plan' && (
diff --git a/src/index.css b/src/index.css index 2f202aa..99a875f 100644 --- a/src/index.css +++ b/src/index.css @@ -592,3 +592,137 @@ pre { align-items: stretch; } } + +.functionality-summary, +.functionality-signal { + display: flex; + align-items: center; + gap: 0.6rem; + flex-wrap: wrap; +} + +.functionality-hero { + display: grid; + grid-template-columns: minmax(0, 1.4fr) minmax(260px, 0.6fr); + gap: 1rem; + overflow: hidden; + position: relative; +} + +.functionality-hero::after { + content: ''; + position: absolute; + inset: 0; + background: linear-gradient(135deg, rgba(45, 212, 191, 0.08), rgba(129, 140, 248, 0.08), transparent 70%); + pointer-events: none; +} + +.functionality-hero > *, +.functionality-card > *, +.functionality-roadmap > * { + position: relative; + z-index: 1; +} + +.functionality-hero h3, +.functionality-card h3, +.functionality-roadmap h3 { + margin: 0; +} + +.functionality-hero p, +.functionality-card p, +.functionality-roadmap p { + color: #c9d4ea; +} + +.functionality-scorecard, +.roadmap-grid { + display: grid; + gap: 0.75rem; +} + +.functionality-scorecard div, +.roadmap-grid div { + border: 1px solid rgba(148, 163, 184, 0.14); + border-radius: 18px; + background: rgba(30, 41, 59, 0.58); + padding: 1rem; +} + +.functionality-scorecard span { + display: block; + color: #9fb4d9; + font-size: 0.82rem; +} + +.functionality-scorecard strong { + display: block; + margin-top: 0.25rem; + font-size: 1.45rem; +} + +.functionality-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 1rem; +} + +.functionality-card { + display: flex; + flex-direction: column; + gap: 0.85rem; + min-height: 240px; + overflow: hidden; + position: relative; +} + +.functionality-card::before { + content: ''; + position: absolute; + inset: 0; + border-radius: inherit; + opacity: 0.7; + pointer-events: none; +} + +.functionality-live::before, +.functionality-active::before { + background: linear-gradient(180deg, rgba(52, 211, 153, 0.09), transparent 55%); +} + +.functionality-ready::before, +.functionality-syncing::before { + background: linear-gradient(180deg, rgba(96, 165, 250, 0.09), transparent 55%); +} + +.functionality-degraded::before { + background: linear-gradient(180deg, rgba(248, 113, 113, 0.12), transparent 55%); +} + +.functionality-signal { + margin-top: auto; + justify-content: space-between; + color: #bfdbfe; + font-size: 0.9rem; +} + +.roadmap-grid { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +@media (max-width: 1080px) { + .functionality-grid, + .functionality-hero, + .roadmap-grid { + grid-template-columns: 1fr; + } +} + +@media (max-width: 720px) { + .functionality-summary, + .functionality-signal { + align-items: stretch; + flex-direction: column; + } +} diff --git a/src/store/types.ts b/src/store/types.ts index e337634..f1edf6c 100644 --- a/src/store/types.ts +++ b/src/store/types.ts @@ -99,4 +99,4 @@ export interface AppState { settings: Settings } -export type TabKey = 'feature-plan' | 'parking-lot' | 'pulse-log' | 'export' +export type TabKey = 'functionalities' | 'feature-plan' | 'parking-lot' | 'pulse-log' | 'export'