refactor(ui): share one collapsible content treatment (#721)
* refactor(ui): share one collapsible content treatment Three disclosures drove Reka directly with three different motion treatments, and the chat tool-call one named utilities that do not exist: `collapsible-up`/`collapsible-down` without the `animate-` prefix, so that collapse never animated and ignored reduced motion. Export the shared content treatment from the collapsible theme and use it in the chat tool call, reasoning, and frame preset disclosures. Their headers keep their own shape — a status icon, a thought icon, a text chevron — because they are structurally different from the labeled row disclosure. * docs: note the chat disclosure animation fix
This commit is contained in:
parent
81a07c0c9a
commit
3ba79e6c0d
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
### Fixed
|
||||
|
||||
- Animate the AI chat tool-call disclosure, which expanded and collapsed without motion because its animation classes were misspelled.
|
||||
|
||||
- Explain why the local MCP server did not start — a missing `@open-pencil/mcp` install, a denied command, an early exit, a rejected local connection, or an unreachable address — with translated guidance and collapsible technical details instead of one generic health failure.
|
||||
- Find a globally installed `@open-pencil/mcp` when the desktop app is launched from the system shell, by searching the common global binary directories that a graphical launch omits from `PATH`.
|
||||
- Mark unsaved documents and ask whether to save before closing a tab, the desktop window, or the application, rather than relying on recovery alone.
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import AttachmentList from '@/components/chat/attachment/AttachmentList.vue'
|
|||
import ChatMarkdown from '@/components/chat/ChatMarkdown.vue'
|
||||
import ReasoningBlock from '@/components/chat/ReasoningBlock.vue'
|
||||
import IconButton from '@/components/ui/button/IconButton.vue'
|
||||
import { collapsibleContentMotion } from '@/theme/collapsible/collapsible'
|
||||
|
||||
import { classifyToolState } from './tool-state'
|
||||
|
||||
|
|
@ -142,7 +143,7 @@ function partKey(part: UIMessagePart<UIDataTypes, UITools>, index: number): stri
|
|||
</CollapsibleTrigger>
|
||||
<CollapsibleContent
|
||||
v-if="toolState(part) !== 'pending'"
|
||||
class="data-[state=closed]:collapsible-up data-[state=open]:collapsible-down overflow-hidden text-[10px]"
|
||||
:class="[collapsibleContentMotion, 'text-[10px]']"
|
||||
>
|
||||
<pre class="mt-1 overflow-x-auto rounded bg-input p-2 text-muted">{{
|
||||
part.state === 'output-error' && part.errorText
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { computed, ref, watch } from 'vue'
|
|||
|
||||
import type { ReasoningDisplay } from '@/app/settings/preferences/store'
|
||||
import ChatMarkdown from '@/components/chat/ChatMarkdown.vue'
|
||||
import { collapsibleContentMotion } from '@/theme/collapsible/collapsible'
|
||||
|
||||
const {
|
||||
display = 'collapsed',
|
||||
|
|
@ -84,10 +85,7 @@ function updateOpen(value: boolean): void {
|
|||
aria-hidden="true"
|
||||
/>
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent
|
||||
data-slot="chat-reasoning-content"
|
||||
class="motion-safe:data-[state=closed]:animate-collapsible-up motion-safe:data-[state=open]:animate-collapsible-down animation-duration-150 overflow-hidden"
|
||||
>
|
||||
<CollapsibleContent data-slot="chat-reasoning-content" :class="collapsibleContentMotion">
|
||||
<div class="border-t border-border px-2 py-1.5 text-[11px] leading-relaxed text-muted">
|
||||
<ChatMarkdown :content="text" :mode="markdownMode" surface="reasoning" />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { useI18n } from '@open-pencil/vue'
|
|||
|
||||
import { useEditorStore } from '@/app/editor/active-store'
|
||||
import { FRAME_PRESET_CATEGORIES, type FramePreset } from '@/app/editor/frame-presets'
|
||||
import { collapsibleContentMotion } from '@/theme/collapsible/collapsible'
|
||||
|
||||
const store = useEditorStore()
|
||||
const { panels } = useI18n()
|
||||
|
|
@ -40,20 +41,23 @@ function createFrame(preset: FramePreset) {
|
|||
<span class="min-w-0 flex-1 truncate">{{ panels[category.labelKey] }}</span>
|
||||
</CollapsibleTrigger>
|
||||
|
||||
<CollapsibleContent class="pb-1.5">
|
||||
<button
|
||||
v-for="preset in category.presets"
|
||||
:key="preset.id"
|
||||
type="button"
|
||||
:data-frame-preset="preset.id"
|
||||
class="flex h-7 w-full items-center gap-2 px-7 text-left text-[11px] text-surface hover:bg-hover"
|
||||
@click="createFrame(preset)"
|
||||
>
|
||||
<span class="min-w-0 flex-1 truncate">{{ preset.name }}</span>
|
||||
<span class="shrink-0 tabular-nums text-muted">
|
||||
{{ preset.width }} × {{ preset.height }}
|
||||
</span>
|
||||
</button>
|
||||
<CollapsibleContent :class="collapsibleContentMotion">
|
||||
<!-- Padding stays inside the animated wrapper so it cannot snap. -->
|
||||
<div class="pb-1.5">
|
||||
<button
|
||||
v-for="preset in category.presets"
|
||||
:key="preset.id"
|
||||
type="button"
|
||||
:data-frame-preset="preset.id"
|
||||
class="flex h-7 w-full items-center gap-2 px-7 text-left text-[11px] text-surface hover:bg-hover"
|
||||
@click="createFrame(preset)"
|
||||
>
|
||||
<span class="min-w-0 flex-1 truncate">{{ preset.name }}</span>
|
||||
<span class="shrink-0 tabular-nums text-muted">
|
||||
{{ preset.width }} × {{ preset.height }}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</CollapsibleContent>
|
||||
</CollapsibleRoot>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,12 @@
|
|||
import { tv } from 'tailwind-variants'
|
||||
|
||||
/**
|
||||
* Shared content treatment for every collapsible, including the call sites that
|
||||
* keep their own header shape and drive Reka directly.
|
||||
*/
|
||||
export const collapsibleContentMotion =
|
||||
'overflow-hidden data-[state=open]:animate-collapsible-down data-[state=closed]:animate-collapsible-up duration-180 motion-reduce:data-[state=open]:animate-none motion-reduce:data-[state=closed]:animate-none'
|
||||
|
||||
export const collapsibleTheme = {
|
||||
slots: {
|
||||
root: '',
|
||||
|
|
@ -11,8 +18,7 @@ export const collapsibleTheme = {
|
|||
actions: 'shrink-0',
|
||||
// Padding and borders belong to the slot inside this wrapper so they cannot
|
||||
// snap while the measured height animates.
|
||||
content:
|
||||
'overflow-hidden data-[state=open]:animate-collapsible-down data-[state=closed]:animate-collapsible-up duration-180 motion-reduce:data-[state=open]:animate-none motion-reduce:data-[state=closed]:animate-none'
|
||||
content: collapsibleContentMotion
|
||||
}
|
||||
} as const
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue