diff --git a/README.md b/README.md index 0d2869e..b57c901 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@

- Luthor feature preview + Luthor feature preview

## :sparkles: Why Luthor diff --git a/apps/demo/src/App.css b/apps/demo/src/App.css index e773040..efe06b4 100644 --- a/apps/demo/src/App.css +++ b/apps/demo/src/App.css @@ -73,3 +73,101 @@ [data-theme="dark"] .slash-panel-label { color: #94a3b8; } + +.app-shell { + min-height: 100vh; + width: 100%; + background: var(--demo-bg); + color: var(--demo-fg); +} + +.app-layout { + box-sizing: border-box; + width: min(1200px, 100%); + min-height: 100vh; + margin: 0 auto; + padding: 20px; + display: grid; + grid-template-rows: auto 1fr; + gap: 16px; +} + +.app-header { + display: flex; + align-items: flex-end; + justify-content: space-between; + gap: 12px; + padding: 14px; + border: 1px solid var(--demo-border); + border-radius: 12px; + background: var(--demo-panel-bg); +} + +.control-group { + display: flex; + flex-direction: column; + gap: 6px; +} + +.control-label { + font-size: 12px; + font-weight: 600; + letter-spacing: 0.03em; + text-transform: uppercase; + color: var(--demo-muted); +} + +.preset-select, +.theme-toggle { + height: 38px; + border-radius: 8px; + border: 1px solid var(--demo-border); + background: var(--demo-control-bg); + color: var(--demo-fg); + font: inherit; + font-weight: 500; +} + +.preset-select { + min-width: 220px; + padding: 0 12px; +} + +.theme-toggle { + cursor: pointer; + padding: 0 14px; +} + +.theme-toggle:hover { + background: var(--demo-control-hover-bg); +} + +.editor-stage { + min-height: 0; + border: 1px solid var(--demo-border); + border-radius: 12px; + background: var(--demo-panel-bg); + overflow: auto; + padding: 20px; +} + +.editor-frame { + width: min(980px, 100%); + margin: 0 auto; +} + +@media (max-width: 720px) { + .app-layout { + padding: 12px; + } + + .app-header { + flex-direction: column; + align-items: stretch; + } + + .preset-select, + .theme-toggle { + width: 100%; + } +} diff --git a/apps/demo/src/App.tsx b/apps/demo/src/App.tsx index 0dafd6f..d9e7af6 100644 --- a/apps/demo/src/App.tsx +++ b/apps/demo/src/App.tsx @@ -14,25 +14,33 @@ import { useMemo, useState } from "react"; import { useDemoTheme } from "./hooks/useDemoTheme"; import "highlight.js/styles/github.css"; +type PresetId = + | "extensive" + | "simple-text" + | "rich-text-box" + | "chat-window" + | "email-compose" + | "md-text" + | "notion-like" + | "headless-editor" + | "notes"; + +const PRESET_OPTIONS: Array<{ value: PresetId; label: string }> = [ + { value: "extensive", label: "Extensive" }, + { value: "simple-text", label: "Simple Text" }, + { value: "rich-text-box", label: "Rich Text Box" }, + { value: "chat-window", label: "Chat Window" }, + { value: "email-compose", label: "Email Compose" }, + { value: "md-text", label: "MD Text" }, + { value: "notion-like", label: "Notion Like" }, + { value: "headless-editor", label: "Headless" }, + { value: "notes", label: "Notes" }, +]; + function App() { const { theme, toggleTheme } = useDemoTheme(); - const [preset, setPreset] = useState("extensive"); + const [preset, setPreset] = useState("extensive"); - const fontFamilyOptions = [ - { value: "default", label: "Default", fontFamily: "inherit" }, - { - value: "geist", - label: "Geist", - fontFamily: "'Geist', 'Segoe UI', Arial, sans-serif", - cssImportUrl: "https://fonts.googleapis.com/css2?family=Geist:wght@400;500;700&display=swap", - }, - { - value: "comfortaa", - label: "Comfortaa", - fontFamily: "'Comfortaa', 'Segoe UI', Arial, sans-serif", - cssImportUrl: "https://fonts.googleapis.com/css2?family=Comfortaa:wght@300..700&display=swap", - }, - ]; const presetNode = useMemo(() => { switch (preset) { case "simple-text": @@ -40,7 +48,25 @@ function App() { case "rich-text-box": return ; case "chat-window": - return console.log("chat send", payload)} />; + return {} }, + { id: 'image', content: 'Image', ariaLabel: 'Add image', onClick: () => {} }, + ]} + sendButtonPlacement="inside" + outputFormat="md" + formattingOptions={{ bold: true, italic: true, strikethrough: true }} + onSend={({ format, text, markdown, json }) => { + console.log("chat-send", { format, text }); + // Full payload available if needed: + // console.log({ format, text, markdown, json }); + }} + /> case "email-compose": return ; case "md-text": @@ -62,55 +88,37 @@ function App() { /> ); } - }, [fontFamilyOptions, preset, theme]); + }, [preset]); return (
- +
+
+
+ + +
+ +
-
- - {presetNode} +
+
{presetNode}
+
); diff --git a/apps/demo/src/index.css b/apps/demo/src/index.css index 176685a..6d65dbe 100644 --- a/apps/demo/src/index.css +++ b/apps/demo/src/index.css @@ -2,6 +2,13 @@ font-family: Inter, "SF Pro Text", "Segoe UI", Roboto, Helvetica, Arial, sans-serif; line-height: 1.5; font-weight: 400; + --demo-bg: #f8fafc; + --demo-fg: #0f172a; + --demo-muted: #475569; + --demo-border: #cbd5e1; + --demo-panel-bg: #ffffff; + --demo-control-bg: #ffffff; + --demo-control-hover-bg: #f1f5f9; color: #0f172a; background-color: #f8fafc; @@ -26,6 +33,16 @@ html[data-theme="dark"] body { color: #e2e8f0; } +html[data-theme="dark"] { + --demo-bg: #05070c; + --demo-fg: #e2e8f0; + --demo-muted: #94a3b8; + --demo-border: #334155; + --demo-panel-bg: #0f172a; + --demo-control-bg: #111827; + --demo-control-hover-bg: #1f2937; +} + #root { min-height: 100vh; } diff --git a/apps/web/public/features/Feature1.gif b/apps/web/public/features/Feature1.gif new file mode 100644 index 0000000..5b636dd Binary files /dev/null and b/apps/web/public/features/Feature1.gif differ diff --git a/apps/web/public/features/Feature10.gif b/apps/web/public/features/Feature10.gif new file mode 100644 index 0000000..fe1cdd9 Binary files /dev/null and b/apps/web/public/features/Feature10.gif differ diff --git a/apps/web/public/features/Feature11.gif b/apps/web/public/features/Feature11.gif new file mode 100644 index 0000000..7ca60bd Binary files /dev/null and b/apps/web/public/features/Feature11.gif differ diff --git a/apps/web/public/features/Feature12.gif b/apps/web/public/features/Feature12.gif new file mode 100644 index 0000000..aee50e5 Binary files /dev/null and b/apps/web/public/features/Feature12.gif differ diff --git a/apps/web/public/features/Feature2.gif b/apps/web/public/features/Feature2.gif new file mode 100644 index 0000000..f56bfcc Binary files /dev/null and b/apps/web/public/features/Feature2.gif differ diff --git a/apps/web/public/features/Feature3.gif b/apps/web/public/features/Feature3.gif new file mode 100644 index 0000000..4d56ad4 Binary files /dev/null and b/apps/web/public/features/Feature3.gif differ diff --git a/apps/web/public/features/Feature4.gif b/apps/web/public/features/Feature4.gif new file mode 100644 index 0000000..e5ffdb2 Binary files /dev/null and b/apps/web/public/features/Feature4.gif differ diff --git a/apps/web/public/features/Feature5.gif b/apps/web/public/features/Feature5.gif new file mode 100644 index 0000000..3face77 Binary files /dev/null and b/apps/web/public/features/Feature5.gif differ diff --git a/apps/web/public/features/Feature8.gif b/apps/web/public/features/Feature8.gif new file mode 100644 index 0000000..006bda7 Binary files /dev/null and b/apps/web/public/features/Feature8.gif differ diff --git a/apps/web/public/features/Feature9.gif b/apps/web/public/features/Feature9.gif new file mode 100644 index 0000000..84e19bb Binary files /dev/null and b/apps/web/public/features/Feature9.gif differ diff --git a/apps/web/public/llms-full.txt b/apps/web/public/llms-full.txt index 04971ce..1a2e145 100644 --- a/apps/web/public/llms-full.txt +++ b/apps/web/public/llms-full.txt @@ -3,7 +3,7 @@ Concatenated markdown corpus for AI ingestion. Source root: C:\Users\rahul\Documents\GitHub\luthor\apps\web\src\content\docs -Generated at: 2026-02-27T15:42:20.933Z +Generated at: 2026-02-28T07:29:58.806Z --- ## FILE: getting-started/index.md @@ -27,7 +27,7 @@ Use this when you want a production-ready editor quickly. - Includes `@lyfie/luthor-headless` under the hood - Best for fast shipping with strong defaults -## @lyfie/headless +## @lyfie/luthor-headless Use this when you want full control over UI and behavior. @@ -50,7 +50,7 @@ Based on package metadata in `packages/luthor/package.json` and `packages/headle 1. [Introduction](/docs/getting-started/) 2. [Installation](/docs/getting-started/installation/) -3. [@lyfie/headless](/docs/getting-started/luthor-headless/) +3. [@lyfie/luthor-headless](/docs/getting-started/luthor-headless/) 4. [@lyfie/luthor](/docs/getting-started/luthor/) --- @@ -116,11 +116,11 @@ npm uninstall lexical @lexical/code @lexical/link @lexical/list @lexical/markdow --- --- -title: "@lyfie/headless" +title: "@lyfie/luthor-headless" description: Minimal setup and validation for @lyfie/luthor-headless. --- -# @lyfie/headless +# @lyfie/luthor-headless Use this when you need full control over editor UI. @@ -695,12 +695,45 @@ import { headless } from '@lyfie/luthor'; --- title: Chat Window -description: Usage and prop defaults for the chat-style editor preset. +description: Complete usage, prop options, and behavior reference for the chat input preset. --- # Chat Window -Chat composer style preset with send and action controls. +`ChatWindowEditor` is a constrained chat-composer preset built for message input UX. + +It is intentionally locked down to a small formatting set and single visual mode. + +## Allowed formatting only + +The chat preset only supports these text features: + +- Bold +- Italic +- Strikethrough + +Everything else is disabled in this preset: + +- No underline +- No lists +- No links +- No images +- No tables +- No inline code +- No code blocks +- No embeds +- No command palette/slash menu/context menu +- No theme-toggle/history features in-editor + +## Keyboard shortcuts + +Allowed shortcuts: + +- Bold: `Ctrl/Cmd + B` +- Italic: `Ctrl/Cmd + I` +- Strikethrough: `Ctrl/Cmd + Shift + X` + +No other chat formatting shortcuts should be relied on. ## Usage @@ -711,30 +744,158 @@ import '@lyfie/luthor/styles.css'; export function App() { return ( console.log(json)} - submitOnEnter - allowShiftEnter + placeholder="Type a message" + formattingOptions={{ + bold: true, + italic: true, + strikethrough: true, + }} + minHeight={56} + maxHeight={220} + minWidth={260} + maxWidth={520} + submitOnEnter={false} + showBottomToolbar + toolbarButtons={[ + { id: 'attachment', content: 'Attach', ariaLabel: 'Attach file', onClick: () => {} }, + { id: 'image', content: 'Image', ariaLabel: 'Add image', onClick: () => {} }, + ]} + sendButtonPlacement="inside" + outputFormat="md" + onSend={({ format, text, markdown, json }) => { + // `text` follows `outputFormat` + console.log({ format, text, markdown, json }); + }} /> ); } ``` -## Props +## Prop Reference -`ChatWindowEditorProps` inherits `ExtensiveEditorProps` except `featureFlags` and `isToolbarEnabled`. +`ChatWindowEditorProps` is purpose-built for chat and does not expose the full `ExtensiveEditorProps` surface. -- `onSend`: `undefined (default) | (payload: { json: string }) => void` -- `submitOnEnter`: `true (default) | false` +### Core editor props + +- `className`: `undefined (default) | string` +- `variantClassName`: `undefined (default) | string` +- `initialTheme`: `'light' (default) | 'dark'` +- `onThemeChange`: `undefined (default) | (theme: 'light' | 'dark') => void` +- `theme`: `undefined (default) | Partial` +- `defaultContent`: `undefined (default) | string` +- `showDefaultContent`: `false (default) | true` +- `placeholder`: `undefined (default) | ExtensiveEditorProps['placeholder']` + +### Formatting control + +- `formattingOptions`: `undefined (default) | ChatWindowFormattingOptions` + +`ChatWindowFormattingOptions`: + +- `bold`: `true (default) | false` +- `italic`: `true (default) | false` +- `strikethrough`: `true (default) | false` + +### Send/output behavior + +- `onSend`: `undefined (default) | (payload: ChatWindowEditorSendPayload) => void` +- `outputFormat`: `'md' (default) | 'json'` +- `clearOnSend`: `true (default) | false` +- `allowEmptySend`: `false (default) | true` +- `submitOnEnter`: `false (default) | true` - `allowShiftEnter`: `true (default) | false` -- `showVoiceButton`: `false (default) | true` -- `showImageButton`: `true (default) | false` + +### Composer size/layout + +- `minHeight`: `56 (default) | number | string` +- `maxHeight`: `220 (default) | number | string` +- `minWidth`: `240 (default) | number | string` +- `maxWidth`: `'100%' (default) | number | string` + +### Bottom toolbar props + +- `showBottomToolbar`: `true (default) | false` +- `toolbarButtons`: `[] (default) | readonly ChatWindowToolbarButton[]` +- `toolbarClassName`: `undefined (default) | string` +- `toolbarStyle`: `undefined (default) | React.CSSProperties` + +### Send button props + - `showSendButton`: `true (default) | false` +- `sendButtonPlacement`: `'inside' (default) | 'right'` +- `sendButtonContent`: `'Send' (default) | ReactNode` +- `sendButtonAriaLabel`: `'Send message' (default) | string` +- `sendButtonClassName`: `undefined (default) | string` + +### Scroll area styling + +- `scrollAreaClassName`: `undefined (default) | string` + +### Types + +```ts +type ChatWindowOutputFormat = 'md' | 'json'; + +type ChatWindowEditorSendPayload = { + format: ChatWindowOutputFormat; + // Mirrors outputFormat ('md' -> markdown, 'json' -> json) + text: string; + // Always included + markdown: string; + // Always included + json: string; +}; + +type ChatWindowFormattingOptions = { + bold?: boolean; + italic?: boolean; + strikethrough?: boolean; +}; + +type ChatWindowToolbarButton = { + id: string; + content: React.ReactNode; + ariaLabel: string; + onClick?: () => void; + disabled?: boolean; + title?: string; + className?: string; +}; +``` + +## Detailed behavior + +### Output format behavior + +- If `outputFormat="md"`, then `payload.text === payload.markdown`. +- If `outputFormat="json"`, then `payload.text === payload.json`. +- `payload.markdown` and `payload.json` are always provided for consumers that need both. + +### Enter behavior + +- `submitOnEnter=false` (default): Enter inserts newline. +- `submitOnEnter=true`: Enter sends via `onSend`. +- `allowShiftEnter=true`: Shift+Enter keeps newline behavior even if submit-on-enter is on. + +### Auto-grow behavior + +- Height grows from `minHeight` as content increases. +- At `maxHeight`, growth stops and internal composer scrolling is used. + +### Cursor behavior + +- Clicking in the non-interactive area of the editor shell focuses the editor and places caret near the first line. + +### Visual mode only + +- Chat preset always runs single visual mode. +- Visual/JSON tabs are not shown. ## Behavior -- Toolbar is disabled by preset defaults -- Visual mode only -- Enter-to-send behavior is configurable +- Use this preset when you need a constrained chat input, not a general rich document editor. +- Use `formattingOptions` to selectively turn off any of the three allowed formatting features. +- Keep your product logic on `onSend` and treat `text` as the canonical value based on `outputFormat`. --- ## FILE: luthor/presets/email-compose-editor.md diff --git a/apps/web/src/app/demo/page.tsx b/apps/web/src/app/demo/page.tsx index 4102acb..b827d12 100644 --- a/apps/web/src/app/demo/page.tsx +++ b/apps/web/src/app/demo/page.tsx @@ -1,11 +1,10 @@ import type { Metadata } from 'next'; -import { PRIMARY_PACKAGE_NAME } from '@/config/site'; -import { ExtensiveEditorShell } from '@/features/editor/extensive-editor-shell'; +import { PresetShowcaseShell } from '@/features/editor/preset-showcase-shell'; export const metadata: Metadata = { title: 'Luthor Demo Playground', - description: 'Try the Luthor Extensive Editor preset live and evaluate React + Lexical rich text editing before integrating.', - keywords: ['luthor demo', 'react rich text editor demo', 'lexical editor demo'], + description: 'Explore every Luthor preset live, switch instantly, and evaluate the right React + Lexical editing experience before integrating.', + keywords: ['luthor demo', 'react rich text editor demo', 'lexical editor demo', 'luthor presets'], alternates: { canonical: '/demo/', }, @@ -15,22 +14,16 @@ export default function DemoPage() { return (
-

