Workspace UI overhaul: rail note panel, split task detail, project goal header

- WorkspaceNoteEditor: replace toggling list/editor views with always-visible
  left rail (155px) showing note list alongside editor pane; persist last-open
  note per project in localStorage (workspace_note_{projectId})
- WorkspaceTaskPanel: add priority dot + due date on task rows; replace full
  overlay detail with bottom-split (44% list / 56% detail); close button
  replaces back nav; active row highlighted; fade transition
- WorkspaceView: show project goal in header instead of 'Workspace'; non-equal
  panel widths (0.8fr / 1.1fr / 1.1fr); empty chat quick-action chips (Project
  status / New note / Add tasks); prefill() helper

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-10 23:51:17 -04:00
parent e4b2cd3aeb
commit 71301f631d
3 changed files with 580 additions and 542 deletions
+54 -16
View File
@@ -22,6 +22,7 @@ const projectId = computed(() => Number(route.params.projectId));
interface Project {
id: number;
title: string;
goal?: string;
}
const project = ref<Project | null>(null);
@@ -56,12 +57,11 @@ function _loadPanelState(pid: number) {
const panelOpen = ref({ tasks: true, chat: true, notes: true });
const gridColumns = computed(() => {
const cols = [
panelOpen.value.tasks ? "1fr" : "0px",
panelOpen.value.chat ? "1fr" : "0px",
panelOpen.value.notes ? "1fr" : "0px",
];
return cols.join(" ");
return [
panelOpen.value.tasks ? "minmax(0, 0.8fr)" : "0px",
panelOpen.value.chat ? "minmax(0, 1.1fr)" : "0px",
panelOpen.value.notes ? "minmax(0, 1.1fr)" : "0px",
].join(" ");
});
// SSE watcher
@@ -124,6 +124,11 @@ function togglePanel(panel: keyof typeof panelOpen.value) {
localStorage.setItem(_panelKey(projectId.value), JSON.stringify(panelOpen.value));
}
function prefill(text: string) {
messageInput.value = text;
nextTick(() => inputEl.value?.focus());
}
async function sendMessage() {
const content = messageInput.value.trim();
if (!content) return;
@@ -230,7 +235,7 @@ onUnmounted(async () => {
<router-link :to="project ? `/projects/${project.id}` : '/projects'" class="ws-back">
{{ project?.title ?? "Project" }}
</router-link>
<span class="ws-title">Workspace</span>
<span class="ws-title">{{ project?.goal ?? '' }}</span>
<div class="ws-panel-toggles">
<button
:class="['panel-toggle', { active: panelOpen.tasks }]"
@@ -341,12 +346,17 @@ onUnmounted(async () => {
</div>
</template>
<p
<div
v-if="chatStore.currentConversation && !chatStore.currentConversation.messages.length && !chatStore.streaming"
class="empty-chat-msg"
class="empty-chat-prompt"
>
Ask the agent to create notes or tasks for this project.
</p>
<p class="empty-hint">What would you like to work on?</p>
<div class="quick-chips">
<button class="quick-chip" @click="prefill('Summarize the current status of this project')">📊 Project status</button>
<button class="quick-chip" @click="prefill('Create a note about ')">📝 New note</button>
<button class="quick-chip" @click="prefill('Add tasks for ')"> Add tasks</button>
</div>
</div>
</div>
<div class="chat-input-area">
@@ -428,10 +438,13 @@ onUnmounted(async () => {
}
.ws-title {
font-weight: 600;
font-size: 0.9rem;
font-size: 0.78rem;
color: var(--color-text-muted);
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-style: italic;
}
.ws-panel-toggles {
@@ -501,12 +514,37 @@ onUnmounted(async () => {
gap: 0.75rem;
}
.empty-chat-msg {
color: var(--color-text-muted);
font-size: 0.875rem;
.empty-chat-prompt {
text-align: center;
padding: 2rem 1rem;
}
.empty-hint {
margin: 0 0 1rem;
color: var(--color-text-secondary);
font-size: 0.9rem;
}
.quick-chips {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
justify-content: center;
}
.quick-chip {
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: 20px;
padding: 0.4rem 0.85rem;
font-size: 0.82rem;
color: var(--color-text-secondary);
cursor: pointer;
transition: border-color 0.15s, color 0.15s, background 0.15s;
font-family: inherit;
}
.quick-chip:hover {
border-color: var(--color-primary);
color: var(--color-primary);
background: color-mix(in srgb, var(--color-primary) 5%, var(--color-surface));
}
.chat-input-area {
display: flex;