mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-18 16:02:10 +00:00
feat: add github dark mode variant switchable in user settings (#10002)
* feat: add github dark mode variant switchable in user settings Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat: boot the UI Builder iframe into the current theme Seed the UI Builder iframe URL with the live dark/variant state (`?dark=&variant=`) so the VS Code workbench boots straight into GitHub Dark / Nord / Light instead of flashing the dark default until the host's `setDarkMode` message lands. Read from the <html> classes rather than the reactive state so the src is computed once at mount — a later theme toggle still updates the workbench via postMessage and does not reload the iframe. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat: align github-dark surfaces to GitHub Primer's elevation scale Snap the invented mid-greys to Primer's real dark canvas scale (inset=neutral.0, default=neutral.1, muted=neutral.2). Recessed surfaces now use true inset (#010409) instead of washed-out half-steps that sat in no-man's-land between inset and the page; raised surfaces align to muted. - surface-secondary #0b0e13 → #010409 (true inset) - surface-input #0c0f14 → #010409 (true inset) - surface-disabled #0b0e13 → #151b23 (muted) - surface-tertiary #161b22 → #151b23 (Primer neutral.2) - component-virtual-node #161b22 → #151b23 - surface-selected #21262d → #212830 (Primer neutral.3) surface-sunken (#010409) and surface-primary (#0d1117) already matched Primer inset/default exactly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: keep github-dark inputs subtle instead of full inset surface-input at true inset (#010409) made form fields read as recessed as the sidebar rail — too much contrast against the canvas. Inputs aren't sunken, so keep the subtle #0c0f14 (a hair below the page) while the recessed panel surfaces stay at inset. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: bump UI Builder pin to the github-dark build (76ee616) windmill-code-ui-builder PR #16 is merged and published to R2, so pin the artifact to that build. The embedded raw-app editor now renders GitHub Dark (and honors the `variant` message) natively from the pinned build — no local swap needed. Reword the variant comment now that the dependency is resolved. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: address codex review nits on the github-dark theme - UserSettings: re-read the dark variant when the settings drawer opens, so a change made through one mounted instance (page-local drawer) isn't shown stale by another (the always-mounted layout instance). - PublicApp / OAuth login callback: restore the `github-dark` variant class, not just the base `dark` class. These routes bypass the (root) layout, so they previously fell back to the default dark palette despite the saved preference. - RawAppEditor: condense the two new theme comments to the durable constraints per AGENTS.md (drop narration and the ephemeral artifact-pin history). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: address CI review findings on the github-dark theme - Move the hand-authored `github-dark` token set out of the Figma-generated tokens.json into githubDark.json, merged back in at the two consumers (tailwind.config.cjs before the rgb pass, utils.ts) so a Figma re-export of tokens.json can no longer drop the set and crash the build. - Restore the default-dark sidebar divider to #374151 — the PR must leave the Default variant unchanged; only the github variant adapts it to border-light. - Extract the `github-dark` DOM-class read into getAppliedDarkModeVariant() and reuse it across RawAppEditor and vscode.ts; drop the redundant `void darkVariant`. - Override the Monaco popup vars (suggest/hover widgets) in the github variant so they match the GitHub palette instead of VS Code's greys. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"baseUrl": "https://pub-06154ed168a24e73a86ab84db6bf15d8.r2.dev",
|
||||
"version": "1f1fe4f",
|
||||
"sha256": "4c20b2b51f324e93dda3b46d914ebf7bc7d0cb4dc7eb2d37068408191220d206"
|
||||
"version": "76ee616",
|
||||
"sha256": "1d696d38a152179ef6e1cada5df302d078cc677b6dbc244b5c42335e11feabac"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"surface-accent-primary": "#506ab8",
|
||||
"surface-accent-hover": "#5873c8",
|
||||
"surface-accent-clicked": "#425797",
|
||||
"text-primary": "#d5dbe2",
|
||||
"text-secondary": "#8b949e",
|
||||
"text-primary-inverse": "#24292f",
|
||||
"text-secondary-inverse": "#57606a",
|
||||
"text-tertiary-inverse": "#6e7781",
|
||||
"surface-selected": "#212830",
|
||||
"surface-disabled": "#151b23",
|
||||
"surface-secondary": "#010409",
|
||||
"surface-hover": "#6e768119",
|
||||
"surface-primary": "#0d1117",
|
||||
"border-light": "#242a32",
|
||||
"border-normal": "#6e7681",
|
||||
"border-accent": "#4a6ed6",
|
||||
"surface-accent-selected": "#141a2e",
|
||||
"surface-accent-secondary": "#e3e8ee",
|
||||
"surface-tertiary": "#151b23",
|
||||
"text-emphasis": "#dfe5eb",
|
||||
"text-hint": "#6e7681",
|
||||
"text-disabled": "#484f58",
|
||||
"surface-accent-secondary-hover": "#d0d7de",
|
||||
"surface-accent-secondary-clicked": "#afb8c1",
|
||||
"component-button-accent-secondary": "#f0f6fc",
|
||||
"text-emphasis-inverse": "#1f2328",
|
||||
"reserved-ai": "#d2a8ff",
|
||||
"component-virtual-node": "#151b23",
|
||||
"text-accent": "#bdc8ee",
|
||||
"border-selected": "#5a72c4",
|
||||
"surface-sunken": "#010409",
|
||||
"text-tertiary": "#6e7681",
|
||||
"surface-input": "#0c0f14"
|
||||
}
|
||||
@@ -239,7 +239,12 @@
|
||||
"magenta-950": "#6e2ba1"
|
||||
}
|
||||
},
|
||||
"guidelines": { "mode-1": { "blue": "#5e81ac", "demo-background": "#ffffff00" } },
|
||||
"guidelines": {
|
||||
"mode-1": {
|
||||
"blue": "#5e81ac",
|
||||
"demo-background": "#ffffff00"
|
||||
}
|
||||
},
|
||||
"tailwind-c-s-s-v-3-3-2": {
|
||||
"mode-1": {
|
||||
"black": "#000000",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { isInitialized } from './vscode'
|
||||
import { getEditorTheme, isInitialized } from './vscode'
|
||||
|
||||
import { editor as meditor } from 'monaco-editor'
|
||||
|
||||
@@ -8,11 +8,7 @@
|
||||
|
||||
function onThemeChange() {
|
||||
if (isInitialized) {
|
||||
if (document.documentElement.classList.contains('dark')) {
|
||||
meditor.setTheme('nord')
|
||||
} else {
|
||||
meditor.setTheme('myTheme')
|
||||
}
|
||||
meditor.setTheme(getEditorTheme())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2505,7 +2505,7 @@
|
||||
{/snippet}
|
||||
|
||||
{#snippet editorContent()}
|
||||
<div class="h-full !overflow-visible bg-surface dark:bg-[#272D38] relative flex flex-col">
|
||||
<div class="h-full !overflow-visible bg-surface dark:bg-surface-secondary relative flex flex-col">
|
||||
{#if supportsModules}
|
||||
<div
|
||||
class="flex items-center border-b border-tertiary/30 bg-surface-secondary px-1 gap-0.5 text-xs overflow-x-auto shrink-0"
|
||||
|
||||
@@ -3,10 +3,17 @@
|
||||
import DrawerContent from '$lib/components/common/drawer/DrawerContent.svelte'
|
||||
import Version from './Version.svelte'
|
||||
import DarkModeToggle from './sidebar/DarkModeToggle.svelte'
|
||||
import ToggleButton from './common/toggleButton-v2/ToggleButton.svelte'
|
||||
import ToggleButtonGroup from './common/toggleButton-v2/ToggleButtonGroup.svelte'
|
||||
import TokensTable from './settings/TokensTable.svelte'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import UserInfoSettings from './settings/UserInfoSettings.svelte'
|
||||
import AIUserSettings from './settings/AIUserSettings.svelte'
|
||||
import {
|
||||
getDarkModeVariant,
|
||||
setDarkModeVariant,
|
||||
type DarkModeVariant
|
||||
} from '$lib/darkModeVariant'
|
||||
|
||||
interface Props {
|
||||
scopes?: string[] | undefined
|
||||
@@ -28,6 +35,7 @@
|
||||
|
||||
let drawer: Drawer | undefined = $state()
|
||||
let openWithMcpMode = $state(false)
|
||||
let darkVariant = $state<DarkModeVariant>(getDarkModeVariant())
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -51,15 +59,39 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Drawer bind:this={drawer} size="900px" on:close={removeHash} {disableChatOffset}>
|
||||
<Drawer
|
||||
bind:this={drawer}
|
||||
size="900px"
|
||||
on:open={() => (darkVariant = getDarkModeVariant())}
|
||||
on:close={removeHash}
|
||||
{disableChatOffset}
|
||||
>
|
||||
<DrawerContent title="User settings" on:close={closeDrawer}>
|
||||
<div class="flex flex-col gap-6 pb-8">
|
||||
{#if scopes == undefined}
|
||||
<div
|
||||
class="flex flex-row justify-between items-start gap-2 border border-border-light p-4 rounded-md"
|
||||
>
|
||||
<div class="font-semibold text-emphasis text-xs flex items-center">
|
||||
Theme <DarkModeToggle forcedDarkMode={false} />
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="font-semibold text-emphasis text-xs flex items-center">
|
||||
Theme <DarkModeToggle forcedDarkMode={false} />
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-xs text-secondary">Dark variant</span>
|
||||
<ToggleButtonGroup
|
||||
selected={darkVariant}
|
||||
class="w-fit"
|
||||
onSelected={(v) => {
|
||||
darkVariant = v
|
||||
setDarkModeVariant(v)
|
||||
}}
|
||||
>
|
||||
{#snippet children({ item })}
|
||||
<ToggleButton value="default" label="Default" size="sm" {item} />
|
||||
<ToggleButton value="github" label="GitHub" size="sm" {item} />
|
||||
{/snippet}
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-xs text-emphasis flex-col flex">
|
||||
Windmill <Version />
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { User, UserRoundX } from 'lucide-svelte'
|
||||
import { applyDarkModeVariant } from '$lib/darkModeVariant'
|
||||
import { enterpriseLicense, userStore } from '$lib/stores'
|
||||
import { base } from '$app/paths'
|
||||
import { page } from '$app/state'
|
||||
@@ -73,6 +74,8 @@
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark')
|
||||
}
|
||||
// This route bypasses the (root) layout, so restore the variant class too.
|
||||
applyDarkModeVariant()
|
||||
|
||||
let globalUser = $state<GlobalWhoamiResponse | undefined>(undefined)
|
||||
async function loadGlobalUser() {
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
import { runDomQueryOnHtml, type RawAppDomQuery, type RawAppDomRequester } from './rawAppDom'
|
||||
import InlineElementPrompt from './InlineElementPrompt.svelte'
|
||||
import DarkModeObserver from '../DarkModeObserver.svelte'
|
||||
import { getAppliedDarkModeVariant, type DarkModeVariant } from '$lib/darkModeVariant'
|
||||
import RawAppSidebar from './RawAppSidebar.svelte'
|
||||
import type { Modules } from './RawAppModules.svelte'
|
||||
import { isRunnableByName, isRunnableByPath } from '../apps/inputType'
|
||||
@@ -1304,7 +1305,7 @@
|
||||
// it always matches the editor's current state. Plain rebuilds use
|
||||
// `syncExternalPreview` alone (the theme hasn't changed).
|
||||
function feedExternalPreview() {
|
||||
postToExternalPreview({ type: 'setDarkMode', dark: darkMode })
|
||||
postToExternalPreview({ type: 'setDarkMode', dark: darkMode, variant: darkVariant })
|
||||
syncExternalPreview()
|
||||
}
|
||||
|
||||
@@ -1697,6 +1698,17 @@
|
||||
})
|
||||
|
||||
let darkMode: boolean = $state(false)
|
||||
// Mirrors the `github-dark` class (the runtime source of truth); the
|
||||
// DarkModeObserver below must keep it in sync or it goes stale.
|
||||
let darkVariant: DarkModeVariant = $state(getAppliedDarkModeVariant())
|
||||
// Read the DOM classes, not reactive state, so the src stays constant after
|
||||
// mount: a reactive src would reload the iframe on every theme toggle. Live
|
||||
// theme changes travel through postMessage instead (see the $effect below).
|
||||
function uiBuilderIframeSrc(): string {
|
||||
const dark = document.documentElement.classList.contains('dark')
|
||||
const variant = getAppliedDarkModeVariant()
|
||||
return `/ui_builder/index.html?dark=${dark}&variant=${variant}`
|
||||
}
|
||||
// Host's computed `text-xs` size in px. Windmill bumps :root to 18px at
|
||||
// ≥1760px viewports, so this re-evaluates on resize via the listener below.
|
||||
let editorFontSize = $state(12)
|
||||
@@ -1746,12 +1758,18 @@
|
||||
// Push dark mode to both children. The UI Builder iframe and the
|
||||
// preview iframe each listen for `setDarkMode` separately.
|
||||
if (iframe && iframeLoaded) {
|
||||
iframe.contentWindow?.postMessage({ type: 'setDarkMode', dark: darkMode }, '*')
|
||||
iframe.contentWindow?.postMessage(
|
||||
{ type: 'setDarkMode', dark: darkMode, variant: darkVariant },
|
||||
'*'
|
||||
)
|
||||
}
|
||||
if (previewIframe && previewIframeLoaded) {
|
||||
previewIframe.contentWindow?.postMessage({ type: 'setDarkMode', dark: darkMode }, '*')
|
||||
previewIframe.contentWindow?.postMessage(
|
||||
{ type: 'setDarkMode', dark: darkMode, variant: darkVariant },
|
||||
'*'
|
||||
)
|
||||
}
|
||||
postToExternalPreview({ type: 'setDarkMode', dark: darkMode })
|
||||
postToExternalPreview({ type: 'setDarkMode', dark: darkMode, variant: darkVariant })
|
||||
})
|
||||
$effect(() => {
|
||||
// Match VS Code's editor font size to Windmill's text-xs.
|
||||
@@ -2036,7 +2054,12 @@
|
||||
</script>
|
||||
|
||||
<svelte:window onmessage={listener} onkeydown={handleKeydown} />
|
||||
<DarkModeObserver bind:darkMode />
|
||||
<DarkModeObserver
|
||||
bind:darkMode
|
||||
on:change={() => {
|
||||
darkVariant = getAppliedDarkModeVariant()
|
||||
}}
|
||||
/>
|
||||
|
||||
<RawAppBackgroundRunner
|
||||
workspace={opWorkspace ?? ''}
|
||||
@@ -2214,7 +2237,7 @@
|
||||
<iframe
|
||||
bind:this={iframe}
|
||||
title="UI builder"
|
||||
src="/ui_builder/index.html"
|
||||
src={uiBuilderIframeSrc()}
|
||||
class="w-full h-full block"
|
||||
onload={attachIframeSaveShortcut}
|
||||
></iframe>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Sidebar rail background. Deliberately raw hex — no theme token exists for
|
||||
// the rail chrome — kept here as the single source for every host that renders
|
||||
// or mimics the sidebar (app layout, kitchen-sink harness).
|
||||
// Sidebar rail background. Kept here as the single source for every host that
|
||||
// renders or mimics the sidebar (app layout, kitchen-sink harness).
|
||||
// The dark value is a theme-driven CSS variable so it adapts across dark
|
||||
// variants (default vs GitHub dark) — see tailwind.config.cjs `--sidebar-bg-dark`.
|
||||
export const SIDEBAR_BG = '#F3F3F7'
|
||||
export const SIDEBAR_BG_DARK = '#1e232e'
|
||||
export const SIDEBAR_BG_DARK = 'var(--sidebar-bg-dark)'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import '@codingame/monaco-vscode-standalone-typescript-language-features'
|
||||
|
||||
import { editor as meditor, Uri as mUri } from 'monaco-editor'
|
||||
import { getAppliedDarkModeVariant } from '$lib/darkModeVariant'
|
||||
|
||||
export let isInitialized = false
|
||||
export let isInitializing = false
|
||||
@@ -12,6 +13,15 @@ import {
|
||||
import getLanguagesServiceOverride from '@codingame/monaco-vscode-languages-service-override'
|
||||
import { getCssColor } from '$lib/utils'
|
||||
|
||||
// Monaco theme name matching the current document theme (light / dark / GitHub dark variant).
|
||||
export function getEditorTheme(): string {
|
||||
const classes = document.documentElement.classList
|
||||
if (!classes.contains('dark')) {
|
||||
return 'myTheme'
|
||||
}
|
||||
return getAppliedDarkModeVariant() === 'github' ? 'github-dark' : 'nord'
|
||||
}
|
||||
|
||||
export function buildWorkerDefinition() {
|
||||
const envEnhanced = getEnhancedMonacoEnvironment()
|
||||
const getWorker = (moduleId: string, label: string) => {
|
||||
@@ -248,6 +258,47 @@ export async function initializeVscode(caller?: string, htmlContainer?: HTMLElem
|
||||
}
|
||||
})
|
||||
|
||||
meditor.defineTheme('github-dark', {
|
||||
base: 'vs-dark',
|
||||
inherit: true,
|
||||
rules: [
|
||||
{ background: '0D1117', token: '' },
|
||||
{ foreground: '8b949e', token: 'comment' },
|
||||
{ foreground: 'a5d6ff', token: 'string' },
|
||||
{ foreground: '79c0ff', token: 'constant.numeric' },
|
||||
{ foreground: '79c0ff', token: 'constant.language' },
|
||||
{ foreground: 'ff7b72', token: 'keyword' },
|
||||
{ foreground: 'ff7b72', token: 'storage' },
|
||||
{ foreground: 'ff7b72', token: 'storage.type' },
|
||||
{ foreground: 'ffa657', token: 'entity.name.class' },
|
||||
{ foreground: 'ffa657', fontStyle: 'bold', token: 'entity.other.inherited-class' },
|
||||
{ foreground: 'd2a8ff', token: 'entity.name.function' },
|
||||
{ foreground: '7ee787', token: 'entity.name.tag' },
|
||||
{ foreground: '79c0ff', token: 'entity.other.attribute-name' },
|
||||
{ foreground: 'd2a8ff', token: 'support.function' },
|
||||
{ foreground: 'f0f6fc', background: 'f85149', token: 'invalid' },
|
||||
{ foreground: 'f0f6fc', background: 'bd561d', token: 'invalid.deprecated' },
|
||||
{ foreground: '79c0ff', token: 'constant.color.other.rgb-value' },
|
||||
{ foreground: '79c0ff', token: 'constant.character.escape' },
|
||||
{ foreground: 'ffa657', token: 'variable.other.constant' },
|
||||
{ token: 'string.value.json', foreground: 'a5d6ff' }, // string values in JSON
|
||||
{ token: 'keyword.json', foreground: '79c0ff' } // true, false, null in JSON
|
||||
],
|
||||
colors: {
|
||||
'editor.foreground': '#e6edf3',
|
||||
'editor.background': getCssColor('surface-input', { format: 'hex-github-dark' }),
|
||||
'editor.selectionBackground': '#3392FF44',
|
||||
'editor.inactiveSelectionBackground': '#3392FF22',
|
||||
'editor.lineHighlightBackground': '#161b22',
|
||||
'editorCursor.foreground': '#e6edf3',
|
||||
'editorWhitespace.foreground': '#484f58',
|
||||
'editorIndentGuide.background1': '#21262d',
|
||||
'editorIndentGuide.activeBackground1': '#30363d',
|
||||
'editorLineNumber.foreground': '#6e7681',
|
||||
'editorLineNumber.activeForeground': '#e6edf3'
|
||||
}
|
||||
})
|
||||
|
||||
meditor.defineTheme('myTheme', {
|
||||
base: 'vs',
|
||||
inherit: true,
|
||||
@@ -262,11 +313,7 @@ export async function initializeVscode(caller?: string, htmlContainer?: HTMLElem
|
||||
}
|
||||
})
|
||||
|
||||
if (document.documentElement.classList.contains('dark')) {
|
||||
meditor.setTheme('nord')
|
||||
} else {
|
||||
meditor.setTheme('myTheme')
|
||||
}
|
||||
meditor.setTheme(getEditorTheme())
|
||||
} catch (e) {
|
||||
console.error('Failed to initialize monaco services', e)
|
||||
} finally {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
export type DarkModeVariant = 'default' | 'github'
|
||||
|
||||
const DARK_MODE_VARIANT_KEY = 'dark-mode-variant'
|
||||
|
||||
export function getDarkModeVariant(): DarkModeVariant {
|
||||
return window.localStorage.getItem(DARK_MODE_VARIANT_KEY) === 'github' ? 'github' : 'default'
|
||||
}
|
||||
|
||||
// The variant currently applied to the DOM (the `github-dark` class) — the
|
||||
// runtime source of truth, as opposed to the persisted preference above.
|
||||
export function getAppliedDarkModeVariant(): DarkModeVariant {
|
||||
return typeof document !== 'undefined' &&
|
||||
document.documentElement.classList.contains('github-dark')
|
||||
? 'github'
|
||||
: 'default'
|
||||
}
|
||||
|
||||
// The `github-dark` class only takes effect when `dark` is also present (see tailwind.config.cjs).
|
||||
// It lives on <html> permanently so it survives the many independent `dark` toggle sites.
|
||||
export function applyDarkModeVariant(variant: DarkModeVariant = getDarkModeVariant()): void {
|
||||
document.documentElement.classList.toggle('github-dark', variant === 'github')
|
||||
}
|
||||
|
||||
export function setDarkModeVariant(variant: DarkModeVariant): void {
|
||||
window.localStorage.setItem(DARK_MODE_VARIANT_KEY, variant)
|
||||
applyDarkModeVariant(variant)
|
||||
}
|
||||
@@ -2029,6 +2029,8 @@ export function validateRetryConfig(retry: Retry | undefined): string | null {
|
||||
}
|
||||
export type CssColor = keyof (typeof tokensFile)['tokens']['light']
|
||||
import tokensFile from './assets/tokens/tokens.json'
|
||||
// Hand-authored variant kept out of the Figma-generated tokens.json.
|
||||
import githubDarkTokens from './assets/tokens/githubDark.json'
|
||||
import { darkModeName, lightModeName } from './assets/tokens/colorTokensConfig'
|
||||
import BarsStaggered from './components/icons/BarsStaggered.svelte'
|
||||
import { GitIcon } from './components/icons'
|
||||
@@ -2041,7 +2043,7 @@ export function getCssColor(
|
||||
format = 'css-var'
|
||||
}: {
|
||||
alpha?: number
|
||||
format?: 'css-var' | 'hex-dark' | 'hex-light'
|
||||
format?: 'css-var' | 'hex-dark' | 'hex-light' | 'hex-github-dark'
|
||||
}
|
||||
): string {
|
||||
if (format === 'hex-light') {
|
||||
@@ -2050,6 +2052,9 @@ export function getCssColor(
|
||||
if (format === 'hex-dark') {
|
||||
return tokensFile.tokens[darkModeName][color]
|
||||
}
|
||||
if (format === 'hex-github-dark') {
|
||||
return githubDarkTokens[color]
|
||||
}
|
||||
return `rgb(var(--color-${color}) / ${alpha})`
|
||||
}
|
||||
|
||||
|
||||
@@ -1052,7 +1052,7 @@
|
||||
style:width="{railWidth}rem"
|
||||
>
|
||||
<div
|
||||
class="flex-1 flex flex-col min-h-0 h-screen shadow-[inset_-1px_0_0_0_rgb(var(--color-border-light))] dark:shadow-[inset_-1px_0_0_0_#374151]"
|
||||
class="flex-1 flex flex-col min-h-0 h-screen shadow-[inset_-1px_0_0_0_rgb(var(--color-border-light))] dark:shadow-[inset_-1px_0_0_0_#374151] [html.github-dark_&]:shadow-[inset_-1px_0_0_0_rgb(var(--color-border-light))]"
|
||||
style:background-color={darkMode ? SIDEBAR_BG_DARK : SIDEBAR_BG}
|
||||
>
|
||||
{#if !isCollapsed}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
// import EditorTheme from '$lib/components/EditorTheme.svelte'
|
||||
import { computeDrift } from '$lib/forLater'
|
||||
import { setLicense } from '$lib/enterpriseUtils'
|
||||
import { applyDarkModeVariant } from '$lib/darkModeVariant'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
interface Props {
|
||||
children?: import('svelte').Snippet
|
||||
@@ -255,6 +256,8 @@
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark')
|
||||
}
|
||||
|
||||
applyDarkModeVariant()
|
||||
</script>
|
||||
|
||||
{@render children?.()}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$lib/navigation'
|
||||
import { applyDarkModeVariant } from '$lib/darkModeVariant'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import { onMount } from 'svelte'
|
||||
import { UserService, WorkspaceService } from '$lib/gen'
|
||||
@@ -132,6 +133,8 @@
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark')
|
||||
}
|
||||
// This route bypasses the (root) layout, so restore the variant class too.
|
||||
applyDarkModeVariant()
|
||||
|
||||
function closeUponLoginSuccess() {
|
||||
const message = { type: 'success' }
|
||||
|
||||
@@ -4,10 +4,18 @@ const {
|
||||
} = require('./src/lib/components/apps/editor/componentsPanel/tailwindUtils')
|
||||
const { zIndexes } = require('./src/lib/zIndexes')
|
||||
|
||||
const figmaTokens = makeRgb(require('./src/lib/assets/tokens/tokens.json'))
|
||||
const rawTokens = require('./src/lib/assets/tokens/tokens.json')
|
||||
// `github-dark` is a hand-authored variant kept out of the Figma-generated
|
||||
// tokens.json (a re-export would drop it); merge it in before the rgb pass.
|
||||
rawTokens.tokens['github-dark'] = require('./src/lib/assets/tokens/githubDark.json')
|
||||
const figmaTokens = makeRgb(rawTokens)
|
||||
const { darkModeName, lightModeName } = require('./src/lib/assets/tokens/colorTokensConfig')
|
||||
|
||||
const tokens = { dark: figmaTokens.tokens[darkModeName], light: figmaTokens.tokens[lightModeName] }
|
||||
const tokens = {
|
||||
dark: figmaTokens.tokens[darkModeName],
|
||||
light: figmaTokens.tokens[lightModeName],
|
||||
githubDark: figmaTokens.tokens['github-dark']
|
||||
}
|
||||
const primitives = figmaTokens.primitives.light
|
||||
|
||||
// Helper function to create color definition based on whether the value contains alpha
|
||||
@@ -571,6 +579,12 @@ const config = {
|
||||
...Object.entries(tokens.dark).map(([key, value]) => [`--color-${key}`, value]),
|
||||
...Object.entries(tokens.light).map(([key, value]) => [`--color-${key}-inverse`, value])
|
||||
])
|
||||
// GitHub dark variant: overrides the default dark variables when the
|
||||
// `github-dark` class is present alongside `dark` (see html.&.dark.github-dark below).
|
||||
let githubDarkColorVariables = Object.fromEntries([
|
||||
...Object.entries(tokens.githubDark).map(([key, value]) => [`--color-${key}`, value]),
|
||||
...Object.entries(tokens.light).map(([key, value]) => [`--color-${key}-inverse`, value])
|
||||
])
|
||||
|
||||
addBase({
|
||||
html: {
|
||||
@@ -600,10 +614,31 @@ const config = {
|
||||
|
||||
...darkColorVariables,
|
||||
|
||||
// Sidebar rail chrome — consumed via var(--sidebar-bg-dark) (see sidebarChrome.ts).
|
||||
'--sidebar-bg-dark': '#1e232e',
|
||||
|
||||
'--vscode-editorSuggestWidget-background': '#252526',
|
||||
'--vscode-editorHoverWidget-foreground': '#cccccc',
|
||||
'--vscode-editorHoverWidget-border': '#454545',
|
||||
'--vscode-editorHoverWidget-statusBarBackground': '#2c2c2d'
|
||||
},
|
||||
'&.dark.github-dark': {
|
||||
backgroundColor: `rgb(${tokens.githubDark['surface-primary']})`,
|
||||
color: `rgb(${tokens.githubDark['text-primary']})`,
|
||||
|
||||
...githubDarkColorVariables,
|
||||
|
||||
// GitHub's canvas.inset — the authentic recessed surface, sitting below
|
||||
// the canvas (#0d1117) like the sidebar-vs-primary relationship in the
|
||||
// default dark theme.
|
||||
'--sidebar-bg-dark': '#010409',
|
||||
|
||||
// Monaco popup chrome — the &.dark defaults above are VS Code's greys;
|
||||
// override so the widgets match the GitHub Dark palette in this variant.
|
||||
'--vscode-editorSuggestWidget-background': '#1c2128',
|
||||
'--vscode-editorHoverWidget-foreground': '#c9d1d9',
|
||||
'--vscode-editorHoverWidget-border': '#30363d',
|
||||
'--vscode-editorHoverWidget-statusBarBackground': '#1c2128'
|
||||
}
|
||||
},
|
||||
h1: {
|
||||
@@ -842,20 +877,20 @@ const config = {
|
||||
'colors.gray.600'
|
||||
)})`
|
||||
},
|
||||
// Drive pane/splitter chrome off the theme CSS variables (not literal token
|
||||
// values) so every theme — light, dark, and the GitHub dark variant — is
|
||||
// covered by a single rule. A `.dark`-specific literal here would freeze the
|
||||
// default dark color and ignore the variant.
|
||||
'.splitpanes__pane': {
|
||||
backgroundColor: `rgb(${tokens.light['surface-primary']})` + ' !important',
|
||||
overflow: 'auto !important'
|
||||
},
|
||||
'.dark .splitpanes__pane': {
|
||||
backgroundColor: `rgb(${tokens.dark['surface-primary']})` + ' !important',
|
||||
backgroundColor: 'rgb(var(--color-surface-primary)) !important',
|
||||
overflow: 'auto !important'
|
||||
},
|
||||
'.splitpanes__splitter': {
|
||||
backgroundColor: `rgb(${tokens.light['border-light']})` + ' !important',
|
||||
backgroundColor: 'rgb(var(--color-border-light)) !important',
|
||||
margin: '0 !important',
|
||||
border: 'none !important',
|
||||
'&::after': {
|
||||
backgroundColor: `rgb(${tokens.light['border-light']})` + ' !important',
|
||||
backgroundColor: 'rgb(var(--color-border-light)) !important',
|
||||
margin: '0 !important',
|
||||
transform: 'none !important',
|
||||
transition: 'opacity 200ms !important',
|
||||
@@ -868,12 +903,6 @@ const config = {
|
||||
zIndex: '1001 !important'
|
||||
}
|
||||
},
|
||||
'.dark .splitpanes__splitter': {
|
||||
backgroundColor: `rgb(${tokens.dark['border-light']})` + ' !important',
|
||||
'&::after': {
|
||||
backgroundColor: `rgb(${tokens.dark['border-light']})` + ' !important'
|
||||
}
|
||||
},
|
||||
'.splitpanes--vertical>.splitpanes__splitter': {
|
||||
width: '1px !important',
|
||||
'&::before': {
|
||||
|
||||
Reference in New Issue
Block a user