mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
Refine appearance settings and syntax highlighting
Keep parsed chat and tool detail content in sync with appearance token changes. Hide font-family controls on native, use One as the default syntax theme, add Swift highlighting, and give Nord a distinct light palette.
This commit is contained in:
@@ -301,6 +301,7 @@ Gotchas:
|
|||||||
- **Narrow the discriminated union before spreading.** `updateTheme`'s updater returns the theme union; spreading the union widens `colorScheme` to `"light" | "dark"`, which is assignable to neither concrete member. Branch on `t.colorScheme` so each branch spreads a single narrowed theme type (no `as`).
|
- **Narrow the discriminated union before spreading.** `updateTheme`'s updater returns the theme union; spreading the union widens `colorScheme` to `"light" | "dark"`, which is assignable to neither concrete member. Branch on `t.colorScheme` so each branch spreads a single narrowed theme type (no `as`).
|
||||||
- **`lineHeight.diff` is the code/diff line-height axis** — it is coupled to the code-font-size control (≈ `codeFontSize * 1.5`). Do NOT use it for prose. Markdown body line-height scales with the UI ramp (`Math.round(theme.fontSize.base * 1.4)`); routing prose through `lineHeight.diff` clips text at small code sizes.
|
- **`lineHeight.diff` is the code/diff line-height axis** — it is coupled to the code-font-size control (≈ `codeFontSize * 1.5`). Do NOT use it for prose. Markdown body line-height scales with the UI ramp (`Math.round(theme.fontSize.base * 1.4)`); routing prose through `lineHeight.diff` clips text at small code sizes.
|
||||||
- **High-churn draft values** (live-while-typing in the appearance preview) bypass the theme: apply them as inline styles marked with `inlineUnistylesStyle` so per-keystroke values don't grow the `#unistyles-web` CSS registry.
|
- **High-churn draft values** (live-while-typing in the appearance preview) bypass the theme: apply them as inline styles marked with `inlineUnistylesStyle` so per-keystroke values don't grow the `#unistyles-web` CSS registry.
|
||||||
|
- **Mounted parsed content uses `AppearanceStyleBoundary`.** Markdown, syntax-highlighted code, and tool-call detail bodies can contain memoized/custom renderer trees that do not naturally re-run when runtime-patched appearance tokens change. Wrap the parsed surface once with `packages/app/src/components/appearance-style-boundary.tsx`; do not add local "appearance key" props at each callsite.
|
||||||
- **Dynamic font tokens stay widened.** `fontFamily`, `fontSize`, and `lineHeight` on `commonTheme` are annotated `string`/`number` (not narrowed by `as const`) so the updater's return assigns; the platform default stacks live in `DEFAULT_UI_FONT_STACK` / `DEFAULT_MONO_FONT_STACK`.
|
- **Dynamic font tokens stay widened.** `fontFamily`, `fontSize`, and `lineHeight` on `commonTheme` are annotated `string`/`number` (not narrowed by `as const`) so the updater's return assigns; the platform default stacks live in `DEFAULT_UI_FONT_STACK` / `DEFAULT_MONO_FONT_STACK`.
|
||||||
|
|
||||||
## Debugging
|
## Debugging
|
||||||
|
|||||||
70
package-lock.json
generated
70
package-lock.json
generated
@@ -2813,6 +2813,50 @@
|
|||||||
"integrity": "sha512-jFlTTD+0MK/01TdL5sHIsQ8RqzfmvBsGl4hSp87INv2+JIs/JF6EL9J8enuCz6z3fNdfOKISNbGCIrzZRXVrcw==",
|
"integrity": "sha512-jFlTTD+0MK/01TdL5sHIsQ8RqzfmvBsGl4hSp87INv2+JIs/JF6EL9J8enuCz6z3fNdfOKISNbGCIrzZRXVrcw==",
|
||||||
"license": "MIT OR Apache-2.0"
|
"license": "MIT OR Apache-2.0"
|
||||||
},
|
},
|
||||||
|
"node_modules/@codemirror/language": {
|
||||||
|
"version": "6.12.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.3.tgz",
|
||||||
|
"integrity": "sha512-QwCZW6Tt1siP37Jet9Tb02Zs81TQt6qQrZR2H+eGMcFsL1zMrk2/b9CLC7/9ieP1fjIUMgviLWMmgiHoJrj+ZA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.23.0",
|
||||||
|
"@lezer/common": "^1.5.0",
|
||||||
|
"@lezer/highlight": "^1.0.0",
|
||||||
|
"@lezer/lr": "^1.0.0",
|
||||||
|
"style-mod": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/legacy-modes": {
|
||||||
|
"version": "6.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/legacy-modes/-/legacy-modes-6.5.3.tgz",
|
||||||
|
"integrity": "sha512-xCsmIzH78MyWkib9jlPaaun57XNkfbMIhagfaZVd0iLTqlpw3jXaIcbZm72MTmmn64eTZpBVNjbyYh+QXnxRsg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/language": "^6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/state": {
|
||||||
|
"version": "6.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.6.0.tgz",
|
||||||
|
"integrity": "sha512-4nbvra5R5EtiCzr9BTHiTLc+MLXK2QGiAVYMyi8PkQd3SR+6ixar/Q/01Fa21TBIDOZXgeWV4WppsQolSreAPQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@marijn/find-cluster-break": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/view": {
|
||||||
|
"version": "6.43.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.0.tgz",
|
||||||
|
"integrity": "sha512-V7ZCLQO3Jus9hzh2jVCCPW3mO4IBMr43O37PqSUYautJSnnJF41YlgLw21x0fLJTYvJ+Vkm6Gp+qKGH9pltgXA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/state": "^6.6.0",
|
||||||
|
"crelt": "^1.0.6",
|
||||||
|
"style-mod": "^4.1.0",
|
||||||
|
"w3c-keyname": "^2.2.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@cspotcode/source-map-support": {
|
"node_modules/@cspotcode/source-map-support": {
|
||||||
"version": "0.8.1",
|
"version": "0.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||||
@@ -7874,6 +7918,12 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@marijn/find-cluster-break": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@napi-rs/wasm-runtime": {
|
"node_modules/@napi-rs/wasm-runtime": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz",
|
||||||
@@ -16979,6 +17029,12 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/crelt": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
|
||||||
|
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/cross-env": {
|
"node_modules/cross-env": {
|
||||||
"version": "10.1.0",
|
"version": "10.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz",
|
||||||
@@ -34033,6 +34089,12 @@
|
|||||||
"integrity": "sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg==",
|
"integrity": "sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/style-mod": {
|
||||||
|
"version": "4.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz",
|
||||||
|
"integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/style-to-js": {
|
"node_modules/style-to-js": {
|
||||||
"version": "1.1.21",
|
"version": "1.1.21",
|
||||||
"resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz",
|
"resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz",
|
||||||
@@ -36027,6 +36089,12 @@
|
|||||||
"integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==",
|
"integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/w3c-keyname": {
|
||||||
|
"version": "2.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
|
||||||
|
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/w3c-xmlserializer": {
|
"node_modules/w3c-xmlserializer": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz",
|
||||||
@@ -37674,6 +37742,8 @@
|
|||||||
"name": "@getpaseo/highlight",
|
"name": "@getpaseo/highlight",
|
||||||
"version": "0.1.87",
|
"version": "0.1.87",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@codemirror/language": "^6.12.3",
|
||||||
|
"@codemirror/legacy-modes": "^6.5.3",
|
||||||
"@lezer/common": "^1.5.0",
|
"@lezer/common": "^1.5.0",
|
||||||
"@lezer/cpp": "^1.1.5",
|
"@lezer/cpp": "^1.1.5",
|
||||||
"@lezer/css": "^1.3.0",
|
"@lezer/css": "^1.3.0",
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { isNative, isWeb } from "@/constants/platform";
|
|||||||
import { Shortcut } from "@/components/ui/shortcut";
|
import { Shortcut } from "@/components/ui/shortcut";
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||||
import { useStableEvent } from "@/hooks/use-stable-event";
|
import { useStableEvent } from "@/hooks/use-stable-event";
|
||||||
|
import { CODE_SURFACE_DATASET } from "@/styles/code-surface";
|
||||||
import { useAssistantFileLinkResolverContext } from "./provider";
|
import { useAssistantFileLinkResolverContext } from "./provider";
|
||||||
import type { AssistantFileLinkSource } from "./resolver";
|
import type { AssistantFileLinkSource } from "./resolver";
|
||||||
import { useFileLink } from "./use-file-link";
|
import { useFileLink } from "./use-file-link";
|
||||||
@@ -19,10 +20,16 @@ import { useFileLink } from "./use-file-link";
|
|||||||
interface AssistantMarkdownLinkProps {
|
interface AssistantMarkdownLinkProps {
|
||||||
source: AssistantFileLinkSource;
|
source: AssistantFileLinkSource;
|
||||||
style: StyleProp<TextStyle>;
|
style: StyleProp<TextStyle>;
|
||||||
|
monoSurface?: boolean;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AssistantMarkdownLink({ source, style, children }: AssistantMarkdownLinkProps) {
|
export function AssistantMarkdownLink({
|
||||||
|
source,
|
||||||
|
style,
|
||||||
|
monoSurface,
|
||||||
|
children,
|
||||||
|
}: AssistantMarkdownLinkProps) {
|
||||||
const [hovered, setHovered] = useState(false);
|
const [hovered, setHovered] = useState(false);
|
||||||
const { target, onHoverIn, onPress, onAuxPress } = useFileLink(source);
|
const { target, onHoverIn, onPress, onAuxPress } = useFileLink(source);
|
||||||
const { configRef } = useAssistantFileLinkResolverContext();
|
const { configRef } = useAssistantFileLinkResolverContext();
|
||||||
@@ -52,7 +59,12 @@ export function AssistantMarkdownLink({ source, style, children }: AssistantMark
|
|||||||
if (isNative) {
|
if (isNative) {
|
||||||
return (
|
return (
|
||||||
<FileLinkHoverTooltip filePath={tooltipPath}>
|
<FileLinkHoverTooltip filePath={tooltipPath}>
|
||||||
<Text accessibilityRole="link" onPress={onPress} style={style}>
|
<Text
|
||||||
|
accessibilityRole="link"
|
||||||
|
dataSet={monoSurface ? CODE_SURFACE_DATASET : undefined}
|
||||||
|
onPress={onPress}
|
||||||
|
style={style}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</Text>
|
</Text>
|
||||||
</FileLinkHoverTooltip>
|
</FileLinkHoverTooltip>
|
||||||
@@ -72,7 +84,9 @@ export function AssistantMarkdownLink({ source, style, children }: AssistantMark
|
|||||||
onHoverIn={handleHoverIn}
|
onHoverIn={handleHoverIn}
|
||||||
onHoverOut={handleHoverOut}
|
onHoverOut={handleHoverOut}
|
||||||
>
|
>
|
||||||
<Text style={hoveredTextStyle}>{children}</Text>
|
<Text dataSet={monoSurface ? CODE_SURFACE_DATASET : undefined} style={hoveredTextStyle}>
|
||||||
|
{children}
|
||||||
|
</Text>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
@@ -100,7 +114,7 @@ export function AssistantMarkdownCodeLink({
|
|||||||
[inheritedStyles, codeInlineStyle, linkStyle],
|
[inheritedStyles, codeInlineStyle, linkStyle],
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<AssistantMarkdownLink source={source} style={style}>
|
<AssistantMarkdownLink source={source} style={style} monoSurface>
|
||||||
{children}
|
{children}
|
||||||
</AssistantMarkdownLink>
|
</AssistantMarkdownLink>
|
||||||
);
|
);
|
||||||
|
|||||||
66
packages/app/src/components/appearance-style-boundary.tsx
Normal file
66
packages/app/src/components/appearance-style-boundary.tsx
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { Fragment, type ReactNode } from "react";
|
||||||
|
import { withUnistyles } from "react-native-unistyles";
|
||||||
|
import type { Theme } from "@/styles/theme";
|
||||||
|
|
||||||
|
interface AppearanceStyleBoundaryProps {
|
||||||
|
appearanceKey?: string;
|
||||||
|
children: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
function AppearanceStyleBoundaryBase({ appearanceKey, children }: AppearanceStyleBoundaryProps) {
|
||||||
|
return <Fragment key={appearanceKey}>{children}</Fragment>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const appearanceStyleBoundaryMapping = (theme: Theme): Partial<AppearanceStyleBoundaryProps> => ({
|
||||||
|
appearanceKey: [
|
||||||
|
theme.fontFamily.ui,
|
||||||
|
theme.fontFamily.mono,
|
||||||
|
theme.fontSize.xs,
|
||||||
|
theme.fontSize.sm,
|
||||||
|
theme.fontSize.base,
|
||||||
|
theme.fontSize.lg,
|
||||||
|
theme.fontSize.xl,
|
||||||
|
theme.fontSize["2xl"],
|
||||||
|
theme.fontSize["3xl"],
|
||||||
|
theme.fontSize["4xl"],
|
||||||
|
theme.fontSize.code,
|
||||||
|
theme.lineHeight.diff,
|
||||||
|
theme.colors.foreground,
|
||||||
|
theme.colors.foregroundMuted,
|
||||||
|
theme.colors.mutedForeground,
|
||||||
|
theme.colors.surface1,
|
||||||
|
theme.colors.surface2,
|
||||||
|
theme.colors.border,
|
||||||
|
theme.colors.accentBright,
|
||||||
|
theme.colors.syntax.keyword,
|
||||||
|
theme.colors.syntax.comment,
|
||||||
|
theme.colors.syntax.string,
|
||||||
|
theme.colors.syntax.number,
|
||||||
|
theme.colors.syntax.literal,
|
||||||
|
theme.colors.syntax.function,
|
||||||
|
theme.colors.syntax.definition,
|
||||||
|
theme.colors.syntax.class,
|
||||||
|
theme.colors.syntax.type,
|
||||||
|
theme.colors.syntax.tag,
|
||||||
|
theme.colors.syntax.attribute,
|
||||||
|
theme.colors.syntax.property,
|
||||||
|
theme.colors.syntax.variable,
|
||||||
|
theme.colors.syntax.operator,
|
||||||
|
theme.colors.syntax.punctuation,
|
||||||
|
theme.colors.syntax.regexp,
|
||||||
|
theme.colors.syntax.escape,
|
||||||
|
theme.colors.syntax.meta,
|
||||||
|
theme.colors.syntax.heading,
|
||||||
|
theme.colors.syntax.link,
|
||||||
|
].join("\u0000"),
|
||||||
|
});
|
||||||
|
|
||||||
|
const ThemedAppearanceStyleBoundary = withUnistyles(AppearanceStyleBoundaryBase);
|
||||||
|
|
||||||
|
export function AppearanceStyleBoundary({ children }: { children: ReactNode }) {
|
||||||
|
return (
|
||||||
|
<ThemedAppearanceStyleBoundary uniProps={appearanceStyleBoundaryMapping}>
|
||||||
|
{children}
|
||||||
|
</ThemedAppearanceStyleBoundary>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import { Text, View, type StyleProp, type TextStyle, type ViewStyle } from "reac
|
|||||||
|
|
||||||
interface MarkdownTextSpanProps {
|
interface MarkdownTextSpanProps {
|
||||||
style?: StyleProp<TextStyle>;
|
style?: StyleProp<TextStyle>;
|
||||||
|
monoSurface?: boolean;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { UITextView } from "react-native-uitextview";
|
|||||||
|
|
||||||
interface MarkdownTextSpanProps {
|
interface MarkdownTextSpanProps {
|
||||||
style?: StyleProp<TextStyle>;
|
style?: StyleProp<TextStyle>;
|
||||||
|
monoSurface?: boolean;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import { useMemo, type ReactNode } from "react";
|
import { useMemo, type ReactNode } from "react";
|
||||||
import { Text, View, type StyleProp, type TextStyle, type ViewStyle } from "react-native";
|
import { Text, View, type StyleProp, type TextStyle, type ViewStyle } from "react-native";
|
||||||
|
import { CODE_SURFACE_DATASET } from "@/styles/code-surface";
|
||||||
|
|
||||||
interface MarkdownTextSpanProps {
|
interface MarkdownTextSpanProps {
|
||||||
style?: StyleProp<TextStyle>;
|
style?: StyleProp<TextStyle>;
|
||||||
|
monoSurface?: boolean;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11,8 +13,12 @@ interface MarkdownTextSpanProps {
|
|||||||
// react-native-uitextview: its transitive import of codegenNativeComponent
|
// react-native-uitextview: its transitive import of codegenNativeComponent
|
||||||
// pulls in setUpReactDevTools, which doesn't resolve under Metro's web
|
// pulls in setUpReactDevTools, which doesn't resolve under Metro's web
|
||||||
// target in dev mode.
|
// target in dev mode.
|
||||||
export function MarkdownTextSpan({ style, children }: MarkdownTextSpanProps) {
|
export function MarkdownTextSpan({ style, monoSurface, children }: MarkdownTextSpanProps) {
|
||||||
return <Text style={style}>{children}</Text>;
|
return (
|
||||||
|
<Text dataSet={monoSurface ? CODE_SURFACE_DATASET : undefined} style={style}>
|
||||||
|
{children}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MarkdownParagraphViewProps {
|
interface MarkdownParagraphViewProps {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
type TextStyle,
|
type TextStyle,
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
import { MarkdownParagraphView, MarkdownTextSpan } from "@/components/markdown-text";
|
import { MarkdownParagraphView, MarkdownTextSpan } from "@/components/markdown-text";
|
||||||
|
import { AppearanceStyleBoundary } from "@/components/appearance-style-boundary";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {
|
import {
|
||||||
useState,
|
useState,
|
||||||
@@ -1496,16 +1497,18 @@ const MemoizedMarkdownBlock = React.memo(function MemoizedMarkdownBlock({
|
|||||||
onLinkPress,
|
onLinkPress,
|
||||||
}: MemoizedMarkdownBlockProps) {
|
}: MemoizedMarkdownBlockProps) {
|
||||||
return (
|
return (
|
||||||
<ThemedMarkdown
|
<AppearanceStyleBoundary>
|
||||||
uniProps={markdownStyleMapping}
|
<ThemedMarkdown
|
||||||
rules={rules}
|
uniProps={markdownStyleMapping}
|
||||||
markdownit={parser}
|
rules={rules}
|
||||||
onLinkPress={onLinkPress}
|
markdownit={parser}
|
||||||
allowedImageHandlers={MARKDOWN_ALLOWED_IMAGE_HANDLERS}
|
onLinkPress={onLinkPress}
|
||||||
topLevelMaxExceededItem={MARKDOWN_TOP_LEVEL_MAX_EXCEEDED_ITEM}
|
allowedImageHandlers={MARKDOWN_ALLOWED_IMAGE_HANDLERS}
|
||||||
>
|
topLevelMaxExceededItem={MARKDOWN_TOP_LEVEL_MAX_EXCEEDED_ITEM}
|
||||||
{text}
|
>
|
||||||
</ThemedMarkdown>
|
{text}
|
||||||
|
</ThemedMarkdown>
|
||||||
|
</AppearanceStyleBoundary>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1513,6 +1516,7 @@ interface MarkdownInheritedTextProps {
|
|||||||
inheritedStyles: TextStyle;
|
inheritedStyles: TextStyle;
|
||||||
textStyle: TextStyle;
|
textStyle: TextStyle;
|
||||||
style?: StyleProp<TextStyle>;
|
style?: StyleProp<TextStyle>;
|
||||||
|
monoSurface?: boolean;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1520,13 +1524,18 @@ function MarkdownInheritedText({
|
|||||||
inheritedStyles,
|
inheritedStyles,
|
||||||
textStyle,
|
textStyle,
|
||||||
style: overrideStyle,
|
style: overrideStyle,
|
||||||
|
monoSurface,
|
||||||
children,
|
children,
|
||||||
}: MarkdownInheritedTextProps) {
|
}: MarkdownInheritedTextProps) {
|
||||||
const style = useMemo(
|
const style = useMemo(
|
||||||
() => [inheritedStyles, textStyle, overrideStyle],
|
() => [inheritedStyles, textStyle, overrideStyle],
|
||||||
[inheritedStyles, textStyle, overrideStyle],
|
[inheritedStyles, textStyle, overrideStyle],
|
||||||
);
|
);
|
||||||
return <MarkdownTextSpan style={style}>{children}</MarkdownTextSpan>;
|
return (
|
||||||
|
<MarkdownTextSpan monoSurface={monoSurface} style={style}>
|
||||||
|
{children}
|
||||||
|
</MarkdownTextSpan>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MarkdownListItemContentProps {
|
interface MarkdownListItemContentProps {
|
||||||
@@ -1696,6 +1705,7 @@ export const AssistantMessage = memo(function AssistantMessage({
|
|||||||
key={node.key}
|
key={node.key}
|
||||||
inheritedStyles={inheritedStyles}
|
inheritedStyles={inheritedStyles}
|
||||||
textStyle={styles.code_inline}
|
textStyle={styles.code_inline}
|
||||||
|
monoSurface
|
||||||
>
|
>
|
||||||
{content}
|
{content}
|
||||||
</MarkdownInheritedText>
|
</MarkdownInheritedText>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
} from "react-native";
|
} from "react-native";
|
||||||
import { ScrollView as GHScrollView } from "react-native-gesture-handler";
|
import { ScrollView as GHScrollView } from "react-native-gesture-handler";
|
||||||
import { StyleSheet } from "react-native-unistyles";
|
import { StyleSheet } from "react-native-unistyles";
|
||||||
|
import { AppearanceStyleBoundary } from "@/components/appearance-style-boundary";
|
||||||
import type { ToolCallDetail } from "@getpaseo/protocol/agent-types";
|
import type { ToolCallDetail } from "@getpaseo/protocol/agent-types";
|
||||||
import { buildLineDiff, parseUnifiedDiff, type DiffLine } from "@/utils/tool-call-parsers";
|
import { buildLineDiff, parseUnifiedDiff, type DiffLine } from "@/utils/tool-call-parsers";
|
||||||
import { highlightDiffLines } from "@/utils/diff-highlight";
|
import { highlightDiffLines } from "@/utils/diff-highlight";
|
||||||
@@ -756,7 +757,15 @@ function LoadingSkeleton({ containerStyle }: { containerStyle: StyleProp<ViewSty
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ToolCallDetailsContent({
|
export function ToolCallDetailsContent({ ...props }: ToolCallDetailsContentProps) {
|
||||||
|
return (
|
||||||
|
<AppearanceStyleBoundary>
|
||||||
|
<ToolCallDetailsContentInner {...props} />
|
||||||
|
</AppearanceStyleBoundary>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ToolCallDetailsContentInner({
|
||||||
detail,
|
detail,
|
||||||
errorText,
|
errorText,
|
||||||
maxHeight,
|
maxHeight,
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ describe("appearance settings", () => {
|
|||||||
expect(result.monoFontFamily).toBe("");
|
expect(result.monoFontFamily).toBe("");
|
||||||
expect(result.uiFontSize).toBe(DEFAULT_UI_FONT_SIZE);
|
expect(result.uiFontSize).toBe(DEFAULT_UI_FONT_SIZE);
|
||||||
expect(result.codeFontSize).toBe(DEFAULT_CODE_FONT_SIZE);
|
expect(result.codeFontSize).toBe(DEFAULT_CODE_FONT_SIZE);
|
||||||
expect(result.syntaxTheme).toBe("github");
|
expect(result.syntaxTheme).toBe("one");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("clamps the UI font size into range and rejects non-numeric values", async () => {
|
it("clamps the UI font size into range and rejects non-numeric values", async () => {
|
||||||
@@ -346,7 +346,7 @@ describe("appearance settings", () => {
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
expect((await loadAppSettingsFromStorage(deps)).syntaxTheme).toBe("github");
|
expect((await loadAppSettingsFromStorage(deps)).syntaxTheme).toBe("one");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("drops an unknown syntax theme id back to the default", async () => {
|
it("drops an unknown syntax theme id back to the default", async () => {
|
||||||
@@ -356,7 +356,7 @@ describe("appearance settings", () => {
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
expect((await loadAppSettingsFromStorage(deps)).syntaxTheme).toBe("github");
|
expect((await loadAppSettingsFromStorage(deps)).syntaxTheme).toBe("one");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export interface AppSettings {
|
|||||||
monoFontFamily: string; // "" = platform default mono stack
|
monoFontFamily: string; // "" = platform default mono stack
|
||||||
uiFontSize: number; // clamped px, default 16
|
uiFontSize: number; // clamped px, default 16
|
||||||
codeFontSize: number; // clamped px, default 12
|
codeFontSize: number; // clamped px, default 12
|
||||||
syntaxTheme: SyntaxThemeId; // default "auto"
|
syntaxTheme: SyntaxThemeId; // default "one"
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Settings extends AppSettings {
|
export interface Settings extends AppSettings {
|
||||||
@@ -50,7 +50,7 @@ export const DEFAULT_CLIENT_SETTINGS: AppSettings = {
|
|||||||
monoFontFamily: "",
|
monoFontFamily: "",
|
||||||
uiFontSize: DEFAULT_UI_FONT_SIZE,
|
uiFontSize: DEFAULT_UI_FONT_SIZE,
|
||||||
codeFontSize: DEFAULT_CODE_FONT_SIZE,
|
codeFontSize: DEFAULT_CODE_FONT_SIZE,
|
||||||
syntaxTheme: "github",
|
syntaxTheme: "one",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DEFAULT_APP_SETTINGS: Settings = {
|
export const DEFAULT_APP_SETTINGS: Settings = {
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import {
|
|||||||
THEME_SWATCHES,
|
THEME_SWATCHES,
|
||||||
type Theme,
|
type Theme,
|
||||||
} from "@/styles/theme";
|
} from "@/styles/theme";
|
||||||
|
import { isNative } from "@/constants/platform";
|
||||||
import { settingsStyles } from "@/styles/settings";
|
import { settingsStyles } from "@/styles/settings";
|
||||||
import { AppearancePreview } from "./appearance-preview";
|
import { AppearancePreview } from "./appearance-preview";
|
||||||
|
|
||||||
@@ -247,6 +248,7 @@ interface FontSizeRowProps {
|
|||||||
title: string;
|
title: string;
|
||||||
accessibilityLabel: string;
|
accessibilityLabel: string;
|
||||||
draft: string;
|
draft: string;
|
||||||
|
withBorder?: boolean;
|
||||||
onChangeDraft: (value: string) => void;
|
onChangeDraft: (value: string) => void;
|
||||||
onCommit: () => void;
|
onCommit: () => void;
|
||||||
}
|
}
|
||||||
@@ -255,11 +257,12 @@ function FontSizeRow({
|
|||||||
title,
|
title,
|
||||||
accessibilityLabel,
|
accessibilityLabel,
|
||||||
draft,
|
draft,
|
||||||
|
withBorder = true,
|
||||||
onChangeDraft,
|
onChangeDraft,
|
||||||
onCommit,
|
onCommit,
|
||||||
}: FontSizeRowProps) {
|
}: FontSizeRowProps) {
|
||||||
return (
|
return (
|
||||||
<View style={styles.rowWithBorder}>
|
<View style={withBorder ? styles.rowWithBorder : settingsStyles.row}>
|
||||||
<View style={settingsStyles.rowContent}>
|
<View style={settingsStyles.rowContent}>
|
||||||
<Text style={settingsStyles.rowTitle}>{title}</Text>
|
<Text style={settingsStyles.rowTitle}>{title}</Text>
|
||||||
</View>
|
</View>
|
||||||
@@ -348,6 +351,7 @@ function SyntaxRow({ value, onChange }: SyntaxRowProps) {
|
|||||||
|
|
||||||
export function AppearanceSection() {
|
export function AppearanceSection() {
|
||||||
const { settings, updateSettings } = useAppSettings();
|
const { settings, updateSettings } = useAppSettings();
|
||||||
|
const showFontFamilyRows = !isNative;
|
||||||
|
|
||||||
const [uiFontDraft, setUiFontDraft] = useState(settings.uiFontFamily);
|
const [uiFontDraft, setUiFontDraft] = useState(settings.uiFontFamily);
|
||||||
const [monoFontDraft, setMonoFontDraft] = useState(settings.monoFontFamily);
|
const [monoFontDraft, setMonoFontDraft] = useState(settings.monoFontFamily);
|
||||||
@@ -458,35 +462,40 @@ export function AppearanceSection() {
|
|||||||
</SettingsSection>
|
</SettingsSection>
|
||||||
<SettingsSection title="Fonts">
|
<SettingsSection title="Fonts">
|
||||||
<View style={settingsStyles.card}>
|
<View style={settingsStyles.card}>
|
||||||
<FontFamilyRow
|
{showFontFamilyRows ? (
|
||||||
title="Interface font"
|
<FontFamilyRow
|
||||||
hint="Used across the app. Leave empty for the system default"
|
title="Interface font"
|
||||||
accessibilityLabel="Interface font family"
|
hint="Used across the app. Leave empty for the system default"
|
||||||
placeholder={UI_FONT_PLACEHOLDER}
|
accessibilityLabel="Interface font family"
|
||||||
value={settings.uiFontFamily}
|
placeholder={UI_FONT_PLACEHOLDER}
|
||||||
draft={uiFontDraft}
|
value={settings.uiFontFamily}
|
||||||
withBorder={false}
|
draft={uiFontDraft}
|
||||||
onChangeDraft={setUiFontDraft}
|
withBorder={false}
|
||||||
onCommit={commitUiFontFamily}
|
onChangeDraft={setUiFontDraft}
|
||||||
/>
|
onCommit={commitUiFontFamily}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
<FontSizeRow
|
<FontSizeRow
|
||||||
title="Interface size"
|
title="Interface size"
|
||||||
accessibilityLabel="Interface font size"
|
accessibilityLabel="Interface font size"
|
||||||
draft={uiSizeDraft}
|
draft={uiSizeDraft}
|
||||||
|
withBorder={showFontFamilyRows}
|
||||||
onChangeDraft={handleUiSizeChange}
|
onChangeDraft={handleUiSizeChange}
|
||||||
onCommit={commitUiSize}
|
onCommit={commitUiSize}
|
||||||
/>
|
/>
|
||||||
<FontFamilyRow
|
{showFontFamilyRows ? (
|
||||||
title="Code font"
|
<FontFamilyRow
|
||||||
hint="Used in code, diffs, and the terminal output. Leave empty for the system default"
|
title="Code font"
|
||||||
accessibilityLabel="Code font family"
|
hint="Used in code, diffs, and the terminal output. Leave empty for the system default"
|
||||||
placeholder={MONO_FONT_PLACEHOLDER}
|
accessibilityLabel="Code font family"
|
||||||
value={settings.monoFontFamily}
|
placeholder={MONO_FONT_PLACEHOLDER}
|
||||||
draft={monoFontDraft}
|
value={settings.monoFontFamily}
|
||||||
withBorder
|
draft={monoFontDraft}
|
||||||
onChangeDraft={setMonoFontDraft}
|
withBorder
|
||||||
onCommit={commitMonoFontFamily}
|
onChangeDraft={setMonoFontDraft}
|
||||||
/>
|
onCommit={commitMonoFontFamily}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
<FontSizeRow
|
<FontSizeRow
|
||||||
title="Code size"
|
title="Code size"
|
||||||
accessibilityLabel="Code font size"
|
accessibilityLabel="Code font size"
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ function makeInput(overrides: Partial<AppearanceInput> = {}): AppearanceInput {
|
|||||||
monoFontFamily: "",
|
monoFontFamily: "",
|
||||||
uiFontSize: 16,
|
uiFontSize: 16,
|
||||||
codeFontSize: 12,
|
codeFontSize: 12,
|
||||||
syntaxTheme: "github",
|
syntaxTheme: "one",
|
||||||
...overrides,
|
...overrides,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { createMarkdownStyles } from "./markdown-styles";
|
import { createCompactMarkdownStyles, createMarkdownStyles } from "./markdown-styles";
|
||||||
import { darkTheme } from "./theme";
|
import { darkTheme } from "./theme";
|
||||||
|
|
||||||
describe("createMarkdownStyles", () => {
|
describe("createMarkdownStyles", () => {
|
||||||
@@ -69,4 +69,28 @@ describe("createMarkdownStyles", () => {
|
|||||||
userSelect: "text",
|
userSelect: "text",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("uses the mono font-size token directly for inline and block code", () => {
|
||||||
|
const styles = createMarkdownStyles(darkTheme);
|
||||||
|
const compactStyles = createCompactMarkdownStyles(darkTheme);
|
||||||
|
|
||||||
|
expect(styles.code_inline).toMatchObject({
|
||||||
|
fontFamily: darkTheme.fontFamily.mono,
|
||||||
|
fontSize: darkTheme.fontSize.code,
|
||||||
|
lineHeight: Math.round(darkTheme.fontSize.code * 1.45),
|
||||||
|
});
|
||||||
|
expect(styles.code_block).toMatchObject({
|
||||||
|
fontFamily: darkTheme.fontFamily.mono,
|
||||||
|
fontSize: darkTheme.fontSize.code,
|
||||||
|
});
|
||||||
|
expect(styles.fence).toMatchObject({
|
||||||
|
fontFamily: darkTheme.fontFamily.mono,
|
||||||
|
fontSize: darkTheme.fontSize.code,
|
||||||
|
});
|
||||||
|
expect(compactStyles.code_inline).toMatchObject({
|
||||||
|
fontFamily: darkTheme.fontFamily.mono,
|
||||||
|
fontSize: darkTheme.fontSize.code,
|
||||||
|
lineHeight: Math.round(darkTheme.fontSize.code * 1.45),
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -172,6 +172,7 @@ export function createMarkdownStyles(theme: Theme) {
|
|||||||
borderWidth: 0,
|
borderWidth: 0,
|
||||||
fontFamily: theme.fontFamily.mono,
|
fontFamily: theme.fontFamily.mono,
|
||||||
fontSize: theme.fontSize.code,
|
fontSize: theme.fontSize.code,
|
||||||
|
lineHeight: Math.round(theme.fontSize.code * 1.45),
|
||||||
},
|
},
|
||||||
|
|
||||||
code_block: {
|
code_block: {
|
||||||
@@ -389,7 +390,7 @@ export function createCompactMarkdownStyles(theme: Theme) {
|
|||||||
|
|
||||||
code_inline: {
|
code_inline: {
|
||||||
...baseStyles.code_inline,
|
...baseStyles.code_inline,
|
||||||
fontSize: theme.fontSize.sm - 3,
|
fontSize: theme.fontSize.code,
|
||||||
},
|
},
|
||||||
|
|
||||||
code_block: {
|
code_block: {
|
||||||
|
|||||||
@@ -25,6 +25,8 @@
|
|||||||
"typecheck": "tsgo --noEmit"
|
"typecheck": "tsgo --noEmit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@codemirror/language": "^6.12.3",
|
||||||
|
"@codemirror/legacy-modes": "^6.5.3",
|
||||||
"@lezer/common": "^1.5.0",
|
"@lezer/common": "^1.5.0",
|
||||||
"@lezer/cpp": "^1.1.5",
|
"@lezer/cpp": "^1.1.5",
|
||||||
"@lezer/css": "^1.3.0",
|
"@lezer/css": "^1.3.0",
|
||||||
|
|||||||
@@ -30,6 +30,22 @@ describe("highlightCode", () => {
|
|||||||
expect(stringToken?.style).toBe("string");
|
expect(stringToken?.style).toBe("string");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("highlights Swift code", () => {
|
||||||
|
const code = 'struct Greeter {\n let message = "hello"\n}';
|
||||||
|
const result = highlightCode(code, "test.swift");
|
||||||
|
|
||||||
|
expect(result).toHaveLength(3);
|
||||||
|
|
||||||
|
const structToken = result[0].find((t) => t.text === "struct");
|
||||||
|
expect(structToken?.style).toBe("keyword");
|
||||||
|
|
||||||
|
const typeToken = result[0].find((t) => t.text === "Greeter");
|
||||||
|
expect(typeToken?.style).toBe("definition");
|
||||||
|
|
||||||
|
const stringToken = result[1].find((t) => t.text.includes("hello"));
|
||||||
|
expect(stringToken?.style).toBe("string");
|
||||||
|
});
|
||||||
|
|
||||||
it("highlights TSX code with correct dialect", () => {
|
it("highlights TSX code with correct dialect", () => {
|
||||||
const code = 'const el = <div className="test">hello</div>;';
|
const code = 'const el = <div className="test">hello</div>;';
|
||||||
const result = highlightCode(code, "test.tsx");
|
const result = highlightCode(code, "test.tsx");
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ describe("isLanguageSupported", () => {
|
|||||||
expect(isLanguageSupported("test.css")).toBe(true);
|
expect(isLanguageSupported("test.css")).toBe(true);
|
||||||
expect(isLanguageSupported("test.html")).toBe(true);
|
expect(isLanguageSupported("test.html")).toBe(true);
|
||||||
expect(isLanguageSupported("test.java")).toBe(true);
|
expect(isLanguageSupported("test.java")).toBe(true);
|
||||||
|
expect(isLanguageSupported("test.swift")).toBe(true);
|
||||||
expect(isLanguageSupported("test.ex")).toBe(true);
|
expect(isLanguageSupported("test.ex")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -49,6 +50,7 @@ describe("getSupportedExtensions", () => {
|
|||||||
expect(extensions).toContain("py");
|
expect(extensions).toContain("py");
|
||||||
expect(extensions).toContain("go");
|
expect(extensions).toContain("go");
|
||||||
expect(extensions).toContain("rs");
|
expect(extensions).toContain("rs");
|
||||||
|
expect(extensions).toContain("swift");
|
||||||
expect(extensions).toContain("json");
|
expect(extensions).toContain("json");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -59,10 +59,19 @@ describe("resolveSyntaxColors", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("dark-only themes ignore the color scheme", () => {
|
it("dark-only themes ignore the color scheme", () => {
|
||||||
for (const id of ["dracula", "nord"] as const) {
|
for (const id of ["dracula"] as const) {
|
||||||
expect(resolveSyntaxColors(id, "light")).toEqual(resolveSyntaxColors(id, "dark"));
|
expect(resolveSyntaxColors(id, "light")).toEqual(resolveSyntaxColors(id, "dark"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("nord uses a dark text palette in light mode", () => {
|
||||||
|
const light = resolveSyntaxColors("nord", "light");
|
||||||
|
const dark = resolveSyntaxColors("nord", "dark");
|
||||||
|
|
||||||
|
expect(light).not.toEqual(dark);
|
||||||
|
expect(light.variable).toBe("#2e3440");
|
||||||
|
expect(light.comment).toBe("#6b7280");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("isSyntaxThemeId", () => {
|
describe("isSyntaxThemeId", () => {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { StreamLanguage } from "@codemirror/language";
|
||||||
|
import { swift } from "@codemirror/legacy-modes/mode/swift";
|
||||||
import { parser as jsParser } from "@lezer/javascript";
|
import { parser as jsParser } from "@lezer/javascript";
|
||||||
import { parser as jsonParser } from "@lezer/json";
|
import { parser as jsonParser } from "@lezer/json";
|
||||||
import { parser as cssParser } from "@lezer/css";
|
import { parser as cssParser } from "@lezer/css";
|
||||||
@@ -55,6 +57,8 @@ const parsersByExtension: Record<string, Parser> = {
|
|||||||
yml: yamlParser,
|
yml: yamlParser,
|
||||||
// Rust
|
// Rust
|
||||||
rs: rustParser,
|
rs: rustParser,
|
||||||
|
// Swift
|
||||||
|
swift: StreamLanguage.define(swift).parser,
|
||||||
// Elixir
|
// Elixir
|
||||||
ex: elixirParser,
|
ex: elixirParser,
|
||||||
exs: elixirParser,
|
exs: elixirParser,
|
||||||
|
|||||||
@@ -180,8 +180,20 @@ const oneDark: RolePalette = {
|
|||||||
operator: "#56b6c2",
|
operator: "#56b6c2",
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- Nord (dark only) ----------------------------------------------------
|
// --- Nord (Snow Storm light / Polar Night dark) ---------------------------
|
||||||
const nord: RolePalette = {
|
const nordLight: RolePalette = {
|
||||||
|
base: "#2e3440",
|
||||||
|
keyword: "#5e81ac",
|
||||||
|
comment: "#6b7280",
|
||||||
|
string: "#4f6f3a",
|
||||||
|
number: "#8f5e91",
|
||||||
|
function: "#2e6f8e",
|
||||||
|
type: "#3b7f87",
|
||||||
|
tag: "#5e81ac",
|
||||||
|
attribute: "#8f5e91",
|
||||||
|
operator: "#5e81ac",
|
||||||
|
};
|
||||||
|
const nordDark: RolePalette = {
|
||||||
base: "#d8dee9",
|
base: "#d8dee9",
|
||||||
keyword: "#81a1c1",
|
keyword: "#81a1c1",
|
||||||
comment: "#616e88",
|
comment: "#616e88",
|
||||||
@@ -269,7 +281,7 @@ export function resolveSyntaxColors(
|
|||||||
case "one":
|
case "one":
|
||||||
return expandRolePalette(dark ? oneDark : oneLight);
|
return expandRolePalette(dark ? oneDark : oneLight);
|
||||||
case "nord":
|
case "nord":
|
||||||
return expandRolePalette(nord);
|
return expandRolePalette(dark ? nordDark : nordLight);
|
||||||
case "gruvbox":
|
case "gruvbox":
|
||||||
return expandRolePalette(dark ? gruvboxDark : gruvboxLight);
|
return expandRolePalette(dark ? gruvboxDark : gruvboxLight);
|
||||||
case "solarized":
|
case "solarized":
|
||||||
|
|||||||
Reference in New Issue
Block a user