feat: project dropdown in sidebar, resizable AI sidebar with snap, ctx & t/s telemetry, live line-number re-anchoring, and thinking indicator
This commit is contained in:
@@ -328,6 +328,25 @@ export class AIStore {
|
||||
});
|
||||
}
|
||||
|
||||
/** Update a suggestion's line number or text anchor when editor blocks shift. */
|
||||
async updateSuggestion(
|
||||
sessionId: string,
|
||||
suggestionId: string,
|
||||
patch: Partial<Pick<AISuggestion, "lineNumber" | "originalText" | "newText" | "text">>,
|
||||
): Promise<AISession> {
|
||||
return this.withTransaction(async () => {
|
||||
const session = this.require(sessionId);
|
||||
const suggestion = session.suggestions.find((s) => s.id === suggestionId);
|
||||
if (!suggestion) return this.clone(session);
|
||||
if (patch.lineNumber !== undefined) suggestion.lineNumber = patch.lineNumber;
|
||||
if (patch.originalText !== undefined) suggestion.originalText = patch.originalText;
|
||||
if (patch.newText !== undefined) suggestion.newText = patch.newText;
|
||||
if (patch.text !== undefined) suggestion.text = patch.text;
|
||||
session.updatedAt = nowIso();
|
||||
return this.clone(session);
|
||||
});
|
||||
}
|
||||
|
||||
/** Add a note to a session and persist it. */
|
||||
async addNote(sessionId: string, note: Omit<AINote, "id" | "createdAt" | "updatedAt">): Promise<AISession> {
|
||||
return this.withTransaction(async () => {
|
||||
|
||||
@@ -184,6 +184,47 @@ body {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Sidebar Project Switcher Dropdown */
|
||||
.ps-sidebar-project-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
padding: 6px 8px;
|
||||
background: var(--ps-surface-2);
|
||||
border: 1px solid var(--ps-border);
|
||||
border-radius: 8px;
|
||||
}
|
||||
.ps-sidebar-project-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
.ps-sidebar-project-select {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding: 5px 8px !important;
|
||||
font-size: 13px !important;
|
||||
font-weight: 600;
|
||||
background: var(--ps-surface) !important;
|
||||
}
|
||||
.ps-sidebar-project-meta {
|
||||
font-size: 11.5px;
|
||||
color: var(--ps-text-muted);
|
||||
padding-left: 26px;
|
||||
}
|
||||
.ps-project-switcher-collapsed {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.ps-project-switcher-native-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
opacity: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ps-topbar {
|
||||
grid-area: topbar;
|
||||
display: flex;
|
||||
@@ -648,8 +689,9 @@ body {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* ---- AI Sidebar (Toggleable Right Rail) ---- */
|
||||
/* ---- AI Sidebar (Toggleable & Resizable Right Rail) ---- */
|
||||
.ps-ai-sidebar {
|
||||
position: relative;
|
||||
width: 370px;
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
@@ -660,6 +702,28 @@ body {
|
||||
font-family: var(--ps-font-ui);
|
||||
overflow: hidden;
|
||||
}
|
||||
.ps-ai-sidebar.is-resizing {
|
||||
user-select: none;
|
||||
}
|
||||
.ps-ai-resize-handle {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: -3px;
|
||||
width: 7px;
|
||||
cursor: col-resize;
|
||||
z-index: 25;
|
||||
background: transparent;
|
||||
transition: background 0.15s ease;
|
||||
}
|
||||
.ps-ai-resize-handle:hover,
|
||||
.ps-ai-sidebar.is-resizing .ps-ai-resize-handle {
|
||||
background: color-mix(in srgb, var(--ps-accent) 55%, transparent);
|
||||
}
|
||||
.ps-ai-sidebar.is-resizing .ps-ai-resize-handle.is-snapped {
|
||||
background: var(--ps-success);
|
||||
box-shadow: 0 0 8px var(--ps-success);
|
||||
}
|
||||
.ps-ai-fullpage {
|
||||
width: 100%;
|
||||
max-width: 900px;
|
||||
@@ -909,6 +973,99 @@ body {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
/* AI Thinking Indicator */
|
||||
.ps-ai-thinking {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 10px;
|
||||
background: color-mix(in srgb, var(--ps-accent) 10%, var(--ps-surface-2));
|
||||
border: 1px dashed color-mix(in srgb, var(--ps-accent) 55%, var(--ps-border));
|
||||
font-size: 12.5px;
|
||||
}
|
||||
.ps-ai-thinking-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: var(--ps-accent);
|
||||
}
|
||||
.ps-ai-thinking-timer {
|
||||
margin-left: auto;
|
||||
font-family: var(--ps-font-mono);
|
||||
font-size: 11px;
|
||||
color: var(--ps-text-muted);
|
||||
}
|
||||
.ps-ai-thinking-stage {
|
||||
font-size: 12px;
|
||||
color: var(--ps-text);
|
||||
}
|
||||
.ps-ai-thinking-trace {
|
||||
font-size: 11.5px;
|
||||
font-style: italic;
|
||||
color: var(--ps-text-muted);
|
||||
border-left: 2px solid var(--ps-border);
|
||||
padding-left: 8px;
|
||||
}
|
||||
.ps-ai-thinking-dots {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
}
|
||||
.ps-ai-thinking-dots > span {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: var(--ps-accent);
|
||||
animation: ps-thinking-bounce 1.2s infinite ease-in-out both;
|
||||
}
|
||||
.ps-ai-thinking-dots > span:nth-child(1) { animation-delay: -0.24s; }
|
||||
.ps-ai-thinking-dots > span:nth-child(2) { animation-delay: -0.12s; }
|
||||
@keyframes ps-thinking-bounce {
|
||||
0%, 80%, 100% { transform: scale(0.45); opacity: 0.4; }
|
||||
40% { transform: scale(1); opacity: 1; }
|
||||
}
|
||||
|
||||
/* Non-intrusive AI Telemetry Bar (Context & t/s) */
|
||||
.ps-ai-telemetry-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
padding: 5px 12px;
|
||||
border-top: 1px solid var(--ps-border);
|
||||
background: var(--ps-surface-2);
|
||||
font-family: var(--ps-font-mono);
|
||||
font-size: 11px;
|
||||
color: var(--ps-text-muted);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.ps-ai-telemetry-ctx {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
}
|
||||
.ps-ai-telemetry-meter {
|
||||
width: 44px;
|
||||
height: 4px;
|
||||
border-radius: 999px;
|
||||
background: var(--ps-border);
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
}
|
||||
.ps-ai-telemetry-meter-fill {
|
||||
display: block;
|
||||
height: 100%;
|
||||
background: var(--ps-accent);
|
||||
border-radius: 999px;
|
||||
transition: width 0.2s ease;
|
||||
}
|
||||
.ps-ai-telemetry-speed {
|
||||
white-space: nowrap;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* ---- Projects Page & Template Wizard ---- */
|
||||
.ps-projects-grid {
|
||||
display: grid;
|
||||
|
||||
Reference in New Issue
Block a user