Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"test-browser": "npx playwright install && node test/unit/browser/index.js",
"test-browser-no-install": "node test/unit/browser/index.js",
"test-node": "mocha test/unit/node/index.js --delay --ui=tdd --timeout=5000 --exit",
"test-phase0-qa": "npm run test-node -- --runGlob \"**/cortexide/test/common/{providerSettingsValidation,onboardingHelpers,attachFileToChat,chatThreadStorageReviver,providerToolFormat,phase0ClaimVerification,modelCapabilities,menubarStackingFix}.test.js\"",
"test-phase0-qa": "npm run test-node -- --runGlob \"**/cortexide/test/common/{providerSettingsValidation,onboardingHelpers,attachFileToChat,chatThreadStorageReviver,providerToolFormat,phase0ClaimVerification,modelCapabilities,menubarStackingFix,designSystem}.test.js\"",
"test-extension": "vscode-test",
"test-build-scripts": "cd build && npm run test",
"check-cyclic-dependencies": "node build/lib/checkCyclicDependencies.ts out",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { PastThreadsList } from './SidebarThreadSelector.js';
import { VoidChatArea, ButtonSubmit, ButtonStop } from './composer/VoidChatArea.js';
import { SelectedFiles } from './composer/SelectedFiles.js';
import { ScrollToBottomContainer } from './composer/ScrollToBottomContainer.js';
import { ContextUsageBar } from './composer/ContextUsageBar.js';
import { LandingPage } from './landing/LandingPage.js';
import { ComposerTabs } from './chrome/ComposerTabs.js';
import { ThreadHeader } from './chrome/ThreadHeader.js';
Expand Down Expand Up @@ -1164,17 +1165,12 @@ export const SidebarChat = () => {

{/* Context usage indicator */}
{modelSel ? (
(() => {
const pctNum = Math.max(0, Math.min(100, Math.round(contextPct * 100)))
const color = contextPct >= 1 ? 'text-[var(--cortex-danger)]' : contextPct > 0.8 ? 'text-[var(--cortex-warning)]' : 'text-void-fg-3'
const barColor = contextPct >= 1 ? 'bg-[var(--cortex-danger)]' : contextPct > 0.8 ? 'bg-[var(--cortex-warning)]' : 'bg-void-fg-3/60'
return <div className='mt-1'>
<div className={`text-[10px] ${color}`}>Context ~{contextTotal} / {contextBudget} tokens ({pctNum}%)</div>
<div className='h-[3px] w-full bg-void-border-3 rounded mt-0.5'>
<div className={`h-[3px] ${barColor} rounded`} style={{ width: `${pctNum}%` }} aria-label={`Context usage ${pctNum}%`} />
</div>
</div>
})()
<ContextUsageBar
className="mt-1"
contextTotal={contextTotal}
contextBudget={contextBudget}
contextPct={contextPct}
/>
) : null}
</div>
</div>
Expand All @@ -1183,17 +1179,12 @@ export const SidebarChat = () => {
<div className='pt-8'>
{inputChatArea}
{modelSel ? (
(() => {
const pctNum = Math.max(0, Math.min(100, Math.round(contextPct * 100)))
const color = contextPct >= 1 ? 'text-[var(--cortex-danger)]' : contextPct > 0.8 ? 'text-[var(--cortex-warning)]' : 'text-void-fg-3'
const barColor = contextPct >= 1 ? 'bg-[var(--cortex-danger)]' : contextPct > 0.8 ? 'bg-[var(--cortex-warning)]' : 'bg-void-fg-3/60'
return <div className='mt-1 px-2'>
<div className={`text-[10px] ${color}`}>Context ~{contextTotal} / {contextBudget} tokens ({pctNum}%)</div>
<div className='h-[3px] w-full bg-void-border-3 rounded mt-0.5'>
<div className={`h-[3px] ${barColor} rounded`} style={{ width: `${pctNum}%` }} aria-label={`Context usage ${pctNum}%`} />
</div>
</div>
})()
<ContextUsageBar
className="mt-1 px-2"
contextTotal={contextTotal}
contextBudget={contextBudget}
contextPct={contextPct}
/>
) : null}
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,44 +215,46 @@ export const UserMessageComponent = ({ chatMessage, messageIdx, isCheckpointGhos
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
<div
// style chatbubble according to role
className={`
text-left rounded-lg max-w-full
${mode === 'edit' ? ''
: mode === 'display' ? 'p-2 flex flex-col bg-void-bg-1 text-void-fg-1 overflow-x-auto cursor-pointer' : ''
}
`}
onClick={() => { if (mode === 'display') { onOpenEdit() } }}
>
{chatbubbleContents}
</div>
{mode === 'display' ? (
<button
type="button"
className="text-left rounded-lg max-w-full p-2 flex flex-col bg-void-bg-1 text-void-fg-1 overflow-x-auto cursor-pointer void-focus-ring"
onClick={onOpenEdit}
aria-label="Edit message"
>
{chatbubbleContents}
</button>
) : (
<div className="text-left rounded-lg max-w-full">
{chatbubbleContents}
</div>
)}



<div
className="absolute -top-1 -right-1 translate-x-0 -translate-y-0 z-1"
// data-tooltip-id='cortex-tooltip'
// data-tooltip-content='Edit message'
// data-tooltip-place='left'
>
<EditSymbol
size={18}
<button
type="button"
className={`
cursor-pointer
p-[2px]
bg-void-bg-1 border border-void-border-1 rounded-md
bg-void-bg-1 border border-void-border-1 rounded-md void-focus-ring
transition-opacity duration-200 ease-in-out
${isHovered || (isFocused && mode === 'edit') ? 'opacity-100' : 'opacity-0'}
`}
aria-label={mode === 'display' ? 'Edit message' : 'Close edit'}
onClick={() => {
if (mode === 'display') {
onOpenEdit()
} else if (mode === 'edit') {
onCloseEdit()
}
}}
/>
>
<EditSymbol size={18} aria-hidden="true" />
</button>
</div>


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/

import React, { useId } from 'react';

export type ContextUsageBarProps = {
contextTotal: number;
contextBudget: number;
contextPct: number;
className?: string;
};

export const ContextUsageBar = ({
contextTotal,
contextBudget,
contextPct,
className = '',
}: ContextUsageBarProps) => {
const labelId = useId();
const pctNum = Math.max(0, Math.min(100, Math.round(contextPct * 100)));
const color = contextPct >= 1
? 'text-[var(--cortex-danger)]'
: contextPct > 0.8
? 'text-[var(--cortex-warning)]'
: 'text-void-fg-3';
const barColor = contextPct >= 1
? 'bg-[var(--cortex-danger)]'
: contextPct > 0.8
? 'bg-[var(--cortex-warning)]'
: 'bg-void-fg-3/60';

return (
<div className={className}>
<div className={`text-[10px] ${color}`} id={labelId}>
Context ~{contextTotal} / {contextBudget} tokens ({pctNum}%)
</div>
<div
className="h-[3px] w-full bg-void-border-3 rounded mt-0.5"
role="progressbar"
aria-labelledby={labelId}
aria-valuemin={0}
aria-valuemax={100}
aria-valuenow={pctNum}
>
<div className={`h-[3px] ${barColor} rounded`} style={{ width: `${pctNum}%` }} />
</div>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -390,12 +390,10 @@ export const VoidChatArea: React.FC<CortexideChatAreaProps> = ({
}}
className={`
gap-x-1
flex flex-col p-2.5 relative input text-left shrink-0
flex flex-col p-2.5 relative cortex-composer-shell text-left shrink-0
rounded-2xl
bg-[#030304]
transition-all duration-200
border border-[rgba(255,255,255,0.08)] focus-within:border-[rgba(255,255,255,0.12)] hover:border-[rgba(255,255,255,0.12)]
${isDragOver ? 'border-blue-500 bg-blue-500/10' : ''}
${isDragOver ? 'border-[var(--cortex-brand)] bg-[var(--cortex-brand-soft)]' : ''}
max-h-[80vh] overflow-y-auto
${className}
`}
Expand Down Expand Up @@ -483,12 +481,19 @@ export const VoidChatArea: React.FC<CortexideChatAreaProps> = ({

{/* Close button (X) if onClose is provided */}
{onClose && (
<div className='absolute -top-1 -right-1 cursor-pointer z-1'>
<IconX
size={12}
className="stroke-[2] opacity-80 text-void-fg-3 hover:brightness-95"
<div className='absolute -top-1 -right-1 z-1'>
<button
type="button"
className="cursor-pointer void-focus-ring rounded-md p-0.5"
onClick={onClose}
/>
aria-label="Close"
>
<IconX
size={12}
className="stroke-[2] opacity-80 text-void-fg-3 hover:brightness-95"
aria-hidden="true"
/>
</button>
</div>
)}
</div>
Expand Down Expand Up @@ -524,8 +529,8 @@ export const ButtonSubmit = ({ className, disabled, ...props }: ButtonProps & Re
return <button
type='button'
className={`rounded-full flex-shrink-0 flex-grow-0 flex items-center justify-center
button-press-animation
${disabled ? 'bg-vscode-disabled-fg cursor-default opacity-50' : 'bg-white cursor-pointer hover:bg-gray-50'}
btn btn-icon btn-submit button-press-animation
${disabled ? 'cursor-default' : 'cursor-pointer'}
${className}
`}
disabled={disabled}
Expand All @@ -542,7 +547,7 @@ export const ButtonSubmit = ({ className, disabled, ...props }: ButtonProps & Re
export const ButtonStop = ({ className, ...props }: ButtonHTMLAttributes<HTMLButtonElement>) => {
return <button
className={`rounded-full flex-shrink-0 flex-grow-0 cursor-pointer flex items-center justify-center
bg-white hover:bg-[var(--cortex-danger)]/5 button-press-animation
btn btn-icon btn-stop button-press-animation
${className}
`}
type='button'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,16 @@ const DEFAULT_PROMPTS = [
];

export const SuggestedPrompts = ({ onSubmit }: { onSubmit: (text: string) => void }) => (
<div className='flex flex-col gap-2 w-full text-nowrap text-void-fg-3 select-none'>
<div className='flex flex-col gap-2 w-full text-nowrap select-none'>
{DEFAULT_PROMPTS.map((text, index) => (
<div
<button
key={index}
className='py-1 px-2 rounded text-sm bg-zinc-700/5 hover:bg-zinc-700/10 dark:bg-zinc-300/5 dark:hover:bg-zinc-300/10 cursor-pointer opacity-80 hover:opacity-100'
type="button"
className='btn btn-sm btn-secondary cortex-prompt-chip w-full text-left py-1 px-2 text-sm opacity-90 hover:opacity-100 void-focus-ring'
onClick={() => onSubmit(text)}
>
{text}
</div>
</button>
))}
</div>
);
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,22 @@ export const ToolHeaderWrapper = ({
{/* title eg "> Edited File" */}
<div className={`
flex items-center min-w-0 overflow-hidden grow
${isClickable ? 'cursor-pointer hover:brightness-125 transition-all duration-150' : ''}
${isClickable ? 'cursor-pointer hover:brightness-125 transition-all duration-150 void-focus-ring rounded-sm' : ''}
`}
role={isClickable ? 'button' : undefined}
tabIndex={isClickable ? 0 : undefined}
onClick={() => {
if (isDropdown) { setIsOpen(v => !v); }
if (onClick) { onClick(); }
}}
onKeyDown={(e) => {
if (!isClickable) return;
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
if (isDropdown) { setIsOpen(v => !v); }
if (onClick) { onClick(); }
}
}}
>
{isDropdown && (<ChevronRight
className={`
Expand Down Expand Up @@ -165,10 +175,19 @@ export const SimplifiedToolHeader = ({
<div className="w-full">
{/* header */}
<div
className={`select-none flex items-center min-h-[24px] ${isDropdown ? 'cursor-pointer' : ''}`}
className={`select-none flex items-center min-h-[24px] ${isDropdown ? 'cursor-pointer void-focus-ring rounded-sm' : ''}`}
role={isDropdown ? 'button' : undefined}
tabIndex={isDropdown ? 0 : undefined}
onClick={() => {
if (isDropdown) { setIsOpen(v => !v); }
}}
onKeyDown={(e) => {
if (!isDropdown) return;
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
setIsOpen(v => !v);
}
}}
>
{isDropdown && (
<ChevronRight
Expand Down
Loading
Loading