diff --git a/packages/views/locales/en/modals.json b/packages/views/locales/en/modals.json
index c47487af6..bdd4ccf55 100644
--- a/packages/views/locales/en/modals.json
+++ b/packages/views/locales/en/modals.json
@@ -109,6 +109,9 @@
"description_hint": "Shared with agents as context for every task in this project.",
"lead": "Lead",
"no_lead": "No lead",
+ "set_start_date": "Set start date...",
+ "set_due_date": "Set due date...",
+ "more_options_aria": "More options",
"lead_placeholder": "Assign lead...",
"members_group": "Members",
"agents_group": "Agents",
diff --git a/packages/views/locales/ja/modals.json b/packages/views/locales/ja/modals.json
index dc8e4133a..4fcb817c1 100644
--- a/packages/views/locales/ja/modals.json
+++ b/packages/views/locales/ja/modals.json
@@ -108,6 +108,9 @@
"description_hint": "このプロジェクトのすべてのタスクで、コンテキストとしてエージェントに共有されます。",
"lead": "リード",
"no_lead": "リードなし",
+ "set_start_date": "開始日を設定...",
+ "set_due_date": "期限を設定...",
+ "more_options_aria": "その他のオプション",
"lead_placeholder": "リードを指定...",
"members_group": "メンバー",
"agents_group": "エージェント",
diff --git a/packages/views/locales/ko/modals.json b/packages/views/locales/ko/modals.json
index 649d4abf4..2a523c524 100644
--- a/packages/views/locales/ko/modals.json
+++ b/packages/views/locales/ko/modals.json
@@ -108,6 +108,9 @@
"description_hint": "이 프로젝트의 모든 작업에서 컨텍스트로 에이전트에게 공유됩니다.",
"lead": "리드",
"no_lead": "리드 없음",
+ "set_start_date": "시작일 설정...",
+ "set_due_date": "마감일 설정...",
+ "more_options_aria": "추가 옵션",
"lead_placeholder": "리드 지정...",
"members_group": "멤버",
"agents_group": "에이전트",
diff --git a/packages/views/locales/zh-Hans/modals.json b/packages/views/locales/zh-Hans/modals.json
index bef20e6d1..7f916bddd 100644
--- a/packages/views/locales/zh-Hans/modals.json
+++ b/packages/views/locales/zh-Hans/modals.json
@@ -108,6 +108,9 @@
"description_hint": "会作为项目上下文提供给智能体,应用于该项目下的每个任务。",
"lead": "负责人",
"no_lead": "无负责人",
+ "set_start_date": "设置开始日期...",
+ "set_due_date": "设置截止日期...",
+ "more_options_aria": "更多选项",
"lead_placeholder": "指派负责人...",
"members_group": "成员",
"agents_group": "智能体",
diff --git a/packages/views/modals/create-project.test.tsx b/packages/views/modals/create-project.test.tsx
index 8148b5066..c1a328e3d 100644
--- a/packages/views/modals/create-project.test.tsx
+++ b/packages/views/modals/create-project.test.tsx
@@ -181,10 +181,18 @@ describe("CreateProjectModal", () => {
expect(screen.getByRole("tooltip", { name: longRepoUrl })).toBeInTheDocument();
});
- it("renders the start-date and due-date pills", () => {
+ it("reveals the start/due date pickers from the ⋯ overflow menu", async () => {
+ const user = userEvent.setup();
renderWithI18n();
+ // Dates are collapsed behind the overflow by default (progressive disclosure).
+ expect(screen.queryByRole("button", { name: "Start date" })).not.toBeInTheDocument();
+ expect(screen.queryByRole("button", { name: "Due date" })).not.toBeInTheDocument();
+
+ await user.click(screen.getByRole("button", { name: /Set start date/ }));
expect(screen.getByRole("button", { name: "Start date" })).toBeInTheDocument();
+
+ await user.click(screen.getByRole("button", { name: /Set due date/ }));
expect(screen.getByRole("button", { name: "Due date" })).toBeInTheDocument();
});
diff --git a/packages/views/modals/create-project.tsx b/packages/views/modals/create-project.tsx
index 146810ee4..00791021f 100644
--- a/packages/views/modals/create-project.tsx
+++ b/packages/views/modals/create-project.tsx
@@ -1,7 +1,7 @@
"use client";
import { useState, useRef } from "react";
-import { ChevronRight, FolderOpen, Maximize2, Minimize2, Search, X as XIcon, UserMinus } from "lucide-react";
+import { CalendarClock, CalendarDays, ChevronRight, FolderOpen, Maximize2, Minimize2, MoreHorizontal, Search, X as XIcon, UserMinus } from "lucide-react";
/**
* GitHub mark — lucide-react v1 dropped brand icons, so we inline the
@@ -59,6 +59,7 @@ import {
} from "../projects/components/labels";
import { ProjectStartDatePicker } from "../projects/components/project-start-date-picker";
import { ProjectDueDatePicker } from "../projects/components/project-due-date-picker";
+import { PillButton } from "../common/pill-button";
import {
isDesktopShell,
pickDirectory,
@@ -66,26 +67,6 @@ import {
} from "../platform/local-directory";
import { useLocalDaemonStatus } from "../platform/use-local-daemon-status";
-function PillButton({
- children,
- className,
- ...props
-}: React.ButtonHTMLAttributes) {
- return (
-
- );
-}
-
function RepoUrlText({
url,
className,
@@ -138,6 +119,10 @@ export function CreateProjectModal({ onClose }: { onClose: () => void }) {
const [icon, setIcon] = useState(draft.icon);
const [startDate, setStartDate] = useState(draft.startDate ?? "");
const [dueDate, setDueDate] = useState(draft.dueDate ?? "");
+ // Dates are collapsed into the ⋯ overflow by default (progressive
+ // disclosure, mirroring create-issue); these flip a pill inline + open.
+ const [startDatePickerOpen, setStartDatePickerOpen] = useState(false);
+ const [dueDatePickerOpen, setDueDatePickerOpen] = useState(false);
const [iconPickerOpen, setIconPickerOpen] = useState(false);
const [submitting, setSubmitting] = useState(false);
const [isExpanded, setIsExpanded] = useState(false);
@@ -407,14 +392,13 @@ export function CreateProjectModal({ onClose }: { onClose: () => void }) {
- {/* Footer: properties (left, wrap) + Create button (right). Single row
- so the modal stays compact — Linear-style.
+ {/* Property toolbar — mirrors the create-issue footer: a wrapping pill
+ row whose low-frequency fields (start/due date) collapse into a ⋯
+ overflow, with the primary action in a separate bar below.
Repos lives here alongside the property pills for now. Once we
support more resource types (Linear / Notion / Figma / Slack), pull
- them out into a dedicated Resources strip above this footer — a
- single Repos pill on its own row looked too sparse. */}
-
-
+ them out into a dedicated Resources strip above this footer. */}
+
void }) {
- updateStartDate(u.start_date ?? "")}
- triggerRender={}
- />
+ {/* Start date — collapsed into ⋯ unless it has a value or was just
+ opened from the overflow (the calendar anchors on the inline pill). */}
+ {(startDate || startDatePickerOpen) && (
+ updateStartDate(u.start_date ?? "")}
+ triggerRender={}
+ open={startDatePickerOpen}
+ onOpenChange={setStartDatePickerOpen}
+ />
+ )}
- updateDueDate(u.due_date ?? "")}
- triggerRender={}
- />
+ {(dueDate || dueDatePickerOpen) && (
+ updateDueDate(u.due_date ?? "")}
+ triggerRender={}
+ open={dueDatePickerOpen}
+ onOpenChange={setDueDatePickerOpen}
+ />
+ )}
void }) {
)}
-
+ {/* Overflow — always the last child so it stays at the end of the
+ wrap flow. Only rendered while a date is still collapsible; when
+ both are set there is nothing left to add. */}
+ {(!startDate || !dueDate) && (
+
+ $.create_project.more_options_aria)}>
+
+
+ }
+ />
+
+ {!dueDate && (
+ setDueDatePickerOpen(true)}>
+
+ {t(($) => $.create_project.set_due_date)}
+
+ )}
+ {!startDate && (
+ setStartDatePickerOpen(true)}>
+
+ {t(($) => $.create_project.set_start_date)}
+
+ )}
+
+
+ )}
+
+
+ {/* Footer action bar — primary action in its own strip, matching
+ create-issue. */}
+