Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .ai/contexts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Five sub-system docs, ~150 lines each, written for AI agents who need to make a
| SQLite, indexing, search, heatmap aggregation, fs.watch | [session-cache](session-cache.md) |
| Cron schedules, schedule `.md` files, `claude --resume -p` spawn | [schedule-runner](schedule-runner.md) |
| Subagent sidebar grouping, transcript view, parent→child wiring | [subagent-observability](subagent-observability.md) |
| Plans/Memory/.work-files tabs, CodeMirror panel, format/delete buttons | [viewer-panel](viewer-panel.md) |
| Memory/.work-files tabs, CodeMirror panel, format/delete buttons | [viewer-panel](viewer-panel.md) |
| New IPC, preload bridge changes, renderer ↔ main protocol | [ipc-bridge](ipc-bridge.md) |
| File-trigger watcher, harness input injection, idle-wait | [trigger-watcher](trigger-watcher.md) |

Expand Down
2 changes: 1 addition & 1 deletion .ai/contexts/schedule-runner.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ cli:
## If you change this, also check

- `public/dialogs.js` (`launchScheduleCreator`) — UI entry point for the schedule creator flow
- `public/plans-memory-view.js` brain tab — lists existing `schedule-*.md` files, surfaces the "run now" play button
- `public/memory-workfiles-view.js` brain tab — lists existing `schedule-*.md` files, surfaces the "run now" play button
- `public/sidebar.js` — `.project-schedule-btn` clock icon wiring per project
- `schedule-ipc.js` `SCHEDULE_CREATOR_TEMPLATE` — if you change the schedule file format, update the template's instructions
- `main.js:1618` (or wherever `startScheduler(log, runScheduleCommand)` is invoked at app boot)
Expand Down
2 changes: 1 addition & 1 deletion .ai/contexts/viewer-panel.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ panel.getContent(); // current editor content
panel.destroy(); // tear down (rare; usually open() replaces)
```

Used at `public/app.js:19-44` for the three panel instances.
Used at `public/app.js:31-55` for the two panel instances.

## Toolbar buttons (visibility rules)

Expand Down
2 changes: 1 addition & 1 deletion .ai/shared-guidelines.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Switchboard is an **Electron desktop app**: renderer + main-process, no Domain/A
| Change SQLite, indexing, watcher, FTS, heatmap | [contexts/session-cache.md](contexts/session-cache.md) |
| Change schedule cron / `.md` files / schedule spawn | [contexts/schedule-runner.md](contexts/schedule-runner.md) |
| Change subagent grouping, transcript view, parent→child | [contexts/subagent-observability.md](contexts/subagent-observability.md) |
| Change Plans/Memory/.work-files panels (CodeMirror) | [contexts/viewer-panel.md](contexts/viewer-panel.md) |
| Change Memory/.work-files panels (CodeMirror) | [contexts/viewer-panel.md](contexts/viewer-panel.md) |
| Change the renderer (sidebar, terminal, app.js) | `public/*.js` — entry is `app.js` |
| Write a test | `test/*.test.js` — node:test + jsdom for renderer files |
| Working practices for AI agents (HANDOFF format, shell pitfalls, review loop) | [agent-practices.md](agent-practices.md) |
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ This repository is a fork of **[doctly/switchboard](https://github.com/doctly/sw
- **Full-Text Search** — Find any session by what was discussed, not just when it happened
- **Sandboxed Sessions** (Linux) — Optionally run Claude inside a [bubblewrap](https://github.com/containers/bubblewrap) sandbox where the rest of `$HOME` is hidden: only the project directory and Claude's own config/state are visible (filesystem isolation — network and environment are shared, see [docs](docs/settings.md#what-the-sandbox-does-and-doesnt-isolate)). Off by default; enable per session, per project, or globally. Needs unprivileged user namespaces, which Ubuntu 23.10+ restricts by default — the wrapper says so and how to fix it
- **IDE Emulation** — Switchboard acts as an IDE for Claude CLI, showing file diffs and opens in a side panel where you can accept, reject, or edit changes before they're applied. Supports both inline and side-by-side diff views. Disable this in Global Settings if you prefer Claude to use your own editor (VS Code, Cursor, etc.)
- **Memory** — Browse and edit your CLAUDE.md memory files in one place
- **Memory & Work Files** — Browse and edit your CLAUDE.md memory files and per-project `.work-files/` scratch notes in one place
- **Activity Stats** — Heatmap of your coding activity across all projects
- **Session Names** — Picks up session names from Claude Code's `/rename` command automatically

Expand Down Expand Up @@ -88,7 +88,7 @@ Full user-facing documentation lives in [docs/](docs/README.md):
- [Session Restore](docs/session-restore.md) — persist open sessions and restore them on restart
- [Keyboard Shortcuts](docs/keyboard-shortcuts.md) — editor/terminal shortcuts and rebindable session-nav keys
- [Notifications](docs/notifications.md) — sidebar status badges
- [Memory and Work Files](docs/plans-memory-workfiles.md) — CodeMirror panels for CLAUDE.md and `.work-files/`
- [Memory and Work Files](docs/memory-workfiles.md) — CodeMirror panels for CLAUDE.md and `.work-files/`
- [Activity Stats](docs/activity-stats.md) — coding activity heatmap
- [Settings Reference](docs/settings.md) — every field in Global and Project Settings
- [Automation](docs/automation.md) — scheduled Claude tasks (cron) and the file-based trigger API
Expand Down
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ This is the documentation for the [devsuitup/switchboard](https://github.com/dev
- [Session Restore](session-restore.md) — persist open sessions and restore them on restart
- [Keyboard Shortcuts](keyboard-shortcuts.md) — editor/terminal shortcuts and rebindable session-nav keys
- [Notifications](notifications.md) — sidebar status badges: waiting for input, permission approval, activity
- [Memory and Work Files](plans-memory-workfiles.md) — edit CLAUDE.md and `.work-files/` in CodeMirror panels
- [Memory and Work Files](memory-workfiles.md) — edit CLAUDE.md and `.work-files/` in CodeMirror panels
- [Activity Stats](activity-stats.md) — coding activity heatmap
- [Settings Reference](settings.md) — every field in Global and Project Settings
- [Automation](automation.md) — scheduled Claude tasks (cron) and the file-based trigger API
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ const rendererCrossFileGlobals = {
WebglAddon: 'readonly',
UnicodeGraphemesAddon: 'readonly',

// Terminal/grid/file-panel/plans/stats/notifications and assorted helpers
// Terminal/grid/file-panel/stats/notifications and assorted helpers
// shared across renderer files (defined somewhere in public/*.js).
TERMINAL_THEME: 'writable',
TERMINAL_THEMES: 'readonly',
Expand Down
5 changes: 1 addition & 4 deletions public/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -1122,9 +1122,6 @@ document.querySelectorAll('.sidebar-tab').forEach(tab => {
});
});

// Plans & viewer helpers → plans-memory-view.js


// Grid view → grid-view.js
// Initialize grid observers now that DOM refs are ready
initGridObservers();
Expand All @@ -1133,7 +1130,7 @@ initGridObservers();

// Stats view (loadStats, buildUsageSection, buildDailyBarChart, buildHeatmap, calculateStreak, buildStatsSummary) → stats-view.js

// Memory viewer → plans-memory-view.js
// Memory & Work Files viewers → memory-workfiles-view.js


// Dialogs (resolveDefaultSessionOptions, forkSession, showNewSessionPopover,
Expand Down
2 changes: 1 addition & 1 deletion public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@
<script src="jsonl-viewer.js"></script>
<script src="dialogs.js"></script>
<script src="sidebar.js"></script>
<script src="plans-memory-view.js"></script>
<script src="memory-workfiles-view.js"></script>
<script src="app.js"></script>
</body>
</html>
2 changes: 1 addition & 1 deletion public/jsonl-viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const liveSubagents = new Set();
// Active subagent file watches for the currently-rendered viewer. Each entry
// is a stopWatch closure created when an Agent block expands and starts a
// live tail. Drained on viewer dismissal so we don't leak fs.watchFile polls.
// Attached to `window` so the cross-file hideAllViewers() (in plans-memory-view.js)
// Attached to `window` so the cross-file hideAllViewers() (in memory-workfiles-view.js)
// can drain via the function declaration below — top-level `const` in classic
// scripts isn't global.
window.__activeViewerWatches = window.__activeViewerWatches || new Set();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// --- Memory & Work Files viewers ---
// Depends on globals: memoryContent, memoryPanel, memoryViewer, placeholder,
// terminalArea, statsViewer, settingsViewer, jsonlViewer (app.js)
// terminalArea, statsViewer, settingsViewer, jsonlViewer, workFilesViewer (app.js)
// Depends on: formatDate (utils.js)

let cachedMemoryData = { global: { files: [] }, projects: [] };
Expand Down
4 changes: 2 additions & 2 deletions public/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -1902,7 +1902,7 @@ body { display: flex; flex-direction: column; }
font-size: 13px;
}

/* Shared viewer toolbar (memory, file panel) */
/* Shared viewer toolbar (memory, work files, file panel) */
.viewer-toolbar {
display: flex;
align-items: center;
Expand Down Expand Up @@ -1970,7 +1970,7 @@ body { display: flex; flex-direction: column; }
background: rgba(120,130,255,0.1);
}

