jamdesk 1.1.118 → 1.1.119
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/vendored/components/navigation/Header.tsx +7 -4
- package/vendored/components/navigation/Sidebar.tsx +2 -2
- package/vendored/lib/build/clone-error-format.ts +87 -0
- package/vendored/lib/build/error-parser.ts +15 -6
- package/vendored/lib/build/error-routing.ts +41 -0
- package/vendored/lib/docs-types.ts +14 -5
- package/vendored/lib/email-templates/components/error-box.tsx +21 -4
- package/vendored/lib/layout-helpers.tsx +23 -15
- package/vendored/schema/docs-schema.json +2 -2
- package/vendored/themes/jam/variables.css +96 -15
- package/vendored/themes/nebula/variables.css +8 -1
- package/vendored/themes/pulsar/variables.css +5 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jamdesk",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.119",
|
|
4
4
|
"description": "CLI for Jamdesk — build, preview, and deploy documentation sites from MDX. Dev server with hot reload, 50+ components, OpenAPI support, AI search, and Mintlify migration",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jamdesk",
|
|
@@ -547,10 +547,13 @@ export function Header({ config, layout = 'header-logo', tabsPosition: tabsPosit
|
|
|
547
547
|
</>
|
|
548
548
|
)}
|
|
549
549
|
|
|
550
|
-
{/* Theme toggle - hidden on mobile since it's in the sidebar menu
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
550
|
+
{/* Theme toggle - hidden on mobile since it's in the sidebar menu.
|
|
551
|
+
Also hidden when appearance.strict pins users to the configured mode. */}
|
|
552
|
+
{!config.appearance?.strict && (
|
|
553
|
+
<div className="hidden lg:block">
|
|
554
|
+
<ThemeToggle />
|
|
555
|
+
</div>
|
|
556
|
+
)}
|
|
554
557
|
</div>
|
|
555
558
|
</div>
|
|
556
559
|
</header>
|
|
@@ -738,7 +738,7 @@ export function Sidebar({ config, layout = 'header-logo', tabsPosition: tabsPosi
|
|
|
738
738
|
<DefaultLogoCompact name={config.name} />
|
|
739
739
|
)}
|
|
740
740
|
</Link>
|
|
741
|
-
<ThemeToggleCycle />
|
|
741
|
+
{!config.appearance?.strict && <ThemeToggleCycle />}
|
|
742
742
|
</div>
|
|
743
743
|
<button
|
|
744
744
|
onClick={onClose}
|
|
@@ -770,7 +770,7 @@ export function Sidebar({ config, layout = 'header-logo', tabsPosition: tabsPosi
|
|
|
770
770
|
<DefaultLogo name={config.name} showBadge={false} />
|
|
771
771
|
)}
|
|
772
772
|
</Link>
|
|
773
|
-
<ThemeToggle />
|
|
773
|
+
{!config.appearance?.strict && <ThemeToggle />}
|
|
774
774
|
</div>
|
|
775
775
|
)}
|
|
776
776
|
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { CloneError } from '../../github.js';
|
|
2
|
+
import type { ErrorDetails } from '../../shared/types.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Format a CloneError into user-facing ErrorDetails.
|
|
6
|
+
*
|
|
7
|
+
* Pure function — no side effects, no regex. The `kind` is set at the source
|
|
8
|
+
* (cloneRepo's catch block in github.ts); this function only renders.
|
|
9
|
+
*
|
|
10
|
+
* Headlines (`message`) are kept under 140 characters because they're
|
|
11
|
+
* forwarded to GitHub commit-status descriptions, which GitHub truncates.
|
|
12
|
+
*/
|
|
13
|
+
export function formatCloneError(err: CloneError): ErrorDetails {
|
|
14
|
+
const HEADLINE_BUDGET = 140;
|
|
15
|
+
const fitInHeadline = (s: string): string =>
|
|
16
|
+
s.length <= HEADLINE_BUDGET ? s : s.slice(0, HEADLINE_BUDGET - 1) + '…';
|
|
17
|
+
|
|
18
|
+
const repoFull = err.repoFullName;
|
|
19
|
+
const repoQuoted = repoFull ? `'${repoFull}'` : "the repository";
|
|
20
|
+
const repoText = repoFull ?? 'the repository';
|
|
21
|
+
|
|
22
|
+
switch (err.kind) {
|
|
23
|
+
case 'branch_not_found': {
|
|
24
|
+
const branch = err.branch ?? 'unknown';
|
|
25
|
+
const branchQuoted = `'${branch}'`;
|
|
26
|
+
return {
|
|
27
|
+
type: 'clone_failed',
|
|
28
|
+
message: fitInHeadline(`Branch ${branchQuoted} not found in ${repoText}`),
|
|
29
|
+
details:
|
|
30
|
+
`We tried to clone the ${branchQuoted} branch from ${repoQuoted}, ` +
|
|
31
|
+
`but it does not exist on GitHub.`,
|
|
32
|
+
suggestion:
|
|
33
|
+
`Either:\n` +
|
|
34
|
+
`• Push a ${branchQuoted} branch to ${repoQuoted}, or\n` +
|
|
35
|
+
`• Update the configured branch in your project's GitHub connection ` +
|
|
36
|
+
`settings to one that exists (e.g. main).`,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
case 'repo_not_found': {
|
|
41
|
+
return {
|
|
42
|
+
type: 'clone_failed',
|
|
43
|
+
message: fitInHeadline(
|
|
44
|
+
repoFull
|
|
45
|
+
? `Repository ${repoFull} not found or not accessible`
|
|
46
|
+
: 'Repository not found or not accessible',
|
|
47
|
+
),
|
|
48
|
+
details:
|
|
49
|
+
`GitHub returned "Repository not found" when we tried to clone ${repoQuoted}. ` +
|
|
50
|
+
`The Jamdesk GitHub App cannot see this repository — either it was not ` +
|
|
51
|
+
`selected during installation, the installation was uninstalled, or the ` +
|
|
52
|
+
`repository was renamed or deleted.`,
|
|
53
|
+
suggestion:
|
|
54
|
+
'Open your project settings and click "Reconnect GitHub". ' +
|
|
55
|
+
`Make sure ${repoQuoted} is selected in the Jamdesk GitHub App installation.`,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
case 'auth_failed': {
|
|
60
|
+
return {
|
|
61
|
+
type: 'clone_failed',
|
|
62
|
+
message: 'GitHub authentication failed',
|
|
63
|
+
details:
|
|
64
|
+
`The GitHub App token was rejected when cloning ${repoQuoted}. ` +
|
|
65
|
+
`The installation may have been suspended, revoked, or had its ` +
|
|
66
|
+
`permissions changed.`,
|
|
67
|
+
suggestion:
|
|
68
|
+
'Open your project settings and click "Reconnect GitHub" to reinstall ' +
|
|
69
|
+
'the Jamdesk GitHub App, then re-trigger the build.',
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
case 'unknown':
|
|
74
|
+
default: {
|
|
75
|
+
return {
|
|
76
|
+
type: 'clone_failed',
|
|
77
|
+
message: 'Failed to clone repository',
|
|
78
|
+
details: `Could not clone ${repoText}.`,
|
|
79
|
+
suggestion:
|
|
80
|
+
'Check that:\n' +
|
|
81
|
+
`• ${repoText} exists and is accessible\n` +
|
|
82
|
+
'• The Jamdesk GitHub App has access to the repository\n' +
|
|
83
|
+
'• The branch name is correct',
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -103,9 +103,13 @@ export function parseErrorDetails(
|
|
|
103
103
|
output: string,
|
|
104
104
|
message: string,
|
|
105
105
|
phase: string,
|
|
106
|
-
opts: {
|
|
106
|
+
opts: {
|
|
107
|
+
pageToFileMap?: Record<string, string>;
|
|
108
|
+
discoveryHint?: string;
|
|
109
|
+
repoFullName?: string;
|
|
110
|
+
} = {},
|
|
107
111
|
): ErrorDetails {
|
|
108
|
-
const { pageToFileMap, discoveryHint } = opts;
|
|
112
|
+
const { pageToFileMap, discoveryHint, repoFullName } = opts;
|
|
109
113
|
const lowerOutput = output.toLowerCase();
|
|
110
114
|
|
|
111
115
|
// Extract error source information upfront - used by multiple error types
|
|
@@ -703,15 +707,20 @@ export function parseErrorDetails(
|
|
|
703
707
|
};
|
|
704
708
|
}
|
|
705
709
|
|
|
706
|
-
// Clone failure
|
|
707
|
-
|
|
710
|
+
// Clone failure — defense-in-depth fallback. The primary path is
|
|
711
|
+
// cloneRepo throwing CloneError, formatted by formatCloneError. This
|
|
712
|
+
// branch only fires if a plain Error somehow reaches the parser during
|
|
713
|
+
// the clone phase. Gate strictly on phase to avoid misclassifying any
|
|
714
|
+
// unrelated build error whose stderr mentions the word "clone".
|
|
715
|
+
if (phase === 'clone') {
|
|
716
|
+
const repoText = repoFullName ?? 'the repository';
|
|
708
717
|
return {
|
|
709
718
|
type: 'clone_failed',
|
|
710
719
|
message: 'Failed to clone repository',
|
|
711
|
-
details:
|
|
720
|
+
details: `Could not clone ${repoText}.`,
|
|
712
721
|
suggestion:
|
|
713
722
|
'Check that:\n' +
|
|
714
|
-
|
|
723
|
+
`• ${repoText} exists and is accessible\n` +
|
|
715
724
|
'• The Jamdesk GitHub App has access to the repository\n' +
|
|
716
725
|
'• The branch name is correct',
|
|
717
726
|
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { CloneError } from '../../github.js';
|
|
2
|
+
import type { ErrorDetails } from '../../shared/types.js';
|
|
3
|
+
import { formatCloneError } from './clone-error-format.js';
|
|
4
|
+
import { parseErrorDetails } from './error-parser.js';
|
|
5
|
+
|
|
6
|
+
export interface ErrorRoutingOpts {
|
|
7
|
+
combinedOutput: string;
|
|
8
|
+
phase: string;
|
|
9
|
+
repoFullName?: string;
|
|
10
|
+
pageToFileMap?: Record<string, string>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Route a caught build error to the appropriate ErrorDetails formatter.
|
|
15
|
+
*
|
|
16
|
+
* - `CloneError` → `formatCloneError` (source-classified, type-safe)
|
|
17
|
+
* - anything else → `parseErrorDetails` (regex-based, legacy)
|
|
18
|
+
*
|
|
19
|
+
* Centralized here so the routing rule is testable in isolation and
|
|
20
|
+
* build.ts's catch block stays short.
|
|
21
|
+
*/
|
|
22
|
+
export function errorDetailsFromCaught(
|
|
23
|
+
error: Error,
|
|
24
|
+
errorRef: string,
|
|
25
|
+
opts: ErrorRoutingOpts,
|
|
26
|
+
): ErrorDetails {
|
|
27
|
+
if (error instanceof CloneError) {
|
|
28
|
+
return { ...formatCloneError(error), errorRef };
|
|
29
|
+
}
|
|
30
|
+
const parsed = parseErrorDetails(
|
|
31
|
+
opts.combinedOutput,
|
|
32
|
+
error.message,
|
|
33
|
+
opts.phase,
|
|
34
|
+
{
|
|
35
|
+
pageToFileMap: opts.pageToFileMap,
|
|
36
|
+
discoveryHint: (error as Error & { discoveryHint?: string }).discoveryHint,
|
|
37
|
+
repoFullName: opts.repoFullName,
|
|
38
|
+
},
|
|
39
|
+
);
|
|
40
|
+
return { ...parsed, errorRef };
|
|
41
|
+
}
|
|
@@ -548,11 +548,20 @@ export type MintlifyLayout = 'sidenav' | 'topnav';
|
|
|
548
548
|
/**
|
|
549
549
|
* Background configuration
|
|
550
550
|
*
|
|
551
|
-
* `decoration
|
|
552
|
-
*
|
|
553
|
-
*
|
|
554
|
-
*
|
|
555
|
-
*
|
|
551
|
+
* `decoration` chooses one of four background treatments:
|
|
552
|
+
* - `"gradient"` (default for Jam) — radial gradient, tunable via `gradient`.
|
|
553
|
+
* - `"grid"` — subtle dot grid pattern.
|
|
554
|
+
* - `"windows"` — Windows 11-style frosted radial spots.
|
|
555
|
+
* - `"none"` — flat `--color-bg-primary` fill, no decoration.
|
|
556
|
+
*
|
|
557
|
+
* When `decoration` is unset, no `data-decoration` attribute is rendered — the
|
|
558
|
+
* gate selector `:not([data-decoration="none"])` treats absence as the default
|
|
559
|
+
* (gradient) case.
|
|
560
|
+
*
|
|
561
|
+
* `color` overrides the theme's `--color-bg-primary` in light and dark mode.
|
|
562
|
+
* `gradient` tunes the Jam gradient (color, size, position, opacity); it is
|
|
563
|
+
* ignored when `decoration` is anything other than `"gradient"` (or unset), or
|
|
564
|
+
* on themes without a default gradient.
|
|
556
565
|
*/
|
|
557
566
|
export interface BackgroundConfig {
|
|
558
567
|
image?: string | { light: string; dark: string };
|
|
@@ -59,9 +59,22 @@ const styles = {
|
|
|
59
59
|
paddingTop: '12px',
|
|
60
60
|
borderTop: '1px solid #FECACA',
|
|
61
61
|
},
|
|
62
|
+
suggestionContinuation: {
|
|
63
|
+
color: colors.textSecondary,
|
|
64
|
+
fontSize: '13px',
|
|
65
|
+
fontStyle: 'italic' as const,
|
|
66
|
+
margin: '4px 0 0 0',
|
|
67
|
+
},
|
|
62
68
|
};
|
|
63
69
|
|
|
64
70
|
export function ErrorBox({ error, errorRef, errorType, errorSuggestion }: ErrorBoxProps) {
|
|
71
|
+
// Split on `\n` so multi-line suggestions render as a real list. A single
|
|
72
|
+
// <Text> (which becomes <p>) collapses whitespace, so "Either:\n• one\n• two"
|
|
73
|
+
// would otherwise reach the reader as a wall of prose.
|
|
74
|
+
const suggestionLines = errorSuggestion
|
|
75
|
+
? errorSuggestion.split('\n').filter(line => line.length > 0)
|
|
76
|
+
: [];
|
|
77
|
+
|
|
65
78
|
return (
|
|
66
79
|
<Section style={styles.container} className="email-error-box">
|
|
67
80
|
<Text style={styles.errorLabel} className="email-error-title">Error</Text>
|
|
@@ -75,11 +88,15 @@ export function ErrorBox({ error, errorRef, errorType, errorSuggestion }: ErrorB
|
|
|
75
88
|
<Text style={styles.errorType} className="email-paragraph">Type: {errorType}</Text>
|
|
76
89
|
)}
|
|
77
90
|
|
|
78
|
-
{
|
|
79
|
-
<Text
|
|
80
|
-
|
|
91
|
+
{suggestionLines.map((line, i) => (
|
|
92
|
+
<Text
|
|
93
|
+
key={i}
|
|
94
|
+
style={i === 0 ? styles.suggestion : styles.suggestionContinuation}
|
|
95
|
+
className="email-paragraph"
|
|
96
|
+
>
|
|
97
|
+
{i === 0 ? `💡 ${line}` : line}
|
|
81
98
|
</Text>
|
|
82
|
-
)}
|
|
99
|
+
))}
|
|
83
100
|
</Section>
|
|
84
101
|
);
|
|
85
102
|
}
|
|
@@ -177,20 +177,26 @@ const BG_POSITION_ALLOWED = new Set([
|
|
|
177
177
|
* to the Jam theme CSS. Returns `null` when nothing is configured or only
|
|
178
178
|
* invalid values were provided.
|
|
179
179
|
*
|
|
180
|
-
* `decoration
|
|
181
|
-
* `data-decoration` attribute so the
|
|
180
|
+
* `decoration` is NOT emitted as CSS here — it's wired through the body's
|
|
181
|
+
* `data-decoration` attribute (see DocsChrome below) so the theme CSS can
|
|
182
|
+
* select on it via `body[data-decoration="..."]` rules.
|
|
183
|
+
*
|
|
184
|
+
* Gradient vars are only emitted when `decoration` is unset or `"gradient"`.
|
|
185
|
+
* For `"grid"`, `"windows"`, and `"none"` the gradient is dead-code in CSS,
|
|
186
|
+
* so the vars are suppressed (and a dev-only warning fires if the caller
|
|
187
|
+
* passed a gradient block alongside one of those decorations).
|
|
182
188
|
*/
|
|
183
189
|
export function generateBackgroundVariables(
|
|
184
190
|
background: BackgroundConfig | undefined,
|
|
185
191
|
): string | null {
|
|
186
192
|
if (!background) return null;
|
|
187
193
|
|
|
188
|
-
//
|
|
189
|
-
//
|
|
190
|
-
//
|
|
191
|
-
//
|
|
192
|
-
|
|
193
|
-
|
|
194
|
+
// Suppress gradient vars unless decoration is unset or explicitly "gradient".
|
|
195
|
+
// The Jam CSS only consumes --jd-gradient-* under the default-gradient gate
|
|
196
|
+
// (variables.css:190/214). Emitting them under grid/windows/none would leak
|
|
197
|
+
// orphan vars onto :root that any future descendant rule could pick up.
|
|
198
|
+
const gradientDisabled =
|
|
199
|
+
background.decoration !== undefined && background.decoration !== 'gradient';
|
|
194
200
|
|
|
195
201
|
// typeof guards defend the .trim() calls against callers that bypass Ajv
|
|
196
202
|
// (dashboard live-preview, hand-written configs). Contract is silent-drop.
|
|
@@ -238,8 +244,9 @@ export function generateBackgroundVariables(
|
|
|
238
244
|
const darkLines: string[] = [];
|
|
239
245
|
if (validDark) darkLines.push(` --color-bg-primary: ${validDark};`);
|
|
240
246
|
|
|
241
|
-
// Dev-only warning when
|
|
242
|
-
// the gradient is silently ignored, which would
|
|
247
|
+
// Dev-only warning when a non-gradient decoration is combined with a
|
|
248
|
+
// gradient:{...} block — the gradient is silently ignored, which would
|
|
249
|
+
// confuse the customer (their tuning has no visible effect).
|
|
243
250
|
if (
|
|
244
251
|
gradientDisabled &&
|
|
245
252
|
background.gradient &&
|
|
@@ -247,7 +254,7 @@ export function generateBackgroundVariables(
|
|
|
247
254
|
process.env.NODE_ENV === 'development'
|
|
248
255
|
) {
|
|
249
256
|
console.warn(
|
|
250
|
-
|
|
257
|
+
`[bg-config] background.gradient is ignored because background.decoration is "${background.decoration}". Set decoration to "gradient" (or omit) to apply gradient tuning.`,
|
|
251
258
|
);
|
|
252
259
|
}
|
|
253
260
|
|
|
@@ -393,10 +400,11 @@ export async function DocsChrome({
|
|
|
393
400
|
config.colors?.dark,
|
|
394
401
|
);
|
|
395
402
|
const backgroundVariables = generateBackgroundVariables(config.background);
|
|
396
|
-
// 'gradient' | 'grid' | 'windows' | 'none' | undefined.
|
|
397
|
-
//
|
|
398
|
-
//
|
|
399
|
-
//
|
|
403
|
+
// 'gradient' | 'grid' | 'windows' | 'none' | undefined. Wired through to the
|
|
404
|
+
// body's `data-decoration` attribute; the Jam theme CSS selects on each value
|
|
405
|
+
// (see variables.css for the rule blocks). Unset / "gradient" both render the
|
|
406
|
+
// default radial gradient; "none" is a solid fill; "grid" and "windows" paint
|
|
407
|
+
// their respective patterns.
|
|
400
408
|
const decoration = config.background?.decoration;
|
|
401
409
|
|
|
402
410
|
const appearanceDefault = config.appearance?.default || 'system';
|
|
@@ -274,7 +274,7 @@
|
|
|
274
274
|
"windows",
|
|
275
275
|
"none"
|
|
276
276
|
],
|
|
277
|
-
"description": "Background decoration style.
|
|
277
|
+
"description": "Background decoration style. `gradient` (default for Jam) renders a radial gradient. `grid` renders a subtle dot grid. `windows` renders Windows 11-style frosted spots. `none` disables the theme's decoration, falling back to a flat --color-bg-primary background."
|
|
278
278
|
},
|
|
279
279
|
"color": {
|
|
280
280
|
"$ref": "#/definitions/colorPairSchema",
|
|
@@ -311,7 +311,7 @@
|
|
|
311
311
|
}
|
|
312
312
|
},
|
|
313
313
|
"additionalProperties": false,
|
|
314
|
-
"description": "Customize the theme's background gradient. Currently applies only to the Jam theme (light mode). Ignored
|
|
314
|
+
"description": "Customize the theme's background gradient. Currently applies only to the Jam theme (light mode). Ignored unless `decoration` is `gradient` (or unset)"
|
|
315
315
|
}
|
|
316
316
|
},
|
|
317
317
|
"additionalProperties": false,
|
|
@@ -187,7 +187,7 @@
|
|
|
187
187
|
* Firefox 113+, Safari 16.2+). Older browsers fall back to the static rgba
|
|
188
188
|
* gradient block below — they see the original look but cannot customize it.
|
|
189
189
|
*/
|
|
190
|
-
html:not(.dark) body[data-theme="jam"]:not([data-decoration="none"]) {
|
|
190
|
+
html:not(.dark) body[data-theme="jam"]:not([data-decoration="none"]):not([data-decoration="grid"]):not([data-decoration="windows"]) {
|
|
191
191
|
/* Override solid background-color from base.css */
|
|
192
192
|
background-color: transparent;
|
|
193
193
|
/* Fallback gradient (static, for browsers without color-mix). Older browsers
|
|
@@ -211,7 +211,7 @@ html:not(.dark) body[data-theme="jam"]:not([data-decoration="none"]) {
|
|
|
211
211
|
Pre-computing in TS avoids fractional-percentage edge cases in older Blink
|
|
212
212
|
(e.g., `calc(0.12 * 66.67%)` → 8.0004% can be clamped). */
|
|
213
213
|
@supports (background: color-mix(in srgb, red, transparent)) {
|
|
214
|
-
html:not(.dark) body[data-theme="jam"]:not([data-decoration="none"]) {
|
|
214
|
+
html:not(.dark) body[data-theme="jam"]:not([data-decoration="none"]):not([data-decoration="grid"]):not([data-decoration="windows"]) {
|
|
215
215
|
background:
|
|
216
216
|
radial-gradient(
|
|
217
217
|
circle var(--jd-gradient-size, 500px) at var(--jd-gradient-position, top center),
|
|
@@ -226,31 +226,34 @@ html:not(.dark) body[data-theme="jam"]:not([data-decoration="none"]) {
|
|
|
226
226
|
}
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
-
/* Make main content / wrappers / chrome transparent in light mode SO the
|
|
230
|
-
|
|
231
|
-
|
|
229
|
+
/* Make main content / wrappers / chrome transparent in light mode SO the body
|
|
230
|
+
decoration shows through. Applies to the default gradient AND the windows
|
|
231
|
+
frosted-spots decoration (whose corner spots are part of the chrome look),
|
|
232
|
+
but NOT to grid (the dot pattern behind sidebar/header hurts readability)
|
|
233
|
+
and NOT to "none" (no decoration to expose). */
|
|
234
|
+
html:not(.dark) body[data-theme="jam"]:not([data-decoration="none"]):not([data-decoration="grid"]) main {
|
|
232
235
|
background-color: transparent !important;
|
|
233
236
|
}
|
|
234
237
|
|
|
235
|
-
html:not(.dark) body[data-theme="jam"]:not([data-decoration="none"]) > div > div.flex:not([role="dialog"]):not(:has([data-chat-panel])) {
|
|
238
|
+
html:not(.dark) body[data-theme="jam"]:not([data-decoration="none"]):not([data-decoration="grid"]) > div > div.flex:not([role="dialog"]):not(:has([data-chat-panel])) {
|
|
236
239
|
background-color: transparent !important;
|
|
237
240
|
}
|
|
238
241
|
|
|
239
|
-
html:not(.dark) body[data-theme="jam"]:not([data-decoration="none"]) > div > div.flex > main {
|
|
242
|
+
html:not(.dark) body[data-theme="jam"]:not([data-decoration="none"]):not([data-decoration="grid"]) > div > div.flex > main {
|
|
240
243
|
background-color: transparent !important;
|
|
241
244
|
}
|
|
242
245
|
|
|
243
|
-
html:not(.dark) body[data-theme="jam"]:not([data-decoration="none"]) div.flex-1.lg\:ml-\[300px\] {
|
|
246
|
+
html:not(.dark) body[data-theme="jam"]:not([data-decoration="none"]):not([data-decoration="grid"]) div.flex-1.lg\:ml-\[300px\] {
|
|
244
247
|
background-color: transparent !important;
|
|
245
248
|
}
|
|
246
249
|
|
|
247
|
-
html:not(.dark) body[data-theme="jam"]:not([data-decoration="none"]) header {
|
|
250
|
+
html:not(.dark) body[data-theme="jam"]:not([data-decoration="none"]):not([data-decoration="grid"]) header {
|
|
248
251
|
background-color: transparent !important;
|
|
249
252
|
background: transparent !important;
|
|
250
253
|
}
|
|
251
254
|
|
|
252
255
|
@media (min-width: 1024px) {
|
|
253
|
-
html:not(.dark) body[data-theme="jam"]:not([data-decoration="none"]) aside {
|
|
256
|
+
html:not(.dark) body[data-theme="jam"]:not([data-decoration="none"]):not([data-decoration="grid"]) aside {
|
|
254
257
|
background-color: transparent !important;
|
|
255
258
|
background: transparent !important;
|
|
256
259
|
}
|
|
@@ -264,22 +267,96 @@ html:not(.dark) body[data-theme="jam"][data-decoration="none"] {
|
|
|
264
267
|
background: var(--color-bg-primary, #ffffff);
|
|
265
268
|
}
|
|
266
269
|
|
|
270
|
+
/* ===== GRID DECORATION (decoration: "grid") =====
|
|
271
|
+
Subtle dot grid layered over --color-bg-primary. 1px dots at 24px spacing,
|
|
272
|
+
theme primary color at 8% opacity. Dark-mode keeps solid bg (handled by
|
|
273
|
+
base.css; no rule needed here). */
|
|
274
|
+
html:not(.dark) body[data-theme="jam"][data-decoration="grid"] {
|
|
275
|
+
background-color: var(--color-bg-primary, #ffffff);
|
|
276
|
+
/* 1.5px radius (= 3 device-pixels on 2x Retina) prevents the dot from
|
|
277
|
+
resolving to fuzzy sub-pixel anti-aliasing. The hard transparent stop at
|
|
278
|
+
1.5px keeps the edge crisp on 1x and 2x. */
|
|
279
|
+
/* Static rgba fallback for browsers without color-mix() */
|
|
280
|
+
background-image: radial-gradient(
|
|
281
|
+
circle 1.5px at center,
|
|
282
|
+
rgba(34, 102, 194, 0.08) 0 1.5px,
|
|
283
|
+
transparent 1.5px
|
|
284
|
+
);
|
|
285
|
+
background-size: 24px 24px;
|
|
286
|
+
background-repeat: repeat;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
@supports (background: color-mix(in srgb, red, transparent)) {
|
|
290
|
+
html:not(.dark) body[data-theme="jam"][data-decoration="grid"] {
|
|
291
|
+
background-image: radial-gradient(
|
|
292
|
+
circle 1.5px at center,
|
|
293
|
+
color-mix(in srgb, var(--color-primary, #2266C2) 8%, transparent) 0 1.5px,
|
|
294
|
+
transparent 1.5px
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/* ===== WINDOWS DECORATION (decoration: "windows") =====
|
|
300
|
+
Windows 11-style frosted look: two soft radial spots in the upper-left and
|
|
301
|
+
upper-right, fading over --color-bg-primary. Light-mode only; dark inherits
|
|
302
|
+
solid bg from base.css. */
|
|
303
|
+
html:not(.dark) body[data-theme="jam"][data-decoration="windows"] {
|
|
304
|
+
/* Static rgba fallback. `background:` shorthand resets all background-*
|
|
305
|
+
sub-properties before applying the layered gradients; the trailing
|
|
306
|
+
var(--color-bg-primary) layer re-sets background-color, so no separate
|
|
307
|
+
declaration is needed. */
|
|
308
|
+
background:
|
|
309
|
+
radial-gradient(circle 600px at 15% 0%, rgba(34, 102, 194, 0.14) 0%, transparent 60%),
|
|
310
|
+
radial-gradient(circle 600px at 85% 0%, rgba(34, 102, 194, 0.10) 0%, transparent 60%),
|
|
311
|
+
var(--color-bg-primary, #ffffff);
|
|
312
|
+
background-repeat: no-repeat;
|
|
313
|
+
/* `fixed` keeps the spots viewport-anchored on scroll (matches the
|
|
314
|
+
default-gradient behavior). Without it the spots scroll out of view
|
|
315
|
+
after the first viewport, leaving the rest of a long page flat. */
|
|
316
|
+
background-attachment: fixed;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
@supports (background: color-mix(in srgb, red, transparent)) {
|
|
320
|
+
html:not(.dark) body[data-theme="jam"][data-decoration="windows"] {
|
|
321
|
+
background:
|
|
322
|
+
radial-gradient(
|
|
323
|
+
circle 600px at 15% 0%,
|
|
324
|
+
color-mix(in srgb, var(--color-primary, #2266C2) 14%, transparent) 0%,
|
|
325
|
+
transparent 60%
|
|
326
|
+
),
|
|
327
|
+
radial-gradient(
|
|
328
|
+
circle 600px at 85% 0%,
|
|
329
|
+
color-mix(in srgb, var(--color-primary, #2266C2) 10%, transparent) 0%,
|
|
330
|
+
transparent 60%
|
|
331
|
+
),
|
|
332
|
+
var(--color-bg-primary, #ffffff);
|
|
333
|
+
/* The `background:` shorthand above resets repeat + attachment. Re-set
|
|
334
|
+
them so the modern path matches the fallback's behavior. */
|
|
335
|
+
background-repeat: no-repeat;
|
|
336
|
+
background-attachment: fixed;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
267
340
|
/* ===== SCROLL-BASED GRADIENT AND HEADER CHANGES ===== */
|
|
268
|
-
html:not(.dark) body[data-theme="jam"]:not([data-decoration="none"]) {
|
|
341
|
+
html:not(.dark) body[data-theme="jam"]:not([data-decoration="none"]):not([data-decoration="grid"]):not([data-decoration="windows"]) {
|
|
269
342
|
transition: background 0.3s ease-out;
|
|
270
343
|
}
|
|
271
344
|
|
|
272
|
-
html:not(.dark) body[data-theme="jam"]:not([data-decoration="none"]).scrolled {
|
|
345
|
+
html:not(.dark) body[data-theme="jam"]:not([data-decoration="none"]):not([data-decoration="grid"]):not([data-decoration="windows"]).scrolled {
|
|
273
346
|
background: var(--color-bg-primary, #ffffff) !important;
|
|
274
347
|
}
|
|
275
348
|
|
|
276
|
-
|
|
349
|
+
/* Header scroll-fade rules — symmetric with the body rules above. Skip the
|
|
350
|
+
pattern decorations (grid, windows) so their chrome behavior (opaque for
|
|
351
|
+
grid, transparent-over-fixed-spots for windows) doesn't get clobbered on
|
|
352
|
+
scroll. */
|
|
353
|
+
html:not(.dark) body[data-theme="jam"]:not([data-decoration="none"]):not([data-decoration="grid"]):not([data-decoration="windows"]).scrolled header {
|
|
277
354
|
background-color: var(--color-bg-primary) !important;
|
|
278
355
|
background: var(--color-bg-primary) !important;
|
|
279
356
|
transition: background 0.3s ease-out;
|
|
280
357
|
}
|
|
281
358
|
|
|
282
|
-
html:not(.dark) body[data-theme="jam"]:not([data-decoration="none"]) header {
|
|
359
|
+
html:not(.dark) body[data-theme="jam"]:not([data-decoration="none"]):not([data-decoration="grid"]):not([data-decoration="windows"]) header {
|
|
283
360
|
transition: background 0.3s ease-out;
|
|
284
361
|
}
|
|
285
362
|
|
|
@@ -287,7 +364,7 @@ html:not(.dark) body[data-theme="jam"]:not([data-decoration="none"]) header {
|
|
|
287
364
|
|
|
288
365
|
/* Prose body */
|
|
289
366
|
body[data-theme="jam"] .prose {
|
|
290
|
-
font-family: 'Inter', sans-serif;
|
|
367
|
+
font-family: var(--font-sans, 'Inter'), sans-serif;
|
|
291
368
|
font-size: var(--prose-body-size);
|
|
292
369
|
line-height: var(--prose-body-line-height);
|
|
293
370
|
font-weight: var(--prose-body-weight);
|
|
@@ -297,6 +374,7 @@ body[data-theme="jam"] .prose {
|
|
|
297
374
|
/* Prose headings */
|
|
298
375
|
body[data-theme="jam"] .prose h1,
|
|
299
376
|
body[data-theme="jam"] h1 {
|
|
377
|
+
font-family: var(--font-heading, var(--font-sans, 'Inter')), sans-serif;
|
|
300
378
|
font-size: var(--prose-h1-size);
|
|
301
379
|
line-height: var(--prose-h1-line-height);
|
|
302
380
|
font-weight: var(--prose-h1-weight);
|
|
@@ -305,6 +383,7 @@ body[data-theme="jam"] h1 {
|
|
|
305
383
|
}
|
|
306
384
|
|
|
307
385
|
body[data-theme="jam"] .prose h2 {
|
|
386
|
+
font-family: var(--font-heading, var(--font-sans, 'Inter')), sans-serif;
|
|
308
387
|
font-size: var(--prose-h2-size);
|
|
309
388
|
line-height: var(--prose-h2-line-height);
|
|
310
389
|
font-weight: var(--prose-h2-weight);
|
|
@@ -313,6 +392,7 @@ body[data-theme="jam"] .prose h2 {
|
|
|
313
392
|
}
|
|
314
393
|
|
|
315
394
|
body[data-theme="jam"] .prose h3 {
|
|
395
|
+
font-family: var(--font-heading, var(--font-sans, 'Inter')), sans-serif;
|
|
316
396
|
font-size: var(--prose-h3-size);
|
|
317
397
|
line-height: var(--prose-h3-line-height);
|
|
318
398
|
font-weight: var(--prose-h3-weight);
|
|
@@ -321,6 +401,7 @@ body[data-theme="jam"] .prose h3 {
|
|
|
321
401
|
}
|
|
322
402
|
|
|
323
403
|
body[data-theme="jam"] .prose h4 {
|
|
404
|
+
font-family: var(--font-heading, var(--font-sans, 'Inter')), sans-serif;
|
|
324
405
|
font-size: var(--prose-h4-size);
|
|
325
406
|
line-height: var(--prose-h4-line-height);
|
|
326
407
|
font-weight: var(--prose-h4-weight);
|
|
@@ -66,13 +66,20 @@
|
|
|
66
66
|
/* ===== NEBULA THEME TYPOGRAPHY ===== */
|
|
67
67
|
|
|
68
68
|
.prose {
|
|
69
|
-
font-family: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
69
|
+
font-family: var(--font-sans, 'IBM Plex Mono'), ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
.prose code:not([class*="language-"]) {
|
|
73
73
|
font-family: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
.prose h1,
|
|
77
|
+
.prose h2,
|
|
78
|
+
.prose h3,
|
|
79
|
+
.prose h4 {
|
|
80
|
+
font-family: var(--font-heading, var(--font-sans, 'IBM Plex Mono')), ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
81
|
+
}
|
|
82
|
+
|
|
76
83
|
.dark {
|
|
77
84
|
/* Dark theme colors - warm dark background */
|
|
78
85
|
--color-bg-primary: #151413;
|
|
@@ -175,7 +175,7 @@
|
|
|
175
175
|
|
|
176
176
|
/* Prose body */
|
|
177
177
|
.prose {
|
|
178
|
-
font-family: 'Inter', sans-serif;
|
|
178
|
+
font-family: var(--font-sans, 'Inter'), sans-serif;
|
|
179
179
|
font-size: var(--prose-body-size);
|
|
180
180
|
line-height: var(--prose-body-line-height);
|
|
181
181
|
font-weight: var(--prose-body-weight);
|
|
@@ -185,6 +185,7 @@
|
|
|
185
185
|
/* Prose headings */
|
|
186
186
|
.prose h1,
|
|
187
187
|
h1 {
|
|
188
|
+
font-family: var(--font-heading, var(--font-sans, 'Inter')), sans-serif;
|
|
188
189
|
font-size: var(--prose-h1-size);
|
|
189
190
|
line-height: var(--prose-h1-line-height);
|
|
190
191
|
font-weight: var(--prose-h1-weight);
|
|
@@ -193,6 +194,7 @@ h1 {
|
|
|
193
194
|
}
|
|
194
195
|
|
|
195
196
|
.prose h2 {
|
|
197
|
+
font-family: var(--font-heading, var(--font-sans, 'Inter')), sans-serif;
|
|
196
198
|
font-size: var(--prose-h2-size);
|
|
197
199
|
line-height: var(--prose-h2-line-height);
|
|
198
200
|
font-weight: var(--prose-h2-weight);
|
|
@@ -201,6 +203,7 @@ h1 {
|
|
|
201
203
|
}
|
|
202
204
|
|
|
203
205
|
.prose h3 {
|
|
206
|
+
font-family: var(--font-heading, var(--font-sans, 'Inter')), sans-serif;
|
|
204
207
|
font-size: var(--prose-h3-size);
|
|
205
208
|
line-height: var(--prose-h3-line-height);
|
|
206
209
|
font-weight: var(--prose-h3-weight);
|
|
@@ -209,6 +212,7 @@ h1 {
|
|
|
209
212
|
}
|
|
210
213
|
|
|
211
214
|
.prose h4 {
|
|
215
|
+
font-family: var(--font-heading, var(--font-sans, 'Inter')), sans-serif;
|
|
212
216
|
font-size: var(--prose-h4-size);
|
|
213
217
|
line-height: var(--prose-h4-line-height);
|
|
214
218
|
font-weight: var(--prose-h4-weight);
|