Swap out primary UI color for Zoo brand blue, add theme color setting to control its hue (#2017)

* Add a setting for themeColor

* Add primary-color to Tailwind, driven by themeColor setting

* Get rid of most uses of "energy" colors

* Change out the rest of the energy colors

* Tweak NetworkHealthIndicator light mode checkmarks

* Handful of other CSS tweaks while I'm here:
- remove the AppHeader bg and border
- pane margins
- better dark mode button styles

* Make Zoo logomark a badge

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* Re-run CI post-snapshots

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* Retrigger CI

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Frank Noirot
2024-04-05 00:59:02 -04:00
committed by GitHub
parent 233f81a879
commit d85781ef99
44 changed files with 203 additions and 134 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -153,7 +153,7 @@ export function App() {
<ModalContainer /> <ModalContainer />
<Resizable <Resizable
className={ className={
'pointer-events-none h-full flex flex-col flex-1 z-10 my-5 ml-5 pr-1 transition-opacity transition-duration-75 ' + 'pointer-events-none h-full flex flex-col flex-1 z-10 my-2 ml-2 pr-1 transition-opacity transition-duration-75 ' +
+paneOpacity +paneOpacity
} }
defaultSize={{ defaultSize={{
@ -166,7 +166,7 @@ export function App() {
maxHeight={'auto'} maxHeight={'auto'}
handleClasses={{ handleClasses={{
right: right:
'hover:bg-chalkboard-10/50 bg-transparent transition-colors duration-75 transition-ease-out delay-100 ' + 'hover:bg-chalkboard-10 hover:dark:bg-chalkboard-110 bg-transparent transition-colors duration-75 transition-ease-out delay-100 ' +
(buttonDownInStream || onboardingStatus.current === 'camera' (buttonDownInStream || onboardingStatus.current === 'camera'
? 'pointer-events-none ' ? 'pointer-events-none '
: 'pointer-events-auto'), : 'pointer-events-auto'),
@ -202,7 +202,7 @@ export function App() {
theme={editorTheme} theme={editorTheme}
open={openPanes.includes('kclErrors')} open={openPanes.includes('kclErrors')}
title="KCL Errors" title="KCL Errors"
iconClassNames={{ icon: 'group-open:text-destroy-30' }} iconClassNames={{ bg: 'group-open:bg-destroy-70' }}
/> />
</section> </section>
</div> </div>

View File

@ -18,8 +18,12 @@ export const Toolbar = () => {
const { commandBarSend } = useCommandsContext() const { commandBarSend } = useCommandsContext()
const { state, send, context } = useModelingContext() const { state, send, context } = useModelingContext()
const toolbarButtonsRef = useRef<HTMLUListElement>(null) const toolbarButtonsRef = useRef<HTMLUListElement>(null)
const iconClassName =
'group-disabled:text-chalkboard-50 group-enabled:group-hover:!text-chalkboard-10 group-pressed:!text-chalkboard-10'
const bgClassName = const bgClassName =
'group-enabled:group-hover:bg-energy-10 group-pressed:bg-energy-10 dark:group-enabled:group-hover:bg-chalkboard-80 dark:group-pressed:bg-chalkboard-80' 'group-disabled:!bg-transparent group-enabled:group-hover:bg-primary group-pressed:bg-primary'
const buttonClassName =
'bg-chalkboard-10 dark:bg-chalkboard-100 hover:bg-chalkboard-10 dark:hover:bg-chalkboard-100'
const pathId = useMemo(() => { const pathId = useMemo(() => {
if (!isSingleCursorInPipe(context.selectionRanges, kclManager.ast)) { if (!isSingleCursorInPipe(context.selectionRanges, kclManager.ast)) {
return false return false
@ -64,12 +68,14 @@ export const Toolbar = () => {
{state.nextEvents.includes('Enter sketch') && ( {state.nextEvents.includes('Enter sketch') && (
<li className="contents"> <li className="contents">
<ActionButton <ActionButton
className={buttonClassName}
Element="button" Element="button"
onClick={() => onClick={() =>
send({ type: 'Enter sketch', data: { forceNewSketch: true } }) send({ type: 'Enter sketch', data: { forceNewSketch: true } })
} }
icon={{ icon={{
icon: 'sketch', icon: 'sketch',
iconClassName,
bgClassName, bgClassName,
}} }}
disabled={disableAllButtons} disabled={disableAllButtons}
@ -81,10 +87,12 @@ export const Toolbar = () => {
{state.nextEvents.includes('Enter sketch') && pathId && ( {state.nextEvents.includes('Enter sketch') && pathId && (
<li className="contents"> <li className="contents">
<ActionButton <ActionButton
className={buttonClassName}
Element="button" Element="button"
onClick={() => send({ type: 'Enter sketch' })} onClick={() => send({ type: 'Enter sketch' })}
icon={{ icon={{
icon: 'sketch', icon: 'sketch',
iconClassName,
bgClassName, bgClassName,
}} }}
disabled={disableAllButtons} disabled={disableAllButtons}
@ -96,10 +104,12 @@ export const Toolbar = () => {
{state.nextEvents.includes('Cancel') && !state.matches('idle') && ( {state.nextEvents.includes('Cancel') && !state.matches('idle') && (
<li className="contents"> <li className="contents">
<ActionButton <ActionButton
className={buttonClassName}
Element="button" Element="button"
onClick={() => send({ type: 'Cancel' })} onClick={() => send({ type: 'Cancel' })}
icon={{ icon={{
icon: 'arrowLeft', icon: 'arrowLeft',
iconClassName,
bgClassName, bgClassName,
}} }}
disabled={disableAllButtons} disabled={disableAllButtons}
@ -112,6 +122,7 @@ export const Toolbar = () => {
<> <>
<li className="contents" key="line-button"> <li className="contents" key="line-button">
<ActionButton <ActionButton
className={buttonClassName}
Element="button" Element="button"
onClick={() => onClick={() =>
state?.matches('Sketch.Line tool') state?.matches('Sketch.Line tool')
@ -119,9 +130,9 @@ export const Toolbar = () => {
: send('Equip Line tool') : send('Equip Line tool')
} }
aria-pressed={state?.matches('Sketch.Line tool')} aria-pressed={state?.matches('Sketch.Line tool')}
className="pressed:bg-energy-10/20 dark:pressed:bg-energy-80"
icon={{ icon={{
icon: 'line', icon: 'line',
iconClassName,
bgClassName, bgClassName,
}} }}
disabled={disableAllButtons} disabled={disableAllButtons}
@ -131,6 +142,7 @@ export const Toolbar = () => {
</li> </li>
<li className="contents" key="tangential-arc-button"> <li className="contents" key="tangential-arc-button">
<ActionButton <ActionButton
className={buttonClassName}
Element="button" Element="button"
onClick={() => onClick={() =>
state.matches('Sketch.Tangential arc to') state.matches('Sketch.Tangential arc to')
@ -138,9 +150,9 @@ export const Toolbar = () => {
: send('Equip tangential arc to') : send('Equip tangential arc to')
} }
aria-pressed={state.matches('Sketch.Tangential arc to')} aria-pressed={state.matches('Sketch.Tangential arc to')}
className="pressed:bg-energy-10/20 dark:pressed:bg-energy-80"
icon={{ icon={{
icon: 'arc', icon: 'arc',
iconClassName,
bgClassName, bgClassName,
}} }}
disabled={ disabled={
@ -179,8 +191,8 @@ export const Toolbar = () => {
.map((eventName) => ( .map((eventName) => (
<li className="contents" key={eventName}> <li className="contents" key={eventName}>
<ActionButton <ActionButton
className={buttonClassName}
Element="button" Element="button"
className="text-sm"
key={eventName} key={eventName}
onClick={() => send(eventName)} onClick={() => send(eventName)}
disabled={ disabled={
@ -191,6 +203,7 @@ export const Toolbar = () => {
title={eventName} title={eventName}
icon={{ icon={{
icon: 'line', icon: 'line',
iconClassName,
bgClassName, bgClassName,
}} }}
> >
@ -203,8 +216,8 @@ export const Toolbar = () => {
{state.matches('idle') && ( {state.matches('idle') && (
<li className="contents"> <li className="contents">
<ActionButton <ActionButton
className={buttonClassName}
Element="button" Element="button"
className="text-sm"
onClick={() => onClick={() =>
commandBarSend({ commandBarSend({
type: 'Find and select command', type: 'Find and select command',
@ -219,6 +232,7 @@ export const Toolbar = () => {
} }
icon={{ icon={{
icon: 'extrude', icon: 'extrude',
iconClassName,
bgClassName, bgClassName,
}} }}
> >
@ -231,14 +245,14 @@ export const Toolbar = () => {
} }
return ( return (
<div className="max-w-full flex items-stretch rounded-l-sm rounded-r-full bg-chalkboard-10 dark:bg-chalkboard-100 relative"> <div className="max-w-full flex items-stretch rounded-l-sm rounded-r-full bg-chalkboard-10/80 dark:bg-chalkboard-110/70 relative">
<menu className="flex-1 pl-1 pr-2 py-0 overflow-hidden rounded-l-sm whitespace-nowrap bg-chalkboard-10 dark:bg-chalkboard-100 border-solid border border-energy-10 dark:border-chalkboard-90 border-r-0"> <menu className="flex-1 pl-1 pr-2 py-0 overflow-hidden rounded-l-sm whitespace-nowrap border-solid border border-primary/30 dark:border-chalkboard-90 border-r-0">
<ToolbarButtons /> <ToolbarButtons />
</menu> </menu>
<ActionButton <ActionButton
Element="button" Element="button"
onClick={() => commandBarSend({ type: 'Open' })} onClick={() => commandBarSend({ type: 'Open' })}
className="rounded-r-full pr-4 self-stretch border-energy-10 hover:border-energy-10 dark:border-chalkboard-80 bg-energy-10/50 hover:bg-energy-10 dark:bg-chalkboard-80 dark:text-energy-10" className="rounded-r-full pr-4 self-stretch border-primary/30 hover:border-primary dark:border-chalkboard-80 dark:bg-chalkboard-80 text-primary"
> >
{platform === 'darwin' ? '⌘K' : 'Ctrl+/'} {platform === 'darwin' ? '⌘K' : 'Ctrl+/'}
</ActionButton> </ActionButton>

View File

@ -1,11 +1,21 @@
:root { :root {
--primary-hue: 264.48;
--primary-chroma: 0.2167;
--primary-lightness: 60%;
--_primary: var(--primary-lightness) var(--primary-chroma)
var(--primary-hue, 264.48);
--primary: oklch(
var(--primary-lightness) var(--primary-chroma) var(--primary-hue, 264.48) /
var(--opacity, 1)
);
/* /*
Generated using Catmosphere Theme Builder Generated using Catmosphere Theme Builder
by KittyCAD by KittyCAD
https://catmosphere-theme-builder.vercel.app/?colors=%5B%7B%22from%22:%7B%22l%22:1,%22c%22:0.01,%22h%22:78%7D,%22to%22:%7B%22l%22:0.065,%22c%22:0.05,%22h%22:182.6%7D,%22stops%22:10,%22steps%22:12%7D,%7B%22from%22:%7B%22l%22:1,%22c%22:0.45,%22h%22:122.4%7D,%22to%22:%7B%22l%22:0.13,%22c%22:0.031,%22h%22:137.2%7D,%22stops%22:10,%22steps%22:12%7D,%7B%22from%22:%7B%22l%22:1,%22c%22:0.13,%22h%22:176%7D,%22to%22:%7B%22l%22:0.116,%22c%22:0.097,%22h%22:213.1%7D,%22stops%22:10,%22steps%22:12%7D,%7B%22from%22:%7B%22l%22:1,%22c%22:0.169,%22h%22:144.4%7D,%22to%22:%7B%22l%22:0.12,%22c%22:0.45,%22h%22:132.7%7D,%22steps%22:12%7D,%7B%22from%22:%7B%22l%22:1,%22c%22:0.087,%22h%22:261.6%7D,%22to%22:%7B%22l%22:0.22,%22c%22:0.084,%22h%22:275.5%7D,%22steps%22:12,%22uuid%22:%227tpx9pf1zd6%22%7D,%7B%22from%22:%7B%22l%22:0.954,%22c%22:0.108,%22h%22:280.6%7D,%22to%22:%7B%22l%22:0.166,%22c%22:0.188,%22h%22:263.8%7D,%22steps%22:12,%22uuid%22:%22vu652mebd3%22%7D,%7B%22from%22:%7B%22l%22:1,%22c%22:0.115,%22h%22:0%7D,%22to%22:%7B%22l%22:0.096,%22c%22:0.261,%22h%22:302%7D,%22steps%22:12%7D,%7B%22from%22:%7B%22l%22:1,%22c%22:0.185,%22h%22:19.8%7D,%22to%22:%7B%22l%22:0.368,%22c%22:0.45,%22h%22:9.4%7D,%22steps%22:8,%22uuid%22:%22g05inkd34l%22%7D,%7B%22from%22:%7B%22l%22:0.912,%22c%22:0.139,%22h%22:87%7D,%22to%22:%7B%22l%22:0.502,%22c%22:0.45,%22h%22:97.7%7D,%22steps%22:8,%22uuid%22:%22l892hcw4ef%22%7D,%7B%22from%22:%7B%22l%22:0.89,%22c%22:0.16,%22h%22:143.4%7D,%22to%22:%7B%22l%22:0.466,%22c%22:0.208,%22h%22:147.7%7D,%22steps%22:8,%22uuid%22:%22hkd09y9ov4h%22%7D%5D https://catmosphere-theme-builder.vercel.app/?colors=%5B%7B%22from%22:%7B%22l%22:1,%22c%22:0.01,%22h%22:78%7D,%22to%22:%7B%22l%22:0.065,%22c%22:0.05,%22h%22:182.6%7D,%22stops%22:10,%22steps%22:12%7D,%7B%22from%22:%7B%22l%22:1,%22c%22:0.45,%22h%22:122.4%7D,%22to%22:%7B%22l%22:0.13,%22c%22:0.031,%22h%22:137.2%7D,%22stops%22:10,%22steps%22:12%7D,%7B%22from%22:%7B%22l%22:1,%22c%22:0.13,%22h%22:176%7D,%22to%22:%7B%22l%22:0.116,%22c%22:0.097,%22h%22:213.1%7D,%22stops%22:10,%22steps%22:12%7D,%7B%22from%22:%7B%22l%22:1,%22c%22:0.169,%22h%22:144.4%7D,%22to%22:%7B%22l%22:0.12,%22c%22:0.45,%22h%22:132.7%7D,%22steps%22:12%7D,%7B%22from%22:%7B%22l%22:1,%22c%22:0.087,%22h%22:261.6%7D,%22to%22:%7B%22l%22:0.22,%22c%22:0.084,%22h%22:275.5%7D,%22steps%22:12,%22uuid%22:%227tpx9pf1zd6%22%7D,%7B%22from%22:%7B%22l%22:0.954,%22c%22:0.108,%22h%22:280.6%7D,%22to%22:%7B%22l%22:0.166,%22c%22:0.188,%22h%22:263.8%7D,%22steps%22:12,%22uuid%22:%22vu652mebd3%22%7D,%7B%22from%22:%7B%22l%22:1,%22c%22:0.115,%22h%22:0%7D,%22to%22:%7B%22l%22:0.096,%22c%22:0.261,%22h%22:302%7D,%22steps%22:12%7D,%7B%22from%22:%7B%22l%22:1,%22c%22:0.185,%22h%22:19.8%7D,%22to%22:%7B%22l%22:0.368,%22c%22:0.45,%22h%22:9.4%7D,%22steps%22:8,%22uuid%22:%22g05inkd34l%22%7D,%7B%22from%22:%7B%22l%22:0.912,%22c%22:0.139,%22h%22:87%7D,%22to%22:%7B%22l%22:0.502,%22c%22:0.45,%22h%22:97.7%7D,%22steps%22:8,%22uuid%22:%22l892hcw4ef%22%7D,%7B%22from%22:%7B%22l%22:0.89,%22c%22:0.16,%22h%22:143.4%7D,%22to%22:%7B%22l%22:0.466,%22c%22:0.208,%22h%22:147.7%7D,%22steps%22:8,%22uuid%22:%22hkd09y9ov4h%22%7D%5D
*/ */
/* Chalkboard */ /* Chalkboard */
--chalkboard-10: oklch(99.7% 0.008766 102.8deg); --chalkboard-10: oklch(99.9% 0.003766 102.8deg);
--chalkboard-20: oklch(91.34% 0.009353 109deg); --chalkboard-20: oklch(91.34% 0.009353 109deg);
--chalkboard-30: oklch(82.99% 0.00994 115.2deg); --chalkboard-30: oklch(82.99% 0.00994 115.2deg);
--chalkboard-40: oklch(74.63% 0.01053 121.4deg); --chalkboard-40: oklch(74.63% 0.01053 121.4deg);

View File

@ -30,9 +30,9 @@ export const ActionIcon = ({
children, children,
}: ActionIconProps) => { }: ActionIconProps) => {
// By default, we reverse the icon color and background color in dark mode // By default, we reverse the icon color and background color in dark mode
const computedIconClassName = `h-auto dark:text-energy-10 !group-disabled:text-chalkboard-60 !group-disabled:text-chalkboard-60 ${iconClassName}` const computedIconClassName = `h-auto text-primary dark:text-current !group-disabled:text-chalkboard-60 !group-disabled:text-chalkboard-60 ${iconClassName}`
const computedBgClassName = `bg-chalkboard-20 dark:bg-chalkboard-90 !group-disabled:bg-chalkboard-30 !dark:group-disabled:bg-chalkboard-80 ${bgClassName}` const computedBgClassName = `bg-primary/10 dark:bg-chalkboard-90 !group-disabled:bg-chalkboard-30 !dark:group-disabled:bg-chalkboard-80 ${bgClassName}`
return ( return (
<div <div

View File

@ -33,7 +33,7 @@ export const AppHeader = ({
className={ className={
'w-full grid ' + 'w-full grid ' +
styles.header + styles.header +
' overlaid-panes sticky top-0 z-20 py-1 px-2 bg-chalkboard-10/70 dark:bg-chalkboard-100/50 border-b dark:border-b-2 border-chalkboard-30 dark:border-chalkboard-90 items-center ' + ' overlaid-panes sticky top-0 z-20 px-2 items-center ' +
className className
} }
> >
@ -53,13 +53,13 @@ export const AppHeader = ({
className="text-sm self-center flex items-center w-fit gap-3" className="text-sm self-center flex items-center w-fit gap-3"
> >
Command Palette{' '} Command Palette{' '}
<kbd className="bg-energy-10/50 dark:bg-chalkboard-100 dark:text-energy-10 inline-block px-1 py-0.5 border-energy-10 dark:border-chalkboard-90"> <kbd className="bg-primary/10 dark:bg-chalkboard-100 dark:text-primary inline-block px-1 py-0.5 border-primary dark:border-chalkboard-90">
{platform === 'darwin' ? '⌘K' : 'Ctrl+/'} {platform === 'darwin' ? '⌘K' : 'Ctrl+/'}
</kbd> </kbd>
</ActionButton> </ActionButton>
)} )}
</div> </div>
<div className="flex items-center gap-1 ml-auto"> <div className="flex items-center gap-1 py-1 ml-auto">
{/* If there are children, show them, otherwise show User menu */} {/* If there are children, show them, otherwise show User menu */}
{children || ( {children || (
<> <>

View File

@ -1,13 +1,13 @@
.button { .button {
@apply flex justify-between items-center gap-2 px-2 py-1 text-left border-none rounded-sm; @apply flex justify-between items-center gap-2 px-2 py-1 text-left border-none rounded-sm;
@apply font-mono !no-underline text-xs font-bold select-none text-chalkboard-90; @apply font-mono !no-underline text-xs font-bold select-none text-chalkboard-90;
@apply ui-active:bg-energy-10/50 ui-active:text-inherit; @apply ui-active:bg-primary/10 ui-active:text-primary ui-active:text-inherit;
@apply transition-colors ease-out; @apply transition-colors ease-out;
} }
:global(.dark) .button { :global(.dark) .button {
@apply text-chalkboard-30; @apply !text-chalkboard-30;
@apply ui-active:bg-chalkboard-80 ui-active:text-energy-10; @apply ui-active:bg-chalkboard-90;
} }
.button small { .button small {

View File

@ -30,12 +30,12 @@ export const CodeMenu = ({ children }: PropsWithChildren) => {
className="p-1" className="p-1"
size="sm" size="sm"
bgClassName={ bgClassName={
'bg-chalkboard-20 dark:bg-chalkboard-110 hover:bg-energy-10/50 hover:dark:bg-chalkboard-90 ui-active:bg-chalkboard-80 ui-active:dark:bg-chalkboard-90 rounded-sm' '!bg-transparent hover:!bg-primary/10 hover:dark:!bg-chalkboard-100 ui-active:!bg-primary/10 dark:ui-active:!bg-chalkboard-100 rounded-sm'
} }
iconClassName={'text-chalkboard-90 dark:text-chalkboard-40'} iconClassName={'!text-chalkboard-90 dark:!text-chalkboard-40'}
/> />
</Menu.Button> </Menu.Button>
<Menu.Items className="absolute right-0 left-auto w-72 flex flex-col gap-1 divide-y divide-chalkboard-20 dark:divide-chalkboard-70 align-stretch px-0 py-1 bg-chalkboard-10 dark:bg-chalkboard-90 rounded-sm shadow-lg border border-solid border-chalkboard-20/50 dark:border-chalkboard-80/50"> <Menu.Items className="absolute right-0 left-auto w-72 flex flex-col gap-1 divide-y divide-chalkboard-20 dark:divide-chalkboard-70 align-stretch px-0 py-1 bg-chalkboard-10 dark:bg-chalkboard-100 rounded-sm shadow-lg border border-solid border-chalkboard-20/50 dark:border-chalkboard-80/50">
<Menu.Item> <Menu.Item>
<button <button
onClick={() => kclManager.format()} onClick={() => kclManager.format()}

View File

@ -3,6 +3,11 @@
@apply bg-chalkboard-10/70 backdrop-blur-sm; @apply bg-chalkboard-10/70 backdrop-blur-sm;
} }
.header::before,
.header::-webkit-details-marker {
display: none;
}
:global(.dark) .panel { :global(.dark) .panel {
@apply bg-chalkboard-110/50 backdrop-blur-0; @apply bg-chalkboard-110/50 backdrop-blur-0;
} }
@ -11,7 +16,7 @@
@apply sticky top-0 z-10 cursor-pointer; @apply sticky top-0 z-10 cursor-pointer;
@apply flex items-center justify-between gap-2 w-full p-2; @apply flex items-center justify-between gap-2 w-full p-2;
@apply font-mono text-xs font-bold select-none text-chalkboard-90; @apply font-mono text-xs font-bold select-none text-chalkboard-90;
@apply bg-chalkboard-20; @apply bg-chalkboard-10;
} }
.header:not(:last-of-type) { .header:not(:last-of-type) {

View File

@ -30,11 +30,11 @@ export const PanelHeader = ({
className="p-1" className="p-1"
size="sm" size="sm"
bgClassName={ bgClassName={
'dark:!bg-chalkboard-100 group-open:bg-chalkboard-80 dark:group-open:!bg-chalkboard-90 border border-transparent dark:group-open:border-chalkboard-60 rounded-sm ' + 'dark:!bg-transparent group-open:bg-primary dark:group-open:!bg-primary rounded-sm ' +
(iconClassNames?.bg || '') (iconClassNames?.bg || '')
} }
iconClassName={ iconClassName={
'group-open:text-energy-10 ' + (iconClassNames?.icon || '') 'group-open:text-chalkboard-10 ' + (iconClassNames?.icon || '')
} }
/> />
{title} {title}

View File

@ -141,7 +141,7 @@ function CommandArgOptionInput({
<Combobox.Option <Combobox.Option
key={option.name} key={option.name}
value={option} value={option}
className="flex items-center gap-2 px-4 py-1 first:mt-2 last:mb-2 ui-active:bg-energy-10/50 dark:ui-active:bg-chalkboard-90" className="flex items-center gap-2 px-4 py-1 first:mt-2 last:mb-2 ui-active:bg-primary/10 dark:ui-active:bg-chalkboard-90"
> >
<p className="flex-grow">{option.name} </p> <p className="flex-grow">{option.name} </p>
{option.value === currentOption?.value && ( {option.value === currentOption?.value && (

View File

@ -104,7 +104,7 @@ function CommandBarHeader({ children }: React.PropsWithChildren<{}>) {
key={argName} key={argName}
className={`relative w-fit px-2 py-1 rounded-sm flex gap-2 items-center border ${ className={`relative w-fit px-2 py-1 rounded-sm flex gap-2 items-center border ${
argName === currentArgument?.name argName === currentArgument?.name
? 'disabled:bg-energy-10/50 dark:disabled:bg-energy-10/20 disabled:border-energy-10 dark:disabled:border-energy-10 disabled:text-chalkboard-100 dark:disabled:text-chalkboard-10' ? 'disabled:bg-primary/10 dark:disabled:bg-primary/20 disabled:border-primary dark:disabled:border-primary disabled:text-chalkboard-100 dark:disabled:text-chalkboard-10'
: 'bg-chalkboard-20/50 dark:bg-chalkboard-80/50 border-chalkboard-20 dark:border-chalkboard-80' : 'bg-chalkboard-20/50 dark:bg-chalkboard-80/50 border-chalkboard-20 dark:border-chalkboard-80'
}`} }`}
> >
@ -129,7 +129,7 @@ function CommandBarHeader({ children }: React.PropsWithChildren<{}>) {
) )
) : null} ) : null}
{showShortcuts && ( {showShortcuts && (
<small className="absolute -top-[1px] right-full translate-x-1/2 px-0.5 rounded-sm bg-chalkboard-80 text-chalkboard-10 dark:bg-energy-10 dark:text-chalkboard-100"> <small className="absolute -top-[1px] right-full translate-x-1/2 px-0.5 rounded-sm bg-chalkboard-80 text-chalkboard-10 dark:bg-primary dark:text-chalkboard-100">
<span className="sr-only">Hotkey: </span> <span className="sr-only">Hotkey: </span>
{i + 1} {i + 1}
</small> </small>
@ -174,12 +174,11 @@ function ReviewingButton() {
autoFocus autoFocus
type="submit" type="submit"
form="review-form" form="review-form"
className="w-fit !p-0 rounded-sm border !border-chalkboard-100 dark:!border-energy-10 hover:shadow" className="w-fit !p-0 rounded-sm border !border-primary hover:shadow"
icon={{ icon={{
icon: 'checkmark', icon: 'checkmark',
bgClassName: bgClassName: 'p-1 rounded-sm !bg-primary hover:brightness-110',
'p-1 rounded-sm !bg-chalkboard-100 hover:!bg-chalkboard-110 dark:!bg-energy-20 dark:hover:!bg-energy-10', iconClassName: '!text-chalkboard-10',
iconClassName: '!text-energy-10 dark:!text-chalkboard-100',
}} }}
> >
<span className="sr-only">Submit command</span> <span className="sr-only">Submit command</span>
@ -193,10 +192,11 @@ function GatheringArgsButton() {
Element="button" Element="button"
type="submit" type="submit"
form="arg-form" form="arg-form"
className="w-fit !p-0 rounded-sm" className="w-fit !p-0 rounded-sm border !border-primary hover:shadow"
icon={{ icon={{
icon: 'arrowRight', icon: 'arrowRight',
bgClassName: 'p-1 rounded-sm', bgClassName: 'p-1 rounded-sm !bg-primary hover:brightness-110',
iconClassName: '!text-chalkboard-10',
}} }}
> >
<span className="sr-only">Continue</span> <span className="sr-only">Continue</span>

View File

@ -9,9 +9,9 @@
.editor :global(.cm-line)::selection { .editor :global(.cm-line)::selection {
@apply px-1; @apply px-1;
@apply text-chalkboard-100; @apply text-chalkboard-100;
@apply bg-energy-10/50; @apply bg-primary/40;
} }
:global(.dark) .editor :global(.cm-line)::selection { :global(.dark) .editor :global(.cm-line)::selection {
@apply text-energy-10; @apply text-chalkboard-10;
@apply bg-energy-10/20; @apply bg-primary/40;
} }

View File

@ -153,7 +153,7 @@ function CommandBarKclInput({
className={ className={
calcResult === 'NAN' calcResult === 'NAN'
? 'text-destroy-80 dark:text-destroy-40' ? 'text-destroy-80 dark:text-destroy-40'
: 'text-energy-60 dark:text-energy-20' : 'text-succeed-80 dark:text-succeed-40'
} }
> >
{calcResult === 'NAN' {calcResult === 'NAN'
@ -173,7 +173,7 @@ function CommandBarKclInput({
type="text" type="text"
id="variable-name" id="variable-name"
name="variable-name" name="variable-name"
className="flex-1 border-none bg-transparent" className="flex-1 border-none bg-transparent focus:outline-none"
placeholder="Variable name" placeholder="Variable name"
value={newVariableName} value={newVariableName}
autoCapitalize="off" autoCapitalize="off"
@ -196,7 +196,7 @@ function CommandBarKclInput({
<span <span
className={ className={
isNewVariableNameUnique isNewVariableNameUnique
? 'text-energy-60 dark:text-energy-20' ? 'text-succeed-60 dark:text-succeed-40'
: 'text-destroy-60 dark:text-destroy-40' : 'text-destroy-60 dark:text-destroy-40'
} }
> >

View File

@ -38,11 +38,11 @@ function CommandComboBox({
<div className="flex items-center gap-2 px-4 pb-2 border-solid border-0 border-b border-b-chalkboard-20 dark:border-b-chalkboard-80"> <div className="flex items-center gap-2 px-4 pb-2 border-solid border-0 border-b border-b-chalkboard-20 dark:border-b-chalkboard-80">
<CustomIcon <CustomIcon
name="search" name="search"
className="w-5 h-5 bg-energy-10/50 dark:bg-chalkboard-90 dark:text-energy-10" className="w-5 h-5 bg-primary/10 text-primary"
/> />
<Combobox.Input <Combobox.Input
onChange={(event) => setQuery(event.target.value)} onChange={(event) => setQuery(event.target.value)}
className="w-full bg-transparent focus:outline-none selection:bg-energy-10/50 dark:selection:bg-energy-10/20 dark:focus:outline-none" className="w-full bg-transparent focus:outline-none selection:bg-primary/20 dark:selection:bg-primary/40 dark:focus:outline-none"
onKeyDown={(event) => { onKeyDown={(event) => {
if ( if (
(event.metaKey && event.key === 'k') || (event.metaKey && event.key === 'k') ||
@ -72,13 +72,10 @@ function CommandComboBox({
<Combobox.Option <Combobox.Option
key={option.name} key={option.name}
value={option} value={option}
className="flex items-center gap-2 px-4 py-1 first:mt-2 last:mb-2 ui-active:bg-energy-10/50 dark:ui-active:bg-chalkboard-90" className="flex items-center gap-2 px-4 py-1 first:mt-2 last:mb-2 ui-active:bg-primary/10 dark:ui-active:bg-chalkboard-90"
> >
{'icon' in option && option.icon && ( {'icon' in option && option.icon && (
<CustomIcon <CustomIcon name={option.icon} className="w-5 h-5" />
name={option.icon}
className="w-5 h-5 dark:text-energy-10"
/>
)} )}
<p className="flex-grow">{option.displayName || option.name} </p> <p className="flex-grow">{option.displayName || option.name} </p>
{option.description && ( {option.description && (

View File

@ -192,8 +192,8 @@ const FileTreeItem = ({
{fileOrDir.children === undefined ? ( {fileOrDir.children === undefined ? (
<li <li
className={ className={
'group m-0 p-0 border-solid border-0 text-energy-100 hover:text-energy-70 hover:bg-energy-10/50 dark:text-energy-30 dark:hover:!text-energy-20 dark:hover:bg-energy-90/50 focus-within:bg-energy-10/80 dark:focus-within:bg-energy-80/50 hover:focus-within:bg-energy-10/80 dark:hover:focus-within:bg-energy-80/50 ' + 'group m-0 p-0 border-solid border-0 hover:text-primary hover:bg-primary/5 focus-within:bg-primary/5 ' +
(isCurrentFile ? 'bg-energy-10/50 dark:bg-energy-90/50' : '') (isCurrentFile ? '!bg-primary/10 !text-primary' : '')
} }
> >
{!isRenaming ? ( {!isRenaming ? (
@ -206,12 +206,7 @@ const FileTreeItem = ({
> >
<CustomIcon <CustomIcon
name={fileOrDir.name?.endsWith(FILE_EXT) ? 'kcl' : 'file'} name={fileOrDir.name?.endsWith(FILE_EXT) ? 'kcl' : 'file'}
className={ className="inline-block w-3 text-current"
'inline-block w-3 ' +
(isCurrentFile
? 'text-energy-90 dark:text-energy-10'
: 'text-energy-50 dark:text-energy-50')
}
/> />
{fileOrDir.name} {fileOrDir.name}
</button> </button>
@ -230,9 +225,9 @@ const FileTreeItem = ({
{!isRenaming ? ( {!isRenaming ? (
<Disclosure.Button <Disclosure.Button
className={ className={
' group border-none text-sm rounded-none p-0 m-0 flex items-center justify-start w-full py-0.5 text-chalkboard-70 dark:text-chalkboard-30 hover:bg-energy-10/50 dark:hover:bg-energy-90/50' + ' group border-none text-sm rounded-none p-0 m-0 flex items-center justify-start w-full py-0.5 hover:text-primary hover:bg-primary/5' +
(context.selectedDirectory.path.includes(fileOrDir.path) (context.selectedDirectory.path.includes(fileOrDir.path)
? ' group-focus-within:bg-chalkboard-20/50 dark:group-focus-within:bg-chalkboard-80/20 hover:group-focus-within:bg-chalkboard-20 dark:hover:group-focus-within:bg-chalkboard-80/20 group-active:bg-chalkboard-20/50 dark:group-active:bg-chalkboard-80/20 hover:group-active:bg-chalkboard-20/50 dark:hover:group-active:bg-chalkboard-80/20' ? ' ui-open:text-primary'
: '') : '')
} }
style={{ paddingInlineStart: getIndentationCSS(level) }} style={{ paddingInlineStart: getIndentationCSS(level) }}
@ -353,17 +348,16 @@ export const FileTree = ({
return ( return (
<div className={className}> <div className={className}>
<div className="flex items-center gap-1 px-4 py-1 bg-chalkboard-20/50 dark:bg-chalkboard-80/50 border-b border-b-chalkboard-30 dark:border-b-chalkboard-80"> <div className="flex items-center gap-1 px-4 py-1 bg-chalkboard-20/40 dark:bg-chalkboard-80/50 border-b border-b-chalkboard-30 dark:border-b-chalkboard-80">
<h2 className="flex-1 m-0 p-0 text-sm mono">Files</h2> <h2 className="flex-1 m-0 p-0 text-sm mono">Files</h2>
<ActionButton <ActionButton
Element="button" Element="button"
icon={{ icon={{
icon: 'filePlus', icon: 'filePlus',
iconClassName: '!text-energy-80 dark:!text-energy-20', iconClassName: '!text-current',
bgClassName: bgClassName: 'bg-transparent',
'bg-chalkboard-20/50 hover:bg-energy-10/50 dark:hover:bg-transparent',
}} }}
className="!p-0 bg-transparent !outline-none" className="!p-0 !bg-transparent hover:text-primary border-transparent hover:border-primary !outline-none"
onClick={createFile} onClick={createFile}
> >
<Tooltip position="inlineStart" delay={750}> <Tooltip position="inlineStart" delay={750}>
@ -375,11 +369,10 @@ export const FileTree = ({
Element="button" Element="button"
icon={{ icon={{
icon: 'folderPlus', icon: 'folderPlus',
iconClassName: '!text-energy-80 dark:!text-energy-20', iconClassName: '!text-current',
bgClassName: bgClassName: 'bg-transparent',
'bg-chalkboard-20/50 hover:bg-energy-10/50 dark:hover:bg-transparent',
}} }}
className="!p-0 bg-transparent !outline-none" className="!p-0 !bg-transparent hover:text-primary border-transparent hover:border-primary !outline-none"
onClick={createFolder} onClick={createFolder}
> >
<Tooltip position="inlineStart" delay={750}> <Tooltip position="inlineStart" delay={750}>

View File

@ -15,23 +15,20 @@ const Loading = ({ children }: React.PropsWithChildren) => {
data-testid="loading" data-testid="loading"
> >
<svg viewBox="0 0 10 10" className="w-8 h-8"> <svg viewBox="0 0 10 10" className="w-8 h-8">
<circle cx="5" cy="5" r="4" stroke="var(--energy-50)" fill="none" />
<circle <circle
cx="5" cx="5"
cy="5" cy="5"
r="4" r="4"
stroke="var(--energy-10)" stroke="var(--primary)"
fill="none" fill="none"
strokeDasharray="4, 4" strokeDasharray="4, 4"
className="animate-spin origin-center" className="animate-spin origin-center"
/> />
</svg> </svg>
<p className="text-base mt-4 text-energy-80 dark:text-energy-30"> <p className="text-base mt-4 text-primary">{children || 'Loading'}</p>
{children || 'Loading'}
</p>
<p <p
className={ className={
'text-sm mt-4 text-energy-70 dark:text-energy-50 transition-opacity duration-500' + 'text-sm mt-4 text-primary/60 transition-opacity duration-500' +
(hasLongLoadTime ? ' opacity-100' : ' opacity-0') (hasLongLoadTime ? ' opacity-100' : ' opacity-0')
} }
> >

View File

@ -50,7 +50,7 @@ const hasIssueToIconColors: Record<string | number | symbol, IconColorConfig> =
bg: 'bg-chalkboard-30 dark:bg-chalkboard-70', bg: 'bg-chalkboard-30 dark:bg-chalkboard-70',
}, },
false: { false: {
icon: 'text-chalkboard-110 dark:!text-chalkboard-10', icon: '!text-chalkboard-110 dark:!text-chalkboard-10',
bg: 'bg-transparent dark:bg-transparent', bg: 'bg-transparent dark:bg-transparent',
}, },
} }
@ -58,8 +58,8 @@ const hasIssueToIconColors: Record<string | number | symbol, IconColorConfig> =
const overallConnectionStateColor: Record<NetworkHealthState, IconColorConfig> = const overallConnectionStateColor: Record<NetworkHealthState, IconColorConfig> =
{ {
[NetworkHealthState.Ok]: { [NetworkHealthState.Ok]: {
icon: 'text-energy-80 dark:text-energy-10', icon: 'text-succeed-80 dark:text-succeed-10',
bg: 'bg-energy-10/30 dark:bg-energy-80/50', bg: 'bg-succeed-10/30 dark:bg-succeed-80/50',
}, },
[NetworkHealthState.Issue]: { [NetworkHealthState.Issue]: {
icon: 'text-destroy-80 dark:text-destroy-10', icon: 'text-destroy-80 dark:text-destroy-10',
@ -214,7 +214,7 @@ export const NetworkHealthIndicator = () => {
'p-0 border-none bg-transparent dark:bg-transparent relative ' + 'p-0 border-none bg-transparent dark:bg-transparent relative ' +
(hasIssues (hasIssues
? 'focus-visible:outline-destroy-80' ? 'focus-visible:outline-destroy-80'
: 'focus-visible:outline-energy-80') : 'focus-visible:outline-succeed-80')
} }
data-testid="network-toggle" data-testid="network-toggle"
> >
@ -227,7 +227,7 @@ export const NetworkHealthIndicator = () => {
'rounded-sm ' + overallConnectionStateColor[overallState].bg 'rounded-sm ' + overallConnectionStateColor[overallState].bg
} }
/> />
<Tooltip position="blockEnd" delay={750} className="ui-open:hidden"> <Tooltip position="left" delay={750} className="ui-open:hidden">
Network Health ({NETWORK_HEALTH_TEXT[overallState]}) Network Health ({NETWORK_HEALTH_TEXT[overallState]})
</Tooltip> </Tooltip>
</Popover.Button> </Popover.Button>

View File

@ -13,6 +13,7 @@ import { getPartsCount, readProject } from '../lib/tauriFS'
import { FILE_EXT } from 'lib/constants' import { FILE_EXT } from 'lib/constants'
import { Dialog } from '@headlessui/react' import { Dialog } from '@headlessui/react'
import { useHotkeys } from 'react-hotkeys-hook' import { useHotkeys } from 'react-hotkeys-hook'
import Tooltip from './Tooltip'
function ProjectCard({ function ProjectCard({
project, project,
@ -64,17 +65,17 @@ function ProjectCard({
inputRef.current.focus() inputRef.current.focus()
inputRef.current.select() inputRef.current.select()
} }
}, [inputRef]) }, [inputRef.current])
return ( return (
<li <li
{...props} {...props}
className="group relative min-h-[5em] p-1 rounded-sm border border-chalkboard-20 dark:border-chalkboard-90 hover:border-energy-10 dark:hover:border-chalkboard-70 hover:bg-energy-10/20 dark:hover:bg-chalkboard-90" className="group relative min-h-[5em] p-1 rounded-sm border border-chalkboard-20 dark:border-chalkboard-80 hover:!border-primary"
> >
{isEditing ? ( {isEditing ? (
<form onSubmit={handleSave} className="flex gap-2 items-center"> <form onSubmit={handleSave} className="flex gap-2 items-center">
<input <input
className="dark:bg-chalkboard-80 dark:border-chalkboard-40 min-w-0 p-1 selection:bg-energy-10/20 focus:outline-none" className="dark:bg-chalkboard-80 dark:border-chalkboard-40 min-w-0 p-1 focus:outline-none"
type="text" type="text"
id="newProjectName" id="newProjectName"
name="newProjectName" name="newProjectName"
@ -87,27 +88,41 @@ function ProjectCard({
<ActionButton <ActionButton
Element="button" Element="button"
type="submit" type="submit"
icon={{ icon: faCheck, size: 'sm', className: 'p-1' }} icon={{
icon: faCheck,
size: 'sm',
className: 'p-1',
bgClassName: '!bg-transparent',
}}
className="!p-0" className="!p-0"
></ActionButton> >
<Tooltip position="left" delay={1000}>
Rename project
</Tooltip>
</ActionButton>
<ActionButton <ActionButton
Element="button" Element="button"
icon={{ icon={{
icon: faX, icon: faX,
size: 'sm', size: 'sm',
iconClassName: 'dark:!text-chalkboard-20', iconClassName: 'dark:!text-chalkboard-20',
bgClassName: '!bg-transparent',
className: 'p-1', className: 'p-1',
}} }}
className="!p-0" className="!p-0"
onClick={() => setIsEditing(false)} onClick={() => setIsEditing(false)}
/> >
<Tooltip position="left" delay={1000}>
Cancel
</Tooltip>
</ActionButton>
</div> </div>
</form> </form>
) : ( ) : (
<> <>
<div className="p-1 flex flex-col h-full gap-2"> <div className="p-1 flex flex-col h-full gap-2">
<Link <Link
className="flex-1 !no-underline text-liquid-100 after:content-[''] after:absolute after:inset-0" className="flex-1 !no-underline !text-chalkboard-110 dark:!text-chalkboard-10 after:content-[''] after:absolute after:inset-0"
to={`${paths.FILE}/${encodeURIComponent(project.path)}`} to={`${paths.FILE}/${encodeURIComponent(project.path)}`}
data-testid="project-link" data-testid="project-link"
> >
@ -130,6 +145,7 @@ function ProjectCard({
icon: faPenAlt, icon: faPenAlt,
className: 'p-1', className: 'p-1',
iconClassName: 'dark:!text-chalkboard-20', iconClassName: 'dark:!text-chalkboard-20',
bgClassName: '!bg-transparent',
size: 'xs', size: 'xs',
}} }}
onClick={(e) => { onClick={(e) => {
@ -138,15 +154,19 @@ function ProjectCard({
setIsEditing(true) setIsEditing(true)
}} }}
className="!p-0" className="!p-0"
/> >
<Tooltip position="left" delay={1000}>
Rename project
</Tooltip>
</ActionButton>
<ActionButton <ActionButton
Element="button" Element="button"
icon={{ icon={{
icon: faTrashAlt, icon: faTrashAlt,
className: 'p-1', className: 'p-1',
size: 'xs', size: 'xs',
bgClassName: 'bg-destroy-80', bgClassName: '!bg-transparent',
iconClassName: '!text-destroy-20 dark:!text-destroy-40', iconClassName: '!text-destroy-70',
}} }}
className="!p-0 hover:border-destroy-40 dark:hover:border-destroy-40" className="!p-0 hover:border-destroy-40 dark:hover:border-destroy-40"
onClick={(e) => { onClick={(e) => {
@ -154,7 +174,11 @@ function ProjectCard({
e.nativeEvent.stopPropagation() e.nativeEvent.stopPropagation()
setIsConfirmingDelete(true) setIsConfirmingDelete(true)
}} }}
/> >
<Tooltip position="left" delay={1000}>
Delete project
</Tooltip>
</ActionButton>
</div> </div>
</div> </div>
<Dialog <Dialog

View File

@ -25,15 +25,15 @@ const ProjectSidebarMenu = ({
}) => { }) => {
const { onProjectClose } = useLspContext() const { onProjectClose } = useLspContext()
return ( return (
<div className="rounded-sm !no-underline h-9 mr-auto max-h-min min-w-max border-0 py-1 px-2 flex items-center gap-2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-energy-50 dark:hover:bg-chalkboard-90"> <div className="!no-underline h-full mr-auto max-h-min min-w-max flex items-center gap-2">
<Link <Link
onClick={() => { onClick={() => {
onProjectClose(file || null, project?.path || null, false) onProjectClose(file || null, project?.path || null, false)
}} }}
to={paths.HOME} to={paths.HOME}
className="group" className="relative h-full grid place-content-center group p-1.5 before:block before:content-[''] before:absolute before:inset-0 before:bottom-2.5 before:z-[-1] before:bg-primary hover:before:brightness-110 before:rounded-b-sm"
> >
<Logo className="w-auto h-5 text-chalkboard-120 dark:text-chalkboard-10 group-hover:text-energy-10" /> <Logo className="w-auto h-4 text-chalkboard-10" />
</Link> </Link>
{renderAsLink ? ( {renderAsLink ? (
<> <>
@ -80,7 +80,7 @@ function ProjectMenuPopover({
return ( return (
<Popover className="relative"> <Popover className="relative">
<Popover.Button <Popover.Button
className="rounded-sm h-9 mr-auto max-h-min min-w-max border-0 py-1 pl-0 pr-2 flex items-center focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-energy-50 dark:hover:bg-chalkboard-90" className="rounded-sm h-9 mr-auto max-h-min min-w-max border-0 py-1 pl-0 pr-2 flex items-center focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary dark:hover:bg-chalkboard-90"
data-testid="project-sidebar-toggle" data-testid="project-sidebar-toggle"
> >
<CustomIcon name="three-dots" className="w-5 h-5 rotate-90" /> <CustomIcon name="three-dots" className="w-5 h-5 rotate-90" />
@ -126,15 +126,12 @@ function ProjectMenuPopover({
<> <>
<div className="flex items-center gap-4 px-4 py-3"> <div className="flex items-center gap-4 px-4 py-3">
<div> <div>
<p <p className="m-0 text-mono" data-testid="projectName">
className="m-0 text-chalkboard-100 dark:text-energy-10 text-mono"
data-testid="projectName"
>
{project?.name ? project.name : APP_NAME} {project?.name ? project.name : APP_NAME}
</p> </p>
{project?.entrypointMetadata && ( {project?.entrypointMetadata && (
<p <p
className="m-0 text-xs text-chalkboard-100 dark:text-energy-40" className="m-0 text-xs text-chalkboard-80 dark:text-chalkboard-40"
data-testid="createdAt" data-testid="createdAt"
> >
Created{' '} Created{' '}
@ -183,11 +180,10 @@ function ProjectMenuPopover({
onProjectClose(file || null, project?.path || null, true) onProjectClose(file || null, project?.path || null, true)
}} }}
icon={{ icon={{
icon: faHome, icon: 'arrowLeft',
className: 'p-1', className: 'p-1',
size: 'sm',
}} }}
className="border-transparent dark:border-transparent hover:bg-energy-10/20 dark:hover:bg-chalkboard-90" className="border-transparent dark:border-transparent"
> >
Go to Home Go to Home
</ActionButton> </ActionButton>

View File

@ -199,6 +199,17 @@ export const SettingsAuthProviderBase = ({
return () => matcher.removeEventListener('change', listener) return () => matcher.removeEventListener('change', listener)
}, [settingsState.context]) }, [settingsState.context])
/**
* Update the --primary-hue CSS variable
* to match the setting app.themeColor.current
*/
useEffect(() => {
document.documentElement.style.setProperty(
`--primary-hue`,
settingsState.context.app.themeColor.current
)
}, [settingsState.context.app.themeColor.current])
// Auth machine setup // Auth machine setup
const [authState, authSend, authActor] = useMachine(authMachine, { const [authState, authSend, authActor] = useMachine(authMachine, {
actions: { actions: {

View File

@ -154,10 +154,9 @@ const UserSidebarMenu = ({ user }: { user?: User }) => {
icon={{ icon={{
icon: faSignOutAlt, icon: faSignOutAlt,
className: 'p-1', className: 'p-1',
bgClassName: 'bg-destroy-80', bgClassName: '!bg-transparent',
size: 'sm', size: 'sm',
iconClassName: iconClassName: '!text-destroy-70',
'text-destroy-20 group-hover:text-destroy-10 hover:text-destroy-10',
}} }}
className="border-transparent dark:border-transparent hover:border-destroy-40 dark:hover:border-destroy-60 hover:bg-destroy-10/20 dark:hover:bg-destroy-80/20" className="border-transparent dark:border-transparent hover:border-destroy-40 dark:hover:border-destroy-60 hover:bg-destroy-10/20 dark:hover:bg-destroy-80/20"
data-testid="user-sidebar-sign-out" data-testid="user-sidebar-sign-out"

View File

@ -64,15 +64,15 @@ select {
} }
button { button {
@apply border border-chalkboard-30 m-0.5 px-3 rounded text-xs focus-visible:ring-energy-10; @apply border border-chalkboard-30 m-0.5 px-3 rounded text-xs focus-visible:ring-primary;
} }
button:hover { button:hover {
@apply border-chalkboard-40 bg-energy-10/20; @apply border-chalkboard-40 bg-primary/5;
} }
.dark button { .dark button {
@apply border-chalkboard-70 focus-visible:ring-energy-10/50; @apply border-chalkboard-70 focus-visible:ring-primary/50;
} }
.dark button:hover { .dark button:hover {
@ -80,7 +80,7 @@ button:hover {
} }
button:disabled { button:disabled {
@apply cursor-not-allowed bg-chalkboard-20 text-chalkboard-60 border-chalkboard-20; @apply cursor-not-allowed bg-chalkboard-20/50 text-chalkboard-60 border-chalkboard-20;
} }
.dark button:disabled { .dark button:disabled {
@ -88,19 +88,19 @@ button:disabled {
} }
a:not(.action-button) { a:not(.action-button) {
@apply text-energy-70 hover:text-energy-60 underline; @apply text-primary underline hover:hue-rotate-15;
} }
.dark a:not(.action-button) { .dark a:not(.action-button) {
@apply text-energy-20 hover:text-energy-10; @apply hover:brightness-110 hover:hue-rotate-0;
} }
input { input {
@apply selection:bg-energy-10/50; @apply selection:bg-primary/50;
} }
.dark input { .dark input {
@apply selection:bg-energy-10/40; @apply selection:bg-primary/40;
} }
.mono { .mono {

View File

@ -28,8 +28,8 @@ root.render(
'bg-chalkboard-10 dark:bg-chalkboard-90 text-chalkboard-110 dark:text-chalkboard-10 rounded-sm border-chalkboard-20/50 dark:border-chalkboard-80/50', 'bg-chalkboard-10 dark:bg-chalkboard-90 text-chalkboard-110 dark:text-chalkboard-10 rounded-sm border-chalkboard-20/50 dark:border-chalkboard-80/50',
success: { success: {
iconTheme: { iconTheme: {
primary: 'oklch(93.31% 0.227 122.3deg)', primary: 'oklch(89% 0.16 143.4deg)',
secondary: 'oklch(24.49% 0.01405 158.7deg)', secondary: 'oklch(48.62% 0.1654 142.5deg)',
}, },
duration: 1500, duration: 1500,
}, },

View File

@ -13,7 +13,7 @@ import {
cameraSystems, cameraSystems,
} from 'lib/cameraControls' } from 'lib/cameraControls'
import { isTauri } from 'lib/isTauri' import { isTauri } from 'lib/isTauri'
import { useRef } from 'react' import { CSSProperties, useRef } from 'react'
import { open } from '@tauri-apps/api/dialog' import { open } from '@tauri-apps/api/dialog'
import { CustomIcon } from 'components/CustomIcon' import { CustomIcon } from 'components/CustomIcon'
import Tooltip from 'components/Tooltip' import Tooltip from 'components/Tooltip'
@ -132,6 +132,31 @@ export function createSettings() {
})), })),
}, },
}), }),
themeColor: new Setting<string>({
defaultValue: '264.5',
description: 'The hue of the primary theme color for the app',
validate: (v) => Number(v) >= 0 && Number(v) < 360,
Component: ({ value, onChange }) => (
<div className="flex item-center gap-2 px-2">
<input
type="range"
onChange={onChange}
value={value}
min={0}
max={259}
step={1}
className="block flex-1"
/>
<span className="text-xs block w-[6ch] text-right">{value}º</span>
<div
className="w-3 h-3 rounded-full bg-primary"
style={{
backgroundColor: `oklch(var(--primary-lightness) var(--primary-chroma) ${value})`,
}}
/>
</div>
),
}),
onboardingStatus: new Setting<string>({ onboardingStatus: new Setting<string>({
defaultValue: '', defaultValue: '',
validate: (v) => typeof v === 'string', validate: (v) => typeof v === 'string',
@ -179,7 +204,7 @@ export function createSettings() {
inputRef.current.value = newValue inputRef.current.value = newValue
} }
}} }}
className="p-0 m-0 border-none hover:bg-energy-10 focus:bg-energy-10 dark:hover:bg-energy-80/50 dark:focus::bg-energy-80/50" className="p-0 m-0 border-none hover:bg-primary/10 focus:bg-primary/10 dark:hover:bg-primary/20 dark:focus::bg-primary/20"
> >
<CustomIcon name="folder" className="w-5 h-5" /> <CustomIcon name="folder" className="w-5 h-5" />
<Tooltip position="inlineStart">Choose a folder</Tooltip> <Tooltip position="inlineStart">Choose a folder</Tooltip>

View File

@ -32,6 +32,11 @@ export const settingsMachine = createMachine(
internal: true, internal: true,
actions: ['setSettingAtLevel', 'persistSettings'], // No toast actions: ['setSettingAtLevel', 'persistSettings'], // No toast
}, },
'set.app.themeColor': {
target: 'idle',
internal: true,
actions: ['setSettingAtLevel', 'persistSettings'], // No toast
},
'set.modeling.defaultUnit': { 'set.modeling.defaultUnit': {
target: 'idle', target: 'idle',

View File

@ -247,7 +247,7 @@ const Home = () => {
<section data-testid="home-section"> <section data-testid="home-section">
<p className="my-4 text-sm text-chalkboard-80 dark:text-chalkboard-30"> <p className="my-4 text-sm text-chalkboard-80 dark:text-chalkboard-30">
Loaded from{' '} Loaded from{' '}
<span className="text-energy-70 dark:text-energy-40"> <span className="text-chalkboard-90 dark:text-chalkboard-20">
{settings.app.projectDirectory.current} {settings.app.projectDirectory.current}
</span> </span>
.{' '} .{' '}

View File

@ -24,8 +24,7 @@ export default function CodeEditor() {
> >
<section className="flex-1"> <section className="flex-1">
<h2 className="text-3xl font-bold"> <h2 className="text-3xl font-bold">
Editing code with{' '} Editing code with <span className="text-primary">kcl</span>
<span className="text-energy-60 dark:text-energy-20">kcl</span>
</h2> </h2>
<p className="my-4"> <p className="my-4">
kcl is our language for describing geometry. Building our own kcl is our language for describing geometry. Building our own

View File

@ -137,7 +137,7 @@ export default function Introduction() {
alt={APP_NAME} alt={APP_NAME}
className="h-20 max-w-full" className="h-20 max-w-full"
/> />
<span className="px-3 py-1 text-base rounded-full bg-energy-10 text-energy-80"> <span className="px-3 py-1 text-base rounded-full bg-primary/10 text-primary">
Alpha Alpha
</span> </span>
</h1> </h1>

View File

@ -49,10 +49,8 @@ export default function ParametricModeling() {
<p className="my-4"> <p className="my-4">
We've received this sketch from a designer highlighting an{' '} We've received this sketch from a designer highlighting an{' '}
<em className="text-energy-60 dark:text-energy-20"> <em className="text-primary">aluminum bracket</em> they need for
aluminum bracket this shelf:
</em>{' '}
they need for this shelf:
</p> </p>
<figure className="my-4 w-2/3 mx-auto"> <figure className="my-4 w-2/3 mx-auto">
<img <img
@ -66,7 +64,7 @@ export default function ParametricModeling() {
<p className="my-4"> <p className="my-4">
We are able to easily calculate the thickness of the material based We are able to easily calculate the thickness of the material based
on the width of the bracket to meet a set safety factor on{' '} on the width of the bracket to meet a set safety factor on{' '}
<em className="text-energy-60 dark:text-energy-20"> <em className="text-primary">
line {bracketThicknessCalculationLine} line {bracketThicknessCalculationLine}
</em> </em>
. .

View File

@ -420,9 +420,7 @@ export function SettingsSection({
className={ className={
'group grid grid-cols-2 gap-6 items-start ' + 'group grid grid-cols-2 gap-6 items-start ' +
className + className +
(settingHasChanged (settingHasChanged ? ' border-0 border-l-2 -ml-0.5 border-primary' : '')
? ' border-0 border-l-2 -ml-0.5 border-energy-50 dark:border-energy-20'
: '')
} }
> >
<div className="ml-2"> <div className="ml-2">
@ -607,17 +605,14 @@ function SettingsTabButton(props: SettingsTabButtonProps) {
<div <div
className={`cursor-pointer select-none flex items-center gap-1 p-1 pr-2 -mb-[1px] border-0 border-b ${ className={`cursor-pointer select-none flex items-center gap-1 p-1 pr-2 -mb-[1px] border-0 border-b ${
checked checked
? 'border-energy-10 dark:border-energy-20' ? 'border-primary'
: 'border-chalkboard-20 dark:border-chalkboard-30 hover:bg-energy-10/50 dark:hover:bg-energy-90/50' : 'border-chalkboard-20 dark:border-chalkboard-30 hover:bg-primary/20 dark:hover:bg-primary/50'
}`} }`}
> >
<CustomIcon <CustomIcon
name={icon} name={icon}
className={ className={
'w-5 h-5 ' + 'w-5 h-5 ' + (checked ? 'bg-primary !text-chalkboard-10' : '')
(checked
? 'bg-energy-10 dark:bg-energy-20 dark:text-chalkboard-110'
: '')
} }
/> />
<span>{text}</span> <span>{text}</span>

View File

@ -32,6 +32,7 @@ module.exports = {
theme: { theme: {
extend: { extend: {
colors: { colors: {
primary: `oklch(var(--_primary) / <alpha-value>)`,
...themeColors, ...themeColors,
}, },
fontFamily: { fontFamily: {