diff --git a/CHANGELOG.md b/CHANGELOG.md
index a80d270..e3668c8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,22 @@
All notable changes to the independently versioned Toolbox SDK packages are
recorded here. The packages currently share one release version.
+## 0.2.2 — 2026-07-23
+
+### Added
+
+- `ToolboxPersonalizePanel` for the portal-style personalization heading,
+ storage warning, appearance picker, and portal extension content.
+
+### Changed
+
+- The invariant top row now fixes the Toolbox brand/home at the far left and
+ Help, source, Apps, and Personalize at the far right in that order while the
+ text identity remains geometrically centered.
+- Default app personalization now matches the central portal's panel styling;
+ portal-only catalogue counts and import/export actions remain extensible
+ content.
+
## 0.2.1 — 2026-07-23
### Added
diff --git a/README.md b/README.md
index e0f6c61..2e0ada7 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@ tools. An app always works by itself. When it receives a trusted same-origin
catalog URL, the same app gains a toolbox home link and an app switcher without
becoming coupled to a portal or client-side router.
-Version `0.2.1` contains three publish-ready packages:
+Version `0.2.2` contains three publish-ready packages:
- `@add-ideas/toolbox-contract` — types, strict v1 runtime parsing, context
discovery/loading, resolved URLs, and contextual link helpers.
@@ -167,11 +167,11 @@ import "@add-ideas/toolbox-shell-react/styles.css";
;
```
-The shell's fixed top row renders Personalize, Apps, source, and Help controls
-in that order, a geometrically centered text identity, and the Toolbox
-brand/home at the far right. Personalize and Apps have labeled desktop controls
-and retain their accessible names when compact. The mutually exclusive popovers
-close on Escape, outside interaction, and link selection.
+The shell's fixed top row renders the Toolbox brand/home at the far left, a
+geometrically centered text identity, and Help, source, Apps, and Personalize
+controls at the far right in that order. Personalize and Apps have labeled
+desktop controls and retain their accessible names when compact. The mutually
+exclusive popovers close on Escape, outside interaction, and link selection.
`AppShell` moves version, derived privacy facts, manifest actions, and legacy
`appActions` into its standard footer, keeping the top row stable in connected
@@ -183,7 +183,9 @@ optional observability.
For a custom portal-style header, `ToolboxHeader` accepts `personalizeContent`
for content hosted inside the shared controlled popover and `brandIconUrl` for
-the Toolbox mark. `onPersonalize` remains available for callback-only
+the Toolbox mark. `ToolboxPersonalizePanel` provides the shared portal-style
+heading, storage warning, and appearance picker, with a child slot for
+portal-specific controls. `onPersonalize` remains available for callback-only
integrations. The deprecated `iconUrl`, `leadingActions`, and `metadata` props
are retained for source compatibility; the centered app icon is intentionally no
longer rendered.
diff --git a/package-lock.json b/package-lock.json
index 0d77dbf..580a56c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@add-ideas/toolbox-sdk-workspace",
- "version": "0.2.1",
+ "version": "0.2.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@add-ideas/toolbox-sdk-workspace",
- "version": "0.2.1",
+ "version": "0.2.2",
"license": "Apache-2.0",
"workspaces": [
"packages/*"
@@ -3946,7 +3946,7 @@
},
"packages/contract": {
"name": "@add-ideas/toolbox-contract",
- "version": "0.2.1",
+ "version": "0.2.2",
"license": "Apache-2.0",
"engines": {
"node": ">=20"
@@ -3954,10 +3954,10 @@
},
"packages/shell-react": {
"name": "@add-ideas/toolbox-shell-react",
- "version": "0.2.1",
+ "version": "0.2.2",
"license": "Apache-2.0",
"dependencies": {
- "@add-ideas/toolbox-contract": "0.2.1"
+ "@add-ideas/toolbox-contract": "0.2.2"
},
"peerDependencies": {
"react": ">=18 <20",
@@ -3966,10 +3966,10 @@
},
"packages/testkit": {
"name": "@add-ideas/toolbox-testkit",
- "version": "0.2.1",
+ "version": "0.2.2",
"license": "Apache-2.0",
"dependencies": {
- "@add-ideas/toolbox-contract": "0.2.1"
+ "@add-ideas/toolbox-contract": "0.2.2"
},
"bin": {
"toolbox-check": "dist/cli.js"
diff --git a/package.json b/package.json
index cc5e57c..afb4bd1 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@add-ideas/toolbox-sdk-workspace",
- "version": "0.2.1",
+ "version": "0.2.2",
"private": true,
"description": "A small, framework-neutral toolbox contract with a React application shell.",
"license": "Apache-2.0",
diff --git a/packages/contract/package.json b/packages/contract/package.json
index c86e49d..5fb6c3d 100644
--- a/packages/contract/package.json
+++ b/packages/contract/package.json
@@ -1,6 +1,6 @@
{
"name": "@add-ideas/toolbox-contract",
- "version": "0.2.1",
+ "version": "0.2.2",
"description": "Runtime-validated manifests, catalogs, discovery, and URL helpers for toolbox applications.",
"license": "Apache-2.0",
"repository": {
diff --git a/packages/shell-react/README.md b/packages/shell-react/README.md
index 0c838a7..ab0f636 100644
--- a/packages/shell-react/README.md
+++ b/packages/shell-react/README.md
@@ -13,14 +13,17 @@ import "@add-ideas/toolbox-shell-react/styles.css";
standalone mode if context is absent, unavailable, cross-origin, or invalid. Its
app switcher is exposed as the accessible navigation landmark
`Toolbox applications` and all destinations use ordinary document navigation.
-The fixed top row exposes Personalize, Apps, Gitea source, and Help in that
-order, a centered title/subtitle, and a Toolbox home/brand block. Personalize
-offers light/dark/system mode by default. Version, privacy, and application
-actions render in the standard footer so connected and standalone headers keep
-the same geometry.
+The fixed top row exposes a Toolbox home/brand block at the far left, a centered
+title/subtitle, and Help, Gitea source, Apps, and Personalize at the far right
+in that order. Personalize uses the portal-style panel and offers
+light/dark/system mode by default. Version, privacy, and application actions
+render in the standard footer so connected and standalone headers keep the same
+geometry.
`ToolboxHeader` is exported separately for custom shells. Its
`personalizeContent` prop puts custom preferences inside the shared controlled
popover, while `brandIconUrl` supplies a portal favicon; an inline Toolbox cube
-is the fallback. Personalize and Apps popovers are mutually exclusive and close
-on Escape, outside interaction, or link selection.
+is the fallback. `ToolboxPersonalizePanel` shares the portal heading, optional
+storage warning, and appearance picker with custom preference content.
+Personalize and Apps popovers are mutually exclusive and close on Escape,
+outside interaction, or link selection.
diff --git a/packages/shell-react/package.json b/packages/shell-react/package.json
index 1cb11f5..6a9c8ee 100644
--- a/packages/shell-react/package.json
+++ b/packages/shell-react/package.json
@@ -1,6 +1,6 @@
{
"name": "@add-ideas/toolbox-shell-react",
- "version": "0.2.1",
+ "version": "0.2.2",
"description": "A lightweight React application shell for toolbox-compatible browser applications.",
"license": "Apache-2.0",
"repository": {
@@ -38,7 +38,7 @@
"react-dom": ">=18 <20"
},
"dependencies": {
- "@add-ideas/toolbox-contract": "0.2.1"
+ "@add-ideas/toolbox-contract": "0.2.2"
},
"scripts": {
"build": "tsc -p tsconfig.build.json && node -e \"const fs=require('node:fs');fs.copyFileSync('src/styles.css','dist/styles.css');fs.copyFileSync('../../LICENSE','LICENSE')\"",
diff --git a/packages/shell-react/src/ToolboxHeader.tsx b/packages/shell-react/src/ToolboxHeader.tsx
index befe5e7..847e189 100644
--- a/packages/shell-react/src/ToolboxHeader.tsx
+++ b/packages/shell-react/src/ToolboxHeader.tsx
@@ -46,6 +46,13 @@ export interface ToolboxHeaderProps {
className?: string | undefined;
}
+export interface ToolboxPersonalizePanelProps {
+ theme: ToolboxThemeMode;
+ onThemeChange: (theme: ToolboxThemeMode) => void;
+ storageAvailable?: boolean | undefined;
+ children?: ReactNode | undefined;
+}
+
type OpenPopover = "personalize" | "apps" | null;
function HelpIcon() {
@@ -117,6 +124,57 @@ function ToolboxBrandMark({ iconUrl }: { iconUrl?: string | undefined }) {
const THEMES: readonly ToolboxThemeMode[] = ["system", "light", "dark"];
+export function ToolboxPersonalizePanel({
+ theme,
+ onThemeChange,
+ storageAvailable,
+ children,
+}: ToolboxPersonalizePanelProps) {
+ const headingId = useId();
+
+ return (
+ On this device
+ Browser storage is unavailable. Changes will last only until this page
+ closes.
+
+ The Toolbox stores your appearance choice in this browser and does not
+ transmit it.
+ Personalize your toolbox
+
Portal preference actions
+