-
-
+
+
- 已写入 {{ action.append?.path || '目标文件' }} 的追加内容(内容已保存至文件)
+ {{ action.append?.summary || '文件追加完成' }}
-
-
-
-
-
向 {{ action.append?.path || '目标文件' }} 写入失败,内容已截获供后续修复。
+
+ {{ action.append.path || '目标文件' }}
+ · 行数 {{ action.append.lines }}
+ · 字节 {{ action.append.bytes }}
+
+
+
+ 未检测到结束标记,请按提示继续补充。
-
-
-
- · 行数 {{ action.append.lines }}
-
-
- · 字节 {{ action.append.bytes }}
-
-
-
-
- 未检测到结束标记,请根据提示继续补充。
-
-
-
-
-
- {{ action.append?.summary || '文件追加完成' }}
-
-
- {{ action.append.path || '目标文件' }}
- · 行数 {{ action.append.lines }}
- · 字节 {{ action.append.bytes }}
-
-
-
- 未检测到结束标记,请按提示继续补充。
-
-
+
-
-
-
+
@@ -184,8 +332,10 @@
diff --git a/static/src/components/personalization/PersonalizationDrawer.vue b/static/src/components/personalization/PersonalizationDrawer.vue
index 9f4c451..01090ae 100644
--- a/static/src/components/personalization/PersonalizationDrawer.vue
+++ b/static/src/components/personalization/PersonalizationDrawer.vue
@@ -207,6 +207,29 @@
+
@@ -469,7 +501,6 @@ const swipeState = ref<{ startY: number; active: boolean }>({ startY: 0, active:
type RunModeValue = 'fast' | 'thinking' | 'deep' | null;
const runModeOptions: Array<{ id: string; label: string; desc: string; value: RunModeValue; badge?: string }> = [
- { id: 'auto', label: '跟随系统', desc: '沿用工作区默认设置', value: null },
{ id: 'fast', label: '快速模式', desc: '追求响应速度,跳过思考模型', value: 'fast' },
{ id: 'thinking', label: '思考模式', desc: '首轮回复会先输出思考过程', value: 'thinking', badge: '推荐' },
{ id: 'deep', label: '深度思考', desc: '整轮对话都使用思考模型', value: 'deep' }
@@ -562,6 +593,11 @@ const handleLiquidGlassToggle = (event: Event) => {
personalization.setLiquidGlassExperimentEnabled(!!target?.checked);
};
+const handleStackedBlocksToggle = (event: Event) => {
+ const target = event.target as HTMLInputElement | null;
+ personalization.setStackedBlocksEnabled(!!target?.checked);
+};
+
const openAdminPanel = () => {
window.open('/admin/monitor', '_blank', 'noopener');
personalization.closeDrawer();
diff --git a/static/src/stores/personalization.ts b/static/src/stores/personalization.ts
index f6811f3..bed9d9c 100644
--- a/static/src/stores/personalization.ts
+++ b/static/src/stores/personalization.ts
@@ -23,6 +23,7 @@ interface LiquidGlassPosition {
interface ExperimentState {
liquidGlassEnabled: boolean;
liquidGlassPosition: LiquidGlassPosition | null;
+ stackedBlocksEnabled: boolean;
}
interface PersonalizationState {
@@ -65,7 +66,8 @@ const defaultForm = (): PersonalForm => ({
const defaultExperimentState = (): ExperimentState => ({
liquidGlassEnabled: false,
- liquidGlassPosition: null
+ liquidGlassPosition: null,
+ stackedBlocksEnabled: true
});
const isValidPosition = (value: any): value is LiquidGlassPosition => {
@@ -91,7 +93,9 @@ const loadExperimentState = (): ExperimentState => {
const parsed = JSON.parse(raw);
return {
liquidGlassEnabled: Boolean(parsed?.liquidGlassEnabled),
- liquidGlassPosition: isValidPosition(parsed?.liquidGlassPosition) ? parsed?.liquidGlassPosition : null
+ liquidGlassPosition: isValidPosition(parsed?.liquidGlassPosition) ? parsed?.liquidGlassPosition : null,
+ stackedBlocksEnabled:
+ typeof parsed?.stackedBlocksEnabled === 'boolean' ? parsed.stackedBlocksEnabled : defaultExperimentState().stackedBlocksEnabled
};
} catch (error) {
console.warn('无法读取实验功能设置:', error);
@@ -452,6 +456,16 @@ export const usePersonalizationStore = defineStore('personalization', {
toggleLiquidGlassExperiment() {
this.setLiquidGlassExperimentEnabled(!this.experiments.liquidGlassEnabled);
},
+ setStackedBlocksEnabled(enabled: boolean) {
+ this.experiments = {
+ ...this.experiments,
+ stackedBlocksEnabled: !!enabled
+ };
+ this.persistExperiments();
+ },
+ toggleStackedBlocks() {
+ this.setStackedBlocksEnabled(!this.experiments.stackedBlocksEnabled);
+ },
updateLiquidGlassPosition(position: LiquidGlassPosition | null) {
this.experiments = {
...this.experiments,
diff --git a/static/src/styles/components/chat/_chat-area.scss b/static/src/styles/components/chat/_chat-area.scss
index 772b85c..396c768 100644
--- a/static/src/styles/components/chat/_chat-area.scss
+++ b/static/src/styles/components/chat/_chat-area.scss
@@ -265,7 +265,9 @@
max-height: 0;
overflow: hidden;
opacity: 0;
- transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
+ transition:
+ max-height 0.26s cubic-bezier(0.4, 0, 0.2, 1),
+ opacity 0.26s cubic-bezier(0.4, 0, 0.2, 1);
}
.collapsible-block.expanded .collapsible-content {
@@ -279,6 +281,11 @@
font-size: 14px;
line-height: 1.6;
color: var(--claude-text-secondary);
+ scrollbar-width: none;
+}
+
+.content-inner::-webkit-scrollbar {
+ display: none;
}
.action-item {
@@ -299,6 +306,131 @@
animation: none;
}
+.stacked-blocks-wrapper {
+ margin: 12px 0 8px;
+}
+
+.stacked-shell {
+ position: relative;
+ border: 1px solid var(--claude-border);
+ border-radius: 16px;
+ background: var(--claude-card);
+ box-shadow: var(--claude-shadow);
+ overflow: hidden;
+ transition:
+ height 280ms cubic-bezier(0.25, 0.9, 0.3, 1),
+ padding-top 280ms cubic-bezier(0.25, 0.9, 0.3, 1);
+ min-height: 0;
+}
+
+.stacked-inner {
+ position: relative;
+ width: 100%;
+ transition: transform 280ms cubic-bezier(0.25, 0.9, 0.3, 1);
+}
+
+.stacked-viewport {
+ overflow: hidden;
+ position: relative;
+ width: 100%;
+}
+
+.stacked-item {
+ border-bottom: 1px solid var(--claude-border);
+}
+
+.stacked-item:last-child {
+ border-bottom: none;
+}
+
+.stacked-block {
+ margin: 0;
+ border: none;
+ border-radius: 0;
+ box-shadow: none;
+ background: transparent;
+}
+
+.stacked-more-block {
+ position: absolute;
+ inset: 0 0 auto 0;
+ background: var(--claude-card);
+ border-bottom: 0 solid var(--claude-border);
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 0 22px;
+ height: 0;
+ opacity: 0;
+ overflow: hidden;
+ cursor: pointer;
+ z-index: 2;
+ transition:
+ height 280ms cubic-bezier(0.25, 0.9, 0.3, 1),
+ padding 280ms cubic-bezier(0.25, 0.9, 0.3, 1),
+ border-bottom-width 280ms cubic-bezier(0.25, 0.9, 0.3, 1),
+ opacity 160ms ease;
+}
+
+.stacked-more-block.visible {
+ opacity: 1;
+ padding: 14px 22px;
+ border-bottom-width: 1px;
+}
+
+.more-icon {
+ width: 18px;
+ height: 18px;
+ display: inline-block;
+ object-fit: contain;
+}
+
+.more-copy {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+}
+
+.more-title {
+ font-weight: 700;
+ color: var(--claude-text);
+}
+
+.more-desc {
+ color: var(--claude-text-secondary);
+ font-size: 12px;
+}
+
+.stacked-enter-from {
+ opacity: 0;
+ transform: translateY(14px);
+}
+
+.stacked-enter-active {
+ transition: all 220ms cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+.stacked-leave-active {
+ position: absolute;
+ width: 100%;
+ transition: all 200ms cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+.stacked-leave-to {
+ opacity: 0;
+ transform: translateY(-12px);
+}
+
+.stacked-move {
+ transition: transform 220ms cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+.stacked-block .collapsible-content {
+ transition:
+ max-height 280ms cubic-bezier(0.25, 0.9, 0.3, 1),
+ opacity 220ms ease;
+}
+
.progress-indicator {
position: absolute;
bottom: 0;
diff --git a/static/src/styles/components/overlays/_overlays.scss b/static/src/styles/components/overlays/_overlays.scss
index 34a9c01..c17cfd4 100644
--- a/static/src/styles/components/overlays/_overlays.scss
+++ b/static/src/styles/components/overlays/_overlays.scss
@@ -170,6 +170,81 @@
margin-bottom: 18px;
}
+.toggle-row {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 12px 14px;
+ border-radius: 14px;
+ border: 1px solid var(--theme-control-border);
+ background: var(--theme-surface-muted);
+ cursor: pointer;
+ transition: border-color 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
+ position: relative;
+}
+
+.toggle-row:hover {
+ border-color: var(--theme-control-border);
+ box-shadow: none;
+ background: var(--theme-surface-muted);
+}
+
+.toggle-row:focus-within {
+ border-color: var(--theme-control-border);
+ box-shadow: none;
+}
+
+.toggle-row input {
+ position: absolute;
+ opacity: 0;
+ pointer-events: none;
+}
+
+.toggle-row .fancy-check {
+ width: 28px;
+ height: 28px;
+ border-radius: 6px;
+ background: transparent;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ box-shadow: none;
+ transition: opacity 0.2s ease;
+}
+
+.toggle-row .fancy-check svg {
+ width: 22px;
+ height: 22px;
+ overflow: visible;
+}
+
+.fancy-path {
+ fill: none;
+ stroke: var(--claude-text-secondary);
+ stroke-width: 5;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ transition: stroke-dasharray 0.5s ease, stroke-dashoffset 0.5s ease, stroke 0.2s ease;
+ stroke-dasharray: 241 9999999;
+ stroke-dashoffset: 0;
+}
+
+.toggle-row input:checked + .fancy-check {
+ background: transparent;
+ box-shadow: none;
+}
+
+.toggle-row input:checked + .fancy-check .fancy-path {
+ stroke: var(--claude-accent);
+ stroke-dasharray: 70.5096664428711 9999999;
+ stroke-dashoffset: -262.2723388671875;
+}
+
+.toggle-row span {
+ color: var(--claude-text);
+ font-weight: 500;
+}
+
.personalization-layout {
display: grid;
grid-template-columns: 200px minmax(0, 1fr);