Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,22 @@ Changelog ist die Upgrade-Anleitung für die Tools.

## [Unreleased]

### @basicbar/ui (→ wird `ui/v0.5.1`)

**CSP-tauglicher `RichTextEditor`** (basicbar#9): TipTap hängte beim Mounten
ein `<style data-tiptap-style>` mit den ProseMirror-Grundregeln an `<head>` —
unter einer Content-Security-Policy mit `style-src 'self'` (ohne
`'unsafe-inline'`) blockiert der Browser das, der Editor verliert dann u. a.
sein Umbruchverhalten. Der Editor läuft jetzt mit `injectCSS: false`; die
Regeln (unverändert aus `@tiptap/core` `src/style.ts`, TipTap 3.31) stehen in
`base.css` — bewusst außerhalb jedes `@layer`, weil Tailwind die zur Laufzeit
gesetzten `ProseMirror-*`-Klassen nicht sieht und gelayerte Regeln sonst
entfernen würde. README: neuer Abschnitt „CSP“.

Migration: keine, solange das Tool `@basicbar/ui/base.css` importiert (wie
im README beschrieben). Wer `base.css` nicht einbindet, muss die
ProseMirror-Regeln selbst bereitstellen.

### basicbar-integrations (→ wird `integrations/v0.2.1`)

**Sicherheitsfix:** `clean_media_url` prüfte den rohen String, sodass
Expand Down
21 changes: 19 additions & 2 deletions packages/ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ Tools identisch aussehen, übergeben sie dieselbe Ramp.
(Plus Jakarta Sans Variable), Dark Mode per `.dark`-Klasse, Motion-Tokens
(`pop`, `fade-up`, `out-quart`). Die `slate`/`brand`-Ramps übergibt das Tool
(OKLCH, Konventionen im Preset-Docstring).
- **`@basicbar/ui/base.css`** — gemeinsamer `@layer base`: Canvas hell/dunkel,
- **`@basicbar/ui/base.css`** — gemeinsame Basis-Styles (`@layer base` plus
ungelayerte ProseMirror-Regeln): Canvas hell/dunkel,
`accent-color`, `::selection`, konsistenter `:focus-visible`-Ring
(WCAG 2.4.7), `color-scheme`-Regeln, Skip-Link (WCAG 2.4.1),
`prefers-reduced-motion`, `text-wrap: balance`, tabellarische Ziffern.
`prefers-reduced-motion`, `text-wrap: balance`, tabellarische Ziffern,
dazu die ProseMirror-Grundregeln für `RichTextEditor` (siehe „CSP“).
Als erste Zeile der Tool-`index.css` importieren.
- **`ThemeProvider` / `useTheme` / `prePaintScript`** — Auto/Light/Dark mit
Live-Systemfolge und `color-scheme`-Sync; `storageKey` konfigurierbar
Expand Down Expand Up @@ -172,3 +174,18 @@ siehe `initI18n`): `"Bold"`, `"Italic"`, `"Heading (large)"`,
`"Enter URL"`, `"Insert image (or drag and drop)"`,
`"Image upload failed"`, `"Image description (alt text)"`,
`"Image description"`.

## CSP

Die Komponenten injizieren zur Laufzeit **keine** Inline-Styles oder
-Skripte, damit Tools eine strikte Content-Security-Policy
(`script-src 'self'; style-src 'self'`, ohne `'unsafe-inline'`) fahren können.
`RichTextEditor` läuft deshalb mit TipTaps `injectCSS: false`; die
ProseMirror-Grundregeln (Umbruchverhalten, Gapcursor, Auswahl) kommen aus
`base.css` — ein Tool, das `base.css` wie oben importiert, muss nichts weiter
tun. React-`style={{}}`-Props (CSSOM) sind unter `style-src 'self'` erlaubt.

Ausnahme: `prePaintScript()` liefert das Theme-Skript als String für ein
Inline-`<script>` in `index.html`. Unter einer strikten CSP schreibt das Tool
den Inhalt stattdessen in eine eigene Datei (z. B. `public/theme-init.js`) und
bindet sie synchron im `<head>` ein (so macht es ausleihbar, #44).
81 changes: 81 additions & 0 deletions packages/ui/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,84 @@
scroll-behavior: auto !important;
}
}

/* ProseMirror/TipTap editor base rules (RichTextEditor). Copied verbatim from
@tiptap/core's `src/style.ts` (TipTap 3.31) because the editor runs with
`injectCSS: false`: TipTap's own runtime <style> injection would break a
CSP with `style-src 'self'` (basicbar#9). Re-check against that file when
upgrading TipTap. Deliberately NOT inside an @layer: the ProseMirror-*
classes are set at runtime by prosemirror-view (node_modules), so Tailwind's
content scan never sees them and would purge layered rules. (Tailwind v3;
re-check the cascade if a consumer moves to v4's native CSS layers, where
unlayered rules outrank utilities.) */
.ProseMirror {
position: relative;
}

.ProseMirror {
word-wrap: break-word;
white-space: pre-wrap;
white-space: break-spaces;
-webkit-font-variant-ligatures: none;
font-variant-ligatures: none;
font-feature-settings: "liga" 0; /* the above doesn't seem to work in Edge */
}

.ProseMirror [contenteditable="false"] {
white-space: normal;
}

.ProseMirror [contenteditable="false"] [contenteditable="true"] {
white-space: pre-wrap;
}

.ProseMirror pre {
white-space: pre-wrap;
}

img.ProseMirror-separator {
display: inline !important;
border: none !important;
margin: 0 !important;
width: 0 !important;
height: 0 !important;
}

.ProseMirror-gapcursor {
display: none;
pointer-events: none;
position: absolute;
margin: 0;
}

.ProseMirror-gapcursor:after {
content: "";
display: block;
position: absolute;
top: -2px;
width: 20px;
border-top: 1px solid black;
animation: ProseMirror-cursor-blink 1.1s steps(2, start) infinite;
}

@keyframes ProseMirror-cursor-blink {
to {
visibility: hidden;
}
}

.ProseMirror-hideselection *::selection {
background: transparent;
}

.ProseMirror-hideselection *::-moz-selection {
background: transparent;
}

.ProseMirror-hideselection * {
caret-color: transparent;
}

.ProseMirror-focused .ProseMirror-gapcursor {
display: block;
}
4 changes: 2 additions & 2 deletions packages/ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@basicbar/ui",
"version": "0.5.0",
"version": "0.5.1",
"description": "Design-System-Basis der virtUOS -bar-Tools: Tailwind-Preset, Basis-Styles, Theme (Dark Mode), i18n-Bootstrap, contentLang, RichTextEditor/RichText (TipTap)",
"license": "Apache-2.0",
"author": "Universität Osnabrück (virtUOS)",
Expand Down
5 changes: 5 additions & 0 deletions packages/ui/src/RichTextEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@ export function RichTextEditor({
// document change) must still re-render this component or those buttons
// go stale until the next actual edit.
shouldRerenderOnTransaction: true,
// TipTap would otherwise append a <style data-tiptap-style> to <head> at
// runtime, which a CSP with `style-src 'self'` (no 'unsafe-inline')
// blocks (ausleihbar#44/#45, basicbar#9). The same ProseMirror base rules
// ship in base.css instead.
injectCSS: false,
extensions: [
StarterKit.configure({
heading: { levels: [2, 3] },
Expand Down
Loading