one header instead of three rows, and the folder lives in the URL
THE TOP WAS THREE ROWS OF FURNITURE before any content: the workspace's chrome header saying "Detail", the panel's own strip saying "Media — Photos and video the site is built from", and then the browser's toolbar. a panel meta may supply its own `header`, which is the mechanism for exactly this and which gitea, invoices and desktop already use. so the section's name goes where a panel's name goes, and the strip inside is gone. "Detail" was never information — the panel is the detail panel, and the useful word is which section is open. the hint went with it. "Photos and video the site is built from" earns its place in the NAV, where somebody is choosing between ten sections. repeated above the one they chose, it is a caption for a decision already made. AND THE FOLDER IS IN THE QUERY STRING. `urlPath` puts it in `?path=`, so a reload lands where you were and a link can be sent to somebody — which for a media folder somebody else uploads into is the point. it is opt-in in the platform because a screen with two browsers would have them fighting over one parameter; this screen has one. `panelId` stays alongside it: the URL covers a reload or a shared link, and panelId covers a panel re-opened later, which the URL cannot. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -35,7 +35,7 @@ import type { PluginManifest } from '@@/plugins/manifest';
|
||||
*/
|
||||
export const manifest: PluginManifest = {
|
||||
publisher: 'officerdev',
|
||||
version: '2026.08.29.22',
|
||||
version: '2026.08.29.23',
|
||||
platform: '>=1.0.0',
|
||||
|
||||
label: 'Fofos',
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { useSearchParams } from 'react-router';
|
||||
import { DEFAULT_SECTION, isSectionKey, sectionFor } from './sections';
|
||||
|
||||
/**
|
||||
* The detail panel's chrome header — the section's own name, not the word "Detail".
|
||||
*
|
||||
* ── Why this replaces a row inside the panel ──
|
||||
*
|
||||
* The panel used to draw its own header strip with the section label and its hint, UNDER the workspace's
|
||||
* chrome header, which said "Detail". Two bars stacked, one of them saying nothing: the top of the panel
|
||||
* was three rows of furniture before any content.
|
||||
*
|
||||
* A panel meta may supply its own `header`, which is the mechanism for exactly this — several plugins
|
||||
* already do it. So the section's name goes where a panel's name goes, and the row inside is gone.
|
||||
*
|
||||
* ── The hint went with it ──
|
||||
*
|
||||
* "Photos and video the site is built from" is useful in the NAV, where somebody is choosing between
|
||||
* ten sections. Repeated above the thing they chose, it is a caption for a decision already made.
|
||||
*/
|
||||
export const DetailHeader = () => {
|
||||
const [params] = useSearchParams();
|
||||
const raw = params.get('section');
|
||||
const section = sectionFor(isSectionKey(raw) ? raw : DEFAULT_SECTION);
|
||||
const Icon = section.icon;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Icon className="h-3.5 w-3.5 shrink-0" />
|
||||
<span className="flex-1 truncate text-xs font-medium">{section.label}</span>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -71,11 +71,6 @@ export const DetailPanel = () => {
|
||||
|
||||
return (
|
||||
<div className="flex size-full flex-col overflow-hidden">
|
||||
<div className="flex items-center justify-between gap-3 border-b border-border px-4 py-2">
|
||||
<h2 className="truncate text-sm font-medium">{current.label}</h2>
|
||||
<span className="truncate text-xs text-muted-foreground">{current.hint}</span>
|
||||
</div>
|
||||
|
||||
<div className="min-h-0 flex-1">{Panel ? <Panel /> : <PlannedSection section={current} />}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
+7
-3
@@ -91,10 +91,14 @@ export const MediaPanel = () => {
|
||||
`rootLabel` so the breadcrumb reads "Media" rather than "originals": the folder's name on disk is
|
||||
an implementation detail of how this plugin lays out a root, and the section is called Media.
|
||||
|
||||
`panelId` so the folder somebody is in survives a reload — without it every refresh drops them
|
||||
back at the top, however deep they were.
|
||||
`urlPath` puts the current folder in `?path=`, so a reload lands where you were and a link can be
|
||||
sent to somebody. it is opt-in in the platform because a screen with TWO browsers would have them
|
||||
fighting over one parameter — this screen has one, so it is safe here and not in general.
|
||||
|
||||
`panelId` stays as well: it remembers the folder for a panel that is re-opened rather than reloaded,
|
||||
which the URL cannot cover.
|
||||
*/
|
||||
return <FileBrowserApp basePath={base} rootLabel="Media" panelId="fofos-admin-media" />;
|
||||
return <FileBrowserApp basePath={base} rootLabel="Media" urlPath panelId="fofos-admin-media" />;
|
||||
};
|
||||
|
||||
const Notice = ({ icon: Icon, children }: { icon: typeof FolderOpen; children: React.ReactNode }) => (
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { AppRegistryMeta } from 'officerdev';
|
||||
import { List, PanelRight } from 'lucide-react';
|
||||
import { NavPanel } from './NavPanel';
|
||||
import { DetailPanel } from './DetailPanel';
|
||||
import { DetailHeader } from './DetailHeader';
|
||||
|
||||
/**
|
||||
* Two panels: a nav, and whatever it selects.
|
||||
@@ -26,6 +27,9 @@ export const appRegistryMetas: AppRegistryMeta[] = [
|
||||
name: 'Detail',
|
||||
icon: PanelRight,
|
||||
component: DetailPanel,
|
||||
// The chrome header says which SECTION is open rather than the word "Detail" — see DetailHeader for
|
||||
// why that replaced a second header row inside the panel.
|
||||
header: DetailHeader,
|
||||
availableOnPanel: false,
|
||||
},
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user