From 3dd4dd659281de32f0b179a20d299d54237333ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Sat, 29 Aug 2026 08:56:15 +0100 Subject: [PATCH] one header instead of three rows, and the folder lives in the URL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- manifest.ts | 2 +- web/DetailHeader.tsx | 33 +++++++++++++++++++++++++++++++++ web/DetailPanel.tsx | 5 ----- web/MediaPanel.tsx | 10 +++++++--- web/panels.ts | 4 ++++ 5 files changed, 45 insertions(+), 9 deletions(-) create mode 100644 web/DetailHeader.tsx diff --git a/manifest.ts b/manifest.ts index e8fb4fa..152b776 100644 --- a/manifest.ts +++ b/manifest.ts @@ -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', diff --git a/web/DetailHeader.tsx b/web/DetailHeader.tsx new file mode 100644 index 0000000..176bb80 --- /dev/null +++ b/web/DetailHeader.tsx @@ -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 ( + <> + + {section.label} + + ); +}; diff --git a/web/DetailPanel.tsx b/web/DetailPanel.tsx index 5219b27..3831a80 100644 --- a/web/DetailPanel.tsx +++ b/web/DetailPanel.tsx @@ -71,11 +71,6 @@ export const DetailPanel = () => { return (
-
-

{current.label}

- {current.hint} -
-
{Panel ? : }
); diff --git a/web/MediaPanel.tsx b/web/MediaPanel.tsx index cff6161..a8e1aec 100644 --- a/web/MediaPanel.tsx +++ b/web/MediaPanel.tsx @@ -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 ; + return ; }; const Notice = ({ icon: Icon, children }: { icon: typeof FolderOpen; children: React.ReactNode }) => ( diff --git a/web/panels.ts b/web/panels.ts index 4a5a23b..7c4f255 100644 --- a/web/panels.ts +++ b/web/panels.ts @@ -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, }, ];