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 />
<Resizable
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
}
defaultSize={{
@ -166,7 +166,7 @@ export function App() {
maxHeight={'auto'}
handleClasses={{
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'
? 'pointer-events-none '
: 'pointer-events-auto'),
@ -202,7 +202,7 @@ export function App() {
theme={editorTheme}
open={openPanes.includes('kclErrors')}
title="KCL Errors"
iconClassNames={{ icon: 'group-open:text-destroy-30' }}
iconClassNames={{ bg: 'group-open:bg-destroy-70' }}
/>
</section>
</div>

View File

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

View File

@ -1,11 +1,21 @@
: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
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
*/
/* 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-30: oklch(82.99% 0.00994 115.2deg);
--chalkboard-40: oklch(74.63% 0.01053 121.4deg);

View File

@ -30,9 +30,9 @@ export const ActionIcon = ({
children,
}: ActionIconProps) => {
// 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 (
<div

View File

@ -33,7 +33,7 @@ export const AppHeader = ({
className={
'w-full grid ' +
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
}
>
@ -53,13 +53,13 @@ export const AppHeader = ({
className="text-sm self-center flex items-center w-fit gap-3"
>
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+/'}
</kbd>
</ActionButton>
)}
</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 */}
{children || (
<>

View File

@ -1,13 +1,13 @@
.button {
@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 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;
}
:global(.dark) .button {
@apply text-chalkboard-30;
@apply ui-active:bg-chalkboard-80 ui-active:text-energy-10;
@apply !text-chalkboard-30;
@apply ui-active:bg-chalkboard-90;
}
.button small {

View File

@ -30,12 +30,12 @@ export const CodeMenu = ({ children }: PropsWithChildren) => {
className="p-1"
size="sm"
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.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>
<button
onClick={() => kclManager.format()}

View File

@ -3,6 +3,11 @@
@apply bg-chalkboard-10/70 backdrop-blur-sm;
}
.header::before,
.header::-webkit-details-marker {
display: none;
}
:global(.dark) .panel {
@apply bg-chalkboard-110/50 backdrop-blur-0;
}
@ -11,7 +16,7 @@
@apply sticky top-0 z-10 cursor-pointer;
@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 bg-chalkboard-20;
@apply bg-chalkboard-10;
}
.header:not(:last-of-type) {

View File

@ -30,11 +30,11 @@ export const PanelHeader = ({
className="p-1"
size="sm"
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 || '')
}
iconClassName={
'group-open:text-energy-10 ' + (iconClassNames?.icon || '')
'group-open:text-chalkboard-10 ' + (iconClassNames?.icon || '')
}
/>
{title}

View File

@ -141,7 +141,7 @@ function CommandArgOptionInput({
<Combobox.Option
key={option.name}
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>
{option.value === currentOption?.value && (

View File

@ -104,7 +104,7 @@ function CommandBarHeader({ children }: React.PropsWithChildren<{}>) {
key={argName}
className={`relative w-fit px-2 py-1 rounded-sm flex gap-2 items-center border ${
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'
}`}
>
@ -129,7 +129,7 @@ function CommandBarHeader({ children }: React.PropsWithChildren<{}>) {
)
) : null}
{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>
{i + 1}
</small>
@ -174,12 +174,11 @@ function ReviewingButton() {
autoFocus
type="submit"
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: 'checkmark',
bgClassName:
'p-1 rounded-sm !bg-chalkboard-100 hover:!bg-chalkboard-110 dark:!bg-energy-20 dark:hover:!bg-energy-10',
iconClassName: '!text-energy-10 dark:!text-chalkboard-100',
bgClassName: 'p-1 rounded-sm !bg-primary hover:brightness-110',
iconClassName: '!text-chalkboard-10',
}}
>
<span className="sr-only">Submit command</span>
@ -193,10 +192,11 @@ function GatheringArgsButton() {
Element="button"
type="submit"
form="arg-form"
className="w-fit !p-0 rounded-sm"
className="w-fit !p-0 rounded-sm border !border-primary hover:shadow"
icon={{
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>

View File

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

View File

@ -153,7 +153,7 @@ function CommandBarKclInput({
className={
calcResult === 'NAN'
? 'text-destroy-80 dark:text-destroy-40'
: 'text-energy-60 dark:text-energy-20'
: 'text-succeed-80 dark:text-succeed-40'
}
>
{calcResult === 'NAN'
@ -173,7 +173,7 @@ function CommandBarKclInput({
type="text"
id="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"
value={newVariableName}
autoCapitalize="off"
@ -196,7 +196,7 @@ function CommandBarKclInput({
<span
className={
isNewVariableNameUnique
? 'text-energy-60 dark:text-energy-20'
? 'text-succeed-60 dark:text-succeed-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">
<CustomIcon
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
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) => {
if (
(event.metaKey && event.key === 'k') ||
@ -72,13 +72,10 @@ function CommandComboBox({
<Combobox.Option
key={option.name}
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 && (
<CustomIcon
name={option.icon}
className="w-5 h-5 dark:text-energy-10"
/>
<CustomIcon name={option.icon} className="w-5 h-5" />
)}
<p className="flex-grow">{option.displayName || option.name} </p>
{option.description && (

View File

@ -192,8 +192,8 @@ const FileTreeItem = ({
{fileOrDir.children === undefined ? (
<li
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 ' +
(isCurrentFile ? 'bg-energy-10/50 dark:bg-energy-90/50' : '')
'group m-0 p-0 border-solid border-0 hover:text-primary hover:bg-primary/5 focus-within:bg-primary/5 ' +
(isCurrentFile ? '!bg-primary/10 !text-primary' : '')
}
>
{!isRenaming ? (
@ -206,12 +206,7 @@ const FileTreeItem = ({
>
<CustomIcon
name={fileOrDir.name?.endsWith(FILE_EXT) ? 'kcl' : 'file'}
className={
'inline-block w-3 ' +
(isCurrentFile
? 'text-energy-90 dark:text-energy-10'
: 'text-energy-50 dark:text-energy-50')
}
className="inline-block w-3 text-current"
/>
{fileOrDir.name}
</button>
@ -230,9 +225,9 @@ const FileTreeItem = ({
{!isRenaming ? (
<Disclosure.Button
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)
? ' 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) }}
@ -353,17 +348,16 @@ export const FileTree = ({
return (
<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>
<ActionButton
Element="button"
icon={{
icon: 'filePlus',
iconClassName: '!text-energy-80 dark:!text-energy-20',
bgClassName:
'bg-chalkboard-20/50 hover:bg-energy-10/50 dark:hover:bg-transparent',
iconClassName: '!text-current',
bgClassName: '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}
>
<Tooltip position="inlineStart" delay={750}>
@ -375,11 +369,10 @@ export const FileTree = ({
Element="button"
icon={{
icon: 'folderPlus',
iconClassName: '!text-energy-80 dark:!text-energy-20',
bgClassName:
'bg-chalkboard-20/50 hover:bg-energy-10/50 dark:hover:bg-transparent',
iconClassName: '!text-current',
bgClassName: '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}
>
<Tooltip position="inlineStart" delay={750}>

View File

@ -15,23 +15,20 @@ const Loading = ({ children }: React.PropsWithChildren) => {
data-testid="loading"
>
<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
cx="5"
cy="5"
r="4"
stroke="var(--energy-10)"
stroke="var(--primary)"
fill="none"
strokeDasharray="4, 4"
className="animate-spin origin-center"
/>
</svg>
<p className="text-base mt-4 text-energy-80 dark:text-energy-30">
{children || 'Loading'}
</p>
<p className="text-base mt-4 text-primary">{children || 'Loading'}</p>
<p
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')
}
>

View File

@ -50,7 +50,7 @@ const hasIssueToIconColors: Record<string | number | symbol, IconColorConfig> =
bg: 'bg-chalkboard-30 dark:bg-chalkboard-70',
},
false: {
icon: 'text-chalkboard-110 dark:!text-chalkboard-10',
icon: '!text-chalkboard-110 dark:!text-chalkboard-10',
bg: 'bg-transparent dark:bg-transparent',
},
}
@ -58,8 +58,8 @@ const hasIssueToIconColors: Record<string | number | symbol, IconColorConfig> =
const overallConnectionStateColor: Record<NetworkHealthState, IconColorConfig> =
{
[NetworkHealthState.Ok]: {
icon: 'text-energy-80 dark:text-energy-10',
bg: 'bg-energy-10/30 dark:bg-energy-80/50',
icon: 'text-succeed-80 dark:text-succeed-10',
bg: 'bg-succeed-10/30 dark:bg-succeed-80/50',
},
[NetworkHealthState.Issue]: {
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 ' +
(hasIssues
? 'focus-visible:outline-destroy-80'
: 'focus-visible:outline-energy-80')
: 'focus-visible:outline-succeed-80')
}
data-testid="network-toggle"
>
@ -227,7 +227,7 @@ export const NetworkHealthIndicator = () => {
'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]})
</Tooltip>
</Popover.Button>

View File

@ -13,6 +13,7 @@ import { getPartsCount, readProject } from '../lib/tauriFS'
import { FILE_EXT } from 'lib/constants'
import { Dialog } from '@headlessui/react'
import { useHotkeys } from 'react-hotkeys-hook'
import Tooltip from './Tooltip'
function ProjectCard({
project,
@ -64,17 +65,17 @@ function ProjectCard({
inputRef.current.focus()
inputRef.current.select()
}
}, [inputRef])
}, [inputRef.current])
return (
<li
{...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 ? (
<form onSubmit={handleSave} className="flex gap-2 items-center">
<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"
id="newProjectName"
name="newProjectName"
@ -87,27 +88,41 @@ function ProjectCard({
<ActionButton
Element="button"
type="submit"
icon={{ icon: faCheck, size: 'sm', className: 'p-1' }}
icon={{
icon: faCheck,
size: 'sm',
className: 'p-1',
bgClassName: '!bg-transparent',
}}
className="!p-0"
></ActionButton>
>
<Tooltip position="left" delay={1000}>
Rename project
</Tooltip>
</ActionButton>
<ActionButton
Element="button"
icon={{
icon: faX,
size: 'sm',
iconClassName: 'dark:!text-chalkboard-20',
bgClassName: '!bg-transparent',
className: 'p-1',
}}
className="!p-0"
onClick={() => setIsEditing(false)}
/>
>
<Tooltip position="left" delay={1000}>
Cancel
</Tooltip>
</ActionButton>
</div>
</form>
) : (
<>
<div className="p-1 flex flex-col h-full gap-2">
<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)}`}
data-testid="project-link"
>
@ -130,6 +145,7 @@ function ProjectCard({
icon: faPenAlt,
className: 'p-1',
iconClassName: 'dark:!text-chalkboard-20',
bgClassName: '!bg-transparent',
size: 'xs',
}}
onClick={(e) => {
@ -138,15 +154,19 @@ function ProjectCard({
setIsEditing(true)
}}
className="!p-0"
/>
>
<Tooltip position="left" delay={1000}>
Rename project
</Tooltip>
</ActionButton>
<ActionButton
Element="button"
icon={{
icon: faTrashAlt,
className: 'p-1',
size: 'xs',
bgClassName: 'bg-destroy-80',
iconClassName: '!text-destroy-20 dark:!text-destroy-40',
bgClassName: '!bg-transparent',
iconClassName: '!text-destroy-70',
}}
className="!p-0 hover:border-destroy-40 dark:hover:border-destroy-40"
onClick={(e) => {
@ -154,7 +174,11 @@ function ProjectCard({
e.nativeEvent.stopPropagation()
setIsConfirmingDelete(true)
}}
/>
>
<Tooltip position="left" delay={1000}>
Delete project
</Tooltip>
</ActionButton>
</div>
</div>
<Dialog

View File

@ -25,15 +25,15 @@ const ProjectSidebarMenu = ({
}) => {
const { onProjectClose } = useLspContext()
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
onClick={() => {
onProjectClose(file || null, project?.path || null, false)
}}
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>
{renderAsLink ? (
<>
@ -80,7 +80,7 @@ function ProjectMenuPopover({
return (
<Popover className="relative">
<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"
>
<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>
<p
className="m-0 text-chalkboard-100 dark:text-energy-10 text-mono"
data-testid="projectName"
>
<p className="m-0 text-mono" data-testid="projectName">
{project?.name ? project.name : APP_NAME}
</p>
{project?.entrypointMetadata && (
<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"
>
Created{' '}
@ -183,11 +180,10 @@ function ProjectMenuPopover({
onProjectClose(file || null, project?.path || null, true)
}}
icon={{
icon: faHome,
icon: 'arrowLeft',
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
</ActionButton>

View File

@ -199,6 +199,17 @@ export const SettingsAuthProviderBase = ({
return () => matcher.removeEventListener('change', listener)
}, [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
const [authState, authSend, authActor] = useMachine(authMachine, {
actions: {

View File

@ -154,10 +154,9 @@ const UserSidebarMenu = ({ user }: { user?: User }) => {
icon={{
icon: faSignOutAlt,
className: 'p-1',
bgClassName: 'bg-destroy-80',
bgClassName: '!bg-transparent',
size: 'sm',
iconClassName:
'text-destroy-20 group-hover:text-destroy-10 hover:text-destroy-10',
iconClassName: '!text-destroy-70',
}}
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"

View File

@ -64,15 +64,15 @@ select {
}
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 {
@apply border-chalkboard-40 bg-energy-10/20;
@apply border-chalkboard-40 bg-primary/5;
}
.dark button {
@apply border-chalkboard-70 focus-visible:ring-energy-10/50;
@apply border-chalkboard-70 focus-visible:ring-primary/50;
}
.dark button:hover {
@ -80,7 +80,7 @@ button:hover {
}
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 {
@ -88,19 +88,19 @@ button:disabled {
}
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) {
@apply text-energy-20 hover:text-energy-10;
@apply hover:brightness-110 hover:hue-rotate-0;
}
input {
@apply selection:bg-energy-10/50;
@apply selection:bg-primary/50;
}
.dark input {
@apply selection:bg-energy-10/40;
@apply selection:bg-primary/40;
}
.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',
success: {
iconTheme: {
primary: 'oklch(93.31% 0.227 122.3deg)',
secondary: 'oklch(24.49% 0.01405 158.7deg)',
primary: 'oklch(89% 0.16 143.4deg)',
secondary: 'oklch(48.62% 0.1654 142.5deg)',
},
duration: 1500,
},

View File

@ -13,7 +13,7 @@ import {
cameraSystems,
} from 'lib/cameraControls'
import { isTauri } from 'lib/isTauri'
import { useRef } from 'react'
import { CSSProperties, useRef } from 'react'
import { open } from '@tauri-apps/api/dialog'
import { CustomIcon } from 'components/CustomIcon'
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>({
defaultValue: '',
validate: (v) => typeof v === 'string',
@ -179,7 +204,7 @@ export function createSettings() {
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" />
<Tooltip position="inlineStart">Choose a folder</Tooltip>

View File

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

View File

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

View File

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

View File

@ -137,7 +137,7 @@ export default function Introduction() {
alt={APP_NAME}
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
</span>
</h1>

View File

@ -49,10 +49,8 @@ export default function ParametricModeling() {
<p className="my-4">
We've received this sketch from a designer highlighting an{' '}
<em className="text-energy-60 dark:text-energy-20">
aluminum bracket
</em>{' '}
they need for this shelf:
<em className="text-primary">aluminum bracket</em> they need for
this shelf:
</p>
<figure className="my-4 w-2/3 mx-auto">
<img
@ -66,7 +64,7 @@ export default function ParametricModeling() {
<p className="my-4">
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{' '}
<em className="text-energy-60 dark:text-energy-20">
<em className="text-primary">
line {bracketThicknessCalculationLine}
</em>
.

View File

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

View File

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