UI polish: frameless plugin blocks in read mode, sticky toolbar, pinned footer, icon uninstall
All checks were successful
CD / Build and push images (push) Successful in 1m11s
CD / Deploy to Test (push) Successful in 11s
CD / Smoke tests against Test (push) Successful in 1m14s
CD / Promote to Int (push) Successful in 10s
CI / Lint, typecheck, test (push) Successful in 4m5s
CI / Build container images (push) Has been skipped
CI / Auth e2e pack (push) Successful in 5m35s
CI / Import/export fidelity gate (push) Successful in 47s
Release / Build release images and notes (push) Successful in 1m6s
Release / Release-candidate operations QA (push) Successful in 40s
Prod deploy / Deploy the released images to Prod (push) Successful in 15s

Three refinements from Stefan's review of the plugin work:

- read mode integrates plugin output like normal content: no border, no
  name bar, no selection outline around plugin blocks — same principle
  as the frameless reading shell (M10)
- the editor toolbar pins to the top of the scrolling content area on
  long articles instead of scrolling away (position: sticky within the
  main scroll container)
- the app footer (connection status + legal links) moved out of the
  scroll container into a main-column wrapper — always visible at the
  bottom edge of the window on every view
- the plugin uninstall buttons in the admin list are icon buttons now
  (Trash2, house pattern: aria-label keeps the accessible name)
- lint hygiene: eslint/prettier ignore packages/plugins/*/vendor —
  the unpacked drawio webapp drove eslint out of memory

Verified in the browser against a local stack (5/5 scripted checks:
icon buttons, toolbar sticky at scroll bottom, footer pinned in edit
and read mode, plugin block computed border/outline none in read mode)
plus 8 layout-sensitive e2e packs re-run individually, all green
(comments, collab, legal, content, plugin-admin, plugin-blocks,
page-tools, plugins).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwZ4jR4KFAPvpjWevfUGX1
This commit is contained in:
Claude Fable 5 2026-07-12 15:33:32 +02:00
parent 97f94f247b
commit c8ec549fa5
5 changed files with 47 additions and 8 deletions

View File

@ -14,3 +14,4 @@ fixtures/import/*.src.html
# Export fidelity corpus (issue #69): the round-trip snapshots must stay exactly # Export fidelity corpus (issue #69): the round-trip snapshots must stay exactly
# as pandoc reads the exported document back — Prettier would break them. # as pandoc reads the exported document back — Prettier would break them.
fixtures/export/*.expected.md fixtures/export/*.expected.md
packages/plugins/*/vendor/

View File

@ -63,10 +63,14 @@ export function AppLayout(): React.JSX.Element {
> >
<Sidebar collapsed={collapsed} /> <Sidebar collapsed={collapsed} />
{!collapsed && <SidebarResizer widthRem={widthRem} onResize={setSidebarWidth} />} {!collapsed && <SidebarResizer widthRem={widthRem} onResize={setSidebarWidth} />}
<main className="main"> <div className="main-column">
<Outlet /> <main className="main">
<Outlet />
</main>
{/* Outside the scroll container: always visible at the bottom
edge of the window (M10 follow-up 3). */}
<Footer /> <Footer />
</main> </div>
</div> </div>
</div> </div>
</PageActionsSlotContext.Provider> </PageActionsSlotContext.Provider>

View File

@ -1,3 +1,4 @@
import { Trash2 } from 'lucide-react';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useRef, useState } from 'react'; import { useRef, useState } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
@ -122,12 +123,15 @@ export function PluginManager(): React.JSX.Element {
</Link> </Link>
<button <button
type="button" type="button"
className="button button--danger" className="icon-button plugin-manager__uninstall"
disabled={plugin.mode === 'required'} disabled={plugin.mode === 'required'}
title={plugin.mode === 'required' ? t('admin.requiredLocked') : undefined} aria-label={t('admin.uninstall')}
title={
plugin.mode === 'required' ? t('admin.requiredLocked') : t('admin.uninstall')
}
onClick={() => uninstall.mutate(plugin.id)} onClick={() => uninstall.mutate(plugin.id)}
> >
{t('admin.uninstall')} <Trash2 aria-hidden />
</button> </button>
</div> </div>
</li> </li>

View File

@ -248,8 +248,16 @@ button {
max-width: 36rem; max-width: 36rem;
} }
.main-column {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
}
.main { .main {
flex: 1; flex: 1;
min-height: 0;
min-width: 0; min-width: 0;
overflow-y: auto; overflow-y: auto;
padding: var(--space-6) var(--space-8); padding: var(--space-6) var(--space-8);
@ -497,9 +505,10 @@ button {
display: flex; display: flex;
align-items: center; align-items: center;
gap: var(--space-2); gap: var(--space-2);
margin-top: var(--space-8); flex-shrink: 0;
padding-top: var(--space-3); padding: var(--space-2) var(--space-8);
border-top: 1px solid var(--color-border); border-top: 1px solid var(--color-border);
background: var(--color-bg);
color: var(--color-text-muted); color: var(--color-text-muted);
font-size: 0.85rem; font-size: 0.85rem;
} }
@ -832,6 +841,11 @@ button {
padding: var(--space-2) var(--space-3); padding: var(--space-2) var(--space-3);
border-bottom: 1px solid var(--color-border); border-bottom: 1px solid var(--color-border);
background: var(--color-bg-subtle); background: var(--color-bg-subtle);
/* Long articles: the toolbar pins to the top of the scrolling content
area instead of scrolling away. */
position: sticky;
top: 0;
z-index: 20;
} }
.editor-toolbar__group { .editor-toolbar__group {
@ -931,6 +945,20 @@ button {
border: none; border: none;
} }
/* Read mode integrates plugin output like normal content: no frame, no
name bar same principle as the frameless reading shell (M10). */
.editor-shell--reading .plugin-block {
border: none;
border-radius: 0;
background: transparent;
/* No selection outline either — reading is not editing. */
outline: none;
}
.editor-shell--reading .plugin-block__bar {
display: none;
}
.plugin-block__fallback { .plugin-block__fallback {
padding: var(--space-2) var(--space-3); padding: var(--space-2) var(--space-3);
} }

View File

@ -9,6 +9,8 @@ export default tseslint.config(
{ {
ignores: [ ignores: [
'**/dist/**', '**/dist/**',
// Vendored third-party apps bundled into plugins (drawio) — never linted.
'packages/plugins/*/vendor/**',
'**/node_modules/**', '**/node_modules/**',
'**/coverage/**', '**/coverage/**',
'**/.pnpm-store/**', '**/.pnpm-store/**',