Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
6a6e87f
feat: expose mobile branding workspace actions
seonghobae May 16, 2026
cd3ede6
fix: address mobile workspace review feedback
seonghobae May 16, 2026
6dabce6
fix: dedupe mobile workspace data fetches
seonghobae May 16, 2026
38c9054
fix: keep inbox text on safe render path
seonghobae May 16, 2026
3f835a8
feat: align branding shell navigation
seonghobae May 17, 2026
d31ab0a
feat: wire shell actions to email detail
seonghobae May 17, 2026
2788799
feat: add startup workspace preference
seonghobae May 17, 2026
2ee6e79
fix: keep startup preference off hash routes
seonghobae May 17, 2026
fbfc9f5
fix: handle blank subjects and storage failures
seonghobae May 17, 2026
3176110
test: assert inbox fixture fetches
seonghobae May 17, 2026
eb027d2
feat: collapse tablet AI panel
seonghobae May 17, 2026
a1713a4
feat: back mobile panels with search API
seonghobae May 17, 2026
1aa1801
feat: build functional AI hub workspace
seonghobae May 17, 2026
9d67dc1
fix: keep mobile workspace hash in sync
seonghobae May 17, 2026
0967785
fix: keep settings API out of startup scope
seonghobae May 17, 2026
20ca2ab
feat: link AI hub sidebar sections
seonghobae May 17, 2026
bc97e51
test: prove safe text rendering path
seonghobae May 17, 2026
519f40b
feat: adopt insight cards in email detail
seonghobae May 17, 2026
bb8b2b6
feat: back startup views with search data
seonghobae May 17, 2026
dfb9fa8
feat: add project workspace roadmap
seonghobae May 17, 2026
3579b98
feat: expose connector bootstrap manifest
seonghobae May 17, 2026
b7ad165
feat: add calendar writeback intent
seonghobae May 17, 2026
cd5512f
feat: add access policy deny precedence
seonghobae May 17, 2026
faffb32
fix: address review followups
seonghobae May 17, 2026
1b68baf
fix: resolve mobile workspace review threads
seonghobae May 17, 2026
81be759
fix(auth): require token for dev header trust
seonghobae May 17, 2026
0699391
fix(strix): scan changed context from PR head
seonghobae May 18, 2026
a770c7a
test(auth): make dev auth overrides opt in
seonghobae May 18, 2026
f7aafab
fix(ci): ignore unsafe regular PR context names
seonghobae May 18, 2026
6789d5f
fix(security): require configured encryption key
seonghobae May 18, 2026
4878079
fix(auth): gate dev headers to local runtimes
seonghobae May 18, 2026
7ca4b4b
fix(auth): fail closed on dev headers
seonghobae May 18, 2026
1564ad6
fix(calendar): trust server writeback sources
seonghobae May 18, 2026
c4328f1
Merge remote-tracking branch 'origin/master' into feature/branding-mo…
seonghobae May 18, 2026
a898a90
fix: scope Strix zero-finding infra bypass
seonghobae May 18, 2026
d7a0dce
fix: add signed session auth
seonghobae May 18, 2026
62304fc
fix: close API ownership trust gaps
seonghobae May 18, 2026
9926a3e
fix: enforce organization-scoped email access
seonghobae May 18, 2026
d149174
fix: harden database and SMTP egress config
seonghobae May 18, 2026
8630b42
fix: close SMTP sockets after pinned send
seonghobae May 18, 2026
9c7f4b0
fix: validate calendar writeback boundaries
seonghobae May 18, 2026
634770b
fix: require DNS validation for SMTP destinations
seonghobae May 18, 2026
48aa217
fix: make SMTP allowlist denial explicit
seonghobae May 18, 2026
5117325
fix: pin session token algorithm
seonghobae May 18, 2026
60c0f0e
fix: avoid Bandit session algorithm false positive
seonghobae May 18, 2026
60c72e9
fix: harden Strix security follow-ups
seonghobae May 18, 2026
71ce036
fix: pin SMTP DNS resolution to socket target
seonghobae May 19, 2026
05f35b7
fix: close north-star workspace gaps
seonghobae May 19, 2026
d856c40
fix: resolve current-head review findings
seonghobae May 19, 2026
4bfb8e5
fix: extend Strix PR scan budget
seonghobae May 19, 2026
5366b15
fix: harden auth and secret encryption gates
seonghobae May 19, 2026
adabbc4
fix: close task tracking and workspace IA gaps
seonghobae May 19, 2026
b327250
fix: make SMTP host allowlist guard explicit
seonghobae May 19, 2026
87540a4
fix: resolve current-head governance review blockers
seonghobae May 19, 2026
84e6f5d
fix: address current-head review follow-ups
seonghobae May 19, 2026
315d3cc
fix: reject SMTP header injection inputs
seonghobae May 19, 2026
f0394da
fix: retry stale Strix PR head fetch
seonghobae May 19, 2026
7c3bc95
fix: include calendar service in Strix PR scope
seonghobae May 19, 2026
ea8c5a1
fix: map Strix narrative file findings safely
seonghobae May 19, 2026
93a0c3e
feat: expand workspace destination surfaces
seonghobae May 19, 2026
356021a
fix: require strong session secret in all runtimes
seonghobae May 19, 2026
1bfe452
fix: allow scoped calendar writeback admins
seonghobae May 19, 2026
c53934e
fix: harden SMTP hostname validation
seonghobae May 19, 2026
73840c4
fix: reject mixed SMTP DNS answers
seonghobae May 20, 2026
28c34e8
Merge remote-tracking branch 'origin/master' into feature/branding-mo…
seonghobae May 20, 2026
f797363
fix: remove workspace mobile button trailing whitespace
seonghobae May 20, 2026
fc09460
fix: keep Strix PR findings fail-closed
seonghobae May 20, 2026
181cd75
fix: harden frontend auth headers
seonghobae May 20, 2026
3ec9c7d
fix: enforce default private api auth
seonghobae May 20, 2026
86d2971
fix: harden task title and session secret inputs
seonghobae May 20, 2026
902a36c
fix: avoid regex task title scanning
seonghobae May 20, 2026
fdafa67
fix: harden llm egress and email display text
seonghobae May 20, 2026
b121f58
Merge branch 'master' into feature/branding-mobile-action-workspace
seonghobae May 20, 2026
c7d256a
test: align Strix fixtures with trusted input root
seonghobae May 21, 2026
93abd0f
fix: include LLM provider URL context in Strix scope
seonghobae May 21, 2026
1e49395
fix: disable redirects for LLM provider requests
seonghobae May 21, 2026
e548a5e
Merge remote-tracking branch 'origin/master' into HEAD
seonghobae May 21, 2026
2c72573
Merge remote-tracking branch 'origin/master' into HEAD
seonghobae May 21, 2026
b767ba9
fix: harden prompt and email safety checks
seonghobae May 21, 2026
6bbe614
test: align prompt API import style
seonghobae May 21, 2026
5774032
fix: scope email sending to authenticated user
seonghobae May 21, 2026
ff6c9e0
fix: scope LLM providers to organizations
seonghobae May 21, 2026
3c6960d
fix: address PR review safety gaps
seonghobae May 21, 2026
dc1697d
fix: route calendar actions through writeback intent
seonghobae May 21, 2026
8a6df82
fix: clarify SMTP DNS validation before connect
seonghobae May 21, 2026
a4ecb19
fix: wire tasks page to signed task API
seonghobae May 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 151 additions & 0 deletions docs/plans/2026-05-16-branding-mobile-action-workspace.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# Branding Mobile Action Workspace Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Make the mobile inbox match the `frontend/branding/uiux` action-workspace direction by exposing inbox, search/context, AI execution, and schedule actions without hover-only controls.

