feat(design): surface phase PR 1 — Lucide icon migration + scale enforcement
Replaces every hand-inlined SVG in the chrome (60 across 15 files) with Lucide components. Snaps icon sizes to the doc's 16/24 scale — all icons in this pass land at 16. AppLogo wordmark stays as the legitimate "custom app icon" exception per the doc; GraphView's <svg> mount point for D3 stays as well. Replaces emoji-as-icons in clear button-affordance cases with their Lucide equivalents: ✕ → X (close/dismiss buttons across 8 files), ✓ → Check (ProjectView task-advance button), 🎤 → Mic (ChatPanel voice CTA), 📎 → Paperclip (ChatView context toggle), ↑ → ArrowUp (ChatInputBar Send), × → X (ChatPanel context-note remove), ☀/☾ → Sun/Moon (AppHeader theme toggle). Inline emoji punctuation in textual confirmation copy ("Saved ✓", "Created ✓", `done: "✓"` status maps, `'✓' : '📄'` interpolations) is left for surface-phase voice/tone touchups — replacing requires structural template changes and is best done per-component. Stroke weight stays at Lucide's default 2px; tightening to the doc's 1.5/1 is deferred per the surface-phase spec (option ii: drop-in + scale enforcement). Adds lucide-vue-next ^0.469.0 to frontend/package.json. Docker rebuild handles the install. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,16 @@ import { apiGet, apiPatch, apiDelete, apiPost } from "@/api/client";
|
||||
import { useToastStore } from "@/stores/toast";
|
||||
import { relativeTime } from "@/composables/useRelativeTime";
|
||||
import ShareDialog from "@/components/ShareDialog.vue";
|
||||
import {
|
||||
LayoutGrid,
|
||||
Clock,
|
||||
FileText,
|
||||
ChevronRight,
|
||||
ChevronDown,
|
||||
Pencil,
|
||||
Trash2,
|
||||
Check,
|
||||
} from "lucide-vue-next";
|
||||
|
||||
interface Milestone {
|
||||
id: number;
|
||||
@@ -330,7 +340,7 @@ async function confirmDelete() {
|
||||
<router-link to="/projects" class="btn-back">← Projects</router-link>
|
||||
<div class="page-header-actions">
|
||||
<router-link v-if="project" :to="`/workspace/${project.id}`" class="btn-workspace">
|
||||
<svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor" aria-hidden="true"><path d="M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z"/></svg>
|
||||
<LayoutGrid :size="16" />
|
||||
Workspace
|
||||
</router-link>
|
||||
<button v-if="project" class="btn-share" @click="showShare = true">Share</button>
|
||||
@@ -359,7 +369,7 @@ async function confirmDelete() {
|
||||
</div>
|
||||
<p v-if="project.goal" class="project-goal">{{ project.goal }}</p>
|
||||
<p v-if="project.summary?.last_activity" class="project-activity">
|
||||
<svg viewBox="0 0 24 24" width="11" height="11" fill="currentColor" aria-hidden="true"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67V7z"/></svg>
|
||||
<Clock :size="16" />
|
||||
Active {{ relativeTime(project.summary.last_activity) }}
|
||||
</p>
|
||||
</div>
|
||||
@@ -382,7 +392,7 @@ async function confirmDelete() {
|
||||
<span class="stat-label">done</span>
|
||||
</div>
|
||||
<div class="stat-chip stat-notes">
|
||||
<svg viewBox="0 0 24 24" width="12" height="12" fill="currentColor" aria-hidden="true" style="opacity:0.6"><path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zM6 20V4h7v5h5v11H6z"/></svg>
|
||||
<FileText :size="16" style="opacity:0.6" />
|
||||
<span class="stat-val">{{ project.summary.note_count }}</span>
|
||||
<span class="stat-label">notes</span>
|
||||
</div>
|
||||
@@ -463,7 +473,8 @@ async function confirmDelete() {
|
||||
@click="group.milestone && renamingMilestoneId !== group.milestone.id && toggleMilestoneCollapse(group.milestone.id)"
|
||||
>
|
||||
<span class="ms-chevron" v-if="group.milestone">
|
||||
<svg viewBox="0 0 24 24" width="10" height="10" fill="currentColor"><path :d="collapsedMilestones.has(group.milestone.id) ? 'M8 5l8 7-8 7V5z' : 'M5 8l7 8 7-8H5z'"/></svg>
|
||||
<ChevronRight v-if="collapsedMilestones.has(group.milestone.id)" :size="16" />
|
||||
<ChevronDown v-else :size="16" />
|
||||
</span>
|
||||
<template v-if="group.milestone && renamingMilestoneId === group.milestone.id">
|
||||
<input
|
||||
@@ -485,10 +496,10 @@ async function confirmDelete() {
|
||||
<span class="ms-pct">{{ group.milestone.pct }}%</span>
|
||||
<div class="ms-actions" @click.stop>
|
||||
<button class="ms-action-btn" title="Rename" @click="startRenameMilestone(group.milestone)">
|
||||
<svg viewBox="0 0 24 24" width="12" height="12" fill="currentColor"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/></svg>
|
||||
<Pencil :size="16" />
|
||||
</button>
|
||||
<button class="ms-action-btn ms-action-delete" title="Delete" @click="deletingMilestone = group.milestone">
|
||||
<svg viewBox="0 0 24 24" width="12" height="12" fill="currentColor"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></svg>
|
||||
<Trash2 :size="16" />
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
@@ -554,7 +565,7 @@ async function confirmDelete() {
|
||||
title="Mark as Done"
|
||||
:disabled="advancingTaskId === task.id"
|
||||
@click="advanceTaskStatus(task, $event)"
|
||||
>✓</button>
|
||||
><Check :size="16" /></button>
|
||||
</div>
|
||||
</router-link>
|
||||
<p v-if="!group.tasks.filter(t => t.status === 'in_progress').length" class="col-empty">No tasks</p>
|
||||
@@ -598,7 +609,7 @@ async function confirmDelete() {
|
||||
</div>
|
||||
<template v-else>
|
||||
<router-link v-for="note in notes" :key="note.id" :to="`/notes/${note.id}`" class="note-row">
|
||||
<svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor" class="note-icon" aria-hidden="true"><path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zM6 20V4h7v5h5v11H6z"/></svg>
|
||||
<FileText class="note-icon" :size="16" />
|
||||
<span class="note-title">{{ note.title || "Untitled" }}</span>
|
||||
<span class="note-date">{{ relativeTime(note.updated_at) }}</span>
|
||||
</router-link>
|
||||
|
||||
Reference in New Issue
Block a user