Demo playground

-

Zero-config Extensive Editor: go from install to full rich text in under a minute.

- -
-
- - {PRIMARY_PACKAGE_NAME} extensive preset -
-
- -
+
+

Live Playground

+

+ Build Faster With Production-Ready Presets +

+

+ Compare editing experiences, fine-tune behavior, and ship the setup that matches your product. +

+
); diff --git a/apps/web/src/app/docs/[[...slug]]/page.tsx b/apps/web/src/app/docs/[[...slug]]/page.tsx index 6c88224..a7f42fb 100644 --- a/apps/web/src/app/docs/[[...slug]]/page.tsx +++ b/apps/web/src/app/docs/[[...slug]]/page.tsx @@ -15,7 +15,7 @@ type NavGroupId = 'getting_started' | 'luthor_headless' | 'luthor' | 'other'; const NAV_GROUP_ORDER: { id: NavGroupId; label: string }[] = [ { id: 'getting_started', label: 'Getting Started' }, - { id: 'luthor_headless', label: '@lyfie/headless' }, + { id: 'luthor_headless', label: '@lyfie/luthor-headless' }, { id: 'luthor', label: '@lyfie/luthor' }, { id: 'other', label: 'Other' }, ]; diff --git a/apps/web/src/app/globals.css b/apps/web/src/app/globals.css index 5ed9793..a44a2cb 100644 --- a/apps/web/src/app/globals.css +++ b/apps/web/src/app/globals.css @@ -91,6 +91,7 @@ body { radial-gradient(85% 70% at 50% 100%, var(--page-glow-3), transparent 58%), var(--bg); min-height: 100vh; + overflow-x: clip; } * { @@ -201,6 +202,19 @@ a { gap: 1.2rem; } +.nav-menu-toggle { + display: none; + align-items: center; + justify-content: center; + width: 2.1rem; + height: 2.1rem; + border-radius: 999px; + border: 1px solid var(--line); + background: color-mix(in srgb, var(--surface) 64%, var(--bg-elevated) 36%); + color: var(--text); + cursor: pointer; +} + .nav-secondary-link { display: inline-flex; } @@ -327,6 +341,8 @@ a { .hero-uses-container{ display: flex; + flex-wrap: wrap; + justify-content: center; gap: var(--space-2); margin: var(--space-3) 0 0; } @@ -435,7 +451,8 @@ a { .btn:focus-visible, .copy-btn:focus-visible, -.theme-toggle:focus-visible { +.theme-toggle:focus-visible, +.nav-menu-toggle:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; } @@ -540,6 +557,7 @@ a { border-radius: 8px; padding: 0.3rem 0.55rem; font-size: 0.82rem; + overflow-wrap: anywhere; } .copy-btn { @@ -567,6 +585,410 @@ a { font-size: 0.95rem; } +.demo-showcase-layout { + display: grid; + grid-template-columns: minmax(220px, 260px) 1fr; + gap: var(--space-3); + align-items: start; +} + +.demo-showcase-main { + display: grid; + gap: 0.75rem; + border: 1px solid color-mix(in srgb, var(--line) 74%, var(--accent) 26%); + border-radius: 14px; + background: color-mix(in srgb, var(--bg-elevated) 90%, var(--surface) 10%); + box-shadow: var(--shadow-sm); + padding: 0.7rem; +} + +.demo-preset-sidebar { + position: sticky; + top: calc(74px + var(--space-3)); + max-height: calc(100vh - 74px - (var(--space-3) * 2)); + height: fit-content; + overflow: auto; + border: 1px solid var(--line); + border-radius: var(--radius-sm); + background: color-mix(in srgb, var(--bg-elevated) 88%, var(--surface) 12%); + backdrop-filter: blur(8px); + box-shadow: var(--shadow-sm); + padding: var(--space-3); +} + +.demo-preset-sidebar h2 { + margin: 0; + font-family: var(--font-display), var(--font-body), sans-serif; + font-size: 1.05rem; +} + +.demo-preset-sidebar-copy { + margin: 0.4rem 0 0.9rem; + color: var(--muted); + font-size: 0.84rem; + line-height: 1.45; +} + +.demo-preset-sidebar nav { + display: grid; + gap: 0.45rem; +} + +.demo-preset-nav-item { + width: 100%; + border: 1px solid var(--line); + border-radius: 10px; + background: color-mix(in srgb, var(--bg-elevated) 90%, var(--surface) 10%); + color: var(--muted); + padding: 0.6rem 0.7rem; + text-align: left; + font: inherit; + font-weight: 650; + cursor: pointer; + transition: border-color 0.2s ease, color 0.2s ease, background-color 0.2s ease; +} + +.demo-preset-nav-item:hover { + color: var(--text); + border-color: color-mix(in srgb, var(--accent) 44%, transparent); +} + +.demo-preset-nav-item.is-active { + color: #fff; + border-color: color-mix(in srgb, var(--accent) 55%, transparent); + background: linear-gradient(130deg, var(--accent), #1387da); +} + +.demo-showcase-frame { + min-height: clamp(360px, 50vh, 620px); + max-height: none; + height: auto; +} + +.demo-showcase-editor-pane { + display: block; + overflow: visible; +} + +.demo-preset-writeup { + margin: 0; + padding: 0.55rem 0.7rem 0.35rem; +} + +.demo-preset-title-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.7rem; +} + +.demo-preset-writeup h3 { + margin: 0; + font-size: 0.98rem; + letter-spacing: -0.01em; +} + +.demo-preset-inline-code { + display: inline-flex; + align-items: center; + border-radius: 9px; + border: 1px solid color-mix(in srgb, var(--accent) 38%, transparent); + background: color-mix(in srgb, var(--surface) 66%, var(--bg-elevated) 34%); + color: var(--accent-strong); + font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + font-size: 0.8rem; + font-weight: 700; + padding: 0.26rem 0.5rem; + white-space: nowrap; +} + +.demo-preset-writeup p { + margin: 0.4rem 0 0; + color: var(--muted); + line-height: 1.48; + font-size: 0.88rem; +} + +.demo-preset-writeup p strong { + color: var(--text); +} + +.demo-preset-docs-link { + margin-top: 0.58rem; + display: inline-flex; + align-items: center; + border-radius: 999px; + border: 1px solid color-mix(in srgb, var(--accent) 38%, transparent); + background: color-mix(in srgb, var(--accent) 12%, transparent); + color: var(--accent-strong); + text-decoration: none; + font-weight: 700; + font-size: 0.82rem; + padding: 0.3rem 0.65rem; +} + +.demo-preset-docs-link:hover { + border-color: color-mix(in srgb, var(--accent) 56%, transparent); + background: color-mix(in srgb, var(--accent) 20%, transparent); +} + +.demo-showcase-editor-pane > * { + width: min(100%, 1080px); + min-height: clamp(320px, 42vh, 540px); +} + +.demo-chat-shell { + width: min(100%, 880px); + margin: 0 auto; + display: grid; + gap: 0.55rem; +} + +.demo-chat-history { + border: 1px solid var(--line); + border-radius: 12px; + background: linear-gradient( + 180deg, + color-mix(in srgb, var(--surface-soft) 82%, var(--bg-elevated) 18%), + color-mix(in srgb, var(--surface) 78%, var(--bg-elevated) 22%) + ); + padding: 0.8rem; + min-height: 260px; + max-height: 420px; + overflow: auto; + display: flex; + flex-direction: column; + align-items: stretch; + align-content: flex-start; + gap: 0.55rem; +} + +.demo-chat-bubble-row { + display: flex; + align-items: flex-start; +} + +.demo-chat-bubble-row-assistant { + justify-content: flex-start; +} + +.demo-chat-bubble-row-user { + justify-content: flex-end; +} + +.demo-chat-bubble { + width: fit-content; + max-width: min(72ch, 84%); + border-radius: 16px; + padding: 0.52rem 0.72rem; + line-height: 1.45; + white-space: pre-wrap; + word-break: break-word; + font-size: 0.95rem; +} + +.demo-chat-bubble-content > :first-child { + margin-top: 0; +} + +.demo-chat-bubble-content > :last-child { + margin-bottom: 0; +} + +.demo-chat-bubble-content p, +.demo-chat-bubble-content ul, +.demo-chat-bubble-content ol, +.demo-chat-bubble-content pre { + margin: 0.14rem 0; +} + +.demo-chat-bubble-content code { + font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + font-size: 0.9em; + border-radius: 5px; + padding: 0.05rem 0.26rem; + background: color-mix(in srgb, var(--surface) 68%, #c2d7ea); +} + +.demo-chat-bubble.is-user .demo-chat-bubble-content code { + background: color-mix(in srgb, #ffffff 28%, transparent); +} + +.demo-chat-bubble.is-assistant { + background: color-mix(in srgb, var(--surface) 80%, var(--bg-elevated) 20%); + border: 1px solid color-mix(in srgb, var(--line) 82%, transparent); + color: var(--text); + transition: border-color 0.2s ease, background-color 0.2s ease; +} + +.demo-chat-bubble.is-assistant:hover { + border-color: color-mix(in srgb, var(--accent) 38%, var(--line) 62%); + background: color-mix(in srgb, var(--surface) 72%, var(--bg-elevated) 28%); +} + +.demo-chat-bubble.is-user { + background: linear-gradient(130deg, var(--accent), #1387da); + color: #fff; + border: 1px solid color-mix(in srgb, var(--accent) 70%, #fff 30%); + transition: filter 0.2s ease, transform 0.2s ease; +} + +.demo-chat-bubble.is-user:hover { + filter: saturate(1.08) brightness(1.02); + transform: translateY(-1px); +} + +.demo-chat-message { + display: grid; + gap: 0.3rem; + width: fit-content; + max-width: 100%; +} + +.demo-chat-message-meta { + display: inline-flex; + align-items: center; + gap: 0.35rem; + font-size: 0.74rem; + color: color-mix(in srgb, var(--muted) 90%, var(--text) 10%); +} + +.demo-chat-bubble-row-user .demo-chat-message-meta { + justify-content: flex-end; +} + +.demo-chat-meta-separator { + opacity: 0.65; + font-size: 0.72rem; +} + +.demo-chat-bubble-typing { + display: inline-flex; + align-items: center; + gap: 0.3rem; + min-width: 3rem; +} + +.demo-chat-bubble-typing span { + width: 0.36rem; + height: 0.36rem; + border-radius: 50%; + background: color-mix(in srgb, var(--muted) 76%, var(--text) 24%); + animation: demo-chat-typing 1s infinite ease-in-out; +} + +.demo-chat-bubble-typing span:nth-child(2) { + animation-delay: 0.14s; +} + +.demo-chat-bubble-typing span:nth-child(3) { + animation-delay: 0.28s; +} + +.demo-chat-composer .luthor-preset-chat-window { + width: 100%; + max-width: 100%; +} + +.demo-chat-composer .luthor-chat-window-composer-row { + align-items: flex-end; + gap: 0.55rem; + width: 100%; +} + +.demo-chat-composer .luthor-chat-window-composer-shell { + flex: 1; + width: auto; + min-width: 0; + max-width: none; +} + +.demo-chat-composer .luthor-chat-window-action-send--right { + width: 46px; + height: 46px; + min-width: 46px; + min-height: 46px; + padding: 0; + border-radius: 50%; + border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent); + background: linear-gradient(130deg, var(--accent), #1387da); + color: #fff; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0; + box-shadow: 0 10px 24px color-mix(in srgb, var(--accent) 26%, transparent); + transition: transform 0.2s ease, filter 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease; + flex-shrink: 0; +} + +.demo-chat-composer .luthor-chat-window-action-send--right:hover { + transform: translateY(-1px); + filter: saturate(1.12); + border-color: color-mix(in srgb, var(--accent) 62%, #fff 38%); + box-shadow: 0 14px 30px color-mix(in srgb, var(--accent) 34%, transparent); +} + +.demo-chat-send-label { + display: none; +} + +[data-theme='dark'] .demo-chat-shell { + background: transparent; +} + +[data-theme='dark'] .demo-chat-history { + border-color: color-mix(in srgb, var(--line) 85%, transparent); + background: linear-gradient( + 180deg, + color-mix(in srgb, var(--surface-soft) 78%, #101a2a 22%), + color-mix(in srgb, var(--surface) 80%, #0f1928 20%) + ); +} + +[data-theme='dark'] .demo-chat-bubble.is-assistant { + background: color-mix(in srgb, var(--surface) 82%, #17273c 18%); +} + +[data-theme='dark'] .demo-chat-bubble.is-assistant:hover { + background: color-mix(in srgb, var(--surface) 74%, #1c3047 26%); +} + +[data-theme='dark'] .demo-showcase-main { + border-color: color-mix(in srgb, var(--line) 62%, var(--accent) 38%); + background: color-mix(in srgb, var(--bg-elevated) 84%, #17273b 16%); +} + +[data-theme='dark'] .demo-preset-inline-code { + border-color: color-mix(in srgb, var(--accent) 52%, transparent); + color: #d7ebff; +} + +.demo-page-title { + margin: 0.5rem 0 0.35rem; + max-width: 19ch; + font-size: clamp(2rem, 4vw, 3.25rem); + letter-spacing: -0.045em; +} + +.demo-page-copy { + margin: 0 auto 1.35rem; + max-width: 66ch; + font-size: 1.03rem; + color: color-mix(in srgb, var(--muted) 92%, var(--text) 8%); +} + +.demo-hero { + display: grid; + justify-items: center; + gap: 0.1rem; +} + +.demo-hero-eyebrow { + margin: 0; + justify-self: center; +} + .section-title { margin: 0 0 var(--space-2); font-family: var(--font-display), var(--font-body), sans-serif; @@ -885,6 +1307,9 @@ a { } .why-feature-media-shell { + display: flex; + justify-content: center; + align-items: center; border: 1px solid var(--line); border-radius: 14px; background: linear-gradient(140deg, color-mix(in srgb, var(--surface) 84%, #fff), var(--bg-elevated)); @@ -892,9 +1317,12 @@ a { } .why-feature-media { - width: 100%; - height: clamp(180px, 38vw, 320px); - object-fit: cover; + display: block; + width: auto; + max-width: 100%; + height: auto; + max-height: min(62vh, 520px); + object-fit: contain; border-radius: 10px; background: color-mix(in srgb, var(--surface) 82%, transparent); } @@ -1595,6 +2023,7 @@ a { align-items: center; justify-content: space-between; gap: 0.75rem; + flex-wrap: wrap; padding: 0.5rem 0.65rem; border-bottom: 1px solid var(--line); background: color-mix(in srgb, var(--surface) 82%, var(--bg-elevated)); @@ -1603,6 +2032,7 @@ a { .doc-content .docs-code-tabs { display: inline-flex; align-items: center; + flex-wrap: wrap; gap: 0.35rem; } @@ -1648,6 +2078,25 @@ a { border-color: color-mix(in srgb, var(--accent) 45%, transparent); } +.doc-content img { + max-width: 100%; + height: auto; +} + +.doc-content table { + width: 100%; + display: block; + overflow-x: auto; + border-collapse: collapse; +} + +.doc-content th, +.doc-content td { + border: 1px solid var(--line); + padding: 0.5rem 0.6rem; + text-align: left; +} + .doc-content .docs-code-block pre { margin: 0; border: 0; @@ -1831,7 +2280,88 @@ a { } } +@keyframes demo-chat-typing { + 0%, + 80%, + 100% { + transform: translateY(0); + opacity: 0.5; + } + 40% { + transform: translateY(-3px); + opacity: 1; + } +} + @media (max-width: 900px) { + .container { + width: min(1140px, 94vw); + } + + .nav-shell { + height: auto; + min-height: 74px; + padding: 0.7rem 0; + flex-wrap: wrap; + position: relative; + } + + .nav-menu-toggle { + display: inline-flex; + } + + .site-nav { + width: 100%; + display: none; + grid-template-columns: 1fr; + gap: 0.45rem; + padding: 0.8rem; + margin: 0 0 0.35rem; + border: 1px solid var(--line); + border-radius: 14px; + background: color-mix(in srgb, var(--bg-elevated) 90%, var(--surface) 10%); + box-shadow: var(--shadow-sm); + } + + .site-nav.is-open { + display: grid; + } + + .site-nav a, + .theme-toggle { + width: 100%; + justify-content: flex-start; + padding: 0.58rem 0.66rem; + border-radius: 10px; + } + + .site-nav a::after { + display: none; + } + + .site-nav a.active, + .site-nav a[aria-current='page'] { + background: color-mix(in srgb, var(--accent) 18%, transparent); + } + + .theme-toggle { + height: 2.1rem; + } + + .demo-showcase-layout { + grid-template-columns: 1fr; + } + + .demo-preset-sidebar { + position: static; + max-height: none; + top: auto; + } + + .demo-preset-sidebar nav { + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + } + .docs-layout { grid-template-columns: 1fr; } @@ -1840,6 +2370,16 @@ a { max-height: none; } + .docs-breadcrumbs { + overflow-x: auto; + padding-bottom: 0.3rem; + } + + .docs-breadcrumbs ol { + width: max-content; + flex-wrap: nowrap; + } + .docs-pager { grid-template-columns: 1fr; } @@ -1847,11 +2387,31 @@ a { .docs-pager div:last-child { text-align: left; } + + .demo-chat-history { + min-height: 220px; + max-height: 340px; + } } @media (max-width: 720px) { - .nav-secondary-link { - display: none !important; + .section { + padding: var(--space-6) 0; + } + + .hero-stage { + min-height: auto; + align-items: flex-start; + } + + .hero-copy { + font-size: 1rem; + line-height: 1.6; + } + + .eyebrow-muted { + font-size: 0.75rem; + padding: 0.26rem 0.58rem; } .start-grid { @@ -1876,7 +2436,6 @@ a { } .site-nav { - gap: 0.8rem; font-size: 0.9rem; } @@ -1899,9 +2458,117 @@ a { flex-wrap: wrap; } + .install-chip { + max-width: 100%; + white-space: normal; + line-height: 1.35; + } + .hero-title { max-width: 16ch; } + + .demo-page-title { + max-width: 13ch; + font-size: clamp(1.75rem, 8.4vw, 2.45rem); + } + + .demo-page-copy { + font-size: 0.93rem; + margin-bottom: 1rem; + } + + .demo-showcase-editor-pane { + padding: 0; + } + + .browser-frame, + .demo-showcase-frame { + max-height: none; + } + + .demo-preset-writeup { + padding: 0.72rem 0.78rem 0.45rem; + } + + .demo-preset-writeup p { + font-size: 0.86rem; + } + + .demo-preset-title-row { + flex-wrap: wrap; + align-items: flex-start; + gap: 0.45rem; + } + + .demo-preset-sidebar nav { + grid-template-columns: 1fr; + } + + .demo-chat-history { + min-height: 190px; + max-height: 300px; + padding: 0.65rem; + } + + .demo-chat-bubble { + max-width: 90%; + font-size: 0.9rem; + } + + .demo-chat-message-meta { + font-size: 0.7rem; + } + + .demo-chat-composer .luthor-chat-window-composer-row { + flex-wrap: wrap; + gap: 0.55rem; + } + + .demo-chat-composer .luthor-chat-window-action-send--right { + width: 46px; + min-width: 46px; + min-height: 42px; + border-radius: 50%; + } + + .why-feature-modal-backdrop { + padding: 0.8rem; + } + + .why-feature-modal { + max-height: calc(100vh - 1.6rem); + padding: var(--space-3); + } + + .link-row .btn { + width: 100%; + } + + .docs-sidebar, + .docs-search { + padding: var(--space-3); + } + + .docs-article { + padding: var(--space-3); + } + + .doc-content pre { + font-size: 0.86rem; + } + + .docs-pager a { + font-size: 0.92rem; + } + + .footer-bottom { + align-items: flex-start; + } + + .footer-bottom p { + width: 100%; + } } @media (max-width: 1020px) and (min-width: 721px) { diff --git a/apps/web/src/components/layout/site-footer.tsx b/apps/web/src/components/layout/site-footer.tsx index 69ee11a..e00d870 100644 --- a/apps/web/src/components/layout/site-footer.tsx +++ b/apps/web/src/components/layout/site-footer.tsx @@ -36,7 +36,7 @@ export function SiteFooter() {