**Architecture:** Keep the desktop layout unchanged. Convert the mobile bottom navigation in `DashboardLayout` into hash-addressable workspace links that call `setMobileWorkspaceView()`, sync `#mobile-{view}`, and dispatch the typed `naruon:mobile-workspace` browser event. Then let `app/page.tsx` switch between the inbox, detail, AI execution graph, search/context placeholder, and schedule placeholder.

**Tech Stack:** Next.js 16 app router, React 19 client components, Vitest/jsdom unit tests, Playwright acceptance tests, Tailwind utility classes.

---

## Source Gap

- `frontend/branding/uiux/uiux2.png` and `frontend/branding/uiux/uiux8.png` define a mobile-first workspace with direct bottom affordances for inbox, context search, relationship/context, schedule, and more/actions.
- `docs/plans/2026-05-11-frontend-brand-redesign-implementation.md` requires Playwright coverage for desktop/mobile shell, local brand assets, and dashboard flows.
- Current `frontend/src/app/page.tsx` has `showMobileActions = false`, so the mobile AI execution section can never be reached.
- Current `frontend/src/components/DashboardLayout.tsx` renders mobile bottom navigation from mail folders, while `frontend/tests/e2e/dashboard-branding.spec.ts` expects visible mobile workspace affordances for `받은편지함`, `맥락 검색`, `AI 실행`, and `일정`.

## Files

- Modify: `frontend/src/components/DashboardLayout.test.tsx`
- Modify: `frontend/tests/e2e/dashboard-branding.spec.ts`
- Modify: `frontend/src/components/DashboardLayout.tsx`
- Modify: `frontend/src/app/page.tsx`

## Task 1: Mobile action navigation exposes hash-addressable workspace links

- [ ] **Step 1: Write failing unit test**

