5 Commits

Author SHA1 Message Date
jay bee3e1373a Fix release version capture.
Test and Build / test-and-build (push) Successful in 2m32s
2026-03-23 10:03:08 -04:00
jay 3c42bcd06c hotfix: make sure active buttons apply hover/active states.
Release / release (push) Successful in 2m43s
2026-03-23 10:01:53 -04:00
jay 0ac692c8b2 Write release version to version file.
Release / release (push) Successful in 2m41s
2026-03-23 09:44:57 -04:00
jay 7c6481a00a Update disabled button rendering. 2026-03-23 09:42:33 -04:00
jay 0ff6c60640 Swap preview pane actions.
Release / release (push) Successful in 2m45s
2026-03-23 09:23:06 -04:00
4 changed files with 89 additions and 47 deletions
+9 -7
View File
@@ -3,7 +3,7 @@ name: Release
on: on:
push: push:
tags: tags:
- 'v*' - "v*"
jobs: jobs:
release: release:
@@ -15,8 +15,8 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
ssh-key: '${{ secrets.SSH_PRIVATE_KEY }}' ssh-key: "${{ secrets.SSH_PRIVATE_KEY }}"
ssh-known-hosts: '${{ secrets.SSH_KNOWN_HOST }}' ssh-known-hosts: "${{ secrets.SSH_KNOWN_HOST }}"
# Install Cypress dependencies # Install Cypress dependencies
- name: Install Cypress dependencies - name: Install Cypress dependencies
@@ -46,7 +46,7 @@ jobs:
- name: Install go - name: Install go
uses: https://github.com/actions/setup-go@v3 uses: https://github.com/actions/setup-go@v3
with: with:
go-version: '>=1.20.1' go-version: ">=1.20.1"
# Install app dependencies # Install app dependencies
- name: Install app dependencies - name: Install app dependencies
@@ -80,7 +80,9 @@ jobs:
# Create archive # Create archive
- name: Create archive - name: Create archive
run: tar -czvf ${{ env.REPO_NAME }}-${{ env.TAG_NAME }}.tar.gz dist VERSION run: |
echo "$TAG_NAME" > VERSION
tar -czvf ${{ env.REPO_NAME }}-${{ env.TAG_NAME }}.tar.gz dist VERSION
# Create release # Create release
- name: Create Release - name: Create Release
@@ -88,5 +90,5 @@ jobs:
with: with:
files: |- files: |-
${{ env.REPO_NAME }}-${{ env.TAG_NAME }}.tar.gz ${{ env.REPO_NAME }}-${{ env.TAG_NAME }}.tar.gz
api_key: '${{ secrets.RELEASE_TOKEN }}' api_key: "${{ secrets.RELEASE_TOKEN }}"
title: 'Release ${{ env.TAG_NAME }}' title: "Release ${{ env.TAG_NAME }}"
@@ -25,7 +25,11 @@
transition-timing-function: ease-out; transition-timing-function: ease-out;
} }
.actionBar .actionButton:hover { .actionBar .actionButton.disabled {
cursor: auto;
}
.actionBar .actionButton.enabled:hover {
background-color: #f0f0f0; background-color: #f0f0f0;
box-shadow: box-shadow:
rgba(60, 64, 67, 0.25) 0px 3px 6px 0px, rgba(60, 64, 67, 0.25) 0px 3px 6px 0px,
@@ -33,7 +37,7 @@
transform: translateY(-1px); transform: translateY(-1px);
} }
.actionBar .actionButton:active { .actionBar .actionButton.enabled:active {
background-color: #d1d1d1; background-color: #d1d1d1;
box-shadow: box-shadow:
rgba(60, 64, 67, 0.3) 0px 1px 1px 0px, rgba(60, 64, 67, 0.3) 0px 1px 1px 0px,
@@ -60,7 +64,7 @@
inset 0 0 4px rgba(255, 255, 255, 0.4); inset 0 0 4px rgba(255, 255, 255, 0.4);
} }
.actionBar .activeButton:hover { .actionBar .actionButton.activeButton:hover {
background-color: #fea944; background-color: #fea944;
box-shadow: box-shadow:
rgba(60, 64, 67, 0.25) 0px 2px 4px 0px, rgba(60, 64, 67, 0.25) 0px 2px 4px 0px,
@@ -69,7 +73,7 @@
transform: translateY(-0.5px); transform: translateY(-0.5px);
} }
.actionBar .activeButton:active { .actionBar .actionButton.activeButton:active {
background-color: #eb8d16; background-color: #eb8d16;
box-shadow: box-shadow:
rgba(60, 64, 67, 0.2) 0px 0px 1px 0px, rgba(60, 64, 67, 0.2) 0px 0px 1px 0px,
@@ -170,7 +170,7 @@ it("can perform actions in normal mode", () => {
cy.get("@row2").contains("#000000"); cy.get("@row2").contains("#000000");
}); });
it("can manually sync picker and palette", () => { it.only("can manually sync picker and palette", () => {
cy.mount(<TestWrapper initialCardState={defaultPaletteCardState} />); cy.mount(<TestWrapper initialCardState={defaultPaletteCardState} />);
cy.dataCy("hex-value-input").as("hex"); cy.dataCy("hex-value-input").as("hex");
@@ -189,10 +189,10 @@ it("can manually sync picker and palette", () => {
// Select a color and sync it to the picker // Select a color and sync it to the picker
cy.get("@row1").click(); cy.get("@row1").click();
cy.get("@palette") cy.get("@palette").should("have.css", "background-color", "rgb(0, 255, 0)");
.should("have.css", "background-color", "rgb(0, 255, 0)") cy.get("@picker")
.click(); .click()
cy.get("@picker").should("have.css", "background-color", "rgb(0, 255, 0)"); .should("have.css", "background-color", "rgb(0, 255, 0)");
cy.get("@hex").should("have.value", "#00FF00"); cy.get("@hex").should("have.value", "#00FF00");
// Select a new color, picker remains the same // Select a new color, picker remains the same
@@ -203,7 +203,7 @@ it("can manually sync picker and palette", () => {
// Change picker color, sync back to palette. // Change picker color, sync back to palette.
cy.get("@hex").focus().type("FFFF00{esc}").wait(0); cy.get("@hex").focus().type("FFFF00{esc}").wait(0);
cy.get("@picker").should("have.css", "background-color", "rgb(255, 255, 0)"); cy.get("@picker").should("have.css", "background-color", "rgb(255, 255, 0)");
cy.get("@picker").click(); cy.get("@palette").click();
cy.get("@row0").contains("#FFFF00"); cy.get("@row0").contains("#FFFF00");
}); });
+66 -30
View File
@@ -190,7 +190,10 @@ function ActionBar({
return ( return (
<div className={styles.actionBar} data-cy="action-bar"> <div className={styles.actionBar} data-cy="action-bar">
<button <button
className={clsx(styles.actionButton, styles.iconButton)} className={clsx(styles.actionButton, styles.iconButton, {
[styles.enabled]: canUndo,
[styles.disabled]: !canUndo,
})}
data-cy="undo" data-cy="undo"
disabled={!canUndo} disabled={!canUndo}
onClick={handleUndo} onClick={handleUndo}
@@ -199,7 +202,10 @@ function ActionBar({
<Undo2 size={16} /> <Undo2 size={16} />
</button> </button>
<button <button
className={clsx(styles.actionButton, styles.iconButton)} className={clsx(styles.actionButton, styles.iconButton, {
[styles.enabled]: canRedo,
[styles.disabled]: !canRedo,
})}
data-cy="redo" data-cy="redo"
disabled={!canRedo} disabled={!canRedo}
onClick={handleRedo} onClick={handleRedo}
@@ -208,7 +214,7 @@ function ActionBar({
<Redo2 size={16} /> <Redo2 size={16} />
</button> </button>
<button <button
className={clsx(styles.actionButton, styles.iconButton)} className={clsx(styles.actionButton, styles.iconButton, styles.enabled)}
data-cy="add" data-cy="add"
onClick={actions.addColor} onClick={actions.addColor}
title="Add Color" title="Add Color"
@@ -216,7 +222,10 @@ function ActionBar({
<Plus size={16} /> <Plus size={16} />
</button> </button>
<button <button
className={clsx(styles.actionButton, styles.iconButton)} className={clsx(styles.actionButton, styles.iconButton, {
[styles.enabled]: hasSelection,
[styles.disabled]: !hasSelection,
})}
data-cy="delete" data-cy="delete"
disabled={!hasSelection} disabled={!hasSelection}
onClick={actions.deleteSelectedColors} onClick={actions.deleteSelectedColors}
@@ -225,7 +234,10 @@ function ActionBar({
<Trash2 size={16} /> <Trash2 size={16} />
</button> </button>
<button <button
className={clsx(styles.actionButton, styles.iconButton)} className={clsx(styles.actionButton, styles.iconButton, {
[styles.enabled]: hasSelection,
[styles.disabled]: !hasSelection,
})}
data-cy="duplicate" data-cy="duplicate"
disabled={!hasSelection} disabled={!hasSelection}
onClick={actions.duplicateSelectedColors} onClick={actions.duplicateSelectedColors}
@@ -234,9 +246,14 @@ function ActionBar({
<Copy size={16} /> <Copy size={16} />
</button> </button>
<button <button
className={clsx(styles.actionButton, styles.wordButton, { className={clsx(
[styles.activeButton]: mode === "reorder", styles.actionButton,
})} styles.wordButton,
styles.enabled,
{
[styles.activeButton]: mode === "reorder",
},
)}
data-cy="reorder" data-cy="reorder"
aria-pressed={mode === "reorder"} aria-pressed={mode === "reorder"}
onClick={() => onClick={() =>
@@ -247,9 +264,14 @@ function ActionBar({
Reorder Reorder
</button> </button>
<button <button
className={clsx(styles.actionButton, styles.wordButton, { className={clsx(
[styles.activeButton]: mode === "select", styles.actionButton,
})} styles.wordButton,
styles.enabled,
{
[styles.activeButton]: mode === "select",
},
)}
data-cy="select" data-cy="select"
aria-pressed={mode === "select"} aria-pressed={mode === "select"}
onClick={() => onClick={() =>
@@ -262,7 +284,11 @@ function ActionBar({
{mode === "select" && ( {mode === "select" && (
<> <>
<button <button
className={clsx(styles.actionButton, styles.iconButton)} className={clsx(
styles.actionButton,
styles.enabled,
styles.iconButton,
)}
data-cy="select-all" data-cy="select-all"
onClick={actions.selectAll} onClick={actions.selectAll}
title="Select All" title="Select All"
@@ -270,7 +296,11 @@ function ActionBar({
<CheckCheck size={16} /> <CheckCheck size={16} />
</button> </button>
<button <button
className={clsx(styles.actionButton, styles.iconButton)} className={clsx(
styles.actionButton,
styles.enabled,
styles.iconButton,
)}
data-cy="clear" data-cy="clear"
onClick={actions.clearSelection} onClick={actions.clearSelection}
title="Clear Selections" title="Clear Selections"
@@ -384,13 +414,15 @@ function PaletteCard({
<CardHeader name={cardState.name} onNameChange={actions.setCardName} /> <CardHeader name={cardState.name} onNameChange={actions.setCardName} />
<PickerColor <PickerColor
pickerColor={pickerColor} pickerColor={pickerColor}
paletteColorId={selectedColor?.id || null} setPickerColor={setPickerColor}
setPaletteColor={actions.setColorValue} selectedColor={selectedColor?.hex || null}
isSynced={isSynced} isSynced={isSynced}
/> />
<PaletteColor <PaletteColor
selectedColor={selectedColor?.hex || null} selectedColor={selectedColor?.hex || null}
setPickerColor={setPickerColor} pickerColor={pickerColor}
paletteColorId={selectedColor?.id || null}
setPaletteColor={actions.setColorValue}
isSynced={isSynced} isSynced={isSynced}
/> />
<Palette cardState={cardState} actions={actions} mode={mode} /> <Palette cardState={cardState} actions={actions} mode={mode} />
@@ -446,20 +478,20 @@ function CardHeader({
function PickerColor({ function PickerColor({
pickerColor, pickerColor,
paletteColorId, setPickerColor,
setPaletteColor, selectedColor,
isSynced, isSynced,
}: { }: {
pickerColor: HexColor; pickerColor: HexColor;
paletteColorId: string | null; setPickerColor: (hex: HexColor) => void;
setPaletteColor: (id: string, hex: HexColor) => void; selectedColor: HexColor | null;
isSynced: boolean; isSynced: boolean;
}) { }) {
const arrowToken = useContrastToken(() => luminanceFromHex(pickerColor)); const arrowToken = useContrastToken(() => luminanceFromHex(pickerColor));
const handleClick = () => { const handleClick = () => {
if (!isSynced && paletteColorId) { if (!isSynced && selectedColor) {
setPaletteColor(paletteColorId, pickerColor); setPickerColor(selectedColor);
} }
}; };
@@ -472,7 +504,7 @@ function PickerColor({
backgroundColor: formatHexString(pickerColor), backgroundColor: formatHexString(pickerColor),
}} }}
onClick={handleClick} onClick={handleClick}
title={!isSynced ? "Send to Picker" : ""} title={!isSynced ? "Set to Palette Color" : ""}
> >
{!isSynced && ( {!isSynced && (
<div <div
@@ -482,7 +514,7 @@ function PickerColor({
[styles.arrowIndicatorLight]: arrowToken === "light", [styles.arrowIndicatorLight]: arrowToken === "light",
})} })}
> >
<ChevronsRight size={32} /> <ChevronsLeft size={32} />
</div> </div>
)} )}
</div> </div>
@@ -491,19 +523,23 @@ function PickerColor({
function PaletteColor({ function PaletteColor({
selectedColor, selectedColor,
setPickerColor, pickerColor,
paletteColorId,
setPaletteColor,
isSynced, isSynced,
}: { }: {
selectedColor: HexColor | null; selectedColor: HexColor | null;
setPickerColor: (hex: HexColor) => void; pickerColor: HexColor;
paletteColorId: string | null;
setPaletteColor: (id: string, hex: HexColor) => void;
isSynced: boolean; isSynced: boolean;
}) { }) {
const bgColor = selectedColor || DEFAULT_BG; const bgColor = selectedColor || DEFAULT_BG;
const arrowToken = useContrastToken(() => luminanceFromHex(bgColor)); const arrowToken = useContrastToken(() => luminanceFromHex(bgColor));
const handleClick = () => { const handleClick = () => {
if (!isSynced && selectedColor) { if (!isSynced && paletteColorId) {
setPickerColor(selectedColor); setPaletteColor(paletteColorId, pickerColor);
} }
}; };
@@ -516,7 +552,7 @@ function PaletteColor({
backgroundColor: formatHexString(bgColor), backgroundColor: formatHexString(bgColor),
}} }}
onClick={handleClick} onClick={handleClick}
title={!isSynced ? "Send to Palette" : ""} title={!isSynced ? "Set to Picker Color" : ""}
> >
{!isSynced && ( {!isSynced && (
<div <div
@@ -526,7 +562,7 @@ function PaletteColor({
[styles.arrowIndicatorLight]: arrowToken === "light", [styles.arrowIndicatorLight]: arrowToken === "light",
})} })}
> >
<ChevronsLeft size={32} /> <ChevronsRight size={32} />
</div> </div>
)} )}
</div> </div>