fae7e920a9
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Routines are the recurring-work surface that lets a company keep operating without a human manually kicking off every task > - The base routine detail Variation C shell already landed in #7848, but the follow-up branch still had polish work for scheduling, section ergonomics, and the list layout > - Operators need routine edit screens to explain trigger behavior clearly, keep long detail pages usable on mobile/touch devices, and make grouped routine lists easier to scan > - This pull request rebases the remaining branch work onto current `master`, drops the duplicate commits already merged through #7848, and keeps only the new routine UI follow-ups > - The benefit is a cleaner routines workflow without reopening the already-merged shell work or carrying unrelated lockfile, workflow, or screenshot changes ## Linked Issues or Issue Description Refs #7848 Feature follow-up: polish the routines UI after the Variation C routine-detail shell landed. Problem / motivation: - Routine trigger configuration needs clearer previews for manual, schedule, API, and webhook execution modes. - Routine detail sections need better responsive spacing and touch ergonomics. - The routines list grouping should scan like grouped records instead of a table with heavy row dividers. - The routine tests need a React 19-compatible render helper so the focused routine suite can run in this workspace. Proposed solution: - Add cron-fire preview helpers and routine-run display helpers with focused tests. - Expand the routine editable and operate sections with richer trigger, variable, run, activity, and history presentation. - Adjust the routine detail shell and sub-sidebar spacing for mobile/touch layout. - Update grouped routine list presentation to use bordered group headers with borderless rows. - Switch affected routine tests to the repo's `flushSync` render-helper pattern. Alternatives considered: - Leaving the duplicate pre-#7848 commits in the branch would recreate conflicts and make the PR review much larger than the remaining change. - Keeping grouped routine rows inside one bordered table was simpler, but made the grouping hierarchy less legible. Roadmap alignment: - ROADMAP.md lists Scheduled Routines as a core shipped capability and Output/Enforced Outcomes as ongoing priorities. This is polish on that existing routines capability, not a new roadmap-level feature. ## What Changed - Added routine scheduling preview helpers and tests for cron/manual/API/webhook fire-policy display. - Added routine run display helpers and tests for deduped trigger labels and run-row subtitles. - Polished routine detail sections, including trigger summaries, operate views, and env/variable editing ergonomics. - Adjusted routine detail page and sub-sidebar spacing so the title/header area is less pinned and touch layouts center better. - Reworked the routines list grouped layout so group headers are bordered cards and routine rows are borderless inside each group. - Added Storybook coverage for the routines list grouped layout and updated the existing routine detail story. - Repaired routine tests to use `flushSync` helpers compatible with the installed React 19 runtime. ## Verification - `pnpm exec vitest run ui/src/lib/cron-fires.test.ts ui/src/lib/routine-run-display.test.ts ui/src/pages/Routines.test.tsx ui/src/components/RoutineSubSidebar.test.tsx ui/src/components/RoutineSaveBar.test.tsx` - Result: 5 test files passed, 37 tests passed. - Confirmed the rebased PR diff does not include `pnpm-lock.yaml`, `.github/workflows/*`, or committed screenshots. - Confirmed `origin/master` is an ancestor of the pushed branch head after rebase. ## Risks - Medium UI risk: this touches the routine detail and routine list surfaces, so visual regressions are possible in edge cases not covered by the focused tests. - Low data risk: no schema, migration, server API, or lockfile changes are included. - Review note: the branch intentionally force-pushed after rebasing because the original first three commits were already merged through #7848. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used OpenAI Codex, GPT-5-based coding agent runtime, with repository shell/tool access. Exact hosted runtime model identifier and context-window size were not exposed in the execution environment. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [ ] If this change affects the UI, I have included before/after screenshots - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1098 lines
29 KiB
CSS
1098 lines
29 KiB
CSS
@import "tailwindcss";
|
||
@plugin "@tailwindcss/typography";
|
||
|
||
@custom-variant dark (&:is(.dark *));
|
||
|
||
@theme inline {
|
||
--color-background: var(--background);
|
||
--color-foreground: var(--foreground);
|
||
--color-card: var(--card);
|
||
--color-card-foreground: var(--card-foreground);
|
||
--color-popover: var(--popover);
|
||
--color-popover-foreground: var(--popover-foreground);
|
||
--color-primary: var(--primary);
|
||
--color-primary-foreground: var(--primary-foreground);
|
||
--color-secondary: var(--secondary);
|
||
--color-secondary-foreground: var(--secondary-foreground);
|
||
--color-muted: var(--muted);
|
||
--color-muted-foreground: var(--muted-foreground);
|
||
--color-accent: var(--accent);
|
||
--color-accent-foreground: var(--accent-foreground);
|
||
--color-destructive: var(--destructive);
|
||
--color-destructive-foreground: var(--destructive-foreground);
|
||
--color-border: var(--border);
|
||
--color-input: var(--input);
|
||
--color-ring: var(--ring);
|
||
--color-chart-1: var(--chart-1);
|
||
--color-chart-2: var(--chart-2);
|
||
--color-chart-3: var(--chart-3);
|
||
--color-chart-4: var(--chart-4);
|
||
--color-chart-5: var(--chart-5);
|
||
--color-sidebar: var(--sidebar);
|
||
--color-sidebar-foreground: var(--sidebar-foreground);
|
||
--color-sidebar-primary: var(--sidebar-primary);
|
||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||
--color-sidebar-accent: var(--sidebar-accent);
|
||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||
--color-sidebar-border: var(--sidebar-border);
|
||
--color-sidebar-ring: var(--sidebar-ring);
|
||
--radius-sm: 0.375rem;
|
||
--radius-md: 0.5rem;
|
||
--radius-lg: 0px;
|
||
--radius-xl: 0px;
|
||
}
|
||
|
||
:root {
|
||
color-scheme: light;
|
||
--radius: 0;
|
||
--background: oklch(1 0 0);
|
||
--foreground: oklch(0.145 0 0);
|
||
--card: oklch(1 0 0);
|
||
--card-foreground: oklch(0.145 0 0);
|
||
--popover: oklch(1 0 0);
|
||
--popover-foreground: oklch(0.145 0 0);
|
||
--primary: oklch(0.205 0 0);
|
||
--primary-foreground: oklch(0.985 0 0);
|
||
--secondary: oklch(0.97 0 0);
|
||
--secondary-foreground: oklch(0.205 0 0);
|
||
--muted: oklch(0.97 0 0);
|
||
--muted-foreground: oklch(0.556 0 0);
|
||
--accent: oklch(0.97 0 0);
|
||
--accent-foreground: oklch(0.205 0 0);
|
||
--destructive: oklch(0.577 0.245 27.325);
|
||
--destructive-foreground: oklch(0.577 0.245 27.325);
|
||
--border: oklch(0.922 0 0);
|
||
--input: oklch(0.922 0 0);
|
||
--ring: oklch(0.708 0 0);
|
||
--chart-1: oklch(0.646 0.222 41.116);
|
||
--chart-2: oklch(0.6 0.118 184.704);
|
||
--chart-3: oklch(0.398 0.07 227.392);
|
||
--chart-4: oklch(0.828 0.189 84.429);
|
||
--chart-5: oklch(0.769 0.188 70.08);
|
||
--sidebar: oklch(0.985 0 0);
|
||
--sidebar-foreground: oklch(0.145 0 0);
|
||
--sidebar-primary: oklch(0.205 0 0);
|
||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||
--sidebar-accent: oklch(0.97 0 0);
|
||
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||
--sidebar-border: oklch(0.922 0 0);
|
||
--sidebar-ring: oklch(0.708 0 0);
|
||
--chip-match-title-bg: oklch(0.97 0.02 265);
|
||
--chip-match-title-fg: oklch(0.4 0.13 265);
|
||
--chip-match-title-border: oklch(0.85 0.05 265);
|
||
--chip-match-comment-bg: oklch(0.97 0.02 145);
|
||
--chip-match-comment-fg: oklch(0.4 0.10 145);
|
||
--chip-match-comment-border: oklch(0.85 0.05 145);
|
||
--chip-match-document-bg: oklch(0.97 0.02 295);
|
||
--chip-match-document-fg: oklch(0.4 0.10 295);
|
||
--chip-match-document-border: oklch(0.85 0.05 295);
|
||
--chip-match-identifier-bg: var(--muted);
|
||
--chip-match-identifier-fg: var(--muted-foreground);
|
||
--chip-match-identifier-border: var(--border);
|
||
--paperclip-doc-annotation-highlight-open: #fef08a;
|
||
--paperclip-doc-annotation-highlight-focused: #fde047;
|
||
--paperclip-doc-annotation-highlight-stale: #fef08a;
|
||
--paperclip-doc-annotation-highlight-resolved: #fef9c3;
|
||
}
|
||
|
||
.dark {
|
||
--background: oklch(0.145 0 0);
|
||
--foreground: oklch(0.985 0 0);
|
||
--card: oklch(0.205 0 0);
|
||
--card-foreground: oklch(0.985 0 0);
|
||
--popover: oklch(0.205 0 0);
|
||
--popover-foreground: oklch(0.985 0 0);
|
||
--primary: oklch(0.985 0 0);
|
||
--primary-foreground: oklch(0.205 0 0);
|
||
--secondary: oklch(0.269 0 0);
|
||
--secondary-foreground: oklch(0.985 0 0);
|
||
--muted: oklch(0.269 0 0);
|
||
--muted-foreground: oklch(0.708 0 0);
|
||
--accent: oklch(0.269 0 0);
|
||
--accent-foreground: oklch(0.985 0 0);
|
||
--destructive: oklch(0.637 0.237 25.331);
|
||
--destructive-foreground: oklch(0.985 0 0);
|
||
--border: oklch(0.269 0 0);
|
||
--input: oklch(0.269 0 0);
|
||
--ring: oklch(0.439 0 0);
|
||
--chart-1: oklch(0.488 0.243 264.376);
|
||
--chart-2: oklch(0.696 0.17 162.48);
|
||
--chart-3: oklch(0.769 0.188 70.08);
|
||
--chart-4: oklch(0.627 0.265 303.9);
|
||
--chart-5: oklch(0.645 0.246 16.439);
|
||
--sidebar: oklch(0.145 0 0);
|
||
--sidebar-foreground: oklch(0.985 0 0);
|
||
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||
--sidebar-accent: oklch(0.269 0 0);
|
||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||
--sidebar-border: oklch(0.269 0 0);
|
||
--sidebar-ring: oklch(0.439 0 0);
|
||
--chip-match-title-bg: oklch(0.27 0.04 265 / 0.5);
|
||
--chip-match-title-fg: oklch(0.78 0.10 265);
|
||
--chip-match-title-border: oklch(0.4 0.06 265);
|
||
--chip-match-comment-bg: oklch(0.27 0.04 145 / 0.5);
|
||
--chip-match-comment-fg: oklch(0.78 0.08 145);
|
||
--chip-match-comment-border: oklch(0.4 0.05 145);
|
||
--chip-match-document-bg: oklch(0.27 0.04 295 / 0.5);
|
||
--chip-match-document-fg: oklch(0.78 0.08 295);
|
||
--chip-match-document-border: oklch(0.4 0.05 295);
|
||
--chip-match-identifier-bg: var(--muted);
|
||
--chip-match-identifier-fg: var(--muted-foreground);
|
||
--chip-match-identifier-border: var(--border);
|
||
--paperclip-doc-annotation-highlight-open: #a16207;
|
||
--paperclip-doc-annotation-highlight-focused: #ca8a04;
|
||
--paperclip-doc-annotation-highlight-stale: #854d0e;
|
||
--paperclip-doc-annotation-highlight-resolved: #713f12;
|
||
}
|
||
|
||
::highlight(paperclip-doc-annotation-open) {
|
||
background-color: var(--paperclip-doc-annotation-highlight-open);
|
||
color: inherit;
|
||
}
|
||
|
||
::highlight(paperclip-doc-annotation-focused) {
|
||
background-color: var(--paperclip-doc-annotation-highlight-focused);
|
||
color: inherit;
|
||
}
|
||
|
||
::highlight(paperclip-doc-annotation-stale) {
|
||
background-color: var(--paperclip-doc-annotation-highlight-stale);
|
||
color: inherit;
|
||
}
|
||
|
||
::highlight(paperclip-doc-annotation-resolved) {
|
||
background-color: var(--paperclip-doc-annotation-highlight-resolved);
|
||
color: inherit;
|
||
}
|
||
|
||
@layer base {
|
||
* {
|
||
@apply border-border;
|
||
}
|
||
html {
|
||
height: 100%;
|
||
-webkit-tap-highlight-color: color-mix(in oklab, var(--foreground) 20%, transparent);
|
||
}
|
||
body {
|
||
@apply bg-background text-foreground antialiased;
|
||
height: 100%;
|
||
overflow: hidden;
|
||
}
|
||
h1,
|
||
h2,
|
||
h3 {
|
||
text-wrap: balance;
|
||
}
|
||
/* Prevent double-tap-to-zoom on interactive elements for mobile */
|
||
a,
|
||
button,
|
||
[role="button"],
|
||
input,
|
||
select,
|
||
textarea,
|
||
label {
|
||
touch-action: manipulation;
|
||
}
|
||
/* Let font-mono (utilities layer) override for monospace editors */
|
||
.paperclip-mdxeditor [class*="_placeholder_"],
|
||
.paperclip-mdxeditor-content {
|
||
font-family: inherit;
|
||
}
|
||
}
|
||
|
||
@media (pointer: coarse) {
|
||
button,
|
||
[role="button"],
|
||
input,
|
||
select,
|
||
textarea,
|
||
[data-slot="select-trigger"] {
|
||
min-height: 44px;
|
||
}
|
||
|
||
/* Small inline widgets keep their design size on touch devices —
|
||
forcing 44px here stretches checkboxes, chip menus, and icon buttons
|
||
that live inside dense rows (sidebar headers, issue rows, filter
|
||
popovers). The surrounding row provides the touch area. */
|
||
[data-slot="toggle"],
|
||
[data-slot="checkbox"],
|
||
[data-slot="icon-button"],
|
||
[data-size="xs"],
|
||
[data-size="icon-xs"],
|
||
[data-size="icon-sm"],
|
||
input[type="checkbox"],
|
||
input[type="radio"],
|
||
/* Auto-resizing title fields are vertically centered inside their own bar,
|
||
which already provides the touch area; the 44px floor would top-align the
|
||
single line of text and push it too high (notably on mobile Safari). */
|
||
textarea[data-autosize-title] {
|
||
min-height: 0;
|
||
}
|
||
}
|
||
|
||
/* Dark mode scrollbars */
|
||
.dark {
|
||
color-scheme: dark;
|
||
}
|
||
|
||
.dark *::-webkit-scrollbar {
|
||
width: 8px;
|
||
height: 8px;
|
||
}
|
||
|
||
.dark *::-webkit-scrollbar-track {
|
||
background: oklch(0.205 0 0);
|
||
}
|
||
|
||
.dark *::-webkit-scrollbar-thumb {
|
||
background: oklch(0.4 0 0);
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.dark *::-webkit-scrollbar-thumb:hover {
|
||
background: oklch(0.5 0 0);
|
||
}
|
||
|
||
/* Auto-hide scrollbar: thin, stable gutter with the thumb visible only on hover */
|
||
.scrollbar-auto-hide {
|
||
scrollbar-gutter: stable;
|
||
scrollbar-width: thin;
|
||
scrollbar-color: transparent transparent;
|
||
}
|
||
|
||
.scrollbar-auto-hide::-webkit-scrollbar {
|
||
width: 8px !important;
|
||
height: 8px !important;
|
||
background: transparent !important;
|
||
}
|
||
.scrollbar-auto-hide::-webkit-scrollbar-track {
|
||
background: transparent !important;
|
||
}
|
||
.scrollbar-auto-hide::-webkit-scrollbar-thumb {
|
||
background: transparent !important;
|
||
}
|
||
/* Light mode scrollbar on hover */
|
||
.scrollbar-auto-hide:hover {
|
||
scrollbar-color: oklch(0.7 0 0) transparent;
|
||
}
|
||
.scrollbar-auto-hide:hover::-webkit-scrollbar-track {
|
||
background: transparent !important;
|
||
}
|
||
.scrollbar-auto-hide:hover::-webkit-scrollbar-thumb {
|
||
background: oklch(0.7 0 0) !important;
|
||
border-radius: 999px !important;
|
||
}
|
||
.scrollbar-auto-hide:hover::-webkit-scrollbar-thumb:hover {
|
||
background: oklch(0.6 0 0) !important;
|
||
}
|
||
/* Dark mode scrollbar on hover */
|
||
.dark .scrollbar-auto-hide:hover {
|
||
scrollbar-color: oklch(0.4 0 0) transparent;
|
||
}
|
||
.dark .scrollbar-auto-hide:hover::-webkit-scrollbar-track {
|
||
background: transparent !important;
|
||
}
|
||
.dark .scrollbar-auto-hide:hover::-webkit-scrollbar-thumb {
|
||
background: oklch(0.4 0 0) !important;
|
||
}
|
||
.dark .scrollbar-auto-hide:hover::-webkit-scrollbar-thumb:hover {
|
||
background: oklch(0.5 0 0) !important;
|
||
}
|
||
|
||
/* Expandable dialog transition for max-width changes */
|
||
[data-slot="dialog-content"] {
|
||
transition: max-width 200ms cubic-bezier(0.16, 1, 0.3, 1);
|
||
}
|
||
|
||
/* Dashboard activity row entry motion */
|
||
@keyframes dashboard-activity-enter {
|
||
0% {
|
||
opacity: 0;
|
||
transform: translateY(-14px) scale(0.985);
|
||
filter: blur(4px);
|
||
}
|
||
62% {
|
||
opacity: 1;
|
||
transform: translateY(2px) scale(1.002);
|
||
filter: blur(0);
|
||
}
|
||
100% {
|
||
opacity: 1;
|
||
transform: translateY(0) scale(1);
|
||
filter: blur(0);
|
||
}
|
||
}
|
||
|
||
@keyframes dashboard-activity-highlight {
|
||
0% {
|
||
box-shadow: inset 2px 0 0 var(--primary);
|
||
background-color: color-mix(in oklab, var(--accent) 55%, transparent);
|
||
}
|
||
100% {
|
||
box-shadow: inset 0 0 0 transparent;
|
||
background-color: transparent;
|
||
}
|
||
}
|
||
|
||
.activity-row-enter {
|
||
animation:
|
||
dashboard-activity-enter 520ms cubic-bezier(0.16, 1, 0.3, 1),
|
||
dashboard-activity-highlight 920ms cubic-bezier(0.16, 1, 0.3, 1);
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.activity-row-enter {
|
||
animation: none;
|
||
}
|
||
}
|
||
|
||
/* Chain-of-thought reasoning line ticker animations.
|
||
Pure translate, no opacity — the overflow-hidden container clips.
|
||
Both keyframes share the same easing so the two spans move in lockstep. */
|
||
@keyframes cot-line-slide-in {
|
||
from { transform: translateY(100%); }
|
||
to { transform: translateY(0); }
|
||
}
|
||
|
||
@keyframes cot-line-slide-out {
|
||
from { transform: translateY(0); }
|
||
to { transform: translateY(-100%); }
|
||
}
|
||
|
||
.cot-line-enter {
|
||
animation: cot-line-slide-in 300ms cubic-bezier(0.4, 0, 0.2, 1) both;
|
||
}
|
||
|
||
.cot-line-exit {
|
||
animation: cot-line-slide-out 300ms cubic-bezier(0.4, 0, 0.2, 1) forwards;
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.cot-line-enter,
|
||
.cot-line-exit {
|
||
animation: none;
|
||
}
|
||
}
|
||
|
||
/* Shimmer text effect for active "Working" state — Cursor-style sweep */
|
||
@keyframes shimmer-text-slide {
|
||
0% { background-position: 100% center; }
|
||
60% { background-position: 0% center; }
|
||
100% { background-position: 0% center; }
|
||
}
|
||
|
||
.shimmer-text {
|
||
--shimmer-base: var(--foreground);
|
||
--shimmer-highlight: color-mix(in oklch, var(--foreground) 35%, transparent);
|
||
background: linear-gradient(
|
||
90deg,
|
||
var(--shimmer-base) 0%,
|
||
var(--shimmer-base) 40%,
|
||
var(--shimmer-highlight) 50%,
|
||
var(--shimmer-base) 60%,
|
||
var(--shimmer-base) 100%
|
||
);
|
||
background-size: 200% 100%;
|
||
-webkit-background-clip: text;
|
||
background-clip: text;
|
||
-webkit-text-fill-color: transparent;
|
||
animation: shimmer-text-slide 2.5s linear infinite;
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.shimmer-text {
|
||
animation: none;
|
||
-webkit-text-fill-color: unset;
|
||
background: none;
|
||
}
|
||
}
|
||
|
||
/* Agent heartbeat capsule motion (PAP-75).
|
||
`hb-pulse` marks a running agent (live); `hb-blink` marks a broken agent
|
||
(error). The blink alternates the fill between brand red and the page
|
||
background so the capsule reads as a faltering heartbeat. */
|
||
@keyframes hb-pulse {
|
||
0%, 100% { opacity: 0.4; transform: scale(0.85); }
|
||
50% { opacity: 1; transform: scale(1); }
|
||
}
|
||
|
||
@keyframes hb-blink {
|
||
0%, 49.9% { background-color: #DC2626; }
|
||
50%, 100% { background-color: var(--background); }
|
||
}
|
||
|
||
.hb-pulse {
|
||
animation: hb-pulse 1.6s ease-in-out infinite;
|
||
}
|
||
|
||
.hb-blink {
|
||
animation: hb-blink 1.2s step-end infinite;
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.hb-pulse,
|
||
.hb-blink {
|
||
animation: none;
|
||
}
|
||
}
|
||
|
||
/* MDXEditor theme integration */
|
||
.paperclip-mdxeditor-scope,
|
||
.paperclip-mdxeditor {
|
||
--baseBase: var(--background);
|
||
--baseBg: transparent;
|
||
--baseBgSubtle: color-mix(in oklab, var(--accent) 35%, transparent);
|
||
--baseLine: var(--border);
|
||
--baseSolid: var(--muted-foreground);
|
||
--baseSolidHover: var(--foreground);
|
||
--baseText: var(--muted-foreground);
|
||
--baseBorderColor: var(--border);
|
||
--baseBorder: var(--border);
|
||
--baseBorderHover: var(--ring);
|
||
--baseTextContrast: var(--foreground);
|
||
--baseTextContrastMuted: var(--muted-foreground);
|
||
--baseTextEmphasis: var(--foreground);
|
||
--basePageBg: var(--background);
|
||
--baseRadius: var(--radius);
|
||
--baseLineHeight: 1.5;
|
||
--accentBorder: color-mix(in oklab, var(--primary) 35%, var(--border));
|
||
--accentSolid: var(--primary);
|
||
--accentSolidHover: var(--primary);
|
||
--accentLine: color-mix(in oklab, var(--primary) 20%, transparent);
|
||
--accentBg: var(--accent);
|
||
--accentBgHover: color-mix(in oklab, var(--accent) 80%, var(--background));
|
||
--accentBgActive: color-mix(in oklab, var(--accent) 72%, var(--background));
|
||
--accentText: var(--accent-foreground);
|
||
font-family: inherit;
|
||
font-size: 1rem;
|
||
line-height: 1.5;
|
||
color: var(--foreground);
|
||
}
|
||
|
||
@media (min-width: 768px) {
|
||
.paperclip-mdxeditor-scope,
|
||
.paperclip-mdxeditor {
|
||
font-size: 0.875rem;
|
||
}
|
||
}
|
||
|
||
.paperclip-mdxeditor-scope [class*="_iconButton_"],
|
||
.paperclip-mdxeditor [class*="_iconButton_"] {
|
||
color: var(--baseText);
|
||
}
|
||
|
||
.paperclip-mdxeditor-scope [class*="_iconButton_"]:hover,
|
||
.paperclip-mdxeditor [class*="_iconButton_"]:hover {
|
||
color: var(--baseTextContrast);
|
||
}
|
||
|
||
.paperclip-mdxeditor .mdxeditor-root-contenteditable {
|
||
min-height: 2.5rem;
|
||
padding: 0;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.paperclip-mdxeditor [class*="_contentEditable_"] {
|
||
padding: 0.375rem 0.625rem !important;
|
||
}
|
||
|
||
.paperclip-mdxeditor--borderless [class*="_contentEditable_"] {
|
||
padding: 0 !important;
|
||
}
|
||
|
||
.paperclip-mdxeditor [class*="_placeholder_"] {
|
||
font-size: inherit;
|
||
line-height: 1.5;
|
||
color: var(--muted-foreground);
|
||
}
|
||
|
||
.paperclip-mdxeditor-content {
|
||
font-size: inherit;
|
||
line-height: inherit;
|
||
color: inherit;
|
||
}
|
||
|
||
.paperclip-edit-in-place-content {
|
||
font-size: 0.9375rem;
|
||
line-height: 1.75rem;
|
||
}
|
||
|
||
.paperclip-mdxeditor-content > *:first-child {
|
||
margin-top: 0;
|
||
}
|
||
|
||
.paperclip-mdxeditor-content > *:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.paperclip-mdxeditor-content p {
|
||
margin: 0;
|
||
line-height: inherit;
|
||
}
|
||
|
||
.paperclip-mdxeditor-content p + p {
|
||
margin-top: 1.1em;
|
||
}
|
||
|
||
.paperclip-mdxeditor-content a:not(.paperclip-mention-chip):not(.paperclip-project-mention-chip) {
|
||
color: color-mix(in oklab, var(--foreground) 76%, #0969da 24%);
|
||
text-decoration: underline;
|
||
text-underline-offset: 0.15em;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.dark .paperclip-mdxeditor-content a:not(.paperclip-mention-chip):not(.paperclip-project-mention-chip) {
|
||
color: color-mix(in oklab, var(--foreground) 80%, #58a6ff 20%);
|
||
}
|
||
|
||
.paperclip-mdxeditor-content a.paperclip-mention-chip,
|
||
.paperclip-mdxeditor-content a.paperclip-project-mention-chip {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.25rem;
|
||
margin: 0 0.1rem;
|
||
padding: 0 0.625rem;
|
||
border: 1px solid var(--border);
|
||
border-radius: 999px;
|
||
font-size: 0.75rem;
|
||
line-height: 1.25;
|
||
text-decoration: none;
|
||
vertical-align: middle;
|
||
position: relative;
|
||
top: -1px;
|
||
white-space: nowrap;
|
||
user-select: none;
|
||
}
|
||
|
||
/* Strip the MDXEditor's default inline-code styling from the text inside chips
|
||
(the link label otherwise picks up a monospace font + gray tint). */
|
||
.paperclip-mdxeditor-content a.paperclip-mention-chip code,
|
||
.paperclip-mdxeditor-content a.paperclip-project-mention-chip code {
|
||
font-family: inherit;
|
||
background: none;
|
||
color: inherit;
|
||
padding: 0;
|
||
}
|
||
|
||
.paperclip-mdxeditor-content a.paperclip-mention-chip::before,
|
||
a.paperclip-mention-chip::before {
|
||
content: "";
|
||
flex: none;
|
||
}
|
||
|
||
.paperclip-mdxeditor-content a.paperclip-mention-chip[data-mention-kind="project"]::before,
|
||
a.paperclip-mention-chip[data-mention-kind="project"]::before {
|
||
width: 0.45rem;
|
||
height: 0.45rem;
|
||
border-radius: 999px;
|
||
background-color: var(--paperclip-mention-project-color, currentColor);
|
||
}
|
||
|
||
.paperclip-mdxeditor-content a.paperclip-mention-chip[data-mention-kind="agent"]::before,
|
||
a.paperclip-mention-chip[data-mention-kind="agent"]::before {
|
||
width: 0.75rem;
|
||
height: 0.75rem;
|
||
background-color: currentColor;
|
||
-webkit-mask-image: var(--paperclip-mention-icon-mask);
|
||
mask-image: var(--paperclip-mention-icon-mask);
|
||
-webkit-mask-position: center;
|
||
mask-position: center;
|
||
-webkit-mask-repeat: no-repeat;
|
||
mask-repeat: no-repeat;
|
||
-webkit-mask-size: contain;
|
||
mask-size: contain;
|
||
}
|
||
|
||
.paperclip-mdxeditor-content ul,
|
||
.paperclip-mdxeditor-content ol {
|
||
margin: 1.1em 0;
|
||
padding-left: 1.6em;
|
||
}
|
||
|
||
.paperclip-mdxeditor-content ul {
|
||
list-style: disc;
|
||
}
|
||
|
||
.paperclip-mdxeditor-content ol {
|
||
list-style: decimal;
|
||
}
|
||
|
||
.paperclip-mdxeditor-content li {
|
||
display: list-item;
|
||
margin: 0.3em 0;
|
||
line-height: inherit;
|
||
}
|
||
|
||
.paperclip-mdxeditor-content li::marker {
|
||
color: var(--muted-foreground);
|
||
}
|
||
|
||
.paperclip-mdxeditor-content h1 {
|
||
margin: 1.4em 0 0.9em;
|
||
font-size: 1.75em;
|
||
font-weight: 700;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.paperclip-mdxeditor-content h2 {
|
||
margin: 1.3em 0 0.85em;
|
||
font-size: 1.35em;
|
||
font-weight: 700;
|
||
line-height: 1.3;
|
||
}
|
||
|
||
.paperclip-mdxeditor-content h3 {
|
||
margin: 1.2em 0 0.8em;
|
||
font-size: 1.15em;
|
||
font-weight: 600;
|
||
line-height: 1.35;
|
||
}
|
||
|
||
.paperclip-mdxeditor-content img {
|
||
max-height: 18rem;
|
||
border-radius: calc(var(--radius) - 2px);
|
||
}
|
||
|
||
.paperclip-mdxeditor-content blockquote {
|
||
margin: 1.2em 0;
|
||
padding-left: 1em;
|
||
border-left: 3px solid var(--border);
|
||
color: var(--muted-foreground);
|
||
line-height: inherit;
|
||
}
|
||
|
||
.paperclip-mdxeditor-content code {
|
||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||
font-size: 1em;
|
||
}
|
||
|
||
.paperclip-mdxeditor-content pre {
|
||
margin: 0.4rem 0;
|
||
padding: 0;
|
||
border: 1px solid color-mix(in oklab, var(--foreground) 12%, transparent);
|
||
border-radius: calc(var(--radius) - 3px);
|
||
background: #1e1e2e;
|
||
color: #cdd6f4;
|
||
overflow-x: auto;
|
||
}
|
||
|
||
/* Dark theme for CodeMirror code blocks inside the MDXEditor.
|
||
Overrides the default cm6-theme-basic-light that MDXEditor bundles. */
|
||
.paperclip-mdxeditor .cm-editor {
|
||
background-color: #1e1e2e !important;
|
||
color: #cdd6f4 !important;
|
||
font-size: 1em;
|
||
}
|
||
|
||
.paperclip-mdxeditor .cm-gutters {
|
||
background-color: #181825 !important;
|
||
color: #585b70 !important;
|
||
border-right: 1px solid #313244 !important;
|
||
}
|
||
|
||
.paperclip-mdxeditor .cm-activeLineGutter {
|
||
background-color: #1e1e2e !important;
|
||
}
|
||
|
||
.paperclip-mdxeditor .cm-activeLine {
|
||
background-color: color-mix(in oklab, #cdd6f4 5%, transparent) !important;
|
||
}
|
||
|
||
.paperclip-mdxeditor .cm-cursor,
|
||
.paperclip-mdxeditor .cm-dropCursor {
|
||
border-left-color: #cdd6f4 !important;
|
||
}
|
||
|
||
.paperclip-mdxeditor .cm-selectionBackground {
|
||
background-color: color-mix(in oklab, #89b4fa 25%, transparent) !important;
|
||
}
|
||
|
||
.paperclip-mdxeditor .cm-focused .cm-selectionBackground {
|
||
background-color: color-mix(in oklab, #89b4fa 30%, transparent) !important;
|
||
}
|
||
|
||
.paperclip-mdxeditor .cm-content {
|
||
caret-color: #cdd6f4;
|
||
}
|
||
|
||
/* MDXEditor code block language selector – show on hover only */
|
||
.paperclip-mdxeditor-content [class*="_codeMirrorWrapper_"] {
|
||
position: relative;
|
||
}
|
||
|
||
.paperclip-mdxeditor-content [class*="_codeMirrorToolbar_"],
|
||
.paperclip-mdxeditor-content [class*="_codeBlockToolbar_"] {
|
||
position: absolute;
|
||
top: 0.25rem;
|
||
right: 0.25rem;
|
||
z-index: 2;
|
||
opacity: 0;
|
||
transition: opacity 150ms ease;
|
||
}
|
||
|
||
.paperclip-mdxeditor-content [class*="_codeMirrorToolbar_"] select,
|
||
.paperclip-mdxeditor-content [class*="_codeBlockToolbar_"] select {
|
||
background-color: #313244;
|
||
color: #cdd6f4;
|
||
border-color: #45475a;
|
||
}
|
||
|
||
.paperclip-mdxeditor-content [class*="_codeMirrorWrapper_"]:hover [class*="_codeMirrorToolbar_"],
|
||
.paperclip-mdxeditor-content [class*="_codeMirrorWrapper_"]:hover [class*="_codeBlockToolbar_"],
|
||
.paperclip-mdxeditor-content [class*="_codeMirrorWrapper_"]:focus-within [class*="_codeMirrorToolbar_"],
|
||
.paperclip-mdxeditor-content [class*="_codeMirrorWrapper_"]:focus-within [class*="_codeBlockToolbar_"] {
|
||
opacity: 1;
|
||
}
|
||
|
||
/* Rendered markdown code blocks & inline code (prose/MarkdownBody context).
|
||
Dark theme code blocks with compact sizing.
|
||
Override prose CSS variables so prose-invert can't revert to defaults. */
|
||
.paperclip-markdown {
|
||
--tw-prose-pre-bg: #1e1e2e;
|
||
--tw-prose-pre-code: #cdd6f4;
|
||
--tw-prose-invert-pre-bg: #1e1e2e;
|
||
--tw-prose-invert-pre-code: #cdd6f4;
|
||
}
|
||
|
||
.paperclip-markdown pre {
|
||
border: 1px solid color-mix(in oklab, var(--foreground) 12%, transparent) !important;
|
||
border-radius: calc(var(--radius) - 3px) !important;
|
||
background-color: #1e1e2e !important;
|
||
color: #cdd6f4 !important;
|
||
padding: 0.5rem 0.65rem !important;
|
||
margin: 0.4rem 0 !important;
|
||
font-size: 1em !important;
|
||
overflow-x: auto;
|
||
white-space: pre;
|
||
}
|
||
|
||
.paperclip-markdown code {
|
||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||
font-size: 1em;
|
||
}
|
||
|
||
.paperclip-markdown pre code {
|
||
font-size: inherit;
|
||
color: inherit;
|
||
background: none;
|
||
}
|
||
|
||
/* Actions for fenced and indented preformatted markdown blocks */
|
||
.paperclip-markdown-codeblock {
|
||
position: relative;
|
||
}
|
||
|
||
.paperclip-markdown-codeblock-actions {
|
||
position: absolute;
|
||
top: 0.4rem;
|
||
right: 0.4rem;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.25rem;
|
||
opacity: 0;
|
||
transition: opacity 0.12s ease;
|
||
}
|
||
|
||
.paperclip-markdown-codeblock-action {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 0.25rem;
|
||
min-height: 1.55rem;
|
||
padding: 0.2rem 0.4rem;
|
||
border-radius: calc(var(--radius) - 4px);
|
||
border: 1px solid color-mix(in oklab, var(--foreground) 14%, transparent);
|
||
background-color: color-mix(in oklab, var(--muted) 92%, var(--background) 8%);
|
||
color: var(--muted-foreground);
|
||
font-size: 0.7rem;
|
||
line-height: 1;
|
||
cursor: pointer;
|
||
transition: background-color 0.12s ease, color 0.12s ease, border-color 0.12s ease;
|
||
}
|
||
|
||
.paperclip-markdown-codeblock:hover .paperclip-markdown-codeblock-actions,
|
||
.paperclip-markdown-codeblock-actions:focus-within,
|
||
.paperclip-markdown-codeblock-actions[data-active] {
|
||
opacity: 1;
|
||
}
|
||
|
||
.paperclip-markdown-codeblock-action:hover {
|
||
background-color: var(--accent);
|
||
color: var(--accent-foreground);
|
||
}
|
||
|
||
.paperclip-markdown-codeblock-action[data-active],
|
||
.paperclip-markdown-codeblock-action[data-copied] {
|
||
border-color: color-mix(in oklab, var(--primary) 38%, transparent);
|
||
color: var(--primary);
|
||
}
|
||
|
||
.paperclip-markdown-codeblock-action[data-failed] {
|
||
color: var(--destructive);
|
||
}
|
||
|
||
.paperclip-markdown-codeblock-action-label {
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* Remove backtick pseudo-elements from inline code (prose default adds them) */
|
||
.prose code::before,
|
||
.prose code::after {
|
||
content: none;
|
||
}
|
||
|
||
/* Inline code background (not inside a code block) */
|
||
.prose :not(pre) > code {
|
||
background-color: color-mix(in oklab, var(--accent) 60%, transparent);
|
||
padding: 0.15em 0.35em;
|
||
border-radius: 3px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.dark .prose :not(pre) > code {
|
||
background-color: #ffffff0f;
|
||
}
|
||
|
||
.paperclip-markdown {
|
||
color: var(--foreground);
|
||
font-size: 0.9375rem;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.paperclip-markdown > :first-child {
|
||
margin-top: 0;
|
||
}
|
||
|
||
.paperclip-markdown > :last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.paperclip-markdown :where(p, ul, ol, blockquote, pre, .paperclip-markdown-table-scroll) {
|
||
margin-top: 0.7rem;
|
||
margin-bottom: 0.7rem;
|
||
}
|
||
|
||
.paperclip-markdown :where(ul, ol) {
|
||
padding-left: 1.15rem;
|
||
}
|
||
|
||
.paperclip-markdown ul {
|
||
list-style-type: disc;
|
||
}
|
||
|
||
.paperclip-markdown ol {
|
||
list-style-type: decimal;
|
||
}
|
||
|
||
.paperclip-markdown li {
|
||
margin: 0.14rem 0;
|
||
padding-left: 0.2rem;
|
||
}
|
||
|
||
.paperclip-markdown li > :where(p, ul, ol) {
|
||
margin-top: 0.3rem;
|
||
margin-bottom: 0.3rem;
|
||
}
|
||
|
||
.paperclip-markdown li::marker {
|
||
color: var(--muted-foreground);
|
||
}
|
||
|
||
.paperclip-markdown h1,
|
||
.paperclip-markdown h2,
|
||
.paperclip-markdown h3,
|
||
.paperclip-markdown h4 {
|
||
margin-top: 1.75rem;
|
||
margin-bottom: 0.45rem;
|
||
color: var(--foreground);
|
||
font-weight: 600;
|
||
letter-spacing: -0.01em;
|
||
line-height: 1.3;
|
||
}
|
||
|
||
.paperclip-markdown h1 {
|
||
font-size: 1.5rem;
|
||
}
|
||
|
||
.paperclip-markdown h2 {
|
||
font-size: 1.25rem;
|
||
}
|
||
|
||
.paperclip-markdown h3 {
|
||
font-size: 1.05rem;
|
||
}
|
||
|
||
.paperclip-markdown h4 {
|
||
font-size: 0.95rem;
|
||
}
|
||
|
||
.paperclip-markdown :where(strong, b) {
|
||
color: var(--foreground);
|
||
font-weight: 600;
|
||
}
|
||
|
||
.paperclip-markdown a {
|
||
color: color-mix(in oklab, var(--foreground) 76%, #0969da 24%);
|
||
text-decoration: underline;
|
||
text-underline-offset: 0.15em;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.paperclip-markdown a.paperclip-mention-chip {
|
||
text-decoration: none;
|
||
}
|
||
|
||
.paperclip-markdown a.paperclip-mention-chip[data-mention-kind="issue"] {
|
||
border-color: color-mix(in oklab, var(--foreground) 14%, var(--border) 86%);
|
||
background: color-mix(in oklab, var(--accent) 42%, transparent);
|
||
}
|
||
|
||
/* Inline issue references in markdown: no pill chrome, just a status icon
|
||
beside the link label — keeps the pair from splitting across lines. */
|
||
.paperclip-markdown-issue-ref {
|
||
display: inline;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.dark .paperclip-markdown a {
|
||
color: color-mix(in oklab, var(--foreground) 80%, #58a6ff 20%);
|
||
}
|
||
|
||
.paperclip-markdown blockquote {
|
||
margin-left: 0;
|
||
padding-left: 0.95rem;
|
||
border-left: 0.24rem solid color-mix(in oklab, var(--border) 84%, var(--muted-foreground) 16%);
|
||
color: var(--muted-foreground);
|
||
}
|
||
|
||
.paperclip-markdown hr {
|
||
margin: 1.25rem 0;
|
||
border-color: var(--border);
|
||
}
|
||
|
||
.paperclip-markdown img {
|
||
border-radius: calc(var(--radius) + 2px);
|
||
box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--foreground) 10%, transparent);
|
||
}
|
||
|
||
.paperclip-markdown-table-scroll {
|
||
max-width: 100%;
|
||
overflow-x: auto;
|
||
overscroll-behavior-x: contain;
|
||
-webkit-overflow-scrolling: touch;
|
||
}
|
||
|
||
.paperclip-markdown-table-scroll:focus-visible {
|
||
outline: 2px solid var(--ring);
|
||
outline-offset: 2px;
|
||
}
|
||
|
||
.paperclip-markdown-table-scroll table {
|
||
width: max-content;
|
||
min-width: 100%;
|
||
margin: 0;
|
||
}
|
||
|
||
.paperclip-markdown-table-scroll :where(th, td) {
|
||
min-width: 8rem;
|
||
max-width: 18rem;
|
||
vertical-align: top;
|
||
}
|
||
|
||
.paperclip-markdown th {
|
||
font-weight: 600;
|
||
text-align: left;
|
||
}
|
||
|
||
.paperclip-mermaid {
|
||
margin: 0.5rem 0;
|
||
padding: 0.45rem 0.55rem;
|
||
border: 1px solid var(--border);
|
||
border-radius: calc(var(--radius) - 3px);
|
||
background-color: color-mix(in oklab, var(--accent) 35%, transparent);
|
||
overflow-x: auto;
|
||
}
|
||
|
||
.paperclip-mermaid svg {
|
||
display: block;
|
||
width: max-content;
|
||
max-width: none;
|
||
min-width: 100%;
|
||
height: auto;
|
||
}
|
||
|
||
.paperclip-mermaid-status {
|
||
margin: 0 0 0.45rem;
|
||
font-size: 0.75rem;
|
||
color: var(--muted-foreground);
|
||
}
|
||
|
||
.paperclip-mermaid-status-error {
|
||
color: var(--destructive);
|
||
}
|
||
|
||
.paperclip-mermaid-source {
|
||
margin: 0;
|
||
padding: 0;
|
||
border: 0;
|
||
background: transparent;
|
||
}
|
||
|
||
/* Mention chips rendered inline in prose (MarkdownBody or inline anchors) */
|
||
a.paperclip-mention-chip,
|
||
a.paperclip-project-mention-chip,
|
||
span.paperclip-mention-chip,
|
||
span.paperclip-project-mention-chip {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.25rem;
|
||
margin: 0 0.1rem;
|
||
padding: 0 0.625rem;
|
||
border: 1px solid var(--border);
|
||
border-radius: 999px;
|
||
font-size: 0.75rem;
|
||
line-height: 1.25;
|
||
text-decoration: none;
|
||
/* Center the pill on the surrounding x-height so it sits on the text line
|
||
instead of hanging below it. inline-flex baseline alignment is unreliable
|
||
across browsers, so use vertical-align: middle for a predictable result.
|
||
Nudge up 1px so it visually centers with the cap height of the text. */
|
||
vertical-align: middle;
|
||
position: relative;
|
||
top: -1px;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
/* When the identifier inside a chip is backtick-wrapped in markdown, strip the
|
||
inline-code monospace/gray styling so the pill label reads cleanly. */
|
||
.paperclip-markdown a.paperclip-mention-chip code,
|
||
.paperclip-markdown a.paperclip-project-mention-chip code,
|
||
.paperclip-markdown span.paperclip-mention-chip code,
|
||
.paperclip-markdown span.paperclip-project-mention-chip code {
|
||
font-family: inherit;
|
||
background: none;
|
||
color: inherit;
|
||
padding: 0;
|
||
font-size: inherit;
|
||
}
|
||
|
||
/* Keep MDXEditor popups above app dialogs, even when they portal to <body>. */
|
||
[class*="_popupContainer_"] {
|
||
z-index: 81 !important;
|
||
}
|
||
|
||
[class*="_dialogOverlay_"] {
|
||
z-index: 80;
|
||
}
|
||
|
||
[class*="_dialogContent_"],
|
||
[class*="_largeDialogContent_"],
|
||
[class*="_popoverContent_"],
|
||
[class*="_linkDialogPopoverContent_"],
|
||
[class*="_tableColumnEditorPopoverContent_"],
|
||
[class*="_toolbarButtonDropdownContainer_"],
|
||
[class*="_toolbarNodeKindSelectContainer_"] {
|
||
z-index: 81 !important;
|
||
}
|