refactor(workspace): match KnowledgeView mini-chat dock style
- Widget is now a bottom-anchored dock spanning the content width (max-width 1200px, rounded top corners, bg-secondary, upward shadow) instead of a floating corner box. - Drop the closed state and ✕ button — widget is always present, matching KnowledgeView's pattern. Collapsed = input-only dock, expanded = full ChatPanel above the input. - Grid ratio 1fr:2fr → 0.85fr:2.15fr so tasks get a touch less room and notes a touch more. - Workspace note-rail (the notes picker column inside the editor) widens from 155px to 200px so note titles breathe. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -49,7 +49,7 @@ watch(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
type WidgetState = 'closed' | 'collapsed' | 'expanded';
|
type WidgetState = 'collapsed' | 'expanded';
|
||||||
const widgetState = ref<WidgetState>('collapsed');
|
const widgetState = ref<WidgetState>('collapsed');
|
||||||
|
|
||||||
const chatPanelRef = ref<InstanceType<typeof ChatPanel> | null>(null);
|
const chatPanelRef = ref<InstanceType<typeof ChatPanel> | null>(null);
|
||||||
@@ -89,7 +89,6 @@ function _storageKey(pid: number) {
|
|||||||
return `workspace_conv_${pid}`;
|
return `workspace_conv_${pid}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function open() { widgetState.value = 'collapsed'; }
|
|
||||||
function expand() {
|
function expand() {
|
||||||
widgetState.value = 'expanded';
|
widgetState.value = 'expanded';
|
||||||
nextTick(() => chatPanelRef.value?.focus());
|
nextTick(() => chatPanelRef.value?.focus());
|
||||||
@@ -98,7 +97,6 @@ function collapse() {
|
|||||||
widgetState.value = 'collapsed';
|
widgetState.value = 'collapsed';
|
||||||
nextTick(() => inputBarRef.value?.focus());
|
nextTick(() => inputBarRef.value?.focus());
|
||||||
}
|
}
|
||||||
function close() { widgetState.value = 'closed'; }
|
|
||||||
|
|
||||||
async function restart() {
|
async function restart() {
|
||||||
const conv = await chatStore.createConversation();
|
const conv = await chatStore.createConversation();
|
||||||
@@ -166,19 +164,8 @@ defineExpose({ prefill });
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<!-- Closed: small floating handle -->
|
<!-- Bottom-anchored chat dock: collapsed (input only) or expanded (full panel) -->
|
||||||
<button
|
|
||||||
v-if="widgetState === 'closed'"
|
|
||||||
class="ws-chat-handle"
|
|
||||||
title="Open project chat"
|
|
||||||
@click="open"
|
|
||||||
>
|
|
||||||
💬 Chat
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Collapsed or expanded: floating panel -->
|
|
||||||
<div
|
<div
|
||||||
v-else
|
|
||||||
class="ws-chat-widget"
|
class="ws-chat-widget"
|
||||||
:class="{ 'ws-chat-widget--expanded': widgetState === 'expanded' }"
|
:class="{ 'ws-chat-widget--expanded': widgetState === 'expanded' }"
|
||||||
>
|
>
|
||||||
@@ -214,7 +201,6 @@ defineExpose({ prefill });
|
|||||||
<polyline points="6 9 12 15 18 9"/>
|
<polyline points="6 9 12 15 18 9"/>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn-icon-sm" title="Close chat" @click="close">✕</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -248,51 +234,32 @@ defineExpose({ prefill });
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.ws-chat-handle {
|
|
||||||
position: absolute;
|
|
||||||
right: 1rem;
|
|
||||||
bottom: 1rem;
|
|
||||||
z-index: 20;
|
|
||||||
padding: 0.5rem 0.9rem;
|
|
||||||
border-radius: 999px;
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
background: var(--color-surface);
|
|
||||||
color: var(--color-text);
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 0.82rem;
|
|
||||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
|
|
||||||
}
|
|
||||||
.ws-chat-handle:hover {
|
|
||||||
border-color: var(--color-primary);
|
|
||||||
color: var(--color-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ws-chat-widget {
|
.ws-chat-widget {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 1rem;
|
bottom: 0;
|
||||||
bottom: 1rem;
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
max-width: var(--page-max-width);
|
||||||
|
margin-inline: auto;
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
width: 380px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background: var(--color-surface);
|
background: var(--color-bg-secondary);
|
||||||
border: 1px solid var(--color-border);
|
border-radius: 16px 16px 0 0;
|
||||||
border-radius: 12px;
|
box-shadow: 0 -8px 32px rgba(0, 0, 0, 0.35), 0 -1px 0 rgba(255, 255, 255, 0.05);
|
||||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.35);
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition: height 0.2s ease, width 0.2s ease;
|
transition: height 0.2s ease;
|
||||||
}
|
}
|
||||||
.ws-chat-widget--expanded {
|
.ws-chat-widget--expanded {
|
||||||
width: 420px;
|
height: 500px;
|
||||||
height: 560px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ws-chat-header {
|
.ws-chat-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 8px 12px;
|
padding: 8px 14px;
|
||||||
border-bottom: 1px solid var(--color-border);
|
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
.ws-chat-title {
|
.ws-chat-title {
|
||||||
|
|||||||
@@ -457,7 +457,7 @@ defineExpose({ reload: loadProjectNotes });
|
|||||||
|
|
||||||
/* ── Left rail ── */
|
/* ── Left rail ── */
|
||||||
.note-rail {
|
.note-rail {
|
||||||
width: 155px;
|
width: 200px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ const panelOpen = ref<{ tasks: boolean; notes: boolean }>({ tasks: true, notes:
|
|||||||
|
|
||||||
const gridColumns = computed(() => {
|
const gridColumns = computed(() => {
|
||||||
return [
|
return [
|
||||||
panelOpen.value.tasks ? "minmax(0, 1fr)" : "0px",
|
panelOpen.value.tasks ? "minmax(0, 0.85fr)" : "0px",
|
||||||
panelOpen.value.notes ? "minmax(0, 2fr)" : "0px",
|
panelOpen.value.notes ? "minmax(0, 2.15fr)" : "0px",
|
||||||
].join(" ");
|
].join(" ");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user