Update `frontend/src/components/DashboardLayout.test.tsx` so the mobile nav assertion expects action links and verifies the custom event:

```tsx
const mobileNavLinks = Array.from(mobileNav?.querySelectorAll('a') ?? []).map(
(link) => link.textContent,
);
expect(mobileNavLinks).toEqual(["받은편지함", "맥락 검색", "AI 실행", "일정"]);
expect(mobileNav?.querySelector('[data-mobile-view="actions"]')?.getAttribute('href')).toBe('#mobile-actions');

const events: string[] = [];
window.addEventListener("naruon:mobile-workspace", ((event: Event) => {
events.push((event as CustomEvent<{ view: string }>).detail.view);
}) as EventListener);

act(() => {
mobileNav?.querySelector<HTMLElement>('[data-mobile-view="actions"]')?.click();
});

expect(events).toContain("actions");
```

- [ ] **Step 2: Verify RED**

Run:

```bash
npm test -- src/components/DashboardLayout.test.tsx
```

Expected: FAIL because the component still renders mail-folder links instead of action workspace links.

- [ ] **Step 3: Implement minimal event-emitting nav**

In `frontend/src/components/DashboardLayout.tsx`, replace the mobile bottom nav source with action items:

```tsx
const mobileWorkspaceItems = [
{ label: '받은편지함', icon: Inbox, view: 'inbox' as const },
{ label: '맥락 검색', icon: Search, view: 'search' as const },
{ label: 'AI 실행', icon: Sparkles, view: 'actions' as const },
{ label: '일정', icon: CalendarDays, view: 'calendar' as const },
];
```

Render each item as `<a href="#mobile-${view}">` and call `setMobileWorkspaceView(view)`. `setMobileWorkspaceView()` is responsible for synchronizing the URL hash and dispatching:

```tsx
window.dispatchEvent(new CustomEvent('naruon:mobile-workspace', { detail: { view } }));
```

- [ ] **Step 4: Verify GREEN**

Run:

```bash
npm test -- src/components/DashboardLayout.test.tsx
```

Expected: PASS.

## Task 2: Mobile page exposes AI execution, search, and schedule panels

- [ ] **Step 1: Write failing Playwright acceptance**

Extend `frontend/tests/e2e/dashboard-branding.spec.ts`:

```ts
await page.getByRole('link', { name: 'AI 실행' }).click();
await expect(page.getByRole('region', { name: '모바일 AI 실행' })).toBeVisible();
await expect(page.getByText('관계 맥락')).toBeVisible();

await page.getByRole('link', { name: '맥락 검색' }).click();
await expect(page.getByRole('region', { name: '모바일 맥락 검색' })).toBeVisible();
await expect(page.getByText('메일, 첨부, 일정, 사람을 한 번에 검색합니다.')).toBeVisible();

await page.getByRole('link', { name: '일정' }).click();
await expect(page.getByRole('region', { name: '모바일 일정 연결' })).toBeVisible();
await expect(page.getByText('캘린더 반영 대기')).toBeVisible();
```

- [ ] **Step 2: Verify RED**

Run:

```bash
npm run test:e2e -- dashboard-branding.spec.ts
```

Expected: FAIL because mobile action buttons are not wired to visible page sections yet.

- [ ] **Step 3: Implement mobile panels**

In `frontend/src/app/page.tsx`, replace `showMobileActions` with `mobileView` state. Listen for `naruon:mobile-workspace` events and render panels for `actions`, `search`, and `calendar`.

- [ ] **Step 4: Verify GREEN**

Run:

```bash
npm test -- src/components/DashboardLayout.test.tsx
npm run test:e2e -- dashboard-branding.spec.ts
```

Expected: PASS.

## Task 3: Final verification

- [ ] Run:

```bash
npm run lint
npm run typecheck
npm test -- src/components/DashboardLayout.test.tsx
```

- [ ] Commit:

```bash
git add docs/plans/2026-05-16-branding-mobile-action-workspace.md frontend/src/components/DashboardLayout.tsx frontend/src/components/DashboardLayout.test.tsx frontend/src/app/page.tsx frontend/tests/e2e/dashboard-branding.spec.ts
git commit -m "feat: expose mobile AI action workspace"
```
118 changes: 118 additions & 0 deletions docs/plans/2026-05-16-branding-roadmap-next-gaps.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# Branding Roadmap Next Gaps Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Continue closing the `docs/plans/` + `frontend/branding/` gap by turning visible Naruon header primary actions into real accessible controls instead of inert status chips.

**Architecture:** Preserve the branded shell and mobile workspace work already in progress. Keep the header action labels and visual treatment, but render them as typed buttons that dispatch a lightweight browser event for downstream page-level handlers. This makes the UI affordance truthful while keeping the delivery slice small and testable.

