feat: add shared Toolbox header and themes

This commit is contained in:
2026-07-23 00:29:21 +02:00
parent e3aaea02ee
commit 4f6089247a
14 changed files with 229 additions and 233 deletions

View File

@@ -551,11 +551,7 @@ export function App() {
<AppShell
app={toolboxApp}
manifestUrl="./toolbox-app.json"
appActions={
<button type="button" onClick={() => setHelpOpen(true)}>
Help
</button>
}
helpAction={{ onClick: () => setHelpOpen(true) }}
>
<OneNoteApplication />
{helpOpen ? <HelpDialog onClose={() => setHelpOpen(false)} /> : null}

43
src/AppShell.test.tsx Normal file
View File

@@ -0,0 +1,43 @@
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { beforeEach, describe, expect, it } from 'vitest';
import { App } from './App.js';
describe('Toolbox application shell', () => {
beforeEach(() => {
localStorage.clear();
});
it('uses the standard app-specific help and source controls', async () => {
const user = userEvent.setup();
render(<App />);
expect(
screen.getByRole('heading', { name: 'OneNote Tools' })
).toBeInTheDocument();
expect(
screen.getByRole('link', {
name: 'Source for OneNote Tools on Gitea',
})
).toHaveAttribute('href', 'https://git.add-ideas.de/zemion/onenote-tools');
await user.click(screen.getByRole('button', { name: 'Help' }));
expect(
screen.getByRole('dialog', { name: 'About OneNote Tools' })
).toBeVisible();
});
it('applies an explicit appearance choice to the complete app', async () => {
const user = userEvent.setup();
const { container } = render(<App />);
await user.click(screen.getByText('Personalize'));
await user.click(screen.getByRole('button', { name: 'Dark' }));
expect(container.querySelector('.toolbox-shell')).toHaveAttribute(
'data-toolbox-theme',
'dark'
);
});
});

View File

@@ -1,6 +1,4 @@
:root {
color: #241b2b;
background: #f6f3f8;
font-family:
Inter,
ui-sans-serif,
@@ -22,45 +20,86 @@ body {
min-height: 100vh;
}
button,
input {
.toolbox-shell__main {
--onenote-page-surface: #fff;
--onenote-page-text: #241b2b;
--onenote-content-surface: #faf7fc;
--onenote-control-surface: #fff;
--onenote-control-hover: #f1e8f8;
--onenote-control-text: #4c1f6b;
--onenote-control-border: #704294;
--onenote-selection-surface: #eee3f5;
--onenote-selection-text: #3e145c;
--onenote-code-surface: #eee7f3;
--onenote-warning-surface: #fbf4e6;
--onenote-warning-text: #66552f;
--onenote-error-surface: #faeaea;
--onenote-error-text: #9b3131;
color: var(--onenote-page-text);
background: var(--toolbox-background);
}
.toolbox-shell[data-toolbox-theme='dark'] .toolbox-shell__main {
--onenote-page-surface: #2b2231;
--onenote-page-text: #f6effa;
--onenote-content-surface: #302638;
--onenote-control-surface: #35283d;
--onenote-control-hover: #3c2b48;
--onenote-control-text: #f4e7fc;
--onenote-control-border: #755e82;
--onenote-selection-surface: #3c2b48;
--onenote-selection-text: #f6effa;
--onenote-code-surface: #3a3040;
--onenote-warning-surface: #493b23;
--onenote-warning-text: #f5e8cd;
--onenote-error-surface: #4d2929;
--onenote-error-text: #ffb3b3;
color-scheme: dark;
}
.toolbox-shell[data-toolbox-theme='light'] .toolbox-shell__main {
color-scheme: light;
}
:where(.toolbox-shell__main) button,
:where(.toolbox-shell__main) input {
font: inherit;
}
button {
border: 1px solid #704294;
:where(.toolbox-shell__main) button {
border: 1px solid var(--onenote-control-border);
border-radius: 0.55rem;
padding: 0.55rem 0.9rem;
color: #4c1f6b;
background: #fff;
color: var(--onenote-control-text);
background: var(--onenote-control-surface);
cursor: pointer;
}
button:hover:not(:disabled) {
background: #f1e8f8;
:where(.toolbox-shell__main) button:hover:not(:disabled) {
background: var(--onenote-control-hover);
}
button:focus-visible,
summary:focus-visible {
outline: 3px solid #b788d4;
:where(.toolbox-shell__main) button:focus-visible,
:where(.toolbox-shell__main) summary:focus-visible {
outline: 3px solid var(--toolbox-focus);
outline-offset: 2px;
}
button:disabled {
:where(.toolbox-shell__main) button:disabled {
cursor: not-allowed;
opacity: 0.55;
}
code {
:where(.toolbox-shell__main) code {
border-radius: 0.25rem;
padding: 0.08rem 0.25rem;
overflow-wrap: anywhere;
background: #eee7f3;
background: var(--onenote-code-surface);
}
h1,
h2,
p {
:where(.toolbox-shell__main) h1,
:where(.toolbox-shell__main) h2,
:where(.toolbox-shell__main) p {
overflow-wrap: anywhere;
}
@@ -77,10 +116,10 @@ p {
.package-inspector,
.export-controls,
.diagnostics {
border: 1px solid #c9b9d5;
border: 1px solid var(--toolbox-border);
border-radius: 1rem;
background: #fff;
box-shadow: 0 0.75rem 2.5rem rgb(62 31 85 / 8%);
background: var(--toolbox-surface);
box-shadow: var(--toolbox-shadow);
}
.export-controls {
@@ -98,7 +137,7 @@ p {
.export-controls > div > p:last-child {
margin: 0.35rem 0 0;
color: #716675;
color: var(--toolbox-muted);
font-size: 0.86rem;
}
@@ -110,18 +149,18 @@ p {
.export-controls__actions label {
grid-column: 1 / -1;
color: #574b5b;
color: var(--toolbox-muted);
font-size: 0.78rem;
font-weight: 700;
}
.export-controls__actions select {
min-width: 0;
border: 1px solid #a98ab9;
border: 1px solid var(--onenote-control-border);
border-radius: 0.55rem;
padding: 0.55rem 2rem 0.55rem 0.7rem;
color: inherit;
background: #fff;
background: var(--onenote-control-surface);
font: inherit;
}
@@ -129,12 +168,12 @@ p {
grid-column: 1 / -1;
min-height: 1.2em;
margin: 0;
color: #716675;
color: var(--toolbox-muted);
font-size: 0.78rem;
}
.export-controls__status--error {
color: #9b3131;
color: var(--onenote-error-text);
}
.drop-zone,
@@ -148,8 +187,8 @@ p {
}
.drop-zone--active {
border-color: #6b368f;
background: #f7effd;
border-color: var(--toolbox-accent);
background: var(--toolbox-accent-soft);
}
.drop-zone h2,
@@ -161,7 +200,7 @@ p {
.status-card__label,
.eyebrow {
margin: 0;
color: #6b368f;
color: var(--toolbox-accent);
font-size: 0.76rem;
font-weight: 750;
letter-spacing: 0.08em;
@@ -170,18 +209,18 @@ p {
.primary-button {
margin-top: 0.75rem;
color: #fff;
background: #5b2c83;
color: var(--toolbox-accent-contrast);
background: var(--toolbox-accent);
}
.primary-button:hover:not(:disabled) {
background: #472164;
background: var(--toolbox-accent-hover);
}
.drop-zone__hint {
display: block;
margin-top: 0.75rem;
color: #6f6476;
color: var(--toolbox-muted);
font-size: 0.9rem;
}
@@ -211,16 +250,16 @@ p {
grid-template-columns: minmax(16rem, 0.78fr) minmax(0, 2fr);
min-height: 30rem;
overflow: hidden;
border: 1px solid #c9b9d5;
border: 1px solid var(--toolbox-border);
border-radius: 1rem;
background: #fff;
box-shadow: 0 0.75rem 2.5rem rgb(62 31 85 / 8%);
background: var(--toolbox-surface);
box-shadow: var(--toolbox-shadow);
}
.source-navigation {
overflow: auto;
border-right: 1px solid #d9cedf;
background: #faf7fc;
border-right: 1px solid var(--toolbox-border);
background: var(--toolbox-surface-soft);
}
.navigation-heading {
@@ -228,8 +267,8 @@ p {
z-index: 1;
top: 0;
padding: 1.2rem;
border-bottom: 1px solid #ded5e3;
background: #faf7fc;
border-bottom: 1px solid var(--toolbox-border);
background: var(--toolbox-surface-soft);
}
.navigation-heading h2 {
@@ -239,7 +278,7 @@ p {
.navigation-heading > p:last-child {
margin: 0.35rem 0 0;
color: #716675;
color: var(--toolbox-muted);
font-size: 0.8rem;
}
@@ -271,7 +310,7 @@ p {
.page-list button span {
display: -webkit-box;
overflow: hidden;
color: #716675;
color: var(--toolbox-muted);
font-size: 0.78rem;
font-weight: 400;
-webkit-box-orient: vertical;
@@ -280,9 +319,9 @@ p {
.page-list button.is-selected,
.notebook-section > button.is-selected {
border-color: #af8bc6;
color: #3e145c;
background: #eee3f5;
border-color: var(--toolbox-accent);
color: var(--onenote-selection-text);
background: var(--onenote-selection-surface);
}
.section-list {
@@ -296,21 +335,21 @@ p {
.notebook-tree ul {
margin-inline-start: 0.7rem;
border-inline-start: 1px solid #ded5e3;
border-inline-start: 1px solid var(--toolbox-border);
padding-inline-start: 0.45rem;
}
.section-group summary {
cursor: pointer;
padding: 0.62rem 0.45rem;
color: #574b5b;
color: var(--toolbox-muted);
font-size: 0.86rem;
font-weight: 700;
}
.notebook-section > .page-list {
margin: 0 0 0.4rem 0.7rem;
border-inline-start: 1px solid #ded5e3;
border-inline-start: 1px solid var(--toolbox-border);
padding: 0.25rem 0 0.2rem 0.45rem;
}
@@ -328,7 +367,7 @@ p {
.empty-message {
margin: 0;
padding: 1rem 1.2rem;
color: #716675;
color: var(--toolbox-muted);
}
.page-reader {
@@ -336,14 +375,14 @@ p {
min-height: 0;
overflow: auto;
padding: clamp(1.4rem, 4vw, 3rem);
background: #fff;
background: var(--onenote-page-surface);
}
.page-reader--empty {
display: grid;
place-items: center;
min-height: 20rem;
color: #716675;
color: var(--toolbox-muted);
text-align: center;
}
@@ -354,7 +393,7 @@ p {
.page-reader__header {
margin-bottom: 2rem;
padding-bottom: 1.25rem;
border-bottom: 1px solid #e1d9e5;
border-bottom: 1px solid var(--toolbox-border);
}
.page-reader__header h2 {
@@ -368,7 +407,7 @@ p {
flex-wrap: wrap;
gap: 0.3rem 1rem;
margin: 1rem 0 0;
color: #716675;
color: var(--toolbox-muted);
font-size: 0.82rem;
}
@@ -409,7 +448,7 @@ p {
}
.onenote-link {
color: #5b2c83;
color: var(--toolbox-accent);
text-decoration-thickness: 0.08em;
text-underline-offset: 0.12em;
}
@@ -425,12 +464,12 @@ p {
min-width: min(100%, 28rem);
border-spacing: 0;
border-collapse: collapse;
background: #fff;
background: var(--onenote-page-surface);
}
.onenote-table td {
min-width: 5rem;
border: 1px solid #cfc5d4;
border: 1px solid var(--toolbox-border);
padding: 0.55rem 0.65rem;
vertical-align: top;
}
@@ -466,7 +505,7 @@ p {
.onenote-image figcaption {
max-width: 40rem;
color: #716675;
color: var(--toolbox-muted);
font-size: 0.82rem;
}
@@ -476,11 +515,11 @@ p {
min-height: 7rem;
max-width: 32rem;
place-items: center;
border: 1px dashed #a58caf;
border: 1px dashed var(--toolbox-border);
border-radius: 0.55rem;
padding: 1rem;
color: #716675;
background: #faf7fc;
color: var(--toolbox-muted);
background: var(--onenote-content-surface);
text-align: center;
}
@@ -490,10 +529,10 @@ p {
align-items: center;
gap: 0.75rem 1rem;
margin-block: 0.8rem;
border: 1px solid #d4c8db;
border: 1px solid var(--toolbox-border);
border-radius: 0.65rem;
padding: 0.8rem;
background: #faf7fc;
background: var(--onenote-content-surface);
}
.attachment-card > div {
@@ -504,7 +543,7 @@ p {
.attachment-card span,
.attachment-card small {
color: #716675;
color: var(--toolbox-muted);
font-size: 0.8rem;
}
@@ -517,7 +556,7 @@ p {
max-width: 100%;
margin-block: 0.75rem;
overflow: visible;
color: #241b2b;
color: var(--onenote-page-text);
}
.onenote-ink svg {
@@ -533,16 +572,16 @@ p {
margin: 0.8rem 0;
border-left: 0.2rem solid #9872ae;
padding: 0.65rem 0.85rem;
color: #5e5362;
background: #f5eff8;
color: var(--toolbox-muted);
background: var(--toolbox-surface-soft);
}
.render-limit-notice {
margin: 1rem 0 0;
border-left: 0.2rem solid #b18124;
padding: 0.7rem 0.85rem;
color: #66552f;
background: #fbf4e6;
color: var(--onenote-warning-text);
background: var(--onenote-warning-surface);
font-size: 0.85rem;
}
@@ -569,8 +608,8 @@ p {
min-height: 2rem;
place-items: center;
border-radius: 999px;
color: #552477;
background: #eee3f5;
color: var(--toolbox-accent);
background: var(--toolbox-accent-soft);
font-weight: 700;
}
@@ -584,11 +623,11 @@ p {
.package-summary > div {
border-radius: 0.65rem;
padding: 0.75rem;
background: #f6f1f8;
background: var(--toolbox-surface-soft);
}
.package-summary dt {
color: #716675;
color: var(--toolbox-muted);
font-size: 0.75rem;
}
@@ -599,13 +638,13 @@ p {
}
.entry-inspector {
border-top: 1px solid #ded5e3;
border-top: 1px solid var(--toolbox-border);
padding-top: 0.9rem;
}
.entry-inspector summary {
width: fit-content;
color: #5b2c83;
color: var(--toolbox-accent);
cursor: pointer;
font-weight: 700;
}
@@ -626,20 +665,20 @@ p {
.entry-table th,
.entry-table td {
padding: 0.62rem;
border-bottom: 1px solid #e5dee9;
border-bottom: 1px solid var(--toolbox-border);
vertical-align: top;
}
.entry-table th {
position: sticky;
top: 0;
background: #fff;
background: var(--toolbox-surface);
}
.entry-table small {
display: block;
margin-top: 0.3rem;
color: #716675;
color: var(--toolbox-muted);
}
.status {
@@ -677,12 +716,14 @@ p {
.diagnostic {
border-left: 0.22rem solid #b18124;
padding: 0.7rem 0.85rem;
background: #fbf4e6;
color: var(--onenote-warning-text);
background: var(--onenote-warning-surface);
}
.diagnostic--error {
border-left-color: #b65252;
background: #faeaea;
color: var(--onenote-error-text);
background: var(--onenote-error-surface);
}
.diagnostic__summary {
@@ -719,13 +760,13 @@ p {
.privacy-note {
border-left: 0.25rem solid #7d4aa0;
padding: 0.85rem 1rem;
color: #4c4054;
background: #eee7f3;
color: var(--toolbox-muted);
background: var(--onenote-code-surface);
}
.dialog-backdrop {
position: fixed;
z-index: 10;
z-index: 70;
inset: 0;
display: grid;
place-items: center;
@@ -737,8 +778,8 @@ p {
width: min(32rem, 100%);
border-radius: 0.8rem;
padding: 1.5rem;
color: #241b2b;
background: #fff;
color: var(--onenote-page-text);
background: var(--onenote-page-surface);
box-shadow: 0 1rem 3rem rgb(0 0 0 / 25%);
}
@@ -771,7 +812,7 @@ p {
.source-navigation {
max-height: 20rem;
border-right: 0;
border-bottom: 1px solid #d9cedf;
border-bottom: 1px solid var(--toolbox-border);
}
.package-summary {
@@ -788,114 +829,21 @@ p {
}
@media (prefers-color-scheme: dark) {
:root {
color: #f6effa;
background: #1f1923;
}
.drop-zone,
.status-card,
.source-toolbar,
.package-inspector,
.diagnostics,
.export-controls,
.reader-workspace,
.page-reader,
.help-dialog,
.entry-table th {
color: #f6effa;
background: #2b2231;
}
.source-navigation,
.navigation-heading {
background: #251d2a;
}
.drop-zone--active,
button:hover:not(:disabled),
.page-list button.is-selected,
.notebook-section > button.is-selected {
color: #f6effa;
background: #3c2b48;
}
button {
color: #f4e7fc;
background: #35283d;
}
.export-controls__actions select {
border-color: #755e82;
background: #35283d;
}
.export-controls > div > p:last-child,
.export-controls__actions label,
.export-controls__status {
color: #cfc2d6;
}
.export-controls__status--error {
color: #ffb3b3;
}
code,
.privacy-note,
.package-summary > div {
color: #eadff0;
background: #3a3040;
}
.drop-zone__hint,
.navigation-heading > p:last-child,
.navigation-empty,
.empty-message,
.page-metadata,
.page-list button span,
.package-summary dt,
.entry-table small {
color: #cfc2d6;
}
.unsupported-block {
color: #e4dbe9;
background: #3a3040;
}
.onenote-link {
color: #d7acef;
}
.onenote-table,
.attachment-card,
.image-placeholder {
color: #f6effa;
background: #302638;
}
.onenote-table td {
border-color: #62536b;
}
.onenote-image figcaption,
.attachment-card span,
.attachment-card small,
.image-placeholder {
color: #cfc2d6;
}
.onenote-ink {
color: #f6effa;
}
.diagnostic {
color: #f5e8cd;
background: #493b23;
}
.diagnostic--error {
color: #f8dfdf;
background: #4d2929;
.toolbox-shell[data-toolbox-theme='system'] .toolbox-shell__main {
--onenote-page-surface: #2b2231;
--onenote-page-text: #f6effa;
--onenote-content-surface: #302638;
--onenote-control-surface: #35283d;
--onenote-control-hover: #3c2b48;
--onenote-control-text: #f4e7fc;
--onenote-control-border: #755e82;
--onenote-selection-surface: #3c2b48;
--onenote-selection-text: #f6effa;
--onenote-code-surface: #3a3040;
--onenote-warning-surface: #493b23;
--onenote-warning-text: #f5e8cd;
--onenote-error-surface: #4d2929;
--onenote-error-text: #ffb3b3;
color-scheme: dark;
}
}

View File

@@ -1 +1 @@
export const APP_VERSION = '0.2.1';
export const APP_VERSION = '0.3.0';