diff --git a/packages/shared/src/validators/project.ts b/packages/shared/src/validators/project.ts index 7eb9811a..65d29c5d 100644 --- a/packages/shared/src/validators/project.ts +++ b/packages/shared/src/validators/project.ts @@ -1,25 +1,6 @@ import { z } from "zod"; import { PROJECT_STATUSES } from "../constants.js"; -export const createProjectSchema = z.object({ - /** @deprecated Use goalIds instead */ - goalId: z.string().uuid().optional().nullable(), - goalIds: z.array(z.string().uuid()).optional(), - name: z.string().min(1), - description: z.string().optional().nullable(), - status: z.enum(PROJECT_STATUSES).optional().default("backlog"), - leadAgentId: z.string().uuid().optional().nullable(), - targetDate: z.string().optional().nullable(), - color: z.string().optional().nullable(), - archivedAt: z.string().datetime().optional().nullable(), -}); - -export type CreateProject = z.infer; - -export const updateProjectSchema = createProjectSchema.partial(); - -export type UpdateProject = z.infer; - const projectWorkspaceFields = { name: z.string().min(1).optional(), cwd: z.string().min(1).optional().nullable(), @@ -51,3 +32,27 @@ export const updateProjectWorkspaceSchema = z.object({ }).partial(); export type UpdateProjectWorkspace = z.infer; + +const projectFields = { + /** @deprecated Use goalIds instead */ + goalId: z.string().uuid().optional().nullable(), + goalIds: z.array(z.string().uuid()).optional(), + name: z.string().min(1), + description: z.string().optional().nullable(), + status: z.enum(PROJECT_STATUSES).optional().default("backlog"), + leadAgentId: z.string().uuid().optional().nullable(), + targetDate: z.string().optional().nullable(), + color: z.string().optional().nullable(), + archivedAt: z.string().datetime().optional().nullable(), +}; + +export const createProjectSchema = z.object({ + ...projectFields, + workspace: createProjectWorkspaceSchema.optional(), +}); + +export type CreateProject = z.infer; + +export const updateProjectSchema = z.object(projectFields).partial(); + +export type UpdateProject = z.infer; diff --git a/ui/src/components/MarkdownEditor.tsx b/ui/src/components/MarkdownEditor.tsx index 94af2527..e41391d6 100644 --- a/ui/src/components/MarkdownEditor.tsx +++ b/ui/src/components/MarkdownEditor.tsx @@ -351,6 +351,23 @@ export const MarkdownEditor = forwardRef const state = mentionStateRef.current; if (!state) return; + if (option.kind === "project" && option.projectId) { + const current = latestValueRef.current; + const next = applyMention(current, state.query, option); + if (next !== current) { + latestValueRef.current = next; + ref.current?.setMarkdown(next); + onChange(next); + } + requestAnimationFrame(() => { + ref.current?.focus(undefined, { defaultSelection: "rootEnd" }); + decorateProjectMentions(); + }); + mentionStateRef.current = null; + setMentionState(null); + return; + } + const replacement = mentionMarkdown(option); // Replace @query directly via DOM selection so the cursor naturally