**Tech Stack:** Next.js 16 app router, React 19 client components, Vitest/jsdom unit tests, Playwright acceptance tests, Tailwind utility classes.

---

## Source Gap

- `docs/plans/2026-05-11-frontend-brand-redesign-design.md` requires a top header with global search and right-side primary actions.
- `docs/plans/2026-05-11-frontend-brand-redesign-implementation.md` requires primary actions: `캘린더 반영`, `답장 초안`, and `할 일 만들기`.
- `frontend/branding/uiux/uiux3.png`, `frontend/branding/uiux/uiux4.png`, and `frontend/branding/brand_assets/4.png`/`5.png` show a decision/action-oriented workspace, but the current header renders those primary actions as non-interactive chips.

## Current Roadmap

1. **P0 — Mobile action workspace:** mostly implemented in `docs/plans/2026-05-16-branding-mobile-action-workspace.md`; remaining semantic cleanup is tracked by tests and review feedback.
2. **P0 — Header primary actions:** implement in this plan so visible action affordances are real buttons.
3. **P1 — Dead sidebar route handling:** replace 404-prone future navigation with disabled/coming-soon states or add route stubs.
4. **P1 — Tablet AI panel behavior:** add a 1024–1279px detail-tab/drawer layout for the right context panel.
5. **P1 — API-backed mobile search/calendar:** replace placeholder panels with real loading/empty/error/success states.
6. **P2 — InsightCard adoption:** refactor `EmailDetail` AI cards to use `InsightCard` consistently.

## Files

- Modify: `frontend/src/components/DashboardLayout.test.tsx`
- Modify: `frontend/tests/e2e/dashboard-branding.spec.ts`
- Modify: `frontend/src/components/DashboardLayout.tsx`

## Task 1: Header primary actions are accessible buttons

- [ ] **Step 1: Write failing unit test**

Update `frontend/src/components/DashboardLayout.test.tsx` so the header actions are queried as buttons and clicking one emits `naruon:header-action`:

```tsx
const headerActionButtons = Array.from(
banner?.querySelectorAll<HTMLButtonElement>('button[data-header-action]') ?? [],
).map((button) => button.textContent);
expect(headerActionButtons).toEqual(["캘린더 반영", "답장 초안", "할 일 만들기"]);

const headerEvents: string[] = [];
window.addEventListener("naruon:header-action", ((event: Event) => {
headerEvents.push((event as CustomEvent<{ action: string }>).detail.action);
}) as EventListener);

act(() => {
banner?.querySelector<HTMLButtonElement>('button[data-header-action="reply-draft"]')?.click();
});

expect(headerEvents).toContain("reply-draft");
```

- [ ] **Step 2: Verify RED**

Run:

```bash
npm test -- src/components/DashboardLayout.test.tsx
```

Expected: FAIL because the header currently uses spans, not buttons.

- [ ] **Step 3: Implement minimal button behavior**

In `frontend/src/components/DashboardLayout.tsx`, give each header action an `action` id and render a `<button type="button" data-header-action="...">` that dispatches:

```tsx
window.dispatchEvent(new CustomEvent('naruon:header-action', { detail: { action } }));
```

- [ ] **Step 4: Verify GREEN**

Run:

```bash
npm test -- src/components/DashboardLayout.test.tsx
npm run lint
npm run typecheck
```

Expected: PASS.

## Task 2: Branding E2E checks truthful header actions

- [ ] **Step 1: Write failing Playwright assertion**

Update `frontend/tests/e2e/dashboard-branding.spec.ts` to expect the three header actions as buttons instead of asserting they are not buttons:

```ts
await expect(header.getByRole('button', { name: '캘린더 반영' })).toBeVisible();
await expect(header.getByRole('button', { name: '답장 초안' })).toBeVisible();
await expect(header.getByRole('button', { name: '할 일 만들기' })).toBeVisible();
```

- [ ] **Step 2: Verify RED**

Run:

```bash
npm run test:e2e -- dashboard-branding.spec.ts
```

Expected: FAIL until the header chip implementation is replaced by real buttons.

- [ ] **Step 3: Verify GREEN after implementation**

Run:

```bash
npm run test:e2e -- dashboard-branding.spec.ts
```

Expected: PASS.
14 changes: 14 additions & 0 deletions frontend/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,17 @@
@apply font-sans;
}
}

@layer components {
body:has(#mobile-search:target) .mobile-workspace-panel,
body:has(#mobile-actions:target) .mobile-workspace-panel,
body:has(#mobile-calendar:target) .mobile-workspace-panel {
display: none !important;
}

body:has(#mobile-search:target) .mobile-workspace-panel-search,
body:has(#mobile-actions:target) .mobile-workspace-panel-actions,
body:has(#mobile-calendar:target) .mobile-workspace-panel-calendar {
display: flex !important;
}
}
Loading
Loading