The inline Paliadin chat surface — reachable from every authenticated
page, replacing the standalone /paliadin route as the primary entry
point. The standalone page survives as the dedicated full-screen mode
(the drawer's "↗ fullscreen" action links to it).
Components:
- frontend/src/components/PaliadinWidget.tsx — emits the floating
trigger button (bottom-right, lime ✨, owner-revealed by JS), a
scrim, and the right-edge slide-out drawer with header (reset /
fullscreen / close), context chip, message stream, empty-state
starter list, and textarea+send form. Loads /assets/paliadin-widget.js.
- frontend/src/client/paliadin-widget.ts — runtime. /api/me probe
reveals the trigger when caller matches PaliadinOwnerEmail (with
optional is_paliadin_owner flag fast-path); Cmd+J / Ctrl+J shortcut
toggles open/close (Cmd+K stays reserved for global search per
client/search.ts). Uses computePaliadinContext() (Slice B) per send
so route + entity + selection flow into every turn. SSE consumer
writes assistant bubbles; localStorage persists per-session history.
- frontend/src/client/paliadin-starters.ts — per-route starter prompt
registry. 14 routes covered (dashboard, projects.*, deadlines.*,
appointments.*, agenda, events, inbox, tools.*, glossary, courts) +
a _default fallback. Bilingual (DE/EN); prompts ending in `: ` seed
the textarea for the user to finish; fully-formed prompts auto-send.
- 39 authenticated TSX pages get a `<PaliadinWidget />` element after
`<Footer />` via a mechanical pass. paliadin.tsx (the standalone)
is intentionally excluded — its dedicated UI is the widget's
fullscreen escape hatch, not a place to overlay another widget.
- frontend/build.ts registers the new bundle.
- frontend/src/styles/global.css gains ~280 lines of widget CSS
(trigger / scrim / drawer / header / context-chip / messages /
bubbles / starters / form / send-btn) using only existing tokens.
Mobile (≤640px): drawer goes full-screen; trigger lifts above
bottom-nav slots.
- 11 new i18n keys × 2 langs = 22 entries under paliadin.widget.*.
Visibility predicate (paliadin-context.shouldSendContext) hides the
widget on /paliadin, /login, /onboarding. Owner-only gate stays on
PaliadinOwnerEmail.
Build clean: i18n 1955 → 1966 keys, IIFE-wrapped 218KB bundle, go test
green.
Refs: docs/design-paliadin-inline-2026-05-08.md §3, §5.
85 lines
3.3 KiB
TypeScript
85 lines
3.3 KiB
TypeScript
import { h } from "./jsx";
|
|
import { Sidebar } from "./components/Sidebar";
|
|
import { PaliadinWidget } from "./components/PaliadinWidget";
|
|
import { BottomNav } from "./components/BottomNav";
|
|
import { Footer } from "./components/Footer";
|
|
import { PWAHead } from "./components/PWAHead";
|
|
import { FIRM } from "./branding";
|
|
|
|
const ICON_WORD = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"/><polyline points="14 2 14 8 20 8"/><path d="M8 13l1.5 5 1.5-4 1.5 4 1.5-5"/></svg>';
|
|
|
|
interface DownloadFile {
|
|
href: string;
|
|
icon: string;
|
|
titleKey: string;
|
|
titleDE: string;
|
|
descKey: string;
|
|
descDE: string;
|
|
}
|
|
|
|
// URL slug stays "hl-patents-style.dotm" — it's a stable public identifier
|
|
// that bookmarks point at; the user-facing title/description are firm-agnostic.
|
|
const files: DownloadFile[] = [
|
|
{
|
|
href: "/files/hl-patents-style.dotm",
|
|
icon: ICON_WORD,
|
|
titleKey: "downloads.style.title",
|
|
titleDE: `${FIRM} Patents Style`,
|
|
descKey: "downloads.style.desc",
|
|
descDE: `Word-Vorlage im ${FIRM} Patents Style. Formatierung, Schriftarten und Makros für standardisierte Schriftsätze.`,
|
|
},
|
|
];
|
|
|
|
export function renderDownloads(): string {
|
|
return "<!DOCTYPE html>" + (
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
|
<meta name="theme-color" content="#BFF355" />
|
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
|
|
<PWAHead />
|
|
<title data-i18n="downloads.title">Downloads — Paliad</title>
|
|
<link rel="stylesheet" href="/assets/global.css" />
|
|
</head>
|
|
<body className="has-sidebar">
|
|
<Sidebar currentPath="/downloads" />
|
|
<BottomNav currentPath="/downloads" />
|
|
|
|
<main>
|
|
<section className="tool-page">
|
|
<div className="container">
|
|
<div className="tool-header">
|
|
<h1 data-i18n="downloads.heading">Downloads</h1>
|
|
<p className="tool-subtitle" data-i18n="downloads.subtitle">
|
|
{`Dateien und Vorlagen für das ${FIRM} Patent-Team.`}
|
|
</p>
|
|
</div>
|
|
|
|
<div className="downloads-grid">
|
|
{files.map((file) => (
|
|
<a href={file.href} className="download-card" download>
|
|
<div className="download-icon" dangerouslySetInnerHTML={{ __html: file.icon }} />
|
|
<div className="download-info">
|
|
<h2 data-i18n={file.titleKey}>{file.titleDE}</h2>
|
|
<p data-i18n={file.descKey}>{file.descDE}</p>
|
|
</div>
|
|
<div className="download-action">
|
|
<span className="download-btn" data-i18n="downloads.btn">Herunterladen</span>
|
|
</div>
|
|
</a>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
|
|
<Footer />
|
|
<PaliadinWidget />
|
|
<script src="/assets/downloads.js"></script>
|
|
</body>
|
|
</html>
|
|
);
|
|
}
|