/* Plan CodeMirror editor */
/* Viewer panel CodeMirror editor */
.viewer-panel-editor {
flex: 1;
overflow: hidden;
Expand Down
2 changes: 1 addition & 1 deletion public/viewer-panel.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* viewer-panel.js — Unified viewer component for CodeMirror-based panels.
*
* A single component used by memory viewer and file panel.
* A single component used by memory viewer, work files viewer, and file panel.
* Manages toolbar, editor, preview area, and all interactions.
* Watches files for external changes and reloads automatically.
*
Expand Down
2 changes: 1 addition & 1 deletion public/viewer-toolbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* viewer-toolbar.js — Shared toolbar factory for all CodeMirror viewer panels.
*
* Creates a consistent toolbar with title, path, and action buttons.
* Used by memory viewer and file panel.
* Used by memory viewer, work files viewer, and file panel.
*/

const SAVE_ICON = '<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 448 512" width="14" height="14" xmlns="http://www.w3.org/2000/svg"><path d="M433.941 129.941l-83.882-83.882A48 48 0 0 0 316.118 32H48C21.49 32 0 53.49 0 80v352c0 26.51 21.49 48 48 48h352c26.51 0 48-21.49 48-48V163.882a48 48 0 0 0-14.059-33.941zM272 80v80H144V80h128zm122 352H54a6 6 0 0 1-6-6V86a6 6 0 0 1 6-6h42v104c0 13.255 10.745 24 24 24h176c13.255 0 24-10.745 24-24V83.882l78.243 78.243a6 6 0 0 1 1.757 4.243V426a6 6 0 0 1-6 6zM224 232c-48.523 0-88 39.477-88 88s39.477 88 88 88 88-39.477 88-88-39.477-88-88-88zm0 128c-22.056 0-40-17.944-40-40s17.944-40 40-40 40 17.944 40 40-17.944 40-40 40z"></path></svg>';
Expand Down
2 changes: 1 addition & 1 deletion test/dom-subagent-transcript.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ function setupDom({ readSubagentJsonlResult = { entries: SAMPLE_ENTRIES }, readS
loadProjects: () => {},
launchScheduleCreator: () => {},
setActiveSession: () => {},
// hideAllViewers is defined in plans-memory-view.js in the real app; stub it here.
// hideAllViewers is defined in memory-workfiles-view.js in the real app; stub it here.
hideAllViewers: () => {},
// openSession is stubbed after eval; we pre-stub it so it's present during eval
openSession: () => {},
Expand Down
14 changes: 7 additions & 7 deletions test/dom-work-files-view.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Smoke tests for the Work Files tab renderer (plans-memory-view.js work-files section).
// Smoke tests for the Work Files tab renderer (memory-workfiles-view.js work-files section).
//
// Strategy: load icons.js + utils.js + plans-memory-view.js into a jsdom window
// Strategy: load icons.js + utils.js + memory-workfiles-view.js into a jsdom window
// that stubs the minimal globals those scripts need (DOM refs, window.api).
// Then call loadWorkFiles / renderWorkFiles / openWorkFile and assert the
// resulting DOM matches expectations.
Expand All @@ -14,7 +14,7 @@ const { JSDOM } = require('jsdom');

const PUBLIC_DIR = path.join(__dirname, '..', 'public');

// Minimal HTML that plans-memory-view.js references via globals set in app.js.
// Minimal HTML that memory-workfiles-view.js references via globals set in app.js.
const INDEX_HTML = `<!DOCTYPE html>
<html>
<body>
Expand All @@ -36,7 +36,7 @@ function evalInWindow(dom, file) {
vm.runInContext(src, dom.getInternalVMContext(), { filename: file });
}

// A minimal ViewerPanel stub — plans-memory-view.js calls workFilesPanel.open(...)
// A minimal ViewerPanel stub — memory-workfiles-view.js calls workFilesPanel.open(...)
function makeViewerPanelStub() {
return { open: () => {}, close: () => {} };
}
Expand All @@ -50,7 +50,7 @@ function setupWorkFilesDom() {
const { window } = dom;

// jsdom doesn't expose CSS.escape — polyfill it.
// plans-memory-view.js uses CSS.escape() in openMemory / openWorkFile.
// memory-workfiles-view.js uses CSS.escape() in openMemory / openWorkFile.
if (!window.CSS) {
Object.defineProperty(window, 'CSS', {
value: {
Expand All @@ -69,7 +69,7 @@ function setupWorkFilesDom() {
runScheduleNow: () => Promise.resolve({ ok: true }),
};

// DOM handles that plans-memory-view.js reads as globals (set by app.js normally)
// DOM handles that memory-workfiles-view.js reads as globals (set by app.js normally)
const stubGlobals = {
memoryContent: window.document.getElementById('memory-content'),
workFilesContent: window.document.getElementById('work-files-content'),
Expand All @@ -94,7 +94,7 @@ function setupWorkFilesDom() {
// Load dependencies
evalInWindow(dom, path.join(PUBLIC_DIR, 'utils.js'));
evalInWindow(dom, path.join(PUBLIC_DIR, 'icons.js'));
evalInWindow(dom, path.join(PUBLIC_DIR, 'plans-memory-view.js'));
evalInWindow(dom, path.join(PUBLIC_DIR, 'memory-workfiles-view.js'));

return {
window,
Expand Down
14 changes: 14 additions & 0 deletions test/search-perf.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -267,3 +267,17 @@ test('search: " a " (1 trimmed char) does NOT call api.search and preserves in
assert.equal(inputEl.value, ' a ', 'input value must be preserved (not cleared)');
});

// ---------------------------------------------------------------------------
// Cross-tab: non-sessions tabs route correctly under 3-char threshold
// ---------------------------------------------------------------------------

test('search: 2-char query on memory tab calls renderMemories (not api.search)', async () => {
const { state, inputEl, runSearchQuery } = makeSearchState();
state.activeTab = 'memory';
inputEl.value = 'me';
let apiCalled = false;
await runSearchQuery(() => { apiCalled = true; });

assert.equal(apiCalled, false, 'api.search not called for 2-char on memory tab');
assert.equal(state.renderMemoriesCalls.length, 1, 'renderMemories called to show unfiltered list');
});
Loading