a code editor panel opens at its dashboard's folder
A dashboard scoped to ~/Tests/platform showed exactly that in the editor's header, above an explorer listing the HOME directory. The two halves of one panel disagreed because only one of them asked: `CodeEditorHeader` reads `cwd` from the workspace, and `CodeEditorPanelWrapper` — three lines — never touched it. It now passes `cwdToPath(cwd)` as `initialPath`, which is what `FileTree` lists from. Still no `urlState`: a dashboard can hold two editors and one `?file=` would drive both. That reasoning, and the shape of the fix, come from FileBrowserPanelWrapper, which had it right already — which is why the file browser was correct in the same dashboard. ── One conversion instead of three ── `cwd` → path existed twice, spelled differently, and the two disagreed: cwd.slice(1) '' -> '' '/abs' -> 'abs' both wrong cwd.replace(/^~\//, '/') '' -> '/' '/abs' -> '/abs' both right An empty cwd gave the file endpoints an empty string, which lists nothing rather than failing — the quiet kind of wrong. Writing a THIRD copy for the editor is what prompted collapsing them: `cwdToPath` now lives beside `useWorkspace`, with 6 tests including the tilde-inside-a-name case that a careless regex would rewrite. Swept the rest rather than assuming this was the only one: all three panel wrappers now read cwd, and the terminal already did. tsgo clean, frontend builds, 846 pass / 7 fail — +6 new, same 7. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,18 @@
|
||||
import { useWorkspace, cwdToPath } from '../../components/Workspace';
|
||||
import { CodeEditorView } from './CodeEditor';
|
||||
|
||||
export const CodeEditorPanelWrapper = () => <CodeEditorView className="h-full w-full" />;
|
||||
/**
|
||||
* The editor as a dashboard panel, rooted at the dashboard's folder.
|
||||
*
|
||||
* It ignored the workspace entirely and always opened the explorer at home, while `CodeEditorHeader` read
|
||||
* `cwd` for itself and displayed the right folder — so a scoped dashboard showed `~/Tests/platform` in the
|
||||
* header above a tree of the home directory. The two halves of the same panel disagreed because only one
|
||||
* of them asked.
|
||||
*
|
||||
* No `urlState`: a dashboard can hold two editors and one `?file=` would drive both. Same reasoning as
|
||||
* FileBrowserPanelWrapper, which is where this pattern came from and which had it right already.
|
||||
*/
|
||||
export const CodeEditorPanelWrapper = () => {
|
||||
const { cwd } = useWorkspace();
|
||||
return <CodeEditorView className="h-full w-full" initialPath={cwdToPath(cwd)} />;
|
||||
};
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
NEW_DASH_TEMPLATE_KEY,
|
||||
} from './constants';
|
||||
import { useNewDashboardDraft } from './useNewDashboardDraft';
|
||||
import { cwdToPath } from '../../components/Workspace';
|
||||
|
||||
export const DashboardListApp = () => {
|
||||
const location = useLocation();
|
||||
@@ -74,7 +75,7 @@ export const DashboardListApp = () => {
|
||||
setName(ws.name);
|
||||
setDescription(ws.description ?? '');
|
||||
setTemplateIdx(ws.templateIdx ?? 0);
|
||||
const cwdPath = !ws.cwd || ws.cwd === '~' ? '/' : ws.cwd.replace(/^~\//, '/');
|
||||
const cwdPath = cwdToPath(ws.cwd);
|
||||
setFilePath(cwdPath);
|
||||
};
|
||||
|
||||
|
||||
+1
-3
@@ -1,8 +1,6 @@
|
||||
import { useWorkspace } from '../../../components/Workspace';
|
||||
import { useWorkspace, cwdToPath } from '../../../components/Workspace';
|
||||
import { FileBrowserApp } from './FileBrowserApp';
|
||||
|
||||
const cwdToPath = (cwd: string) => (cwd === '~' ? '/' : cwd.slice(1));
|
||||
|
||||
export const FileBrowserPanelWrapper = () => {
|
||||
const { cwd, workspace } = useWorkspace();
|
||||
const basePath = cwdToPath(cwd);
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { describe, expect, test } from 'bun:test';
|
||||
import { cwdToPath } from './cwd-path';
|
||||
|
||||
// One conversion, because there were two and they disagreed — and a third was about to be written.
|
||||
|
||||
describe('cwdToPath', () => {
|
||||
test('the home shorthand is the root', () => {
|
||||
expect(cwdToPath('~')).toBe('/');
|
||||
});
|
||||
|
||||
test('a scoped dashboard resolves to its folder', () => {
|
||||
expect(cwdToPath('~/Tests/platform')).toBe('/Tests/platform');
|
||||
});
|
||||
|
||||
test('missing or empty is the root, not the empty string', () => {
|
||||
// `''.slice(1)` gave `''`, which is not a path any endpoint accepts — it listed nothing rather than
|
||||
// failing, which is the quiet kind of wrong.
|
||||
expect(cwdToPath('')).toBe('/');
|
||||
expect(cwdToPath(undefined)).toBe('/');
|
||||
expect(cwdToPath(null)).toBe('/');
|
||||
});
|
||||
|
||||
test('an already-absolute path is left alone', () => {
|
||||
// `'/abs'.slice(1)` gave `'abs'` — a relative path, silently resolved against somewhere else.
|
||||
expect(cwdToPath('/already/abs')).toBe('/already/abs');
|
||||
});
|
||||
|
||||
test('spaces and punctuation are untouched — this is notation, not encoding', () => {
|
||||
expect(cwdToPath('~/a b/c (2)')).toBe('/a b/c (2)');
|
||||
});
|
||||
|
||||
test('a tilde inside a name is not a prefix and must not be rewritten', () => {
|
||||
expect(cwdToPath('~/back~up')).toBe('/back~up');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
// A workspace's `cwd` as a path the file APIs accept.
|
||||
//
|
||||
// A dashboard is created against a directory, and every file-shaped panel in it should open there. The
|
||||
// framework stores that as a `cwd` in the shell's own notation — `~`, `~/Tests/platform` — while the file
|
||||
// endpoints take a path rooted at the home directory: `/`, `/Tests/platform`.
|
||||
//
|
||||
// This existed twice, spelled differently, and the two disagreed:
|
||||
//
|
||||
// cwd.slice(1) '' -> '' '/abs' -> 'abs' ← both wrong
|
||||
// cwd.replace(/^~\//, '/') '' -> '/' '/abs' -> '/abs' ← both right
|
||||
//
|
||||
// The second is the one kept. A third copy was about to be written for the code editor, which is what
|
||||
// prompted collapsing them: two spellings of one conversion is a bug waiting for whichever caller got the
|
||||
// short one.
|
||||
|
||||
/** `~` and `~/x` become `/` and `/x`. An already-absolute path is returned unchanged. */
|
||||
export function cwdToPath(cwd: string | undefined | null): string {
|
||||
if (!cwd || cwd === '~') return '/';
|
||||
return cwd.replace(/^~\//, '/');
|
||||
}
|
||||
@@ -33,6 +33,7 @@ export {
|
||||
export type { WorkspaceIdentity } from './workspace-identity';
|
||||
export { parseWorkspaceKey } from './workspace-identity';
|
||||
export { WorkspaceProvider, useWorkspace } from './WorkspaceContext';
|
||||
export { cwdToPath } from './cwd-path';
|
||||
export { usePanelFullscreen } from './panel-fullscreen';
|
||||
export { usePanelConfig } from './usePanelConfig';
|
||||
export { usePanelClose } from './usePanelClose';
|
||||
|
||||
Reference in New Issue
Block a user