web: accent the brand and add the studio's quick gestures

- the brand "Cam", the avatar and the signed-in name follow the theme accent
- double-click a slider track resets that parameter; double-click the photo
  toggles 1:1 and the whole photo on the stage
- `*`, or a recipe chip dragged onto the star, files the look under FAVORITED
- RESET under CREATE clears the draft form, and the button now sits under a
  rule at the foot of the column
This commit is contained in:
2026-09-18 12:09:06 +07:00
parent 0f52572f20
commit 567f650bbb
6 changed files with 144 additions and 9 deletions
+24 -1
View File
@@ -17,8 +17,18 @@ export const TABS: { id: TabId; label: MsgKey; glyph: string }[] = [
{ id: 'create', label: 'tab.create', glyph: '✎' },
];
export function ToolRail({ tab, onTab }: { tab: TabId; onTab: (tab: TabId) => void }) {
export function ToolRail({
tab,
onTab,
onDropRecipe,
}: {
tab: TabId;
onTab: (tab: TabId) => void;
// A recipe chip dragged onto FAVORITED is filed there (see App's favorite()).
onDropRecipe?: (id: string) => void;
}) {
const { t } = useI18n();
const favorited = (id: TabId) => id === 'favorited';
return (
<nav className="rail" aria-label="tool tabs">
{TABS.map((entry) => (
@@ -27,6 +37,19 @@ export function ToolRail({ tab, onTab }: { tab: TabId; onTab: (tab: TabId) => vo
type="button"
aria-pressed={tab === entry.id}
onClick={() => onTab(entry.id)}
// The star is a drop target: recipes are dragged onto it from the
// RECIPES strip rather than retyped.
onDragOver={(e) => {
if (!favorited(entry.id) || !onDropRecipe) return;
e.preventDefault();
e.dataTransfer.dropEffect = 'copy';
}}
onDrop={(e) => {
if (!favorited(entry.id) || !onDropRecipe) return;
e.preventDefault();
const id = e.dataTransfer.getData('text/plain');
if (id) onDropRecipe(id);
}}
>
<span className="glyph" aria-hidden="true">{entry.glyph}</span>
{t(entry.label)}