feat: rebrand to website design, Apple HIG icons, ASCII onboarding animation
Build & Release / Lint & Typecheck (push) Successful in 38s
Build & Release / Unit Tests (push) Successful in 31s
Build & Release / Windows (arm64) (push) Failing after 11s
Build & Release / Windows (x64) (push) Failing after 27s
Build & Release / Linux (arm64) (push) Canceled after 0s
Build & Release / Linux (x64) (push) Canceled after 0s
Build & Release / Publish Release (push) Canceled after 0s
Build & Release / macOS (arm64) (push) Canceled after 0s
Build & Release / macOS (x64) (push) Canceled after 0s
Build & Release / Lint & Typecheck (push) Successful in 38s
Build & Release / Unit Tests (push) Successful in 31s
Build & Release / Windows (arm64) (push) Failing after 11s
Build & Release / Windows (x64) (push) Failing after 27s
Build & Release / Linux (arm64) (push) Canceled after 0s
Build & Release / Linux (x64) (push) Canceled after 0s
Build & Release / Publish Release (push) Canceled after 0s
Build & Release / macOS (arm64) (push) Canceled after 0s
Build & Release / macOS (x64) (push) Canceled after 0s
- Align Electron CSS with website design schema (Inter font, #FBFAF5/#191919 palette, #213448/#94B4C1 accent) - Generate proper app icons from logo.svg via cairosvg (no more Pillow ladder) - Square bg icons for Electron/tray (Apple HIG — OS applies squircle mask) - Circle bg icons for website favicon only - Add scrolling ASCII agent-activity animation to onboarding hero left panel - Remove dock.setIcon() — let macOS use bundle .icns with system mask - Fix goal title overflow with min-w-0 for flex truncation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
273244817a
commit
eda0e26adb
18 files changed
+337
-237
No files matched your search
@@ -2,7 +2,7 @@ import { spawn } from "node:child_process";
|
||||
import { existsSync } from "node:fs";
|
||||
import { homedir } from "node:os";
|
||||
import { delimiter as pathDelimiter, join } from "node:path";
|
||||
import { app, nativeImage } from "electron";
|
||||
import { app } from "electron";
|
||||
|
||||
interface HostEnvironmentDependencies {
|
||||
iconPath: string;
|
||||
@@ -91,10 +91,7 @@ export async function prepareHostEnvironment({
|
||||
logger.info("[path] Skipping interactive shell PATH probe; using deterministic PATH setup only.");
|
||||
}
|
||||
|
||||
const appIcon = nativeImage.createFromPath(iconPath);
|
||||
if (!appIcon.isEmpty()) {
|
||||
if (process.platform === "darwin" && app.dock) {
|
||||
app.dock.setIcon(appIcon);
|
||||
}
|
||||
}
|
||||
// Dock icon: macOS uses the .icns from the app bundle with the system squircle
|
||||
// mask applied automatically. Do not call dock.setIcon() — it bypasses the mask
|
||||
// and renders the raw PNG oversized and square.
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import { memo } from "react";
|
||||
|
||||
/**
|
||||
* Scrolling ASCII agent-activity backdrop for the onboarding hero panel.
|
||||
* Pure CSS animation (translateY on compositor thread) — no JS ticks.
|
||||
* Content is doubled for seamless looping.
|
||||
*/
|
||||
|
||||
const LINES = [
|
||||
"",
|
||||
" ╭──────────╮",
|
||||
" │ CEO │",
|
||||
" ╰────┬─────╯",
|
||||
" ┌────┴─────┐",
|
||||
" ╭──┴──╮ ╭──┴──╮",
|
||||
" │ CTO │ │ CMO │",
|
||||
" ╰──┬──╯ ╰──┬──╯",
|
||||
" ╭──┴──╮ ╭──┴──╮",
|
||||
" │ Eng │ │ Mkt │",
|
||||
" ╰─────╯ ╰─────╯",
|
||||
"",
|
||||
" $ deploy --agents 12",
|
||||
" ████████████████████ 100%",
|
||||
" [OK] all agents online",
|
||||
"",
|
||||
" CEO │ analyzing requirements",
|
||||
" CEO │ identifying deliverables",
|
||||
" CEO │ delegating to leads",
|
||||
"",
|
||||
" CTO │ reviewing architecture",
|
||||
" CTO │ → Dev.01: auth module",
|
||||
" CTO │ → Dev.02: API layer",
|
||||
" CTO │ → QA: test strategy",
|
||||
"",
|
||||
" CMO │ go-to-market strategy",
|
||||
" CMO │ → Mkt.01: launch copy",
|
||||
" CMO │ → Mkt.02: social plan",
|
||||
"",
|
||||
" ┌─ SPRINT 1 ────────────────┐",
|
||||
" │ tasks ████████░░ 80% │",
|
||||
" │ agents 12 active │",
|
||||
" │ tests 128 passing │",
|
||||
" └──────────────────────────┘",
|
||||
"",
|
||||
" Dev.01 │ scaffolding auth",
|
||||
" Dev.02 │ REST endpoints",
|
||||
" Design │ component library",
|
||||
" QA │ integration tests",
|
||||
" Mkt.01 │ blog post drafted",
|
||||
"",
|
||||
" CEO │ sprint review: ✓",
|
||||
" CTO │ code review: merged",
|
||||
" Dev.01 │ PR #12 → main",
|
||||
" QA │ all tests green",
|
||||
" CMO │ brief approved",
|
||||
"",
|
||||
" [INFO] Sprint 1 complete",
|
||||
" [INFO] Advancing to Sprint 2",
|
||||
"",
|
||||
];
|
||||
|
||||
const BLOCK = LINES.join("\n");
|
||||
|
||||
export const AsciiAnimation = memo(function AsciiAnimation() {
|
||||
return (
|
||||
<div className="pointer-events-none absolute inset-0 select-none overflow-hidden">
|
||||
{/* Fade masks */}
|
||||
<div
|
||||
className="absolute inset-x-0 top-0 z-10 h-32"
|
||||
style={{ background: "linear-gradient(to bottom, var(--bg), transparent)" }}
|
||||
/>
|
||||
<div
|
||||
className="absolute inset-x-0 bottom-0 z-10 h-32"
|
||||
style={{ background: "linear-gradient(to top, var(--bg), transparent)" }}
|
||||
/>
|
||||
|
||||
{/* Scrolling track — doubled for seamless loop */}
|
||||
<div className="ascii-scroll-track whitespace-pre font-mono text-[11px] leading-[1.8] text-[color:var(--accent)] opacity-[0.08]">
|
||||
{BLOCK}
|
||||
{"\n"}
|
||||
{BLOCK}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
import type { ProfileSnapshot } from "@shared/types";
|
||||
import type { AppLocale } from "@shared/locale";
|
||||
import { ConnectorReadiness } from "./ConnectorReadiness";
|
||||
import { AsciiAnimation } from "./AsciiAnimation";
|
||||
|
||||
type Step = "language" | "company" | "mission" | "launch";
|
||||
|
||||
@@ -57,6 +58,9 @@ export function OnboardingHero({
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* ASCII agent activity backdrop */}
|
||||
<AsciiAnimation />
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 24 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
|
||||
@@ -22,12 +22,12 @@ export function GoalProgress({ goals, tasks, onNavigate }: { goals: GoalRecord[]
|
||||
onClick={() => onNavigate("goals")}
|
||||
className="focus-ring w-full rounded-[8px] border border-[color:var(--line)] bg-[color:var(--panel)] px-4 py-3.5 text-left transition hover:border-[color:var(--line-strong)] hover:shadow-sm"
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Target size={15} weight="fill" className="text-[color:var(--accent)]" />
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div className="flex min-w-0 items-center gap-2">
|
||||
<Target size={15} weight="fill" className="shrink-0 text-[color:var(--accent)]" />
|
||||
<span className="truncate text-[13px] font-medium text-[color:var(--text)]">{goal.title}</span>
|
||||
</div>
|
||||
<span className="whitespace-nowrap text-[13px] font-bold tabular-nums text-[color:var(--accent)]">{pct}%</span>
|
||||
<span className="shrink-0 whitespace-nowrap text-[13px] font-bold tabular-nums text-[color:var(--accent)]">{pct}%</span>
|
||||
</div>
|
||||
<div className="mt-2.5 h-2 w-full overflow-hidden rounded-full bg-[color:var(--panel-soft)]">
|
||||
<div
|
||||
|
||||
@@ -32,10 +32,10 @@ export function GoalList({
|
||||
onClick={() => onSelect(goal.id)}
|
||||
className={`cursor-pointer transition-colors duration-100 ${isSelected ? "bg-[color:var(--accent-soft)]" : "hover:bg-[color:var(--panel-soft)]"}`}
|
||||
>
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="mb-2 flex items-center justify-between gap-3">
|
||||
<div className="flex min-w-0 items-center gap-2">
|
||||
{isSelected && (
|
||||
<CheckCircle size={16} weight="fill" className="text-[color:var(--accent)]" />
|
||||
<CheckCircle size={16} weight="fill" className="shrink-0 text-[color:var(--accent)]" />
|
||||
)}
|
||||
<div className="truncate text-[14px] font-semibold text-[color:var(--text)]">{goal.title}</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import "@fontsource-variable/inter";
|
||||
import "@fontsource-variable/jetbrains-mono";
|
||||
import "@fontsource/instrument-serif";
|
||||
import "./styles.css";
|
||||
import { StrictMode } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
|
||||
+66
-53
@@ -1,30 +1,32 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
@theme {
|
||||
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
||||
"Microsoft YaHei", "PingFang SC", "Hiragino Sans GB",
|
||||
--font-sans: "Inter Variable", -apple-system, BlinkMacSystemFont, "Segoe UI",
|
||||
Roboto, "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB",
|
||||
"Noto Sans CJK SC", Helvetica, Arial, sans-serif;
|
||||
--font-serif: "Instrument Serif", Georgia, "Times New Roman", serif;
|
||||
--font-mono: "JetBrains Mono Variable", "Microsoft YaHei", ui-monospace,
|
||||
SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
}
|
||||
|
||||
/* ─── Light theme — aligned with website ─── */
|
||||
:root {
|
||||
color-scheme: light;
|
||||
--bg: #f7f8fa;
|
||||
--bg-subtle: #ffffff;
|
||||
--panel: #ffffff;
|
||||
--panel-soft: #f0f2f5;
|
||||
--panel-strong: #f7f8fa;
|
||||
--line: rgba(50, 65, 85, 0.08);
|
||||
--line-strong: rgba(50, 65, 85, 0.15);
|
||||
--text: #1a1d24;
|
||||
--muted: #6b7280;
|
||||
--muted-strong: #4b5563;
|
||||
--accent: #4a5d7a;
|
||||
--accent-text: #3d5070;
|
||||
--accent-soft: #edf1f7;
|
||||
--accent-hover: #3d4f6d;
|
||||
--text-on-accent: #ffffff;
|
||||
--bg: #FBFAF5;
|
||||
--bg-subtle: #FFFFFF;
|
||||
--panel: #FFFFFF;
|
||||
--panel-soft: #F3F2ED;
|
||||
--panel-strong: #FBFAF5;
|
||||
--line: rgba(25, 25, 25, 0.08);
|
||||
--line-strong: rgba(25, 25, 25, 0.15);
|
||||
--text: #191919;
|
||||
--muted: #7a7a72;
|
||||
--muted-strong: #565650;
|
||||
--accent: #213448;
|
||||
--accent-text: #213448;
|
||||
--accent-soft: rgba(33, 52, 72, 0.07);
|
||||
--accent-hover: #192838;
|
||||
--text-on-accent: #FBFAF5;
|
||||
--danger: #c53030;
|
||||
--danger-soft: #fef2f2;
|
||||
--warn: #b45309;
|
||||
@@ -37,23 +39,24 @@
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
/* ─── Dark theme — aligned with website ─── */
|
||||
:root[data-theme="dark"] {
|
||||
color-scheme: dark;
|
||||
--bg: #16181f;
|
||||
--bg-subtle: #1a1c24;
|
||||
--panel: #1e2028;
|
||||
--panel-soft: #24262e;
|
||||
--panel-strong: #1c1e26;
|
||||
--line: rgba(148, 163, 190, 0.12);
|
||||
--line-strong: rgba(148, 163, 190, 0.2);
|
||||
--text: #edf0f7;
|
||||
--muted: #8b95a5;
|
||||
--muted-strong: #b0b8c8;
|
||||
--accent: #7b9cc4;
|
||||
--accent-text: #7b9cc4;
|
||||
--accent-soft: rgba(123, 156, 196, 0.14);
|
||||
--accent-hover: #93b0d4;
|
||||
--text-on-accent: #ffffff;
|
||||
--bg: #191919;
|
||||
--bg-subtle: #1e1e1e;
|
||||
--panel: #222222;
|
||||
--panel-soft: #2a2a2a;
|
||||
--panel-strong: #1c1c1c;
|
||||
--line: rgba(251, 250, 245, 0.08);
|
||||
--line-strong: rgba(251, 250, 245, 0.15);
|
||||
--text: #FBFAF5;
|
||||
--muted: #8a8a84;
|
||||
--muted-strong: #b8b8b2;
|
||||
--accent: #94B4C1;
|
||||
--accent-text: #94B4C1;
|
||||
--accent-soft: rgba(148, 180, 193, 0.12);
|
||||
--accent-hover: #a8c4cf;
|
||||
--text-on-accent: #191919;
|
||||
--danger: #f87171;
|
||||
--danger-soft: rgba(248, 113, 113, 0.12);
|
||||
--warn: #f59e0b;
|
||||
@@ -65,21 +68,21 @@
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root[data-theme="system"] {
|
||||
color-scheme: dark;
|
||||
--bg: #16181f;
|
||||
--bg-subtle: #1a1c24;
|
||||
--panel: #1e2028;
|
||||
--panel-soft: #24262e;
|
||||
--panel-strong: #1c1e26;
|
||||
--line: rgba(148, 163, 190, 0.12);
|
||||
--line-strong: rgba(148, 163, 190, 0.2);
|
||||
--text: #edf0f7;
|
||||
--muted: #8b95a5;
|
||||
--muted-strong: #b0b8c8;
|
||||
--accent: #7b9cc4;
|
||||
--accent-text: #7b9cc4;
|
||||
--accent-soft: rgba(123, 156, 196, 0.14);
|
||||
--accent-hover: #93b0d4;
|
||||
--text-on-accent: #ffffff;
|
||||
--bg: #191919;
|
||||
--bg-subtle: #1e1e1e;
|
||||
--panel: #222222;
|
||||
--panel-soft: #2a2a2a;
|
||||
--panel-strong: #1c1c1c;
|
||||
--line: rgba(251, 250, 245, 0.08);
|
||||
--line-strong: rgba(251, 250, 245, 0.15);
|
||||
--text: #FBFAF5;
|
||||
--muted: #8a8a84;
|
||||
--muted-strong: #b8b8b2;
|
||||
--accent: #94B4C1;
|
||||
--accent-text: #94B4C1;
|
||||
--accent-soft: rgba(148, 180, 193, 0.12);
|
||||
--accent-hover: #a8c4cf;
|
||||
--text-on-accent: #191919;
|
||||
--danger: #f87171;
|
||||
--danger-soft: rgba(248, 113, 113, 0.12);
|
||||
--warn: #f59e0b;
|
||||
@@ -133,11 +136,11 @@ select {
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
border-radius: 999px;
|
||||
background: rgba(60, 75, 100, 0.2);
|
||||
background: rgba(25, 25, 25, 0.15);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(60, 75, 100, 0.32);
|
||||
background: rgba(25, 25, 25, 0.25);
|
||||
}
|
||||
|
||||
.mono {
|
||||
@@ -147,7 +150,7 @@ select {
|
||||
.surface {
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--line);
|
||||
box-shadow: 0 1px 3px rgba(26, 29, 36, 0.06), 0 0 0 0.5px rgba(26, 29, 36, 0.03);
|
||||
box-shadow: 0 1px 3px rgba(25, 25, 25, 0.06), 0 0 0 0.5px rgba(25, 25, 25, 0.03);
|
||||
}
|
||||
|
||||
.focus-ring {
|
||||
@@ -160,9 +163,19 @@ select {
|
||||
|
||||
.focus-ring:focus-visible {
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(74, 93, 122, 0.5),
|
||||
0 0 0 4px rgba(74, 93, 122, 0.12);
|
||||
border-color: rgba(74, 93, 122, 0.35);
|
||||
0 0 0 1px rgba(33, 52, 72, 0.5),
|
||||
0 0 0 4px rgba(33, 52, 72, 0.12);
|
||||
border-color: rgba(33, 52, 72, 0.35);
|
||||
}
|
||||
|
||||
/* ─── ASCII animation ─── */
|
||||
@keyframes ascii-scroll-up {
|
||||
0% { transform: translateY(0); }
|
||||
100% { transform: translateY(-50%); }
|
||||
}
|
||||
|
||||
.ascii-scroll-track {
|
||||
animation: ascii-scroll-up 80s linear infinite;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
|
||||
Reference in new issue
Block a user