Add tw prefix to tailwind (#1497)

This commit is contained in:
Zero Liu 2025-05-31 22:30:54 -07:00 committed by GitHub
parent 41edb07a5d
commit 972b63d002
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
68 changed files with 1091 additions and 1043 deletions

View file

@ -2,15 +2,16 @@
"root": true,
"parser": "@typescript-eslint/parser",
"env": { "node": true },
"plugins": ["@typescript-eslint"],
"plugins": ["@typescript-eslint", "tailwindcss"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react/recommended",
"plugin:react-hooks/recommended"
"plugin:react-hooks/recommended",
"plugin:tailwindcss/recommended",
],
"parserOptions": {
"sourceType": "module"
"sourceType": "module",
},
"rules": {
"no-unused-vars": "off",
@ -20,20 +21,32 @@
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-explicit-any": "off",
"react/prop-types": "off",
"react-hooks/exhaustive-deps": "error"
"react-hooks/exhaustive-deps": "error",
"tailwindcss/classnames-order": "error",
"tailwindcss/enforces-negative-arbitrary-values": "error",
"tailwindcss/enforces-shorthand": "error",
"tailwindcss/migration-from-tailwind-2": "error",
"tailwindcss/no-arbitrary-value": "off",
"tailwindcss/no-custom-classname": ["error"],
"tailwindcss/no-contradicting-classname": "error",
},
"overrides": [
{
"files": ["*.json", "*.jsonc", ".eslintrc"],
"parser": "jsonc-eslint-parser",
"rules": {
"jsonc/auto": "error"
}
}
"jsonc/auto": "error",
},
},
],
"settings": {
"react": {
"version": "detect"
}
}
"version": "detect",
},
"tailwindcss": {
"callees": ["classnames", "clsx", "ctl", "cn", "cva"],
"config": "./tailwind.config.js",
"cssFiles": ["**/*.css", "!**/node_modules", "!**/.*", "!**/dist", "!**/build"],
},
},
}

View file

@ -15,7 +15,7 @@ jobs:
strategy:
matrix:
node-version: [20.x]
node-version: [22.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:

View file

@ -8,7 +8,7 @@
"css": "src/styles/tailwind.css",
"baseColor": "zinc",
"cssVariables": true,
"prefix": ""
"prefix": "tw"
},
"aliases": {
"components": "@/components",

18
package-lock.json generated
View file

@ -96,6 +96,7 @@
"eslint-plugin-json": "^4.0.1",
"eslint-plugin-react": "^7.37.3",
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-tailwindcss": "^3.18.0",
"husky": "^9.1.5",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.5.0",
@ -11699,6 +11700,22 @@
"semver": "bin/semver.js"
}
},
"node_modules/eslint-plugin-tailwindcss": {
"version": "3.18.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-tailwindcss/-/eslint-plugin-tailwindcss-3.18.0.tgz",
"integrity": "sha512-PQDU4ZMzFH0eb2DrfHPpbgo87Zgg2EXSMOj1NSfzdZm+aJzpuwGerfowMIaVehSREEa0idbf/eoNYAOHSJoDAQ==",
"dev": true,
"dependencies": {
"fast-glob": "^3.2.5",
"postcss": "^8.4.4"
},
"engines": {
"node": ">=18.12.0"
},
"peerDependencies": {
"tailwindcss": "^3.4.0"
}
},
"node_modules/eslint-visitor-keys": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
@ -18176,7 +18193,6 @@
"url": "https://github.com/sponsors/ai"
}
],
"peer": true,
"dependencies": {
"nanoid": "^3.3.6",
"picocolors": "^1.0.0",

View file

@ -59,6 +59,7 @@
"eslint-plugin-json": "^4.0.1",
"eslint-plugin-react": "^7.37.3",
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-tailwindcss": "^3.18.0",
"husky": "^9.1.5",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.5.0",

View file

@ -644,7 +644,7 @@ ${chatContent}`;
const renderChatComponents = () => (
<>
<div className="flex flex-col size-full overflow-hidden">
<div className="tw-flex tw-size-full tw-flex-col tw-overflow-hidden">
<NewVersionBanner currentVersion={plugin.manifest.version} />
<ChatMessages
chatHistory={chatHistory}
@ -694,11 +694,11 @@ ${chatContent}`;
);
return (
<div className="w-full h-full flex flex-col overflow-hidden">
<div className="h-full">
<div className="flex flex-col h-full relative">
<div className="tw-flex tw-size-full tw-flex-col tw-overflow-hidden">
<div className="tw-h-full">
<div className="tw-relative tw-flex tw-h-full tw-flex-col">
{selectedChain === ChainType.PROJECT_CHAIN && (
<div className={`${selectedChain === ChainType.PROJECT_CHAIN ? "z-modal" : ""}`}>
<div className={`${selectedChain === ChainType.PROJECT_CHAIN ? "tw-z-modal" : ""}`}>
<ProjectList
projects={settings.projectList || []}
defaultOpen={true}

View file

@ -2,20 +2,21 @@ import React from "react";
export function CustomPromptSyntaxInstruction() {
return (
<ul className="text-sm px-4 m-0">
<ul className="tw-m-0 tw-px-4 tw-text-sm">
<li>
<span className="font-medium text-accent">{"{}"}</span> represents the selected text.
<span className="tw-font-medium tw-text-accent">{"{}"}</span> represents the selected text.
</li>
<li>
<span className="font-medium text-accent">{`{[[Note Title]]}`}</span> represents a note.
</li>
<li>
<span className="font-medium text-accent">{`{activeNote}`}</span> represents the active
<span className="tw-font-medium tw-text-accent">{`{[[Note Title]]}`}</span> represents a
note.
</li>
<li>
<span className="font-medium text-accent">{`{#tag1, #tag2}`}</span> represents ALL notes
with ANY of the specified tags in their property (an OR operation).
<span className="tw-font-medium tw-text-accent">{`{activeNote}`}</span> represents the
active note.
</li>
<li>
<span className="tw-font-medium tw-text-accent">{`{#tag1, #tag2}`}</span> represents ALL
notes with ANY of the specified tags in their property (an OR operation).
</li>
</ul>
);

View file

@ -23,9 +23,9 @@ export function isEllipsesActive(
function getLineClampClass(lineClamp: number): string {
switch (lineClamp) {
case 2:
return "line-clamp-2";
return "tw-line-clamp-2";
case 3:
return "line-clamp-3";
return "tw-line-clamp-3";
default:
return "";
}
@ -81,8 +81,8 @@ export const TruncatedText = ({
{...props}
ref={textRef}
className={cn(
"max-w-full text-normal",
(!lineClamp || lineClamp <= 1) && "truncate",
"tw-max-w-full tw-text-normal",
(!lineClamp || lineClamp <= 1) && "tw-truncate",
lineClamp && getLineClampClass(lineClamp),
className
)}
@ -91,7 +91,7 @@ export const TruncatedText = ({
{children}
</div>
</TooltipTrigger>
<TooltipContent className="max-w-64 text-wrap break-words text-body-normal">
<TooltipContent className="tw-max-w-64 tw-text-wrap tw-break-words">
{tooltipContent ?? children}
</TooltipContent>
</Tooltip>

View file

@ -40,7 +40,7 @@ export const ChatButtons: React.FC<ChatButtonsProps> = ({
}) => {
return (
<div
className={cn("flex gap-1", {
className={cn("tw-flex tw-gap-1", {
"group-hover:opacity-100 opacity-0": !Platform.isMobile,
})}
>
@ -49,7 +49,7 @@ export const ChatButtons: React.FC<ChatButtonsProps> = ({
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost2" size="fit" onClick={onCopy} title="Copy">
{isCopied ? <Check className="size-4" /> : <Copy className="size-4" />}
{isCopied ? <Check className="tw-size-4" /> : <Copy className="tw-size-4" />}
</Button>
</TooltipTrigger>
<TooltipContent>Copy</TooltipContent>
@ -57,7 +57,7 @@ export const ChatButtons: React.FC<ChatButtonsProps> = ({
<Tooltip>
<TooltipTrigger asChild>
<Button onClick={onEdit} variant="ghost2" size="fit" title="Edit">
<PenSquare className="size-4" />
<PenSquare className="tw-size-4" />
</Button>
</TooltipTrigger>
<TooltipContent>Edit</TooltipContent>
@ -65,7 +65,7 @@ export const ChatButtons: React.FC<ChatButtonsProps> = ({
<Tooltip>
<TooltipTrigger asChild>
<Button onClick={onDelete} variant="ghost2" size="fit" title="Delete">
<Trash2 className="size-4" />
<Trash2 className="tw-size-4" />
</Button>
</TooltipTrigger>
<TooltipContent>Delete</TooltipContent>
@ -77,7 +77,7 @@ export const ChatButtons: React.FC<ChatButtonsProps> = ({
<Tooltip>
<TooltipTrigger asChild>
<Button onClick={onShowSources} variant="ghost2" size="fit" title="Show Sources">
<LibraryBig className="size-4" />
<LibraryBig className="tw-size-4" />
</Button>
</TooltipTrigger>
<TooltipContent>Show Sources</TooltipContent>
@ -91,7 +91,7 @@ export const ChatButtons: React.FC<ChatButtonsProps> = ({
size="fit"
title="Insert / Replace at cursor"
>
<TextCursorInput className="size-4" />
<TextCursorInput className="tw-size-4" />
</Button>
</TooltipTrigger>
<TooltipContent>Insert / Replace at cursor</TooltipContent>
@ -99,7 +99,7 @@ export const ChatButtons: React.FC<ChatButtonsProps> = ({
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost2" size="fit" onClick={onCopy} title="Copy">
{isCopied ? <Check className="size-4" /> : <Copy className="size-4" />}
{isCopied ? <Check className="tw-size-4" /> : <Copy className="tw-size-4" />}
</Button>
</TooltipTrigger>
<TooltipContent>Copy</TooltipContent>
@ -107,7 +107,7 @@ export const ChatButtons: React.FC<ChatButtonsProps> = ({
<Tooltip>
<TooltipTrigger asChild>
<Button onClick={onRegenerate} variant="ghost2" size="fit" title="Regenerate">
<RotateCw className="size-4" />
<RotateCw className="tw-size-4" />
</Button>
</TooltipTrigger>
<TooltipContent>Regenerate</TooltipContent>
@ -115,7 +115,7 @@ export const ChatButtons: React.FC<ChatButtonsProps> = ({
<Tooltip>
<TooltipTrigger asChild>
<Button onClick={onDelete} variant="ghost2" size="fit" title="Delete">
<Trash2 className="size-4" />
<Trash2 className="tw-size-4" />
</Button>
</TooltipTrigger>
<TooltipContent>Delete</TooltipContent>

View file

@ -23,11 +23,11 @@ function ContextNote({
onRemoveContext: (path: string) => void;
}) {
return (
<Badge className="text-xs pl-2 pr-0.5 py-0 items-center">
<div className="flex items-center gap-1">
<span className="max-w-40 truncate">{note.basename}</span>
{isActive && <span className="text-xs text-faint">Current</span>}
{note.extension === "pdf" && <span className="text-xs text-faint">pdf</span>}
<Badge className="tw-items-center tw-py-0 tw-pl-2 tw-pr-0.5 tw-text-xs">
<div className="tw-flex tw-items-center tw-gap-1">
<span className="tw-max-w-40 tw-truncate">{note.basename}</span>
{isActive && <span className="tw-text-xs tw-text-faint">Current</span>}
{note.extension === "pdf" && <span className="tw-text-xs tw-text-faint">pdf</span>}
</div>
<Button
variant="ghost2"
@ -35,7 +35,7 @@ function ContextNote({
onClick={() => onRemoveContext(note.path)}
aria-label="Remove from context"
>
<X className="size-4" />
<X className="tw-size-4" />
</Button>
</Badge>
);
@ -43,10 +43,10 @@ function ContextNote({
function ContextUrl({ url, onRemoveUrl }: { url: string; onRemoveUrl: (url: string) => void }) {
return (
<Badge className="text-xs pl-2 pr-0.5 py-0 items-center">
<div className="flex items-center gap-1">
<span className="max-w-40 truncate">{url}</span>
<span className="text-xs text-faint">Link</span>
<Badge className="tw-items-center tw-py-0 tw-pl-2 tw-pr-0.5 tw-text-xs">
<div className="tw-flex tw-items-center tw-gap-1">
<span className="tw-max-w-40 tw-truncate">{url}</span>
<span className="tw-text-xs tw-text-faint">Link</span>
</div>
<Button
variant="ghost2"
@ -54,7 +54,7 @@ function ContextUrl({ url, onRemoveUrl }: { url: string; onRemoveUrl: (url: stri
onClick={() => onRemoveUrl(url)}
aria-label="Remove from context"
>
<X className="size-4" />
<X className="tw-size-4" />
</Button>
</Badge>
);
@ -87,19 +87,19 @@ export const ChatContextMenu: React.FC<ChatContextMenuProps> = ({
const hasContext = uniqueNotes.length > 0 || uniqueUrls.length > 0 || !!activeNote;
return (
<div className="flex items-center w-full gap-1">
<div className="flex items-start h-full">
<div className="tw-flex tw-w-full tw-items-center tw-gap-1">
<div className="tw-flex tw-h-full tw-items-start">
<Button
onClick={onAddContext}
variant="ghost2"
size="fit"
className="border border-border border-solid rounded-sm ml-1"
className="tw-ml-1 tw-rounded-sm tw-border tw-border-solid tw-border-border"
>
<Plus className="size-4" />
{!hasContext && <span className="text-xs leading-4 pr-1">Add context</span>}
<Plus className="tw-size-4" />
{!hasContext && <span className="tw-pr-1 tw-text-xs tw-leading-4">Add context</span>}
</Button>
</div>
<div className="flex gap-1 flex-wrap flex-1">
<div className="tw-flex tw-flex-1 tw-flex-wrap tw-gap-1">
{activeNote && (
<ContextNote
key={activeNote.path}

View file

@ -157,21 +157,21 @@ export function ChatControls({
};
return (
<div className="w-full py-1 flex justify-between items-center px-1">
<div className="flex-1">
<div className="tw-flex tw-w-full tw-items-center tw-justify-between tw-p-1">
<div className="tw-flex-1">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost2" size="fit" className="ml-1">
<Button variant="ghost2" size="fit" className="tw-ml-1">
{selectedChain === ChainType.LLM_CHAIN && "chat"}
{selectedChain === ChainType.VAULT_QA_CHAIN && "vault QA"}
{selectedChain === ChainType.COPILOT_PLUS_CHAIN && (
<div className="flex items-center gap-1">
<Sparkles className="size-4" />
<div className="tw-flex tw-items-center tw-gap-1">
<Sparkles className="tw-size-4" />
copilot plus (beta)
</div>
)}
{selectedChain === ChainType.PROJECT_CHAIN && "projects (alpha)"}
<ChevronDown className="size-5 mt-0.5" />
<ChevronDown className="tw-mt-0.5 tw-size-5" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
@ -195,8 +195,8 @@ export function ChatControls({
handleModeChange(ChainType.COPILOT_PLUS_CHAIN);
}}
>
<div className="flex items-center gap-1">
<Sparkles className="size-4" />
<div className="tw-flex tw-items-center tw-gap-1">
<Sparkles className="tw-size-4" />
copilot plus (beta)
</div>
</DropdownMenuItem>
@ -208,18 +208,18 @@ export function ChatControls({
}}
>
copilot plus (beta)
<SquareArrowOutUpRight className="size-3" />
<SquareArrowOutUpRight className="tw-size-3" />
</DropdownMenuItem>
)}
{isPlusUser ? (
<DropdownMenuItem
className="flex items-center gap-1"
className="tw-flex tw-items-center tw-gap-1"
onSelect={() => {
handleModeChange(ChainType.PROJECT_CHAIN);
}}
>
<LibraryBig className="size-4" />
<LibraryBig className="tw-size-4" />
projects (alpha)
</DropdownMenuItem>
) : (
@ -230,7 +230,7 @@ export function ChatControls({
}}
>
copilot plus (beta)
<SquareArrowOutUpRight className="size-3" />
<SquareArrowOutUpRight className="tw-size-3" />
</DropdownMenuItem>
)}
</DropdownMenuContent>
@ -240,7 +240,7 @@ export function ChatControls({
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost2" size="icon" title="New Chat" onClick={onNewChat}>
<MessageCirclePlus className="size-4" />
<MessageCirclePlus className="tw-size-4" />
</Button>
</TooltipTrigger>
<TooltipContent>New Chat</TooltipContent>
@ -249,7 +249,7 @@ export function ChatControls({
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost2" size="icon" title="Save Chat as Note" onClick={onSaveAsNote}>
<Download className="size-4" />
<Download className="tw-size-4" />
</Button>
</TooltipTrigger>
<TooltipContent>Save Chat as Note</TooltipContent>
@ -258,7 +258,7 @@ export function ChatControls({
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost2" size="icon" title="Chat History" onClick={onLoadHistory}>
<History className="size-4" />
<History className="tw-size-4" />
</Button>
</TooltipTrigger>
<TooltipContent>Chat History</TooltipContent>
@ -267,32 +267,32 @@ export function ChatControls({
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost2" size="icon" title="Advanced Settings">
<MoreHorizontal className="size-4" />
<MoreHorizontal className="tw-size-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-64">
<DropdownMenuContent align="end" className="tw-w-64">
<DropdownMenuItem
className="flex justify-between"
className="tw-flex tw-justify-between"
onSelect={(e) => {
e.preventDefault();
updateSetting("showSuggestedPrompts", !settings.showSuggestedPrompts);
}}
>
<div className="flex items-center gap-2">
<Sparkles className="size-4" />
<div className="tw-flex tw-items-center tw-gap-2">
<Sparkles className="tw-size-4" />
Suggested Prompt
</div>
<SettingSwitch checked={settings.showSuggestedPrompts} />
</DropdownMenuItem>
<DropdownMenuItem
className="flex justify-between"
className="tw-flex tw-justify-between"
onSelect={(e) => {
e.preventDefault();
updateSetting("showRelevantNotes", !settings.showRelevantNotes);
}}
>
<div className="flex items-center gap-2">
<FileText className="size-4" />
<div className="tw-flex tw-items-center tw-gap-2">
<FileText className="tw-size-4" />
Relevant Note
</div>
<SettingSwitch checked={settings.showRelevantNotes} />
@ -300,31 +300,31 @@ export function ChatControls({
{selectedChain === ChainType.PROJECT_CHAIN ? (
<>
<DropdownMenuItem
className="flex items-center gap-2"
className="tw-flex tw-items-center tw-gap-2"
onSelect={() => reloadCurrentProject()}
>
<RefreshCw className="size-4" />
<RefreshCw className="tw-size-4" />
Reload Current Project
</DropdownMenuItem>
<DropdownMenuItem
className="flex items-center gap-2 text-destructive"
className="tw-flex tw-items-center tw-gap-2"
onSelect={() => forceRebuildCurrentProjectContext()}
>
<AlertTriangle className="size-4" />
<AlertTriangle className="tw-size-4" />
Force Rebuild Context
</DropdownMenuItem>
</>
) : (
<>
<DropdownMenuItem
className="flex items-center gap-2"
className="tw-flex tw-items-center tw-gap-2"
onSelect={() => refreshVaultIndex()}
>
<RefreshCw className="size-4" />
<RefreshCw className="tw-size-4" />
Refresh Vault Index
</DropdownMenuItem>
<DropdownMenuItem
className="flex items-center gap-2"
className="tw-flex tw-items-center tw-gap-2"
onSelect={() => {
const modal = new ConfirmModal(
app,
@ -335,7 +335,7 @@ export function ChatControls({
modal.open();
}}
>
<AlertTriangle className="size-4" />
<AlertTriangle className="tw-size-4" />
Force Reindex Vault
</DropdownMenuItem>
</>

View file

@ -432,7 +432,7 @@ const ChatInput = forwardRef<{ focus: () => void }, ChatInputProps>(
return (
<div
className="flex flex-col gap-0.5 w-full border border-border border-solid rounded-md pt-2 pb-1 px-1 @container/chat-input"
className="tw-flex tw-w-full tw-flex-col tw-gap-0.5 tw-rounded-md tw-border tw-border-solid tw-border-border tw-px-1 tw-pb-1 tw-pt-2 tw-@container/chat-input"
ref={containerRef}
>
<ContextControl
@ -461,25 +461,25 @@ const ChatInput = forwardRef<{ focus: () => void }, ChatInputProps>(
onClick={() => setSelectedImages((prev) => prev.filter((_, i) => i !== index))}
title="Remove image"
>
<X className="size-4" />
<X className="tw-size-4" />
</button>
</div>
))}
</div>
)}
<div className="relative" {...(isCopilotPlus ? getRootProps() : {})}>
<div className="tw-relative" {...(isCopilotPlus ? getRootProps() : {})}>
{isProjectLoading && (
<div className="absolute inset-0 z-modal bg-background/80 backdrop-blur-sm flex items-center justify-center">
<div className="flex items-center gap-2">
<Loader2 className="size-4 animate-spin" />
<span className="text-sm">{loadingMessages[loadingMessageIndex]}</span>
<div className="tw-absolute tw-inset-0 tw-z-modal tw-flex tw-items-center tw-justify-center tw-bg-primary tw-opacity-80 tw-backdrop-blur-sm">
<div className="tw-flex tw-items-center tw-gap-2">
<Loader2 className="tw-size-4 tw-animate-spin" />
<span className="tw-text-sm">{loadingMessages[loadingMessageIndex]}</span>
</div>
</div>
)}
<textarea
ref={textAreaRef}
className="w-full bg-transparent focus-visible:ring-0 border-none min-h-[60px] max-h-40 overflow-y-auto resize-none px-2 rounded-md text-sm text-normal"
className="tw-max-h-40 tw-min-h-[60px] tw-w-full tw-resize-none tw-overflow-y-auto tw-rounded-md tw-border-none tw-bg-transparent tw-px-2 tw-text-sm tw-text-normal focus-visible:tw-ring-0"
placeholder={
"Ask anything. [[ for notes. / for custom prompts. " +
(isCopilotPlus ? "@ for tools." : "")
@ -495,18 +495,18 @@ const ChatInput = forwardRef<{ focus: () => void }, ChatInputProps>(
<input {...getInputProps()} />
{/* Overlay that appears when dragging */}
{isDragActive && (
<div className="absolute inset-0 bg-primary border border-dashed border-primary rounded-md flex items-center justify-center">
<span className="text-primary">Drop images here...</span>
<div className="tw-absolute tw-inset-0 tw-flex tw-items-center tw-justify-center tw-rounded-md tw-border tw-border-dashed tw-bg-primary">
<span>Drop images here...</span>
</div>
)}
</>
)}
</div>
<div className="flex gap-1 justify-between px-1 h-6">
<div className="tw-flex tw-h-6 tw-justify-between tw-gap-1 tw-px-1">
{isGenerating ? (
<div className="flex items-center gap-1 px-1 text-faint text-sm">
<Loader2 className="size-3 animate-spin" />
<div className="tw-flex tw-items-center tw-gap-1 tw-px-1 tw-text-sm tw-text-faint">
<Loader2 className="tw-size-3 tw-animate-spin" />
<span>Generating...</span>
</div>
) : (
@ -514,7 +514,7 @@ const ChatInput = forwardRef<{ focus: () => void }, ChatInputProps>(
<DropdownMenuTrigger asChild>
<Button variant="ghost2" size="fit" disabled={disableModelSwitch}>
{modelError ? (
<span className="text-error">Model Load Failed</span>
<span className="tw-text-error">Model Load Failed</span>
) : settings.activeModels.find(
(model) =>
model.enabled && getModelKeyFromModel(model) === getDisplayModelKey()
@ -531,7 +531,7 @@ const ChatInput = forwardRef<{ focus: () => void }, ChatInputProps>(
) : (
"Select Model"
)}
{!disableModelSwitch && <ChevronDown className="size-5 mt-0.5" />}
{!disableModelSwitch && <ChevronDown className="tw-mt-0.5 tw-size-5" />}
</Button>
</DropdownMenuTrigger>
@ -567,7 +567,7 @@ const ChatInput = forwardRef<{ focus: () => void }, ChatInputProps>(
}
}
}}
className={!hasApiKey ? "opacity-50 cursor-not-allowed" : ""}
className={!hasApiKey ? "tw-cursor-not-allowed tw-opacity-50" : ""}
>
<ModelDisplay model={model} iconSize={12} />
</DropdownMenuItem>
@ -577,15 +577,15 @@ const ChatInput = forwardRef<{ focus: () => void }, ChatInputProps>(
</DropdownMenu>
)}
<div className="flex items-center gap-1">
<div className="tw-flex tw-items-center tw-gap-1">
{isGenerating ? (
<Button
variant="ghost2"
size="fit"
className="text-muted"
className="tw-text-muted"
onClick={() => onStopGenerating()}
>
<StopCircle className="size-4" />
<StopCircle className="tw-size-4" />
Stop
</Button>
) : (
@ -598,16 +598,16 @@ const ChatInput = forwardRef<{ focus: () => void }, ChatInputProps>(
new AddImageModal(app, onAddImage).open();
}}
>
<Image className="w-4 h-4" />
<Image className="tw-size-4" />
</Button>
)}
<Button
variant="ghost2"
size="fit"
className="text-muted"
className="tw-text-muted"
onClick={() => onSendMessage(false)}
>
<CornerDownLeft className="!size-3" />
<CornerDownLeft className="!tw-size-3" />
<span>chat</span>
</Button>
@ -615,21 +615,21 @@ const ChatInput = forwardRef<{ focus: () => void }, ChatInputProps>(
<Button
variant="ghost2"
size="fit"
className="text-muted @xs/chat-input:inline-flex hidden"
className="tw-hidden tw-text-muted @xs/chat-input:tw-inline-flex"
onClick={() => onSendMessage(true)}
>
<div className="flex items-center gap-1">
<div className="tw-flex tw-items-center tw-gap-1">
{Platform.isMacOS ? (
<div className="flex items-center">
<Command className="!size-3" />
<ArrowBigUp className="!size-3" />
<CornerDownLeft className="!size-3" />
<div className="tw-flex tw-items-center">
<Command className="!tw-size-3" />
<ArrowBigUp className="!tw-size-3" />
<CornerDownLeft className="!tw-size-3" />
</div>
) : (
<div className="flex items-center">
<div className="tw-flex tw-items-center">
<span>Ctrl</span>
<ArrowBigUp className="size-4" />
<CornerDownLeft className="!size-3" />
<ArrowBigUp className="tw-size-4" />
<CornerDownLeft className="!tw-size-3" />
</div>
)}
<span>vault</span>

View file

@ -64,7 +64,7 @@ const ChatMessages = memo(
if (!chatHistory.filter((message) => message.isVisible).length && !currentAiMessage) {
return (
<div className="flex flex-col gap-2 overflow-y-auto w-full h-full">
<div className="tw-flex tw-size-full tw-flex-col tw-gap-2 tw-overflow-y-auto">
{showHelperComponents && settings.showRelevantNotes && (
<RelevantNotes
onInsertToChat={onInsertToChat}
@ -84,10 +84,10 @@ const ChatMessages = memo(
};
return (
<div className="flex flex-col flex-1 h-full overflow-hidden">
<div className="tw-flex tw-h-full tw-flex-1 tw-flex-col tw-overflow-hidden">
{showHelperComponents && settings.showRelevantNotes && (
<RelevantNotes
className="mb-4"
className="tw-mb-4"
onInsertToChat={onInsertToChat}
defaultOpen={false}
key="relevant-notes-in-chat"
@ -95,7 +95,7 @@ const ChatMessages = memo(
)}
<div
data-testid="chat-messages"
className="flex flex-col items-start justify-start flex-1 overflow-y-auto w-full break-words text-[calc(var(--font-text-size)_-_2px)] box-border scroll-smooth mt-auto select-text"
className="tw-mt-auto tw-box-border tw-flex tw-w-full tw-flex-1 tw-select-text tw-flex-col tw-items-start tw-justify-start tw-overflow-y-auto tw-scroll-smooth tw-break-words tw-text-[calc(var(--font-text-size)_-_2px)]"
>
{chatHistory.map(
(message, index) =>

View file

@ -19,12 +19,12 @@ function MessageContext({ context }: { context: ChatMessage["context"] }) {
}
return (
<div className="flex gap-2 flex-wrap">
<div className="tw-flex tw-flex-wrap tw-gap-2">
{context.notes.map((note) => (
<Tooltip key={note.path}>
<TooltipTrigger asChild>
<Badge variant="secondary">
<span className="max-w-40 truncate">{note.basename}</span>
<span className="tw-max-w-40 tw-truncate">{note.basename}</span>
</Badge>
</TooltipTrigger>
<TooltipContent>{note.path}</TooltipContent>
@ -34,7 +34,7 @@ function MessageContext({ context }: { context: ChatMessage["context"] }) {
<Tooltip key={url}>
<TooltipTrigger asChild>
<Badge variant="secondary">
<span className="max-w-40 truncate">{url}</span>
<span className="tw-max-w-40 tw-truncate">{url}</span>
</Badge>
</TooltipTrigger>
<TooltipContent>{url}</TooltipContent>
@ -101,7 +101,7 @@ const ChatSingleMessage: React.FC<ChatSingleMessageProps> = ({
content = content.replace(/<think>([\s\S]*?)<\/think>/g, (match, thinkContent) => {
return `<details style="${detailsStyle}">
<summary style="${summaryStyle}">Thought for a second</summary>
<div class="text-muted" style="${contentStyle}">${thinkContent.trim()}</div>
<div class="tw-text-muted" style="${contentStyle}">${thinkContent.trim()}</div>
</details>\n\n`;
});
@ -110,7 +110,7 @@ const ChatSingleMessage: React.FC<ChatSingleMessageProps> = ({
/<think>([\s\S]*)$/,
(match, partialContent) => `<div style="${detailsStyle}">
<div style="${summaryStyle}">Thinking...</div>
<div class="text-muted" style="${contentStyle}">${partialContent.trim()}</div>
<div class="tw-text-muted" style="${contentStyle}">${partialContent.trim()}</div>
</div>`
);
return content;
@ -121,7 +121,7 @@ const ChatSingleMessage: React.FC<ChatSingleMessageProps> = ({
return content.replace(thinkRegex, (match, thinkContent) => {
return `<details style="${detailsStyle}">
<summary style="${summaryStyle}">Thought for a second</summary>
<div class="text-muted" style="${contentStyle}">${thinkContent.trim()}</div>
<div class="tw-text-muted" style="${contentStyle}">${thinkContent.trim()}</div>
</details>\n\n`;
});
};
@ -422,11 +422,11 @@ const ChatSingleMessage: React.FC<ChatSingleMessageProps> = ({
const renderMessageContent = () => {
if (message.content) {
return (
<div className="message-content-items">
<div className="tw-flex tw-flex-col tw-gap-3">
{message.content.map((item, index) => {
if (item.type === "text") {
return (
<div key={index} className="message-text-content">
<div key={index}>
{message.sender === USER_SENDER && isEditing ? (
<textarea
ref={textareaRef}
@ -438,13 +438,13 @@ const ChatSingleMessage: React.FC<ChatSingleMessageProps> = ({
className="edit-textarea"
/>
) : message.sender === USER_SENDER ? (
<div className="whitespace-pre-wrap break-words font-normal text-[calc(var(--font-text-size)_-_2px)]">
<div className="tw-whitespace-pre-wrap tw-break-words tw-text-[calc(var(--font-text-size)_-_2px)] tw-font-normal">
{message.message}
</div>
) : (
<div
ref={contentRef}
className={message.isErrorMessage ? "text-error" : ""}
className={message.isErrorMessage ? "tw-text-error" : ""}
></div>
)}
</div>
@ -478,30 +478,32 @@ const ChatSingleMessage: React.FC<ChatSingleMessageProps> = ({
className="edit-textarea"
/>
) : message.sender === USER_SENDER ? (
<div className="whitespace-pre-wrap break-words font-normal text-[calc(var(--font-text-size)_-_2px)]">
<div className="tw-whitespace-pre-wrap tw-break-words tw-text-[calc(var(--font-text-size)_-_2px)] tw-font-normal">
{message.message}
</div>
) : (
<div ref={contentRef} className={message.isErrorMessage ? "text-error" : ""}></div>
<div ref={contentRef} className={message.isErrorMessage ? "tw-text-error" : ""}></div>
);
};
return (
<div className="flex flex-col w-full my-1">
<div className="tw-my-1 tw-flex tw-w-full tw-flex-col">
<div
className={cn(
"flex rounded-md p-2 mx-2 gap-2 group",
message.sender === USER_SENDER && "border border-border border-solid"
"tw-group tw-mx-2 tw-flex tw-gap-2 tw-rounded-md tw-p-2",
message.sender === USER_SENDER && "tw-border tw-border-solid tw-border-border"
)}
>
<div className="w-6 shrink-0">{message.sender === USER_SENDER ? <User /> : <Bot />}</div>
<div className="flex flex-col flex-grow max-w-full gap-2 overflow-hidden">
<div className="tw-w-6 tw-shrink-0">
{message.sender === USER_SENDER ? <User /> : <Bot />}
</div>
<div className="tw-flex tw-max-w-full tw-grow tw-flex-col tw-gap-2 tw-overflow-hidden">
{!isEditing && <MessageContext context={message.context} />}
<div className="message-content">{renderMessageContent()}</div>
{!isStreaming && (
<div className="flex justify-between items-center">
<div className="text-faint text-xs">{message.timestamp?.display}</div>
<div className="tw-flex tw-items-center tw-justify-between">
<div className="tw-text-xs tw-text-faint">{message.timestamp?.display}</div>
<ChatButtons
message={message}
onCopy={copyToClipboard}

View file

@ -105,23 +105,23 @@ export const ComposerCodeBlock: React.FC<ComposerCodeBlockProps> = ({
};
return (
<div className="border border-border border-solid rounded-md my-2 flex flex-col overflow-hidden">
<div className="tw-my-2 tw-flex tw-flex-col tw-overflow-hidden tw-rounded-md tw-border tw-border-solid tw-border-border">
{note_path && (
<div className="flex justify-between items-center border-[0px] border-b border-border border-solid gap-2 p-2 overflow-hidden">
<div className="text-xs p-1 text-muted-foreground truncate flex-1">{note_path}</div>
<div className="flex gap-2">
<Button className="text-muted" variant="ghost2" size="fit" onClick={handleCopy}>
<Clipboard className="h-4 w-4" />
<div className="tw-flex tw-items-center tw-justify-between tw-gap-2 tw-overflow-hidden tw-border-[0px] tw-border-b tw-border-solid tw-border-border tw-p-2">
<div className="tw-flex-1 tw-truncate tw-p-1 tw-text-xs tw-text-muted">{note_path}</div>
<div className="tw-flex tw-gap-2">
<Button className="tw-text-muted" variant="ghost2" size="fit" onClick={handleCopy}>
<Clipboard className="tw-size-4" />
Copy
</Button>
<Button className="text-muted" variant="ghost2" size="fit" onClick={handlePreview}>
<Check className="h-4 w-4" />
<Button className="tw-text-muted" variant="ghost2" size="fit" onClick={handlePreview}>
<Check className="tw-size-4" />
Preview
</Button>
</div>
</div>
)}
<pre className="m-0 border-none">
<pre className="tw-m-0 tw-border-none">
<code>
{note_changes.length > 0
? getRelevantChangesMarkdown(getChangeBlocks(note_changes))

View file

@ -38,29 +38,29 @@ export function NewVersionBanner({ currentVersion }: NewVersionBannerProps) {
return (
<div
className={cn(
"min-h-14 overflow-hidden",
"tw-min-h-14 tw-overflow-hidden",
isVisible
? "animate-in slide-in-from-top duration-300"
: "animate-out slide-out-to-top duration-300"
? "tw-duration-300 tw-animate-in tw-slide-in-from-top"
: "tw-duration-300 tw-animate-out tw-slide-out-to-top"
)}
>
<div className="flex items-center justify-between gap-2 p-2 pl-3 mb-1 text-xs border border-border border-solid rounded-md">
<div className="flex items-center gap-2">
<span className="font-medium">Update available:</span>(
<div className="tw-mb-1 tw-flex tw-items-center tw-justify-between tw-gap-2 tw-rounded-md tw-border tw-border-solid tw-border-border tw-p-2 tw-pl-3 tw-text-xs">
<div className="tw-flex tw-items-center tw-gap-2">
<span className="tw-font-medium">Update available:</span>(
<a
href={`https://github.com/logancyang/obsidian-copilot/releases/latest`}
target="_blank"
rel="noopener noreferrer"
className="text-normal"
className="tw-text-normal"
>
v{latestVersion}
</a>
</div>
<div className="flex items-center gap-2">
<div className="tw-flex tw-items-center tw-gap-2">
<Button
size="fit"
variant="ghost2"
className="text-accent hover:text-accent-hover"
className="tw-text-accent hover:tw-text-accent-hover"
onClick={() => {
window.open(`obsidian://show-plugin?id=copilot`, "_blank");
handleDismiss();
@ -69,7 +69,7 @@ export function NewVersionBanner({ currentVersion }: NewVersionBannerProps) {
Update
</Button>
<Button variant="ghost2" size="icon" onClick={handleDismiss}>
<XIcon className="size-4" />
<XIcon className="tw-size-4" />
</Button>
</div>
</div>

View file

@ -41,37 +41,36 @@ function ProjectItem({
}) {
return (
<div
className="flex gap-2 p-3 justify-between items-center rounded-lg bg-secondary/40 border border-border border-solid group transition-all duration-200 hover:border-interactive-accent/30 hover:bg-interactive-accent/5 hover:shadow-[0_2px_12px_rgba(0,0,0,0.1)] active:scale-[0.98] cursor-pointer"
className="tw-group tw-flex tw-cursor-pointer tw-items-center tw-justify-between tw-gap-2 tw-rounded-lg tw-border tw-border-solid tw-border-border tw-p-3 tw-transition-all tw-duration-200 tw-bg-secondary/40 hover:tw-border-interactive-accent hover:tw-text-accent hover:tw-shadow-[0_2px_12px_rgba(0,0,0,0.1)] active:tw-scale-[0.98]"
onClick={() => loadContext(project)}
>
<div className="flex items-center gap-2 flex-1 overflow-hidden">
<div className="text-blue-400">
<Folder className="size-4" />
<div className="tw-flex tw-flex-1 tw-items-center tw-gap-2 tw-overflow-hidden">
<div className="tw-text-accent">
<Folder className="tw-size-4" />
</div>
<div className="flex flex-col gap-1.5 flex-1 overflow-hidden">
<span className="text-[13px] font-medium text-normal text-ellipsis overflow-hidden whitespace-nowrap w-full">
<div className="tw-flex tw-flex-1 tw-flex-col tw-gap-1.5 tw-overflow-hidden">
<span className="tw-w-full tw-truncate tw-text-[13px] tw-font-medium tw-text-normal">
{project.name}
</span>
{project.description && (
<span className="text-[12px] text-muted/80 text-ellipsis overflow-hidden whitespace-nowrap w-full">
<span className="tw-w-full tw-truncate tw-text-[12px] tw-text-muted/80">
{project.description}
</span>
)}
</div>
</div>
<div className="flex flex-row gap-1 opacity-100 transition-opacity duration-200">
<div className="tw-flex tw-flex-row tw-items-center tw-gap-1 tw-opacity-100 tw-transition-opacity tw-duration-200">
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost2"
size="icon"
className="h-6 w-6 hover:bg-accent/10 hover:text-accent-foreground"
onClick={(e) => {
e.stopPropagation();
onEdit(project);
}}
>
<Edit2 className="h-3.5 w-3.5" />
<Edit2 className="tw-size-4" />
</Button>
</TooltipTrigger>
<TooltipContent side="bottom">Edit Project</TooltipContent>
@ -81,13 +80,12 @@ function ProjectItem({
<Button
variant="ghost2"
size="icon"
className="h-6 w-6 hover:bg-accent/10 hover:text-accent-foreground"
onClick={(e) => {
e.stopPropagation();
loadContext(project);
}}
>
<MessageSquare className="h-3.5 w-3.5" />
<MessageSquare className="tw-size-4" />
</Button>
</TooltipTrigger>
<TooltipContent side="bottom">Start Chat</TooltipContent>
@ -97,7 +95,6 @@ function ProjectItem({
<Button
variant="ghost2"
size="icon"
className="h-6 w-6 hover:bg-destructive/10 hover:text-destructive"
onClick={(e) => {
e.stopPropagation();
const modal = new ConfirmModal(
@ -109,7 +106,7 @@ function ProjectItem({
modal.open();
}}
>
<Trash2 className="h-3.5 w-3.5" />
<Trash2 className="tw-size-4" />
</Button>
</TooltipTrigger>
<TooltipContent side="bottom">Delete Project</TooltipContent>
@ -223,13 +220,13 @@ export const ProjectList = memo(
};
return (
<div className={cn("flex flex-col", className)}>
<div className="overflow-y-auto">
<div className="flex flex-col">
<div className={cn("tw-flex tw-flex-col", className)}>
<div className="tw-overflow-y-auto">
<div className="tw-flex tw-flex-col">
{showChatInput && selectedProject ? (
<div className="flex justify-between items-center px-2 py-3">
<div className="flex gap-2 items-center">
<span className="font-semibold text-normal">Projects</span>
<div className="tw-flex tw-items-center tw-justify-between tw-px-2 tw-py-3">
<div className="tw-flex tw-items-center tw-gap-2">
<span className="tw-font-semibold tw-text-normal">Projects</span>
<Select
value={selectedProject.name}
onValueChange={(value) => {
@ -239,52 +236,50 @@ export const ProjectList = memo(
}
}}
>
<SelectTrigger className="w-[200px]">
<SelectTrigger className="tw-w-[200px]">
<SelectValue>
<div className="flex items-center gap-2">
<Folder className="size-4 text-accent/70" />
<div className="tw-flex tw-items-center tw-gap-2">
<Folder className="tw-size-4 tw-text-accent/70" />
<span>{selectedProject.name}</span>
</div>
</SelectValue>
</SelectTrigger>
<SelectContent>
{projects.map((project) => (
<SelectItem key={project.name} value={project.name}>
<div className="flex items-center gap-2">
<Folder className="size-4" />
<span>{project.name}</span>
<SelectItem
key={project.name}
value={project.name}
className="tw-flex tw-items-center tw-gap-2"
>
<div className="tw-flex tw-items-center tw-gap-2">
<Folder className="tw-size-4" />
{project.name}
</div>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="flex items-center gap-2">
<Tooltip>
<TooltipTrigger asChild>
<Button
size="fit"
variant="ghost2"
onClick={() => handleEditProject(selectedProject)}
className="hover:bg-accent/50 hover:text-on-accent"
>
<Edit2 className="size-4 mr-1" />
Edit
</Button>
</TooltipTrigger>
<TooltipContent side="bottom">Edit Current Project</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<div className="tw-flex tw-items-center tw-gap-2">
<Button
variant="secondary"
onClick={() => handleEditProject(selectedProject)}
className="hover:tw-text-on-accent hover:tw-bg-accent/50"
>
<Edit2 className="tw-mr-1 tw-size-4" />
Edit
</Button>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost2"
size="icon"
onClick={() => {
enableOrDisableProject(false);
}}
aria-label="Close Current Project"
>
<X className="size-4" />
<X className="tw-size-4" />
</Button>
</TooltipTrigger>
<TooltipContent side="bottom">Close Current Project</TooltipContent>
@ -295,37 +290,32 @@ export const ProjectList = memo(
<Collapsible
open={isOpen}
onOpenChange={setIsOpen}
className="transition-all duration-200 ease-in-out"
className="tw-transition-all tw-duration-200 tw-ease-in-out"
>
<div className="flex justify-between items-center px-4 py-3">
<div className="flex gap-2 items-center flex-1">
<span className="font-semibold text-normal">Projects</span>
<div className="tw-flex tw-items-center tw-justify-between tw-px-4 tw-py-3">
<div className="tw-flex tw-flex-1 tw-items-center tw-gap-2">
<span className="tw-font-semibold tw-text-normal">Projects</span>
<Tooltip>
<TooltipTrigger asChild>
<Info className="size-4 text-muted" />
<Info className="tw-size-4 tw-text-muted" />
</TooltipTrigger>
<TooltipContent side="bottom" className="w-64">
<TooltipContent side="bottom" className="tw-w-64">
Manage your projects with different contexts and configurations.
</TooltipContent>
</Tooltip>
</div>
<div className="flex items-center gap-2">
<Tooltip>
<TooltipTrigger asChild>
<Button size="fit" className="px-2" onClick={handleAddProject}>
<div className="tw-flex tw-items-center tw-gap-2">
<Button className="tw-px-2" variant="secondary" onClick={handleAddProject}>
Create
<Plus className="size-3" />
<Plus className="tw-size-3" />
</Button>
</TooltipTrigger>
<TooltipContent side="bottom">Add New Project</TooltipContent>
</Tooltip>
{projects.length > 0 && (
<CollapsibleTrigger asChild>
<Button variant="ghost2" size="icon">
{isOpen ? (
<ChevronUp className="size-5" />
<ChevronUp className="tw-size-5" />
) : (
<ChevronDown className="size-5" />
<ChevronDown className="tw-size-5" />
)}
</Button>
</CollapsibleTrigger>
@ -333,12 +323,12 @@ export const ProjectList = memo(
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="secondary"
variant="ghost2"
size="icon"
onClick={() => onClose()}
aria-label="close project mode"
>
<X className="size-4" />
<X className="tw-size-4" />
</Button>
</TooltipTrigger>
<TooltipContent side="bottom">Close Project Mode</TooltipContent>
@ -346,14 +336,14 @@ export const ProjectList = memo(
</div>
</div>
{projects.length === 0 && (
<div className="px-4 py-2 text-xs text-muted bg-secondary/30">
<div className="tw-px-4 tw-py-2 tw-text-xs tw-text-muted tw-bg-secondary/30">
No projects available
</div>
)}
<CollapsibleContent className="transition-all duration-200 ease-in-out">
<div className="relative bg-secondary/30">
<div className="px-4 pt-3 pb-6 max-h-[calc(3*5.7rem)] overflow-y-auto">
<div className="flex flex-col gap-2 @2xl:grid @2xl:grid-cols-2 @4xl:grid-cols-3">
<CollapsibleContent className="tw-transition-all tw-duration-200 tw-ease-in-out">
<div className="tw-relative tw-bg-secondary/30">
<div className="tw-max-h-[calc(3*5.7rem)] tw-overflow-y-auto tw-px-4 tw-pb-6 tw-pt-3">
<div className="tw-flex tw-flex-col tw-gap-2 @2xl:tw-grid @2xl:tw-grid-cols-2 @4xl:tw-grid-cols-3">
{projects.map((project) => (
<ProjectItem
key={project.name}
@ -366,12 +356,7 @@ export const ProjectList = memo(
</div>
</div>
{projects.length > 0 && (
<div
className="absolute bottom-0 left-0 right-0 h-6"
style={{
background: "linear-gradient(transparent, var(--background-primary) 75%)",
}}
/>
<div className="tw-pointer-events-none tw-absolute tw-inset-x-0 tw-bottom-0 tw-h-8 tw-bg-[linear-gradient(to_top,var(--background-primary)_0%,var(--background-primary)_30%,transparent_100%)]" />
)}
</div>
</CollapsibleContent>
@ -380,15 +365,15 @@ export const ProjectList = memo(
</div>
{!showChatInput && (
<div className="flex flex-col gap-4 items-center justify-center p-8 text-muted bg-secondary/30">
<div className="max-w-[600px] space-y-4">
<p className="text-base text-center">
<div className="tw-flex tw-flex-col tw-items-center tw-justify-center tw-gap-4 tw-p-8 tw-text-muted tw-bg-secondary/30">
<div className="tw-max-w-[600px] tw-space-y-4">
<p className="tw-text-center tw-text-base">
Create your project-based AI assistants with custom instructions, context, and
model configurations.
</p>
<div className="flex flex-col gap-3 text-sm">
<div className="flex items-center gap-2">
<MessageSquare className="size-4" />
<div className="tw-flex tw-flex-col tw-gap-3 tw-text-sm">
<div className="tw-flex tw-items-center tw-gap-2">
<MessageSquare className="tw-size-4" />
<span>Click a project card to start chatting</span>
</div>
</div>

View file

@ -61,7 +61,7 @@ function SimilarityBadge({ score }: { score: number }) {
let text = "🔴";
if (category === 2) text = "🟠";
if (category === 3) text = "🟢";
return <span className="text-sm">{text}</span>;
return <span className="tw-text-sm">{text}</span>;
}
function RelevantNote({
@ -106,24 +106,24 @@ function RelevantNote({
<Collapsible
open={isOpen}
onOpenChange={setIsOpen}
className="rounded-md border border-border border-solid"
className="tw-rounded-md tw-border tw-border-solid tw-border-border"
>
<div className={cn("flex gap-2 p-2 justify-between items-center")}>
<Button variant="ghost2" size="icon" className="shrink-0" asChild>
<div className={cn("tw-flex tw-items-center tw-justify-between tw-gap-2 tw-p-2")}>
<Button variant="ghost2" size="icon" className="tw-shrink-0" asChild>
<CollapsibleTrigger>
<ChevronRight
className={cn("size-4 transition-transform duration-200", {
"transform rotate-90": isOpen,
className={cn("tw-size-4 tw-transition-transform tw-duration-200", {
"rotate-90": isOpen,
})}
/>
</CollapsibleTrigger>
</Button>
<div className="flex items-center gap-2 shrink-0">
<div className="tw-flex tw-shrink-0 tw-items-center tw-gap-2">
<SimilarityBadge score={note.metadata.similarityScore ?? 0} />
</div>
<div className="flex-1 overflow-hidden">
<div className="tw-flex-1 tw-overflow-hidden">
<a
onClick={(e) => {
e.preventDefault();
@ -137,7 +137,7 @@ function RelevantNote({
onNavigateToNote(true);
}
}}
className="text-sm text-normal font-bold text-ellipsis overflow-hidden whitespace-nowrap w-full block"
className="tw-block tw-w-full tw-truncate tw-text-sm tw-font-bold tw-text-normal"
>
{note.document.title}
</a>
@ -145,8 +145,8 @@ function RelevantNote({
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost2" size="icon" onClick={onAddToChat} className="shrink-0">
<PlusCircle className="size-4" />
<Button variant="ghost2" size="icon" onClick={onAddToChat} className="tw-shrink-0">
<PlusCircle className="tw-size-4" />
</Button>
</TooltipTrigger>
<TooltipContent>Add to Chat</TooltipContent>
@ -154,32 +154,32 @@ function RelevantNote({
</div>
<CollapsibleContent>
<div className="px-4 py-2 border-[0px] border-t border-border border-solid">
<div className="text-xs text-muted text-wrap opacity-75 break-all whitespace-pre-wrap">
<div className="tw-border-[0px] tw-border-t tw-border-solid tw-border-border tw-px-4 tw-py-2">
<div className="tw-whitespace-pre-wrap tw-text-wrap tw-break-all tw-text-xs tw-text-muted tw-opacity-75">
{note.document.path}
</div>
{fileContent && (
<div className="text-xs text-normal whitespace-pre-wrap pt-2 pb-4 border-t border-border overflow-hidden">
<div className="tw-overflow-hidden tw-whitespace-pre-wrap tw-border-t tw-border-border tw-pb-4 tw-pt-2 tw-text-xs tw-text-normal">
{fileContent}
</div>
)}
</div>
<div className="flex item-center gap-4 px-4 py-2 border-[0px] border-t border-solid border-border text-xs text-muted">
<div className="tw-flex tw-items-center tw-gap-4 tw-border-[0px] tw-border-t tw-border-solid tw-border-border tw-px-4 tw-py-2 tw-text-xs tw-text-muted">
{note.metadata.similarityScore != null && (
<div className="flex items-center gap-1">
<div className="tw-flex tw-items-center tw-gap-1">
<span>Similarity: {(note.metadata.similarityScore * 100).toFixed(1)}%</span>
</div>
)}
{note.metadata.hasOutgoingLinks && (
<div className="flex items-center gap-1">
<FileOutput className="size-4" />
<div className="tw-flex tw-items-center tw-gap-1">
<FileOutput className="tw-size-4" />
<span>Outgoing links</span>
</div>
)}
{note.metadata.hasBacklinks && (
<div className="flex items-center gap-1">
<FileInput className="size-4" />
<div className="tw-flex tw-items-center tw-gap-1">
<FileInput className="tw-size-4" />
<span>Backlinks</span>
</div>
)}
@ -203,24 +203,24 @@ function RelevantNotePopover({
return (
<Popover key={note.document.path}>
<PopoverTrigger asChild>{children}</PopoverTrigger>
<PopoverContent className="flex flex-col gap-2 overflow-hidden w-fit min-w-72 max-w-96">
<span className="text-sm text-normal">{note.document.title}</span>
<span className="text-xs text-muted">{note.document.path}</span>
<div className="flex gap-2">
<PopoverContent className="tw-flex tw-w-fit tw-min-w-72 tw-max-w-96 tw-flex-col tw-gap-2 tw-overflow-hidden">
<span className="tw-text-sm tw-text-normal">{note.document.title}</span>
<span className="tw-text-xs tw-text-muted">{note.document.path}</span>
<div className="tw-flex tw-gap-2">
<button
onClick={onAddToChat}
className="!bg-transparent inline-flex items-center gap-2 border border-border border-solid !shadow-none hover:!bg-interactive-hover"
className="tw-inline-flex tw-items-center tw-gap-2 tw-border tw-border-solid tw-border-border !tw-bg-transparent !tw-shadow-none hover:!tw-bg-interactive-hover"
>
Add to Chat <PlusCircle className="size-4" />
Add to Chat <PlusCircle className="tw-size-4" />
</button>
<button
onClick={(e) => {
const openInNewLeaf = e.metaKey || e.ctrlKey;
onNavigateToNote(openInNewLeaf);
}}
className="!bg-transparent inline-flex items-center gap-2 border border-border border-solid !shadow-none hover:!bg-interactive-hover"
className="tw-inline-flex tw-items-center tw-gap-2 tw-border tw-border-solid tw-border-border !tw-bg-transparent !tw-shadow-none hover:!tw-bg-interactive-hover"
>
Navigate to Note <ArrowRight className="size-4" />
Navigate to Note <ArrowRight className="tw-size-4" />
</button>
</div>
</PopoverContent>
@ -263,19 +263,19 @@ export const RelevantNotes = memo(
return (
<div
className={cn(
"w-full border border-transparent border-b-border border-solid pb-2",
"tw-w-full tw-border tw-border-solid tw-border-transparent tw-border-b-border tw-pb-2",
className
)}
>
<Collapsible open={isOpen} onOpenChange={setIsOpen}>
<div className="flex justify-between items-center pl-1 pb-2">
<div className="flex gap-2 items-center flex-1">
<span className="font-semibold text-normal">Relevant Notes</span>
<div className="tw-flex tw-items-center tw-justify-between tw-pb-2 tw-pl-1">
<div className="tw-flex tw-flex-1 tw-items-center tw-gap-2">
<span className="tw-font-semibold tw-text-normal">Relevant Notes</span>
<Tooltip>
<TooltipTrigger asChild>
<Info className="size-4 text-muted" />
<Info className="tw-size-4 tw-text-muted" />
</TooltipTrigger>
<TooltipContent side="bottom" className="w-64">
<TooltipContent side="bottom" className="tw-w-64">
Relevance is a combination of semantic similarity and links.
</TooltipContent>
</Tooltip>
@ -283,17 +283,17 @@ export const RelevantNotes = memo(
{!hasIndex && (
<Tooltip>
<TooltipTrigger asChild>
<TriangleAlert className="size-4 text-warning" />
<TriangleAlert className="tw-size-4 tw-text-warning" />
</TooltipTrigger>
<TooltipContent side="bottom">Note has not been indexed</TooltipContent>
</Tooltip>
)}
</div>
<div className="flex items-center">
<div className="tw-flex tw-items-center">
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost2" size="icon" onClick={refreshIndex}>
<RefreshCcw className="size-4" />
<RefreshCcw className="tw-size-4" />
</Button>
</TooltipTrigger>
<TooltipContent side="bottom">Reindex Current Note</TooltipContent>
@ -301,19 +301,23 @@ export const RelevantNotes = memo(
{relevantNotes.length > 0 && (
<CollapsibleTrigger asChild>
<Button variant="ghost2" size="icon">
{isOpen ? <ChevronUp className="size-5" /> : <ChevronDown className="size-5" />}
{isOpen ? (
<ChevronUp className="tw-size-5" />
) : (
<ChevronDown className="tw-size-5" />
)}
</Button>
</CollapsibleTrigger>
)}
</div>
</div>
{relevantNotes.length === 0 && (
<div className="flex flex-wrap gap-x-2 gap-y-1 max-h-12 overflow-y-hidden px-1">
<span className="text-xs text-muted">No relevant notes found</span>
<div className="tw-flex tw-max-h-12 tw-flex-wrap tw-gap-x-2 tw-gap-y-1 tw-overflow-y-hidden tw-px-1">
<span className="tw-text-xs tw-text-muted">No relevant notes found</span>
</div>
)}
{!isOpen && relevantNotes.length > 0 && (
<div className="flex flex-wrap gap-x-2 gap-y-1 max-h-6 overflow-y-hidden px-1">
<div className="tw-flex tw-max-h-6 tw-flex-wrap tw-gap-x-2 tw-gap-y-1 tw-overflow-y-hidden tw-px-1">
{relevantNotes.map((note) => (
<RelevantNotePopover
key={note.document.path}
@ -326,18 +330,16 @@ export const RelevantNotes = memo(
<Badge
variant="outline"
key={note.document.path}
className="text-xs max-w-40 text-muted hover:cursor-pointer hover:bg-interactive-hover"
className="tw-max-w-40 tw-text-xs tw-text-muted hover:tw-cursor-pointer hover:tw-bg-interactive-hover"
>
<span className="text-ellipsis overflow-hidden whitespace-nowrap">
{note.document.title}
</span>
<span className="tw-truncate">{note.document.title}</span>
</Badge>
</RelevantNotePopover>
))}
</div>
)}
<CollapsibleContent>
<div className="px-1 py-2 max-h-screen overflow-y-auto flex flex-col gap-2">
<div className="tw-flex tw-max-h-screen tw-flex-col tw-gap-2 tw-overflow-y-auto tw-px-1 tw-py-2">
{relevantNotes.map((note) => (
<RelevantNote
note={note}

View file

@ -96,32 +96,32 @@ export const SuggestedPrompts: React.FC<SuggestedPromptsProps> = ({ onClick }) =
const indexVaultToVectorStore = settings.indexVaultToVectorStore as VAULT_VECTOR_STORE_STRATEGY;
return (
<div className="flex flex-col gap-4">
<Card className="w-full bg-transparent">
<CardHeader className="px-2">
<div className="tw-flex tw-flex-col tw-gap-4">
<Card className="tw-w-full tw-bg-transparent">
<CardHeader className="tw-px-2">
<CardTitle>Suggested Prompts</CardTitle>
</CardHeader>
<CardContent className="p-2 pt-0">
<div className="flex flex-col gap-2">
<CardContent className="tw-p-2 tw-pt-0">
<div className="tw-flex tw-flex-col tw-gap-2">
{prompts.map((prompt, i) => (
<div
key={i}
className="flex gap-2 p-2 justify-between text-sm rounded-md border border-border border-solid"
className="tw-flex tw-justify-between tw-gap-2 tw-rounded-md tw-border tw-border-solid tw-border-border tw-p-2 tw-text-sm"
>
<div className="flex flex-col gap-1">
<div className="text-muted">{prompt.title}</div>
<div className="tw-flex tw-flex-col tw-gap-1">
<div className="tw-text-muted">{prompt.title}</div>
<div>{prompt.text}</div>
</div>
<div className="flex items-start h-full">
<div className="tw-flex tw-h-full tw-items-start">
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost2"
size="fit"
className="text-muted"
className="tw-text-muted"
onClick={() => onClick(prompt.text)}
>
<PlusCircle className="size-4" />
<PlusCircle className="tw-size-4" />
</Button>
</TooltipTrigger>
<TooltipContent>Add to Chat</TooltipContent>
@ -133,20 +133,20 @@ export const SuggestedPrompts: React.FC<SuggestedPromptsProps> = ({ onClick }) =
</CardContent>
</Card>
{chainType === ChainType.VAULT_QA_CHAIN && (
<div className="text-sm border border-border border-solid p-2 rounded-md">
<div className="tw-rounded-md tw-border tw-border-solid tw-border-border tw-p-2 tw-text-sm">
Please note that this is a retrieval-based QA. Questions should contain keywords and
concepts that exist literally in your vault
</div>
)}
{chainType === ChainType.VAULT_QA_CHAIN &&
indexVaultToVectorStore === VAULT_VECTOR_STORE_STRATEGY.NEVER && (
<div className="text-sm border border-border border-solid p-2 rounded-md">
<div className="tw-rounded-md tw-border tw-border-solid tw-border-border tw-p-2 tw-text-sm">
<div>
<TriangleAlert className="size-4" /> Your auto-index strategy is set to <b>NEVER</b>.
Before proceeding, click the <span className="text-accent">Refresh Index</span> button
below or run the{" "}
<span className="text-accent">Copilot command: Index (refresh) vault for QA</span> to
update the index.
<TriangleAlert className="tw-size-4" /> Your auto-index strategy is set to{" "}
<b>NEVER</b>. Before proceeding, click the{" "}
<span className="tw-text-accent">Refresh Index</span> button below or run the{" "}
<span className="tw-text-accent">Copilot command: Index (refresh) vault for QA</span>{" "}
to update the index.
</div>
</div>
)}

View file

@ -96,9 +96,9 @@ const ApplyViewRoot: React.FC<ApplyViewRootProps> = ({ app, state, close }) => {
if (!state || !state.changes) {
logError("Invalid state:", state);
return (
<div className="flex flex-col h-full items-center justify-center">
<div className="text-error">Error: Invalid state - missing changes</div>
<Button onClick={close} className="mt-4">
<div className="tw-flex tw-h-full tw-flex-col tw-items-center tw-justify-center">
<div className="tw-text-error">Error: Invalid state - missing changes</div>
<Button onClick={close} className="tw-mt-4">
Close
</Button>
</div>
@ -237,22 +237,22 @@ const ApplyViewRoot: React.FC<ApplyViewRootProps> = ({ app, state, close }) => {
};
return (
<div className="flex flex-col h-full relative">
<div className="flex z-[1] gap-2 fixed bottom-2 left-1/2 -translate-x-1/2 p-2 border border-solid border-border rounded-md bg-secondary">
<div className="tw-relative tw-flex tw-h-full tw-flex-col">
<div className="tw-fixed tw-bottom-2 tw-left-1/2 tw-z-[1] tw-flex tw-gap-2 tw-rounded-md tw-border tw-border-solid tw-border-border tw-bg-secondary tw-p-2">
<Button variant="destructive" size="sm" onClick={handleReject}>
<XIcon className="size-4" />
<XIcon className="tw-size-4" />
Reject
</Button>
<Button variant="success" size="sm" onClick={handleAccept}>
<Check className="size-4" />
<Check className="tw-size-4" />
Accept
</Button>
</div>
<div className="flex items-center p-2 border-[0px] border-solid border-b border-border text-sm font-medium">
<div className="tw-flex tw-items-center tw-border-b tw-border-solid tw-border-border tw-p-2 tw-text-sm tw-font-medium">
{state.path}
</div>
<div className="flex-1 overflow-auto p-2">
<div className="tw-flex-1 tw-overflow-auto tw-p-2">
{changeBlocks?.map((block, blockIndex) => {
// Check if this block contains any changes (added or removed)
const hasChanges = block.some((change) => change.added || change.removed);
@ -277,13 +277,16 @@ const ApplyViewRoot: React.FC<ApplyViewRootProps> = ({ app, state, close }) => {
<div
key={blockIndex}
ref={(el) => (blockRefs.current[blockIndex] = el)}
className={cn("mb-4 border rounded-md overflow-hidden border-border", {
"border-solid": blockStatus !== "unchanged",
})}
className={cn(
"tw-mb-4 tw-overflow-hidden tw-rounded-md tw-border tw-border-solid tw-border-border",
{
"tw-border-solid": blockStatus !== "unchanged",
}
)}
>
{blockStatus === "accepted" ? (
// Show only the accepted version
<div className="flex-1 font-mono text-sm whitespace-pre-wrap py-1 px-2 text-text-normal">
<div className="tw-flex-1 tw-whitespace-pre-wrap tw-px-2 tw-py-1 tw-font-mono tw-text-sm tw-text-normal">
{block
.filter((change) => !change.removed)
.map((change, idx) => (
@ -292,7 +295,7 @@ const ApplyViewRoot: React.FC<ApplyViewRootProps> = ({ app, state, close }) => {
</div>
) : blockStatus === "rejected" ? (
// Show only the original version
<div className="flex-1 font-mono text-sm whitespace-pre-wrap py-1 px-2 text-text-normal">
<div className="tw-flex-1 tw-whitespace-pre-wrap tw-px-2 tw-py-1 tw-font-mono tw-text-sm tw-text-normal">
{block
.filter((change) => !change.added)
.map((change, idx) => (
@ -304,12 +307,12 @@ const ApplyViewRoot: React.FC<ApplyViewRootProps> = ({ app, state, close }) => {
block.map((change, changeIndex) => (
<div
key={`${blockIndex}-${changeIndex}`}
className={cn("flex relative border-l-3", {
"bg-success border-l-green": change.added,
"bg-error border-l-red": change.removed,
className={cn("tw-relative", {
"tw-bg-success tw-border-l-green": change.added,
"tw-bg-error tw-border-l-red": change.removed,
})}
>
<div className="flex-1 font-mono text-sm whitespace-pre-wrap py-1 px-2 text-text-normal">
<div className="tw-flex-1 tw-whitespace-pre-wrap tw-px-2 tw-py-1 tw-font-mono tw-text-sm tw-text-normal">
{change.value}
</div>
</div>
@ -318,14 +321,14 @@ const ApplyViewRoot: React.FC<ApplyViewRootProps> = ({ app, state, close }) => {
{/* Only show accept/reject buttons for blocks with changes that are undecided */}
{hasChanges && blockStatus === "undecided" && (
<div className="flex items-center justify-end p-2 border-[0px] border-solid border-t border-border">
<div className="flex items-center gap-2">
<div className="tw-flex tw-items-center tw-justify-end tw-border-[0px] tw-border-t tw-border-solid tw-border-border tw-p-2">
<div className="tw-flex tw-items-center tw-gap-2">
<Button variant="destructive" size="sm" onClick={() => rejectBlock(blockIndex)}>
<XIcon className="size-4" />
<XIcon className="tw-size-4" />
Reject
</Button>
<Button variant="success" size="sm" onClick={() => acceptBlock(blockIndex)}>
<Check className="size-4" />
<Check className="tw-size-4" />
Accept
</Button>
</div>
@ -334,17 +337,17 @@ const ApplyViewRoot: React.FC<ApplyViewRootProps> = ({ app, state, close }) => {
{/* Show status for decided blocks with revert option */}
{hasChanges && (blockStatus === "accepted" || blockStatus === "rejected") && (
<div className="flex items-center justify-end p-2 border-[0px] border-solid border-t border-border">
<div className="flex items-center gap-2">
<div className="text-sm font-medium mr-2">
<div className="tw-flex tw-items-center tw-justify-end tw-border-[0px] tw-border-t tw-border-solid tw-border-border tw-p-2">
<div className="tw-flex tw-items-center tw-gap-2">
<div className="tw-mr-2 tw-text-sm tw-font-medium">
{blockStatus === "accepted" ? (
<div className="flex items-center gap-1 text-success">
<Check className="size-4" />
<div className="tw-flex tw-items-center tw-gap-1 tw-text-success">
<Check className="tw-size-4" />
<div>Accepted</div>
</div>
) : (
<div className="flex items-center gap-1 text-error">
<XIcon className="size-4" />
<div className="tw-flex tw-items-center tw-gap-1 tw-text-error">
<XIcon className="tw-size-4" />
<div>Rejected</div>
</div>
)}

View file

@ -129,10 +129,10 @@ function AddProjectModalContent({ initialProject, onSave, onCancel }: AddProject
};
return (
<div className="flex flex-col gap-2 p-4">
<div className="text-xl font-bold text-normal mb-2">Add New Project</div>
<div className="tw-flex tw-flex-col tw-gap-2 tw-p-4">
<div className="tw-mb-2 tw-text-xl tw-font-bold tw-text-normal">Add New Project</div>
<div className="flex flex-col gap-2">
<div className="tw-flex tw-flex-col tw-gap-2">
<FormField
label="Project Name"
required
@ -144,7 +144,7 @@ function AddProjectModalContent({ initialProject, onSave, onCancel }: AddProject
value={formData.name}
onChange={(e) => handleInputChange("name", e.target.value)}
onBlur={() => setTouched((prev) => ({ ...prev, name: true }))}
className="w-full"
className="tw-w-full"
/>
</FormField>
@ -156,7 +156,7 @@ function AddProjectModalContent({ initialProject, onSave, onCancel }: AddProject
type="text"
value={formData.description}
onChange={(e) => handleInputChange("description", e.target.value)}
className="w-full"
className="tw-w-full"
/>
</FormField>
@ -168,7 +168,7 @@ function AddProjectModalContent({ initialProject, onSave, onCancel }: AddProject
value={formData.systemPrompt}
onChange={(e) => handleInputChange("systemPrompt", e.target.value)}
onBlur={() => setTouched((prev) => ({ ...prev, systemPrompt: true }))}
className="min-h-[8rem]"
className="tw-min-h-32"
/>
</FormField>
@ -205,9 +205,9 @@ function AddProjectModalContent({ initialProject, onSave, onCancel }: AddProject
/>
</FormField>
<div className="space-y-4">
<div className="text-base font-medium">Model Configuration</div>
<div className="grid grid-cols-1 gap-4">
<div className="tw-space-y-4">
<div className="tw-text-base tw-font-medium">Model Configuration</div>
<div className="tw-grid tw-grid-cols-1 tw-gap-4">
<FormField label="Temperature">
<SettingSlider
value={formData.modelConfigs?.temperature ?? 1}
@ -215,7 +215,7 @@ function AddProjectModalContent({ initialProject, onSave, onCancel }: AddProject
min={0}
max={2}
step={0.01}
className="w-full"
className="tw-w-full"
/>
</FormField>
<FormField label="Token Limit">
@ -225,25 +225,25 @@ function AddProjectModalContent({ initialProject, onSave, onCancel }: AddProject
min={1}
max={16000}
step={1}
className="w-full"
className="tw-w-full"
/>
</FormField>
</div>
</div>
<div className="space-y-4">
<div className="text-base font-medium">Context Sources</div>
<div className="tw-space-y-4">
<div className="tw-text-base tw-font-medium">Context Sources</div>
<FormField
label={
<div className="flex items-center gap-2">
<div className="tw-flex tw-items-center tw-gap-2">
<span>Inclusions</span>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<HelpCircle className="size-4 text-muted" />
<HelpCircle className="tw-size-4 tw-text-muted" />
</TooltipTrigger>
<TooltipContent>
<div className="max-w-80">
<div className="tw-max-w-80">
<strong>Supported File Types:</strong>
<br />
<strong> Documents:</strong> pdf, doc, docx, ppt, pptx, epub, txt, rtf and
@ -260,8 +260,8 @@ function AddProjectModalContent({ initialProject, onSave, onCancel }: AddProject
}
description="Define patterns to include specific files or folders in the project context"
>
<div className="flex items-center gap-2">
<div className="flex-1 text-xs text-muted">
<div className="tw-flex tw-items-center tw-gap-2">
<div className="tw-flex-1 tw-text-xs tw-text-muted">
{formData.contextSource?.inclusions?.trim()
? "Patterns configured"
: "No patterns configured"}
@ -288,8 +288,8 @@ function AddProjectModalContent({ initialProject, onSave, onCancel }: AddProject
label="Exclusions"
description="Exclude specific files or patterns from the included folders above"
>
<div className="flex items-center gap-2">
<div className="flex-1 text-xs text-muted">
<div className="tw-flex tw-items-center tw-gap-2">
<div className="tw-flex-1 tw-text-xs tw-text-muted">
{formData.contextSource?.exclusions?.trim()
? "Patterns configured"
: "No patterns configured"}
@ -332,7 +332,7 @@ function AddProjectModalContent({ initialProject, onSave, onCancel }: AddProject
handleInputChange("contextSource.webUrls", processedUrls.join("\n"));
}}
placeholder="Enter web URLs, one per line"
className="min-h-[80px] w-full"
className="tw-min-h-20 tw-w-full"
/>
</FormField>
@ -362,13 +362,13 @@ function AddProjectModalContent({ initialProject, onSave, onCancel }: AddProject
handleInputChange("contextSource.youtubeUrls", processedUrls.join("\n"));
}}
placeholder="Enter YouTube URLs, one per line"
className="min-h-[80px] w-full"
className="tw-min-h-20 tw-w-full"
/>
</FormField>
</div>
</div>
<div className="flex items-center justify-end gap-2 mt-4">
<div className="tw-mt-4 tw-flex tw-items-center tw-justify-end tw-gap-2">
<Button variant="ghost" onClick={onCancel} disabled={isSubmitting}>
Cancel
</Button>

View file

@ -58,17 +58,17 @@ function AddPromptModalContent({
const isValid = title.trim() !== "" && prompt.trim() !== "" && !hasInvalidChars;
return (
<div className="flex flex-col gap-4 p-4">
<div className="text-xl font-bold text-normal mb-2">User Custom Prompt</div>
<div className="tw-flex tw-flex-col tw-gap-4 tw-p-4">
<div className="tw-mb-2 tw-text-xl tw-font-bold tw-text-normal">User Custom Prompt</div>
<div className="flex flex-col">
<div className="flex items-center gap-1">
<div className="text-base font-medium text-normal">Title</div>
<span className="text-error">*</span>
<div className="tw-flex tw-flex-col">
<div className="tw-flex tw-items-center tw-gap-1">
<div className="tw-text-base tw-font-medium tw-text-normal">Title</div>
<span className="tw-text-error">*</span>
</div>
<div className="flex flex-col gap-1">
<div className="text-sm text-muted">The title of the prompt, must be unique.</div>
<div className="text-xs text-warning">
<div className="tw-flex tw-flex-col tw-gap-1">
<div className="tw-text-sm tw-text-muted">The title of the prompt, must be unique.</div>
<div className="tw-text-xs tw-text-warning">
Note: Title will be used as filename. Avoid using: {'< > : " / \\ | ? *'}
</div>
</div>
@ -81,22 +81,26 @@ function AddPromptModalContent({
}}
onBlur={() => setTouched((prev) => ({ ...prev, title: true }))}
disabled={disabledTitle}
className={`w-full mt-1`}
className="tw-mt-1 tw-w-full"
required
/>
{showTitleError && <div className="text-error text-xs mt-1">Title is required</div>}
{showTitleError && (
<div className="tw-mt-1 tw-text-xs tw-text-error">Title is required</div>
)}
{hasInvalidChars && (
<div className="text-error text-xs mt-1">Title contains invalid characters</div>
<div className="tw-mt-1 tw-text-xs tw-text-error">Title contains invalid characters</div>
)}
</div>
<div className="flex flex-col gap-1">
<div className="space-y-2">
<div className="flex items-center gap-1">
<div className="text-base font-medium text-normal">Prompt</div>
<span className="text-error">*</span>
<div className="tw-flex tw-flex-col tw-gap-1">
<div className="tw-space-y-2">
<div className="tw-flex tw-items-center tw-gap-1">
<div className="tw-text-base tw-font-medium tw-text-normal">Prompt</div>
<span className="tw-text-error">*</span>
</div>
<div className="tw--mt-1 tw-text-sm tw-text-muted">
Use the following syntax in your prompt:
</div>
<div className="text-sm text-muted -mt-1">Use the following syntax in your prompt:</div>
</div>
<CustomPromptSyntaxInstruction />
@ -107,12 +111,14 @@ function AddPromptModalContent({
if (!touched.prompt) setTouched((prev) => ({ ...prev, prompt: true }));
}}
onBlur={() => setTouched((prev) => ({ ...prev, prompt: true }))}
className={`!min-h-[8rem] mt-1`}
className="tw-mt-1 !tw-min-h-32"
required
/>
{showPromptError && <div className="text-error text-xs mt-1">Prompt is required</div>}
{showPromptError && (
<div className="tw-mt-1 tw-text-xs tw-text-error">Prompt is required</div>
)}
<div className="flex flex-col text-xs text-muted gap-2 mt-2">
<div className="tw-mt-2 tw-flex tw-flex-col tw-gap-2 tw-text-xs tw-text-muted">
<div>
Save the prompt to the local prompt library. You can then use it with the Copilot
command: <strong>Apply custom prompt to selection.</strong>
@ -122,7 +128,7 @@ function AddPromptModalContent({
<a
href="https://github.com/f/awesome-chatgpt-prompts"
target="_blank"
className="text-accent hover:text-accent-hover"
className="tw-text-accent hover:tw-text-accent-hover"
rel="noreferrer"
>
awesome chatGPT prompts
@ -132,7 +138,7 @@ function AddPromptModalContent({
</div>
</div>
<div className="flex items-center justify-end gap-2">
<div className="tw-flex tw-items-center tw-justify-end tw-gap-2">
<Button variant="secondary" onClick={onCancel} disabled={isSubmitting}>
Cancel
</Button>

View file

@ -14,20 +14,20 @@ function CopilotPlusExpiredModalContent({ onCancel }: { onCancel: () => void })
isPlusModel(settings.defaultModelKey) && isPlusModel(settings.embeddingModelKey);
return (
<div className="flex flex-col gap-4">
<div className="flex flex-col gap-2">
<div className="tw-flex tw-flex-col tw-gap-4">
<div className="tw-flex tw-flex-col tw-gap-2">
<div>
Your Copilot Plus license key is no longer valid. Please renew your subscription to
continue using Copilot Plus.
</div>
{isUsingPlusModels && (
<div className="text-sm text-warning">
<div className="tw-text-sm tw-text-warning">
The Copilot Plus exclusive models will stop working. You can switch to the default
models in the Settings.
</div>
)}
</div>
<div className="flex gap-2 justify-end w-full">
<div className="tw-flex tw-w-full tw-justify-end tw-gap-2">
<Button variant="ghost" onClick={onCancel}>
Close
</Button>
@ -37,7 +37,7 @@ function CopilotPlusExpiredModalContent({ onCancel }: { onCancel: () => void })
navigateToPlusPage(PLUS_UTM_MEDIUMS.EXPIRED_MODAL);
}}
>
Renew Now <ExternalLink className="size-4" />
Renew Now <ExternalLink className="tw-size-4" />
</Button>
</div>
</div>

View file

@ -21,7 +21,7 @@ function CopilotPlusWelcomeModalContent({
}) {
const settings = getSettings();
return (
<div className="flex flex-col gap-4">
<div className="tw-flex tw-flex-col tw-gap-4">
<div>
<p>
Thanks for purchasing <b>Copilot Plus</b>! You have unlocked the full power of Copilot,
@ -32,27 +32,28 @@ function CopilotPlusWelcomeModalContent({
Would you like to apply the Copilot Plus settings now? You can always change this later in
Settings.
</p>
<ul className="pl-4">
<ul className="tw-pl-4">
<li>
Default mode: <b className="text-accent">Copilot Plus</b>
Default mode: <b className="tw-text-accent">Copilot Plus</b>
</li>
<li>
Chat model: <b className="text-accent">{DEFAULT_COPILOT_PLUS_CHAT_MODEL}</b>
Chat model: <b className="tw-text-accent">{DEFAULT_COPILOT_PLUS_CHAT_MODEL}</b>
</li>
<li>
<div>
Embedding model: <b className="text-accent">{DEFAULT_COPILOT_PLUS_EMBEDDING_MODEL}</b>
Embedding model:{" "}
<b className="tw-text-accent">{DEFAULT_COPILOT_PLUS_EMBEDDING_MODEL}</b>
</div>
{settings.embeddingModelKey !== DEFAULT_COPILOT_PLUS_EMBEDDING_MODEL_KEY && (
<div className="text-sm text-warning flex items-center gap-1">
<TriangleAlert className="size-4" /> It will rebuild your embeddings for the entire
vault
<div className="tw-flex tw-items-center tw-gap-1 tw-text-sm tw-text-warning">
<TriangleAlert className="tw-size-4" /> It will rebuild your embeddings for the
entire vault
</div>
)}
</li>
</ul>
</div>
<div className="flex gap-2 justify-end w-full">
<div className="tw-flex tw-w-full tw-justify-end tw-gap-2">
<Button variant="ghost" onClick={onCancel}>
Apply Later
</Button>

View file

@ -21,8 +21,8 @@ function CustomPatternInputModalContent({
};
return (
<div className="flex flex-col gap-4">
<div className="flex flex-col gap-4">
<div className="tw-flex tw-flex-col tw-gap-4">
<div className="tw-flex tw-flex-col tw-gap-4">
<div>
Comma separated list of paths, tags, note titles or file extension e.g. folder1,
folder1/folder2, #tag1, #tag2, [[note1]], [[note2]], *.jpg, *.excallidraw.md
@ -34,7 +34,7 @@ function CustomPatternInputModalContent({
onKeyDown={handleKeyDown}
/>
</div>
<div className="flex justify-end gap-2">
<div className="tw-flex tw-justify-end tw-gap-2">
<Button variant="secondary" onClick={onCancel}>
Cancel
</Button>

View file

@ -30,8 +30,8 @@ function ExtensionInputModalContent({
};
return (
<div className="flex flex-col gap-4">
<div className="flex flex-col gap-2">
<div className="tw-flex tw-flex-col tw-gap-4">
<div className="tw-flex tw-flex-col tw-gap-2">
<Input
placeholder="Enter the extension (e.g. txt, excalidraw.md)"
value={extension}
@ -41,9 +41,9 @@ function ExtensionInputModalContent({
}}
onKeyDown={handleKeyDown}
/>
{error && <p className="text-error text-sm">{error}</p>}
{error && <p className="tw-text-sm tw-text-error">{error}</p>}
</div>
<div className="flex justify-end gap-2">
<div className="tw-flex tw-justify-end tw-gap-2">
<Button variant="secondary" onClick={onCancel}>
Cancel
</Button>

View file

@ -77,8 +77,8 @@ function InlineEditCommandSettingsModalContent({
};
return (
<div className="flex flex-col gap-4 p-4">
<div className="flex flex-col gap-2">
<div className="tw-flex tw-flex-col tw-gap-4 tw-p-4">
<div className="tw-flex tw-flex-col tw-gap-2">
<Label htmlFor="name">Name</Label>
<Input
id="name"
@ -86,10 +86,10 @@ function InlineEditCommandSettingsModalContent({
onChange={(e) => handleUpdate("name", e.target.value)}
placeholder="Enter command name"
/>
{errors.name && <div className="text-error text-sm">{errors.name}</div>}
{errors.name && <div className="tw-text-sm tw-text-error">{errors.name}</div>}
</div>
<div className="flex flex-col gap-2">
<div className="tw-flex tw-flex-col tw-gap-2">
<Label htmlFor="prompt">Prompt</Label>
<CustomPromptSyntaxInstruction />
<Textarea
@ -97,14 +97,14 @@ function InlineEditCommandSettingsModalContent({
value={command.prompt}
onChange={(e) => handleUpdate("prompt", e.target.value)}
placeholder="Enter command prompt"
className="min-h-[200px]"
className="tw-min-h-[200px]"
/>
{errors.prompt && <div className="text-error text-sm">{errors.prompt}</div>}
{errors.prompt && <div className="tw-text-sm tw-text-error">{errors.prompt}</div>}
</div>
<div className="flex flex-col gap-2">
<div className="tw-flex tw-flex-col tw-gap-2">
<Label htmlFor="modelKey">Model (Optional)</Label>
<div className="relative w-full group">
<div className="tw-group tw-relative tw-w-full">
<select
value={command.modelKey}
onChange={(e) => {
@ -122,12 +122,12 @@ function InlineEditCommandSettingsModalContent({
handleUpdate("modelKey", e.target.value);
}}
className={cn(
"w-full appearance-none",
"flex h-9 rounded-md border border-solid border-border bg-dropdown px-3 py-1 pr-8",
"text-sm !shadow transition-colors",
"focus:outline-none focus:ring-1 focus:ring-ring",
"disabled:cursor-not-allowed disabled:opacity-50",
"hover:bg-interactive-hover hover:text-normal"
"tw-w-full tw-appearance-none",
"tw-flex tw-h-9 tw-rounded-md tw-border tw-border-solid tw-border-border tw-bg-dropdown tw-px-3 tw-py-1 tw-pr-8",
"tw-text-sm !tw-shadow tw-transition-colors",
"focus:tw-outline-none focus:tw-ring-1 focus:tw-ring-ring",
"disabled:tw-cursor-not-allowed disabled:tw-opacity-50",
"hover:tw-bg-interactive-hover hover:tw-text-normal"
)}
>
<option value="">Inherit from chat model</option>
@ -139,16 +139,16 @@ function InlineEditCommandSettingsModalContent({
</select>
<div
className={cn(
"pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2",
"transition-colors group-hover:[&>svg]:text-normal"
"tw-pointer-events-none tw-absolute tw-inset-y-0 tw-right-0 tw-flex tw-items-center tw-pr-2",
"tw-transition-colors group-hover:[&>svg]:tw-text-normal"
)}
>
<ChevronDown className="h-4 w-4" />
<ChevronDown className="tw-size-4" />
</div>
</div>
</div>
<div className="flex items-center gap-2">
<div className="tw-flex tw-items-center tw-gap-2">
<Checkbox
id="showInContextMenu"
checked={command.showInContextMenu}
@ -157,7 +157,7 @@ function InlineEditCommandSettingsModalContent({
<Label htmlFor="showInContextMenu">Show in context menu</Label>
</div>
<div className="flex justify-end gap-2">
<div className="tw-flex tw-justify-end tw-gap-2">
<Button variant="secondary" onClick={onCancel}>
Cancel
</Button>

View file

@ -243,43 +243,45 @@ function InlineEditModalContent({
const showFollowupSubmit = !generating && followupInstruction.trim().length > 0;
return (
<div className="flex flex-col gap-4" onKeyDown={handleKeyDown}>
<div className="max-h-60 overflow-y-auto text-muted whitespace-pre-wrap">{originalText}</div>
<div className="flex flex-col gap-2">
<div className="tw-flex tw-flex-col tw-gap-4" onKeyDown={handleKeyDown}>
<div className="tw-max-h-60 tw-overflow-y-auto tw-whitespace-pre-wrap tw-text-muted">
{originalText}
</div>
<div className="tw-flex tw-flex-col tw-gap-2">
{commandName && (
<div className="text-normal flex items-center gap-2 font-bold">
<PenLine className="w-4 h-4" />
<div className="tw-flex tw-items-center tw-gap-2 tw-font-bold tw-text-normal">
<PenLine className="tw-size-4" />
{commandName}
</div>
)}
</div>
<div className="relative group">
<div className="tw-group tw-relative">
<textarea
ref={textareaRef}
className="w-full h-60 text-text peer"
className="tw-peer tw-h-60 tw-w-full tw-text-text"
value={processedMessage ?? aiCurrentMessage ?? "loading..."}
disabled={processedMessage == null}
onChange={(e) => setProcessedMessage(e.target.value)}
/>
{processedMessage && (
<button
className="absolute top-2 right-2 opacity-0 peer-focus-visible:!opacity-0 group-hover:opacity-100 transition-opacity"
className="tw-absolute tw-right-2 tw-top-2 tw-opacity-0 tw-transition-opacity group-hover:tw-opacity-100 peer-focus-visible:!tw-opacity-0"
onClick={() => {
navigator.clipboard.writeText(processedMessage);
new Notice("Copied to clipboard");
}}
>
<Copy className="w-4 h-4 text-muted-foreground hover:text-accent" />
<Copy className="tw-size-4 hover:tw-text-accent" />
</button>
)}
</div>
{!generating && processedMessage && (
<div className="flex flex-col gap-2">
<div className="tw-flex tw-flex-col tw-gap-2">
<textarea
autoFocus
ref={followupRef}
className="w-full h-20 text-text"
className="tw-h-20 tw-w-full tw-text-text"
placeholder="Enter follow-up instructions..."
value={followupInstruction}
onChange={(e) => setFollowupInstruction(e.target.value)}
@ -287,12 +289,12 @@ function InlineEditModalContent({
</div>
)}
<div className="flex justify-between gap-2">
<div className="text-faint text-xs flex items-center gap-2 font-bold">
<Bot className="w-4 h-4" />
<div className="tw-flex tw-justify-between tw-gap-2">
<div className="tw-flex tw-items-center tw-gap-2 tw-text-xs tw-font-bold tw-text-faint">
<Bot className="tw-size-4" />
{getModelDisplayText(selectedModel)}
</div>
<div className="flex gap-2">
<div className="tw-flex tw-gap-2">
{generating ? (
// When generating, show Stop button
<Button variant="secondary" onClick={handleStopGeneration}>
@ -300,49 +302,49 @@ function InlineEditModalContent({
</Button>
) : showFollowupSubmit ? (
// When follow-up instruction has content, show Submit button with Enter shortcut
<Button onClick={handleFollowupSubmit} className="flex items-center gap-1">
<Button onClick={handleFollowupSubmit} className="tw-flex tw-items-center tw-gap-1">
<span>Submit</span>
<CornerDownLeft className="size-3" />
<CornerDownLeft className="tw-size-3" />
</Button>
) : (
// Otherwise, show Insert and Replace buttons with shortcut indicators
<>
<Button
onClick={() => onInsert(processedMessage ?? "")}
className="flex items-center gap-1"
className="tw-flex tw-items-center tw-gap-1"
>
<span>Insert</span>
<div className="flex items-center text-xs text-muted">
<div className="tw-flex tw-items-center tw-text-xs tw-text-muted">
{Platform.isMacOS ? (
<>
<Command className="size-3" />
<ArrowBigUp className="size-3" />
<CornerDownLeft className="size-3" />
<Command className="tw-size-3" />
<ArrowBigUp className="tw-size-3" />
<CornerDownLeft className="tw-size-3" />
</>
) : (
<>
<span className="text-xs">Ctrl</span>
<ArrowBigUp className="size-3" />
<CornerDownLeft className="size-3" />
<span className="tw-text-xs">Ctrl</span>
<ArrowBigUp className="tw-size-3" />
<CornerDownLeft className="tw-size-3" />
</>
)}
</div>
</Button>
<Button
onClick={() => onReplace(processedMessage ?? "")}
className="flex items-center gap-1"
className="tw-flex tw-items-center tw-gap-1"
>
<span>Replace</span>
<div className="flex items-center text-xs text-muted">
<div className="tw-flex tw-items-center tw-text-xs tw-text-muted">
{Platform.isMacOS ? (
<>
<Command className="size-3" />
<CornerDownLeft className="size-3" />
<Command className="tw-size-3" />
<CornerDownLeft className="tw-size-3" />
</>
) : (
<>
<span className="text-xs">Ctrl</span>
<CornerDownLeft className="size-3" />
<span className="tw-text-xs">Ctrl</span>
<CornerDownLeft className="tw-size-3" />
</>
)}
</div>

View file

@ -31,14 +31,17 @@ function PatternListGroup({
onRemove: (pattern: string) => void;
}) {
return (
<div className="grid grid-cols-4 gap-2">
<div className="font-bold">{title}</div>
<ul className="list-disc list-inside pl-0 m-0 col-span-3 flex flex-col gap-1">
<div className="tw-grid tw-grid-cols-4 tw-gap-2">
<div className="tw-font-bold">{title}</div>
<ul className="tw-col-span-3 tw-m-0 tw-flex tw-list-inside tw-list-disc tw-flex-col tw-gap-1 tw-pl-0">
{patterns.map((pattern) => (
<li key={pattern} className="flex gap-2 hover:bg-dropdown-hover pl-2 pr-1 rounded-md">
<TruncatedText className="flex-1">{pattern}</TruncatedText>
<li
key={pattern}
className="tw-flex tw-gap-2 tw-rounded-md tw-pl-2 tw-pr-1 hover:tw-bg-dropdown-hover"
>
<TruncatedText className="tw-flex-1">{pattern}</TruncatedText>
<Button variant="ghost2" size="fit" onClick={() => onRemove(pattern)}>
<X className="size-4" />
<X className="tw-size-4" />
</Button>
</li>
))}
@ -84,11 +87,9 @@ function PatternMatchingModalContent({
notePatterns.length > 0;
return (
<div className="flex flex-col gap-4 mt-2">
<div className="flex flex-col gap-2 p-4 border border-border border-solid rounded-md max-h-[400px] overflow-y-auto">
{!hasValue && (
<div className="text-center text-sm text-muted-foreground">No patterns specified</div>
)}
<div className="tw-mt-2 tw-flex tw-flex-col tw-gap-4">
<div className="tw-flex tw-max-h-[400px] tw-flex-col tw-gap-2 tw-overflow-y-auto tw-rounded-md tw-border tw-border-solid tw-border-border tw-p-4">
{!hasValue && <div className="tw-text-center tw-text-sm">No patterns specified</div>}
{tagPatterns.length > 0 && (
<PatternListGroup
title="Tags"
@ -138,7 +139,7 @@ function PatternMatchingModalContent({
/>
)}
</div>
<div className="flex justify-end gap-2">
<div className="tw-flex tw-justify-end tw-gap-2">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="secondary">Add...</Button>
@ -157,8 +158,8 @@ function PatternMatchingModalContent({
}).open();
}}
>
<div className="flex items-center gap-2">
<Tag className="size-4" />
<div className="tw-flex tw-items-center tw-gap-2">
<Tag className="tw-size-4" />
Tag
</div>
</DropdownMenuItem>
@ -174,8 +175,8 @@ function PatternMatchingModalContent({
}).open();
}}
>
<div className="flex items-center gap-2">
<Folder className="size-4" />
<div className="tw-flex tw-items-center tw-gap-2">
<Folder className="tw-size-4" />
Folder
</div>
</DropdownMenuItem>
@ -197,8 +198,8 @@ function PatternMatchingModalContent({
}).open();
}}
>
<div className="flex items-center gap-2">
<FileText className="size-4" />
<div className="tw-flex tw-items-center tw-gap-2">
<FileText className="tw-size-4" />
Note
</div>
</DropdownMenuItem>
@ -215,8 +216,8 @@ function PatternMatchingModalContent({
}).open();
}}
>
<div className="flex items-center gap-2">
<File className="size-4" />
<div className="tw-flex tw-items-center tw-gap-2">
<File className="tw-size-4" />
Extension
</div>
</DropdownMenuItem>
@ -239,8 +240,8 @@ function PatternMatchingModalContent({
}).open();
}}
>
<div className="flex items-center gap-2">
<Wrench className="size-4" />
<div className="tw-flex tw-items-center tw-gap-2">
<Wrench className="tw-size-4" />
Custom
</div>
</DropdownMenuItem>

View file

@ -31,14 +31,17 @@ function PatternListGroup({
onRemove: (pattern: string) => void;
}) {
return (
<div className="grid grid-cols-4 gap-2">
<div className="font-bold">{title}</div>
<ul className="list-disc list-inside pl-0 m-0 col-span-3 flex flex-col gap-1">
<div className="tw-grid tw-grid-cols-4 tw-gap-2">
<div className="tw-font-bold">{title}</div>
<ul className="tw-col-span-3 tw-m-0 tw-flex tw-list-inside tw-list-disc tw-flex-col tw-gap-1 tw-pl-0">
{patterns.map((pattern) => (
<li key={pattern} className="flex gap-2 hover:bg-dropdown-hover pl-2 pr-1 rounded-md">
<TruncatedText className="flex-1">{pattern}</TruncatedText>
<li
key={pattern}
className="tw-flex tw-gap-2 tw-rounded-md tw-pl-2 tw-pr-1 hover:tw-bg-dropdown-hover"
>
<TruncatedText className="tw-flex-1">{pattern}</TruncatedText>
<Button variant="ghost2" size="fit" onClick={() => onRemove(pattern)}>
<X className="size-4" />
<X className="tw-size-4" />
</Button>
</li>
))}
@ -84,11 +87,9 @@ function ProjectPatternMatchingModalContent({
notePatterns.length > 0;
return (
<div className="flex flex-col gap-4 mt-2">
<div className="flex flex-col gap-2 p-4 border border-border border-solid rounded-md max-h-[400px] overflow-y-auto">
{!hasValue && (
<div className="text-center text-sm text-muted-foreground">No patterns specified</div>
)}
<div className="tw-mt-2 tw-flex tw-flex-col tw-gap-4">
<div className="tw-flex tw-max-h-[400px] tw-flex-col tw-gap-2 tw-overflow-y-auto tw-rounded-md tw-border tw-border-solid tw-border-border tw-p-4">
{!hasValue && <div className="tw-text-center tw-text-sm">No patterns specified</div>}
{tagPatterns.length > 0 && (
<PatternListGroup
title="Tags"
@ -138,7 +139,7 @@ function ProjectPatternMatchingModalContent({
/>
)}
</div>
<div className="flex justify-end gap-2">
<div className="tw-flex tw-justify-end tw-gap-2">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="secondary">Add...</Button>
@ -157,8 +158,8 @@ function ProjectPatternMatchingModalContent({
}).open();
}}
>
<div className="flex items-center gap-2">
<Tag className="size-4" />
<div className="tw-flex tw-items-center tw-gap-2">
<Tag className="tw-size-4" />
Tag
</div>
</DropdownMenuItem>
@ -174,8 +175,8 @@ function ProjectPatternMatchingModalContent({
}).open();
}}
>
<div className="flex items-center gap-2">
<Folder className="size-4" />
<div className="tw-flex tw-items-center tw-gap-2">
<Folder className="tw-size-4" />
Folder
</div>
</DropdownMenuItem>
@ -197,8 +198,8 @@ function ProjectPatternMatchingModalContent({
}).open();
}}
>
<div className="flex items-center gap-2">
<FileText className="size-4" />
<div className="tw-flex tw-items-center tw-gap-2">
<FileText className="tw-size-4" />
Files
</div>
</DropdownMenuItem>
@ -215,8 +216,8 @@ function ProjectPatternMatchingModalContent({
}).open();
}}
>
<div className="flex items-center gap-2">
<File className="size-4" />
<div className="tw-flex tw-items-center tw-gap-2">
<File className="tw-size-4" />
Extension
</div>
</DropdownMenuItem>
@ -239,8 +240,8 @@ function ProjectPatternMatchingModalContent({
}).open();
}}
>
<div className="flex items-center gap-2">
<Wrench className="size-4" />
<div className="tw-flex tw-items-center tw-gap-2">
<Wrench className="tw-size-4" />
Custom
</div>
</DropdownMenuItem>

View file

@ -4,14 +4,14 @@ import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const badgeVariants = cva(
"inline-flex items-center rounded-md px-2.5 py-0.5 text-normal text-ui-smaller transition-colors focus:outline-none",
"tw-inline-flex tw-items-center tw-rounded-md tw-px-2.5 tw-py-0.5 tw-text-ui-smaller tw-text-normal tw-transition-colors focus:tw-outline-none",
{
variants: {
variant: {
default: "bg-primary-alt shadow",
secondary: "bg-secondary-alt",
destructive: "bg-modifier-error shadow",
outline: "border border-border border-solid",
default: "tw-bg-primary-alt tw-shadow",
secondary: "tw-bg-secondary-alt",
destructive: "tw-bg-modifier-error tw-shadow",
outline: "tw-border tw-border-solid tw-border-border",
},
},
defaultVariants: {

View file

@ -5,28 +5,29 @@ import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const buttonVariants = cva(
"inline-flex items-center justify-center gap-1 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:shrink-0",
"tw-inline-flex tw-items-center tw-justify-center tw-gap-1 tw-whitespace-nowrap tw-rounded-md tw-text-sm tw-font-medium tw-transition-colors focus-visible:tw-outline-none focus-visible:tw-ring-1 focus-visible:tw-ring-ring disabled:tw-pointer-events-none disabled:tw-opacity-50 [&_svg]:tw-shrink-0",
{
variants: {
variant: {
default:
"mod-cta bg-interactive-accent text-on-accent shadow hover:bg-interactive-accent-hover",
"mod-cta tw-bg-interactive-accent tw-text-on-accent tw-shadow hover:tw-bg-interactive-accent-hover",
destructive:
"clickable-icon bg-modifier-error text-on-accent hover:bg-modifier-error hover:text-on-accent",
secondary: "bg-secondary text-normal shadow-sm hover:bg-interactive-hover",
ghost: "clickable-icon bg-transparent hover:bg-interactive-accent hover:text-on-accent",
link: "text-accent underline-offset-4 hover:underline",
"clickable-icon tw-bg-modifier-error tw-text-on-accent hover:tw-bg-modifier-error hover:tw-text-on-accent",
secondary: "tw-bg-secondary tw-text-normal tw-shadow-sm hover:tw-bg-interactive-hover",
ghost:
"clickable-icon tw-bg-transparent hover:tw-bg-interactive-accent hover:tw-text-on-accent",
link: "tw-text-accent tw-underline-offset-4 hover:tw-underline",
success:
"clickable-icon bg-modifier-success text-on-accent hover:bg-modifier-success hover:text-on-accent",
"clickable-icon tw-bg-modifier-success tw-text-on-accent hover:tw-bg-modifier-success hover:tw-text-on-accent",
ghost2:
"text-faint clickable-icon bg-transparent hover:bg-opacity-100 hover:text-normal hover:bg-transparent outline-none focus-visible:outline-none focus-visible:text-normal focus-visible:ring-0",
"clickable-icon tw-bg-transparent tw-text-faint tw-outline-none hover:tw-bg-transparent hover:tw-bg-opacity-100 hover:tw-text-normal focus-visible:tw-text-normal focus-visible:tw-outline-none focus-visible:tw-ring-0",
},
size: {
default: "h-9 px-4 py-2",
sm: "h-6 rounded-md px-3 text-xs",
lg: "h-10 rounded-md px-8",
icon: "size-7",
fit: "px-1 text-xs gap-1",
default: "tw-h-9 tw-px-4 tw-py-2",
sm: "tw-h-6 tw-rounded-md tw-px-3 tw-text-xs",
lg: "tw-h-10 tw-rounded-md tw-px-8",
icon: "tw-size-7",
fit: "tw-gap-1 tw-px-1 tw-text-xs",
},
},
defaultVariants: {

View file

@ -6,7 +6,10 @@ const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElemen
({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("rounded-xl border border-border bg-primary text-normal shadow", className)}
className={cn(
"tw-rounded-xl tw-border tw-border-border tw-bg-primary tw-text-normal tw-shadow",
className
)}
{...props}
/>
)
@ -15,7 +18,11 @@ Card.displayName = "Card";
const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div ref={ref} className={cn("flex flex-col space-y-1.5 p-6", className)} {...props} />
<div
ref={ref}
className={cn("tw-flex tw-flex-col tw-space-y-1.5 tw-p-6", className)}
{...props}
/>
)
);
CardHeader.displayName = "CardHeader";
@ -24,7 +31,7 @@ const CardTitle = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivE
({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("font-semibold leading-none tracking-tight", className)}
className={cn("tw-font-semibold tw-leading-none tw-tracking-tight", className)}
{...props}
/>
)
@ -33,21 +40,21 @@ CardTitle.displayName = "CardTitle";
const CardDescription = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div ref={ref} className={cn("text-sm text-muted", className)} {...props} />
<div ref={ref} className={cn("tw-text-sm tw-text-muted", className)} {...props} />
)
);
CardDescription.displayName = "CardDescription";
const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
<div ref={ref} className={cn("tw-p-6 tw-pt-0", className)} {...props} />
)
);
CardContent.displayName = "CardContent";
const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => (
<div ref={ref} className={cn("flex items-center p-6 pt-0", className)} {...props} />
<div ref={ref} className={cn("tw-flex tw-items-center tw-p-6 tw-pt-0", className)} {...props} />
)
);
CardFooter.displayName = "CardFooter";

View file

@ -11,15 +11,17 @@ const Checkbox = React.forwardRef<
<CheckboxPrimitive.Root
ref={ref}
className={cn(
"p-0 border-solid !bg-transparent !shadow transition-colors", // custom styles
"hover:!bg-interactive-accent hover:!text-on-accent data-[state=checked]:!bg-interactive-accent data-[state=checked]:!text-on-accent", // custom styles
"peer h-4 w-4 shrink-0 rounded-sm border border-interactive-accent focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-interactive-accent data-[state=checked]:text-on-accent",
"tw-border-solid !tw-bg-transparent tw-p-0 !tw-shadow tw-transition-colors", // custom styles
"hover:!tw-bg-interactive-accent hover:!tw-text-on-accent data-[state=checked]:!tw-bg-interactive-accent data-[state=checked]:!tw-text-on-accent", // custom styles
"tw-peer tw-size-4 tw-shrink-0 tw-rounded-sm tw-border tw-border-interactive-accent focus-visible:tw-outline-none focus-visible:tw-ring-1 focus-visible:tw-ring-ring disabled:tw-cursor-not-allowed disabled:tw-opacity-50",
className
)}
{...props}
>
<CheckboxPrimitive.Indicator className={cn("flex items-center justify-center text-current")}>
<Check className="h-4 w-4" />
<CheckboxPrimitive.Indicator
className={cn("tw-flex tw-items-center tw-justify-center tw-text-current")}
>
<Check className="tw-size-4" />
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>
));

View file

@ -19,8 +19,8 @@ const DialogOverlay = React.forwardRef<
<DialogPrimitive.Overlay
ref={ref}
className={cn(
"fixed inset-0 z-modal data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
"bg-overlay/50", // custom style
"tw-fixed tw-inset-0 tw-z-modal data-[state=open]:tw-animate-in data-[state=closed]:tw-animate-out data-[state=closed]:tw-fade-out-0 data-[state=open]:tw-fade-in-0",
"tw-bg-overlay/50", // custom style
className
)}
{...props}
@ -39,15 +39,15 @@ const DialogContent = React.forwardRef<
<DialogPrimitive.Content
ref={ref}
className={cn(
"fixed left-[50%] top-[50%] z-modal grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-primary p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
"tw-fixed tw-left-1/2 tw-top-1/2 tw-z-modal tw-grid tw-w-full tw-max-w-lg -tw-translate-x-1/2 -tw-translate-y-1/2 tw-gap-4 tw-border tw-bg-primary tw-p-6 tw-shadow-lg tw-duration-200 data-[state=open]:tw-animate-in data-[state=closed]:tw-animate-out data-[state=closed]:tw-fade-out-0 data-[state=open]:tw-fade-in-0 data-[state=closed]:tw-zoom-out-95 data-[state=open]:tw-zoom-in-95 data-[state=closed]:tw-slide-out-to-left-1/2 data-[state=closed]:tw-slide-out-to-top-[48%] data-[state=open]:tw-slide-in-from-left-1/2 data-[state=open]:tw-slide-in-from-top-[48%] sm:tw-rounded-lg",
className
)}
{...props}
>
{children}
<DialogPrimitive.Close className="absolute border-none right-4 top-4 text-faint clickable-icon bg-transparent hover:bg-opacity-100 hover:text-normal hover:bg-transparent outline-none focus-visible:outline-none focus-visible:text-normal focus-visible:ring-0">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
<DialogPrimitive.Close className="clickable-icon tw-absolute tw-right-4 tw-top-4 tw-border-none tw-bg-transparent tw-text-faint tw-outline-none hover:tw-bg-transparent hover:tw-bg-opacity-100 hover:tw-text-normal focus-visible:tw-text-normal focus-visible:tw-outline-none focus-visible:tw-ring-0">
<X className="tw-size-4" />
<span className="tw-sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogPortal>
@ -55,13 +55,19 @@ const DialogContent = React.forwardRef<
DialogContent.displayName = DialogPrimitive.Content.displayName;
const DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div className={cn("flex flex-col space-y-0.5 text-center sm:text-left", className)} {...props} />
<div
className={cn("tw-flex tw-flex-col tw-space-y-0.5 tw-text-center sm:tw-text-left", className)}
{...props}
/>
);
DialogHeader.displayName = "DialogHeader";
const DialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)}
className={cn(
"tw-flex tw-flex-col-reverse sm:tw-flex-row sm:tw-justify-end sm:tw-space-x-2",
className
)}
{...props}
/>
);
@ -73,7 +79,10 @@ const DialogTitle = React.forwardRef<
>(({ className, ...props }, ref) => (
<DialogPrimitive.Title
ref={ref}
className={cn("text-lg font-semibold leading-none tracking-tight mt-0", className)}
className={cn(
"tw-mt-0 tw-text-lg tw-font-semibold tw-leading-none tw-tracking-tight",
className
)}
{...props}
/>
));
@ -85,7 +94,7 @@ const DialogDescription = React.forwardRef<
>(({ className, ...props }, ref) => (
<DialogPrimitive.Description
ref={ref}
className={cn("text-sm text-muted", className)}
className={cn("tw-text-sm tw-text-muted", className)}
{...props}
/>
));

View file

@ -25,14 +25,14 @@ const DropdownMenuSubTrigger = React.forwardRef<
<DropdownMenuPrimitive.SubTrigger
ref={ref}
className={cn(
"flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-dropdown-hover data-[state=open]:bg-dropdown-hover [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
inset && "pl-8",
"tw-flex tw-cursor-default tw-select-none tw-items-center tw-gap-2 tw-rounded-sm tw-px-2 tw-py-1.5 tw-text-sm tw-outline-none focus:tw-bg-dropdown-hover data-[state=open]:tw-bg-dropdown-hover [&_svg]:tw-pointer-events-none [&_svg]:tw-size-4 [&_svg]:tw-shrink-0",
inset && "tw-pl-8",
className
)}
{...props}
>
{children}
<ChevronRight className="ml-auto" />
<ChevronRight className="tw-ml-auto" />
</DropdownMenuPrimitive.SubTrigger>
));
DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
@ -44,7 +44,7 @@ const DropdownMenuSubContent = React.forwardRef<
<DropdownMenuPrimitive.SubContent
ref={ref}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-primary p-1 text-normal shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
"tw-z-[50] tw-min-w-32 tw-overflow-hidden tw-rounded-md tw-border tw-bg-primary tw-p-1 tw-text-normal tw-shadow-lg data-[state=open]:tw-animate-in data-[state=closed]:tw-animate-out data-[state=closed]:tw-fade-out-0 data-[state=open]:tw-fade-in-0 data-[state=closed]:tw-zoom-out-95 data-[state=open]:tw-zoom-in-95 data-[side=bottom]:tw-slide-in-from-top-2 data-[side=left]:tw-slide-in-from-right-2 data-[side=right]:tw-slide-in-from-left-2 data-[side=top]:tw-slide-in-from-bottom-2",
className
)}
{...props}
@ -63,8 +63,8 @@ const DropdownMenuContent = React.forwardRef<
ref={ref}
sideOffset={sideOffset}
className={cn(
"z-50 min-w-[8rem] overflow-hidden rounded-md border border-border border-solid bg-primary p-1 text-normal shadow-md",
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
"tw-z-[50] tw-min-w-32 tw-overflow-hidden tw-rounded-md tw-border tw-border-solid tw-border-border tw-bg-primary tw-p-1 tw-text-normal tw-shadow-md",
"data-[state=open]:tw-animate-in data-[state=closed]:tw-animate-out data-[state=closed]:tw-fade-out-0 data-[state=open]:tw-fade-in-0 data-[state=closed]:tw-zoom-out-95 data-[state=open]:tw-zoom-in-95 data-[side=bottom]:tw-slide-in-from-top-2 data-[side=left]:tw-slide-in-from-right-2 data-[side=right]:tw-slide-in-from-left-2 data-[side=top]:tw-slide-in-from-bottom-2",
className
)}
{...props}
@ -82,8 +82,8 @@ const DropdownMenuItem = React.forwardRef<
<DropdownMenuPrimitive.Item
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-dropdown-hover focus:text-normal data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0",
inset && "pl-8",
"tw-relative tw-flex tw-cursor-default tw-select-none tw-items-center tw-gap-2 tw-rounded-sm tw-px-2 tw-py-1.5 tw-text-sm tw-outline-none tw-transition-colors focus:tw-bg-dropdown-hover focus:tw-text-normal data-[disabled]:tw-pointer-events-none data-[disabled]:tw-opacity-50 [&>svg]:tw-size-4 [&>svg]:tw-shrink-0",
inset && "tw-pl-8",
className
)}
{...props}
@ -98,15 +98,15 @@ const DropdownMenuCheckboxItem = React.forwardRef<
<DropdownMenuPrimitive.CheckboxItem
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-dropdown-hover focus:text-normal data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
"tw-relative tw-flex tw-cursor-default tw-select-none tw-items-center tw-rounded-sm tw-py-1.5 tw-pl-8 tw-pr-2 tw-text-sm tw-outline-none tw-transition-colors focus:tw-bg-dropdown-hover focus:tw-text-normal data-[disabled]:tw-pointer-events-none data-[disabled]:tw-opacity-50",
className
)}
checked={checked}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<span className="tw-absolute tw-left-2 tw-flex tw-size-3.5 tw-items-center tw-justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
<Check className="tw-size-4" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
@ -121,14 +121,14 @@ const DropdownMenuRadioItem = React.forwardRef<
<DropdownMenuPrimitive.RadioItem
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-dropdown-hover focus:text-normal data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
"tw-relative tw-flex tw-cursor-default tw-select-none tw-items-center tw-rounded-sm tw-py-1.5 tw-pl-8 tw-pr-2 tw-text-sm tw-outline-none tw-transition-colors focus:tw-bg-dropdown-hover focus:tw-text-normal data-[disabled]:tw-pointer-events-none data-[disabled]:tw-opacity-50",
className
)}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<span className="tw-absolute tw-left-2 tw-flex tw-size-3.5 tw-items-center tw-justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<Circle className="h-2 w-2 fill-current" />
<Circle className="tw-size-2 tw-fill-current" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
@ -144,7 +144,7 @@ const DropdownMenuLabel = React.forwardRef<
>(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Label
ref={ref}
className={cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className)}
className={cn("tw-px-2 tw-py-1.5 tw-text-sm tw-font-semibold", inset && "tw-pl-8", className)}
{...props}
/>
));
@ -156,7 +156,7 @@ const DropdownMenuSeparator = React.forwardRef<
>(({ className, ...props }, ref) => (
<DropdownMenuPrimitive.Separator
ref={ref}
className={cn("-mx-1 my-1 h-px bg-muted", className)}
className={cn("-tw-mx-1 tw-my-1 tw-h-px", className)}
{...props}
/>
));
@ -164,7 +164,10 @@ DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
return (
<span className={cn("ml-auto text-xs tracking-widest opacity-60", className)} {...props} />
<span
className={cn("tw-ml-auto tw-text-xs tw-tracking-widest tw-opacity-60", className)}
{...props}
/>
);
};
DropdownMenuShortcut.displayName = "DropdownMenuShortcut";

View file

@ -19,13 +19,13 @@ export const FormField: React.FC<FormFieldProps> = ({
children,
}) => {
return (
<div className="space-y-2">
<Label className={error ? "text-error" : ""}>
{label} {required && <span className="text-error">*</span>}
<div className="tw-space-y-2">
<Label className={error ? "tw-text-error" : ""}>
{label} {required && <span className="tw-text-error">*</span>}
</Label>
{children}
{error && <p className="text-xs text-error">{errorMessage}</p>}
{description && <p className="text-sm text-muted">{description}</p>}
{error && <p className="tw-text-xs tw-text-error">{errorMessage}</p>}
{description && <p className="tw-text-sm tw-text-muted">{description}</p>}
</div>
);
};

View file

@ -8,10 +8,10 @@ const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
<input
type={type}
className={cn(
"!h-9 !min-w-[50px] !border border-border border-solid !rounded-md !bg-transparent !px-3 !py-1 md:!text-base !text-sm !transition-colors",
"focus-visible:!shadow-sm focus-visible:!outline-none focus-visible:!ring-1 focus-visible:!ring-ring",
"placeholder:text-sm",
"flex w-full shadow-sm placeholder:text-muted disabled:cursor-not-allowed disabled:opacity-50",
"!tw-h-9 !tw-min-w-[50px] !tw-rounded-md !tw-border tw-border-solid tw-border-border !tw-bg-transparent !tw-px-3 !tw-py-1 !tw-text-sm !tw-transition-colors md:!tw-text-base",
"focus-visible:!tw-shadow-sm focus-visible:!tw-outline-none focus-visible:!tw-ring-1 focus-visible:!tw-ring-ring",
"placeholder:tw-text-sm",
"tw-flex tw-w-full tw-shadow-sm placeholder:tw-text-muted disabled:tw-cursor-not-allowed disabled:tw-opacity-50",
className
)}
ref={ref}

View file

@ -5,7 +5,7 @@ import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
"tw-text-sm tw-font-medium tw-leading-none peer-disabled:tw-cursor-not-allowed peer-disabled:tw-opacity-70"
);
const Label = React.forwardRef<

View file

@ -28,7 +28,7 @@ export const ModelCapabilityIcons: React.FC<ModelCapabilityIconsProps> = ({
return (
<Lightbulb
key={index}
className="text-model-capabilities-blue"
className="tw-text-model-capabilities-blue"
style={{ width: iconSize, height: iconSize }}
/>
);
@ -36,7 +36,7 @@ export const ModelCapabilityIcons: React.FC<ModelCapabilityIconsProps> = ({
return (
<Eye
key={index}
className="text-model-capabilities-green"
className="tw-text-model-capabilities-green"
style={{ width: iconSize, height: iconSize }}
/>
);
@ -44,7 +44,7 @@ export const ModelCapabilityIcons: React.FC<ModelCapabilityIconsProps> = ({
return (
<Globe
key={index}
className="text-model-capabilities-blue"
className="tw-text-model-capabilities-blue"
style={{ width: iconSize, height: iconSize }}
/>
);
@ -59,10 +59,10 @@ export const ModelCapabilityIcons: React.FC<ModelCapabilityIconsProps> = ({
export const ModelDisplay: React.FC<ModelDisplayProps> = ({ model, iconSize = 14 }) => {
const displayName = model.displayName || model.name;
return (
<div className="flex items-center gap-1">
<div className="tw-flex tw-items-center tw-gap-1">
<span>{displayName}</span>
{model.capabilities && model.capabilities.length > 0 && (
<div className="flex gap-0.5 items-center">
<div className="tw-flex tw-items-center tw-gap-0.5">
<ModelCapabilityIcons capabilities={model.capabilities} iconSize={iconSize} />
</div>
)}

View file

@ -21,15 +21,15 @@ export function ObsidianNativeSelect({
...props
}: ObsidianNativeSelectProps) {
return (
<div className={cn("relative w-full group", containerClassName)}>
<div className={cn("tw-group tw-relative tw-w-full", containerClassName)}>
<select
className={cn(
"w-full appearance-none",
"flex h-9 rounded-md border border-solid border-border bg-dropdown px-3 py-1 pr-8",
"text-sm !shadow transition-colors",
"focus:outline-none focus:ring-1 focus:ring-ring",
"disabled:cursor-not-allowed disabled:opacity-50",
"hover:bg-interactive-hover hover:text-normal",
"tw-w-full tw-appearance-none",
"tw-flex tw-h-9 tw-rounded-md tw-border tw-border-solid tw-border-border tw-bg-dropdown tw-px-3 tw-py-1 tw-pr-8",
"tw-text-sm tw-shadow tw-transition-colors",
"focus:tw-outline-none focus:tw-ring-1 focus:tw-ring-ring",
"disabled:tw-cursor-not-allowed disabled:tw-opacity-50",
"hover:tw-bg-interactive-hover hover:tw-text-normal",
className
)}
{...props}
@ -47,12 +47,12 @@ export function ObsidianNativeSelect({
</select>
<div
className={cn(
"pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2",
"transition-colors group-hover:[&>svg]:text-normal",
props.disabled && "opacity-50"
"tw-pointer-events-none tw-absolute tw-inset-y-0 tw-right-0 tw-flex tw-items-center tw-pr-2",
"tw-transition-colors group-hover:[&>svg]:tw-text-normal",
props.disabled && "tw-opacity-50"
)}
>
<ChevronDown className="h-4 w-4" />
<ChevronDown className="tw-size-4" />
</div>
</div>
);

View file

@ -45,21 +45,21 @@ export function PasswordInput({
}, [value]);
return (
<div className={cn("relative", className)}>
<div className={cn("tw-relative", className)}>
<Input
ref={inputRef}
type={showPassword ? "text" : "password"}
onChange={(e) => onChange?.(e.target.value)}
placeholder={placeholder}
disabled={disabled}
className={cn("![padding-right:1.75rem] w-full")}
className={cn("!tw-[padding-right:1.75rem] tw-w-full")}
/>
<div
onClick={() => !disabled && setShowPassword(!showPassword)}
className={cn(
"absolute right-2 top-0 bottom-0 flex items-center justify-center",
"cursor-pointer",
disabled && "opacity-50 cursor-not-allowed"
"tw-absolute tw-inset-y-0 tw-right-2 tw-flex tw-items-center tw-justify-center",
"tw-cursor-pointer",
disabled && "tw-cursor-not-allowed tw-opacity-50"
)}
role="button"
aria-label={showPassword ? "Hide password" : "Show password"}
@ -67,17 +67,17 @@ export function PasswordInput({
{showPassword ? (
<EyeOff
className={cn(
"h-3.5 w-3.5",
"text-muted/60 hover:text-accent",
"transition-colors duration-200"
"tw-size-3.5",
"tw-text-muted/60 hover:tw-text-accent",
"tw-transition-colors tw-duration-200"
)}
/>
) : (
<Eye
className={cn(
"h-3.5 w-3.5",
"text-muted/60 hover:text-accent",
"transition-colors duration-200"
"tw-size-3.5",
"tw-text-muted/60 hover:tw-text-accent",
"tw-transition-colors tw-duration-200"
)}
/>
)}

View file

@ -21,7 +21,7 @@ const PopoverContent = React.forwardRef<
align={align}
sideOffset={sideOffset}
className={cn(
"z-popover w-72 rounded-md border border-border bg-primary p-4 text-normal shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
"tw-z-popover tw-w-72 tw-rounded-md tw-border tw-border-border tw-bg-primary tw-p-4 tw-text-normal tw-shadow-md tw-outline-none data-[state=open]:tw-animate-in data-[state=closed]:tw-animate-out data-[state=closed]:tw-fade-out-0 data-[state=open]:tw-fade-in-0 data-[state=closed]:tw-zoom-out-95 data-[state=open]:tw-zoom-in-95 data-[side=bottom]:tw-slide-in-from-top-2 data-[side=left]:tw-slide-in-from-right-2 data-[side=right]:tw-slide-in-from-left-2 data-[side=top]:tw-slide-in-from-bottom-2",
className
)}
{...props}

View file

@ -17,17 +17,17 @@ const SelectTrigger = React.forwardRef<
<SelectPrimitive.Trigger
ref={ref}
className={cn(
"border-solid !bg-dropdown !shadow-sm transition-colors",
"focus-visible:shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
" hover:data-[state=closed]:!bg-interactive-accent hover:data-[state=closed]:!text-on-accent", // custom styles
"flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-border px-3 py-1 text-sm ring-offset-ring placeholder:text-muted focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
"tw-border-solid !tw-bg-dropdown !tw-shadow-sm tw-transition-colors",
"focus-visible:tw-shadow-sm focus-visible:tw-outline-none focus-visible:tw-ring-1 focus-visible:tw-ring-ring",
"hover:data-[state=closed]:!tw-bg-interactive-accent hover:data-[state=closed]:!tw-text-on-accent", // custom styles
"tw-flex tw-h-9 tw-w-full tw-items-center tw-justify-between tw-whitespace-nowrap tw-rounded-md tw-border tw-border-border tw-px-3 tw-py-1 tw-text-sm tw-ring-offset-ring placeholder:tw-text-muted focus:tw-outline-none focus:tw-ring-1 focus:tw-ring-ring disabled:tw-cursor-not-allowed disabled:tw-opacity-50 [&>span]:tw-line-clamp-1",
className
)}
{...props}
>
{children}
<SelectPrimitive.Icon asChild>
<ChevronDown className="h-4 w-4" />
<ChevronDown className="tw-size-4" />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
));
@ -39,10 +39,10 @@ const SelectScrollUpButton = React.forwardRef<
>(({ className, ...props }, ref) => (
<SelectPrimitive.ScrollUpButton
ref={ref}
className={cn("flex cursor-default items-center justify-center py-1", className)}
className={cn("tw-flex tw-cursor-default tw-items-center tw-justify-center tw-py-1", className)}
{...props}
>
<ChevronUp className="h-4 w-4" />
<ChevronUp className="tw-size-4" />
</SelectPrimitive.ScrollUpButton>
));
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
@ -53,10 +53,10 @@ const SelectScrollDownButton = React.forwardRef<
>(({ className, ...props }, ref) => (
<SelectPrimitive.ScrollDownButton
ref={ref}
className={cn("flex cursor-default items-center justify-center py-1", className)}
className={cn("tw-flex tw-cursor-default tw-items-center tw-justify-center tw-py-1", className)}
{...props}
>
<ChevronDown className="h-4 w-4" />
<ChevronDown className="tw-size-4" />
</SelectPrimitive.ScrollDownButton>
));
SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
@ -71,9 +71,9 @@ const SelectContent = React.forwardRef<
<SelectPrimitive.Content
ref={ref}
className={cn(
"relative z-modal max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-primary text-normal shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
"tw-relative tw-z-modal tw-max-h-96 tw-min-w-32 tw-overflow-hidden tw-rounded-md tw-border tw-bg-primary tw-text-normal tw-shadow-md data-[state=open]:tw-animate-in data-[state=closed]:tw-animate-out data-[state=closed]:tw-fade-out-0 data-[state=open]:tw-fade-in-0 data-[state=closed]:tw-zoom-out-95 data-[state=open]:tw-zoom-in-95 data-[side=bottom]:tw-slide-in-from-top-2 data-[side=left]:tw-slide-in-from-right-2 data-[side=right]:tw-slide-in-from-left-2 data-[side=top]:tw-slide-in-from-bottom-2",
position === "popper" &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
"data-[side=bottom]:tw-translate-y-1 data-[side=left]:-tw-translate-x-1 data-[side=right]:tw-translate-x-1 data-[side=top]:-tw-translate-y-1",
className
)}
position={position}
@ -82,9 +82,9 @@ const SelectContent = React.forwardRef<
<SelectScrollUpButton />
<SelectPrimitive.Viewport
className={cn(
"p-1",
"tw-p-1",
position === "popper" &&
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
"tw-h-[var(--radix-select-trigger-height)] tw-w-full tw-min-w-[var(--radix-select-trigger-width)]"
)}
>
{children}
@ -101,7 +101,7 @@ const SelectLabel = React.forwardRef<
>(({ className, ...props }, ref) => (
<SelectPrimitive.Label
ref={ref}
className={cn("px-2 py-1.5 text-sm font-semibold", className)}
className={cn("tw-px-2 tw-py-1.5 tw-text-sm tw-font-semibold", className)}
{...props}
/>
));
@ -114,14 +114,14 @@ const SelectItem = React.forwardRef<
<SelectPrimitive.Item
ref={ref}
className={cn(
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-interactive-accent focus:text-on-accent data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
"tw-relative tw-flex tw-w-full tw-cursor-default tw-select-none tw-items-center tw-rounded-sm tw-py-1.5 tw-pl-2 tw-pr-8 tw-text-sm tw-outline-none focus:tw-bg-interactive-accent focus:tw-text-on-accent data-[disabled]:tw-pointer-events-none data-[disabled]:tw-opacity-50",
className
)}
{...props}
>
<span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
<span className="tw-absolute tw-right-2 tw-flex tw-size-3.5 tw-items-center tw-justify-center">
<SelectPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
<Check className="tw-size-4" />
</SelectPrimitive.ItemIndicator>
</span>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
@ -135,7 +135,7 @@ const SelectSeparator = React.forwardRef<
>(({ className, ...props }, ref) => (
<SelectPrimitive.Separator
ref={ref}
className={cn("-mx-1 my-1 h-px bg-primary-alt", className)}
className={cn("-tw-mx-1 tw-my-1 tw-h-px tw-bg-primary-alt", className)}
{...props}
/>
));

View file

@ -134,7 +134,7 @@ export function SettingItem(props: SettingItemProps) {
}}
placeholder={props.placeholder}
disabled={disabled}
className="w-full sm:w-[200px]"
className="tw-w-full sm:tw-w-[200px]"
/>
);
@ -147,7 +147,7 @@ export function SettingItem(props: SettingItemProps) {
}}
placeholder={props.placeholder}
disabled={disabled}
className="w-full sm:w-[200px]"
className="tw-w-full sm:tw-w-[200px]"
/>
);
@ -161,7 +161,7 @@ export function SettingItem(props: SettingItemProps) {
placeholder={props.placeholder}
rows={props.rows || 3}
disabled={disabled}
className="w-full sm:w-[300px] min-h-[80px]"
className="tw-min-h-[80px] tw-w-full sm:tw-w-[300px]"
/>
);
@ -176,18 +176,18 @@ export function SettingItem(props: SettingItemProps) {
case "select":
return (
<div className="relative w-full sm:w-[200px] group">
<div className="tw-group tw-relative tw-w-full sm:tw-w-[200px]">
<select
value={props.value?.toString()}
onChange={(e) => props.onChange?.(e.target.value)}
disabled={disabled}
className={cn(
"w-full appearance-none",
"flex h-9 rounded-md border border-solid border-border bg-dropdown px-3 py-1 pr-8",
"text-sm !shadow transition-colors",
"focus:outline-none focus:ring-1 focus:ring-ring",
"disabled:cursor-not-allowed disabled:opacity-50",
"hover:bg-interactive-hover hover:text-normal"
"tw-w-full tw-appearance-none",
"tw-flex tw-h-9 tw-rounded-md tw-border tw-border-solid tw-border-border tw-bg-dropdown tw-px-3 tw-py-1 tw-pr-8",
"tw-text-sm !tw-shadow tw-transition-colors",
"focus:tw-outline-none focus:tw-ring-1 focus:tw-ring-ring",
"disabled:tw-cursor-not-allowed disabled:tw-opacity-50",
"hover:tw-bg-interactive-hover hover:tw-text-normal"
)}
>
{props.placeholder && (
@ -203,12 +203,12 @@ export function SettingItem(props: SettingItemProps) {
</select>
<div
className={cn(
"pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2",
"transition-colors group-hover:[&>svg]:text-normal",
disabled && "opacity-50"
"tw-pointer-events-none tw-absolute tw-inset-y-0 tw-right-0 tw-flex tw-items-center tw-pr-2",
"tw-transition-colors group-hover:[&>svg]:tw-text-normal",
disabled && "tw-opacity-50"
)}
>
<ChevronDown className="h-4 w-4" />
<ChevronDown className="tw-size-4" />
</div>
</div>
);
@ -222,7 +222,7 @@ export function SettingItem(props: SettingItemProps) {
max={props.max}
step={props.step}
disabled={disabled}
className="w-full sm:w-[300px]"
className="tw-w-full sm:tw-w-[300px]"
/>
);
@ -252,16 +252,16 @@ export function SettingItem(props: SettingItemProps) {
return (
<div
className={cn(
"flex flex-col sm:flex-row items-start sm:items-center justify-between py-4 gap-4",
"w-full",
"tw-flex tw-flex-col tw-items-start tw-justify-between tw-gap-4 tw-py-4 sm:tw-flex-row sm:tw-items-center",
"tw-w-full",
className
)}
>
<div className="space-y-1.5 w-full sm:w-[300px]">
<div className="text-sm font-medium leading-none">{title}</div>
{description && <div className="text-xs text-muted">{description}</div>}
<div className="tw-w-full tw-space-y-1.5 sm:tw-w-[300px]">
<div className="tw-text-sm tw-font-medium tw-leading-none">{title}</div>
{description && <div className="tw-text-xs tw-text-muted">{description}</div>}
</div>
<div className="flex-1 w-full sm:flex sm:justify-end">{renderControl()}</div>
<div className="tw-w-full tw-flex-1 sm:tw-flex sm:tw-justify-end">{renderControl()}</div>
</div>
);
}

View file

@ -30,7 +30,7 @@ export function SettingSlider({
}, [initialValue]);
return (
<div className={cn("flex items-center gap-4", className)}>
<div className={cn("tw-flex tw-items-center tw-gap-4", className)}>
<Slider
value={[localValue]}
onValueChange={([value]) => setLocalValue(value)}
@ -39,9 +39,9 @@ export function SettingSlider({
max={max}
step={step}
disabled={disabled}
className="flex-1"
className="tw-flex-1"
/>
<div className="min-w-[40px] text-sm text-right">{localValue}</div>
<div className="tw-min-w-[40px] tw-text-right tw-text-sm">{localValue}</div>
</div>
);
}

View file

@ -33,10 +33,10 @@ const SettingSwitch = React.forwardRef<HTMLDivElement, SettingSwitchProps>(
ref={ref}
tabIndex={disabled ? -1 : 0}
className={cn(
"relative inline-flex h-5.5 w-10 shrink-0 cursor-pointer items-center rounded-full transition-colors",
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
checked ? "bg-interactive-accent" : "bg-[--background-modifier-border-hover]",
disabled && "cursor-not-allowed opacity-50",
"tw-relative tw-inline-flex tw-h-5.5 tw-w-10 tw-shrink-0 tw-cursor-pointer tw-items-center tw-rounded-full tw-transition-colors",
"focus-visible:tw-outline-none focus-visible:tw-ring-2 focus-visible:tw-ring-ring focus-visible:tw-ring-offset-2",
checked ? "tw-bg-interactive-accent" : "tw-bg-[--background-modifier-border-hover]",
disabled && "tw-cursor-not-allowed tw-opacity-50",
className
)}
onClick={handleClick}
@ -45,8 +45,8 @@ const SettingSwitch = React.forwardRef<HTMLDivElement, SettingSwitchProps>(
>
<div
className={cn(
"pointer-events-none block h-4 w-4 rounded-full bg-toggle-thumb shadow-lg ring-0 transition-transform",
checked ? "translate-x-5.5" : "translate-x-0.5"
"tw-pointer-events-none tw-block tw-size-4 tw-rounded-full tw-bg-toggle-thumb tw-shadow-lg tw-ring-0 tw-transition-transform",
checked ? "tw-translate-x-5.5" : "tw-translate-x-0.5"
)}
/>
</div>

View file

@ -24,49 +24,50 @@ export const TabItem: React.FC<TabItemProps> = ({ tab, isSelected, onClick, isFi
aria-selected={isSelected}
onClick={onClick}
className={cn(
"flex flex-row items-center",
"h-8",
"px-2 py-1",
"gap-1.5",
"cursor-pointer",
"overflow-hidden",
"whitespace-nowrap",
"text-sm",
"border border-border border-solid",
"rounded-t-md rounded-b-[2px]",
"bg-primary",
"transition-all duration-300 ease-in-out",
"hover:border-interactive-accent hover:border-b-0",
"tw-flex tw-flex-row tw-items-center",
"tw-h-8",
"tw-px-2 tw-py-1",
"tw-gap-1.5",
"tw-cursor-pointer",
"tw-overflow-hidden",
"tw-whitespace-nowrap",
"tw-text-sm",
"tw-border tw-border-solid tw-border-border",
"tw-rounded-b-[2px] tw-rounded-t-md",
"tw-bg-primary",
"tw-transition-all tw-duration-300 tw-ease-in-out",
"hover:tw-border-interactive-accent",
isSelected && [
"!bg-interactive-accent",
"text-on-accent",
"!px-3",
"!max-w-full",
"border-b-0",
"transition-all duration-300 ease-in-out",
"delay-200",
"!tw-bg-interactive-accent",
"tw-text-on-accent",
"!tw-px-3",
"!tw-max-w-full",
"tw-transition-all tw-duration-300 tw-ease-in-out",
"tw-delay-200",
],
"lg:max-w-32",
"md:max-w-32"
"lg:tw-max-w-32",
"md:tw-max-w-32"
)}
>
<div
className={cn(
"flex items-center justify-center",
"w-4 h-4",
"transition-transform duration-200 ease-in-out",
isSelected ? "opacity-100 max-w-[16px] translate-x-0" : "opacity-0 max-w-0 -translate-x-4"
"tw-flex tw-items-center tw-justify-center",
"tw-size-4",
"tw-transition-transform tw-duration-200 tw-ease-in-out",
isSelected
? "tw-max-w-[16px] tw-translate-x-0 tw-opacity-100"
: "tw-max-w-0 tw--translate-x-4 tw-opacity-0"
)}
>
{tab.icon}
</div>
<span
className={cn(
"text-sm",
"font-medium",
"transition-all duration-200 ease-in-out",
"overflow-hidden whitespace-nowrap",
"opacity-100 max-w-[100px] translate-x-0"
"tw-text-sm",
"tw-font-medium",
"tw-transition-all tw-duration-200 tw-ease-in-out",
"tw-overflow-hidden tw-whitespace-nowrap",
"tw-max-w-[100px] tw-translate-x-0 tw-opacity-100"
)}
>
{tab.label}
@ -90,9 +91,9 @@ export const TabContent: React.FC<TabContentProps> = ({ id, children, isSelected
id={`tabpanel-${id}`}
aria-labelledby={`tab-${id}`}
className={cn(
"pt-4",
"transition-all duration-200 ease-in-out",
isSelected ? "opacity-100 translate-y-0" : "opacity-0 translate-y-2"
"tw-pt-4",
"tw-transition-all tw-duration-200 tw-ease-in-out",
isSelected ? "tw-translate-y-0 tw-opacity-100" : "tw-translate-y-2 tw-opacity-0"
)}
>
{children}

View file

@ -9,13 +9,16 @@ const Slider = React.forwardRef<
>(({ className, ...props }, ref) => (
<SliderPrimitive.Root
ref={ref}
className={cn("relative flex w-full touch-none select-none items-center", className)}
className={cn(
"tw-relative tw-flex tw-w-full tw-touch-none tw-select-none tw-items-center",
className
)}
{...props}
>
<SliderPrimitive.Track className="relative h-1.5 w-full grow overflow-hidden border border-solid border-interactive-accent/30 rounded-full bg-interactive-accent/20">
<SliderPrimitive.Range className="absolute h-full bg-interactive-accent" />
<SliderPrimitive.Track className="tw-relative tw-h-1.5 tw-w-full tw-grow tw-overflow-hidden tw-rounded-full tw-border tw-border-solid tw-bg-interactive-accent/20 tw-border-interactive-accent/30">
<SliderPrimitive.Range className="tw-absolute tw-h-full tw-bg-interactive-accent" />
</SliderPrimitive.Track>
<SliderPrimitive.Thumb className="block h-4 w-4 rounded-full border bg-toggle-thumb shadow transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50" />
<SliderPrimitive.Thumb className="tw-block tw-size-4 tw-rounded-full tw-border tw-bg-toggle-thumb tw-shadow tw-transition-colors focus-visible:tw-outline-none focus-visible:tw-ring-1 focus-visible:tw-ring-ring disabled:tw-pointer-events-none disabled:tw-opacity-50" />
</SliderPrimitive.Root>
));
Slider.displayName = SliderPrimitive.Root.displayName;

View file

@ -4,10 +4,10 @@ import { cn } from "@/lib/utils";
const Table = React.forwardRef<HTMLTableElement, React.HTMLAttributes<HTMLTableElement>>(
({ className, ...props }, ref) => (
<div className="relative w-full overflow-auto">
<div className="tw-relative tw-w-full tw-overflow-auto">
<table
ref={ref}
className={cn("w-full caption-bottom text-sm border-collapse", className)}
className={cn("tw-w-full tw-caption-bottom tw-border-collapse tw-text-sm", className)}
{...props}
/>
</div>
@ -19,16 +19,14 @@ const TableHeader = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
<thead ref={ref} className={cn("[&_tr]:tw-border-b", className)} {...props} />
));
TableHeader.displayName = "TableHeader";
const TableBody = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tbody ref={ref} className={cn("[&_tr:last-child]:border-0", className)} {...props} />
));
>(({ className, ...props }, ref) => <tbody ref={ref} className={className} {...props} />);
TableBody.displayName = "TableBody";
const TableFooter = React.forwardRef<
@ -37,7 +35,7 @@ const TableFooter = React.forwardRef<
>(({ className, ...props }, ref) => (
<tfoot
ref={ref}
className={cn("border-t bg-primary-alt/50 font-medium [&>tr]:last:border-b-0", className)}
className={cn("tw-border-t tw-font-medium tw-bg-primary-alt/50", className)}
{...props}
/>
));
@ -48,7 +46,7 @@ const TableRow = React.forwardRef<HTMLTableRowElement, React.HTMLAttributes<HTML
<tr
ref={ref}
className={cn(
"border-b transition-colors hover:bg-primary-alt/50 data-[state=selected]:bg-primary-alt",
"tw-border-b tw-transition-colors hover:tw-bg-primary-alt/50 data-[state=selected]:tw-bg-primary-alt",
className
)}
{...props}
@ -65,7 +63,7 @@ const TableHead = React.forwardRef<
<th
ref={ref}
className={cn(
"h-10 px-2 text-left align-middle font-medium text-muted [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
"tw-h-10 tw-px-2 tw-text-left tw-align-middle tw-font-medium tw-text-muted [&:has([role=checkbox])]:tw-pr-0 [&>[role=checkbox]]:tw-translate-y-[2px]",
className
)}
{...props}
@ -81,7 +79,7 @@ const TableCell = React.forwardRef<
<td
ref={ref}
className={cn(
"p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
"tw-p-2 tw-align-middle [&:has([role=checkbox])]:tw-pr-0 [&>[role=checkbox]]:tw-translate-y-[2px]",
className
)}
{...props}
@ -93,7 +91,7 @@ const TableCaption = React.forwardRef<
HTMLTableCaptionElement,
React.HTMLAttributes<HTMLTableCaptionElement>
>(({ className, ...props }, ref) => (
<caption ref={ref} className={cn("mt-4 text-sm text-muted", className)} {...props} />
<caption ref={ref} className={cn("tw-mt-4 tw-text-sm tw-text-muted", className)} {...props} />
));
TableCaption.displayName = "TableCaption";

View file

@ -12,7 +12,7 @@ const TabsList = React.forwardRef<
<TabsPrimitive.List
ref={ref}
className={cn(
"inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted",
"tw-inline-flex tw-h-9 tw-items-center tw-justify-center tw-rounded-lg tw-p-1 tw-text-muted",
className
)}
{...props}
@ -27,9 +27,9 @@ const TabsTrigger = React.forwardRef<
<TabsPrimitive.Trigger
ref={ref}
className={cn(
"!shadow-none",
"data-[state=active]:!bg-interactive-accent data-[state=active]:!text-on-accent data-[state=active]:!shadow-md",
"inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-ring transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 ",
"!tw-shadow-none",
"data-[state=active]:!tw-bg-interactive-accent data-[state=active]:!tw-text-on-accent data-[state=active]:!tw-shadow-md",
"tw-inline-flex tw-items-center tw-justify-center tw-whitespace-nowrap tw-rounded-md tw-px-3 tw-py-1 tw-text-sm tw-font-medium tw-ring-offset-ring tw-transition-all focus-visible:tw-outline-none focus-visible:tw-ring-2 focus-visible:tw-ring-ring focus-visible:tw-ring-offset-2 disabled:tw-pointer-events-none disabled:tw-opacity-50",
className
)}
{...props}
@ -44,7 +44,7 @@ const TabsContent = React.forwardRef<
<TabsPrimitive.Content
ref={ref}
className={cn(
"mt-2 ring-offset-ring focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
"tw-mt-2 tw-ring-offset-ring focus-visible:tw-outline-none focus-visible:tw-ring-2 focus-visible:tw-ring-ring focus-visible:tw-ring-offset-2",
className
)}
{...props}

View file

@ -38,8 +38,8 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, React.ComponentProps<"tex
return (
<textarea
className={cn(
"border-solid resize-y min-w-fit overflow-auto",
"flex min-h-[60px] max-h-[300px] w-full rounded-md border border-primary-alt bg-transparent px-3 py-2 text-base shadow-sm placeholder:text-muted focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
"tw-min-w-fit tw-resize-y tw-overflow-auto tw-border-solid",
"tw-flex tw-max-h-[300px] tw-min-h-[60px] tw-w-full tw-rounded-md tw-border tw-bg-transparent tw-px-3 tw-py-2 tw-text-base tw-shadow-sm placeholder:tw-text-muted focus-visible:tw-outline-none focus-visible:tw-ring-1 focus-visible:tw-ring-ring disabled:tw-cursor-not-allowed disabled:tw-opacity-50 md:tw-text-sm",
className
)}
value={value}

View file

@ -18,7 +18,7 @@ const TooltipContent = React.forwardRef<
ref={ref}
sideOffset={sideOffset}
className={cn(
"z-50 overflow-hidden rounded-md bg-secondary border-border border border-solid px-3 py-1.5 text-xs text-normal animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
"tw-z-[50] tw-overflow-hidden tw-rounded-md tw-border tw-border-solid tw-border-border tw-bg-secondary tw-px-3 tw-py-1.5 tw-text-xs tw-text-normal tw-animate-in tw-fade-in-0 tw-zoom-in-95 data-[state=closed]:tw-animate-out data-[state=closed]:tw-fade-out-0 data-[state=closed]:tw-zoom-out-95 data-[side=bottom]:tw-slide-in-from-top-2 data-[side=left]:tw-slide-in-from-right-2 data-[side=right]:tw-slide-in-from-left-2 data-[side=top]:tw-slide-in-from-bottom-2",
className
)}
{...props}

View file

@ -2,6 +2,7 @@ import { clsx, type ClassValue } from "clsx";
import { extendTailwindMerge } from "tailwind-merge";
const customTwMerge = extendTailwindMerge({
prefix: "tw-",
extend: {
classGroups: {
"text-color": ["text-muted", "text-success", "text-warning", "text-error", "text-accent"],

View file

@ -19,12 +19,12 @@ type TabId = (typeof TAB_IDS)[number];
// tab icons
const icons: Record<TabId, JSX.Element> = {
basic: <Cog className="w-5 h-5" />,
model: <Cpu className="w-5 h-5" />,
QA: <Database className="w-5 h-5" />,
command: <Command className="w-5 h-5" />,
plus: <Sparkles className="w-5 h-5" />,
advanced: <Wrench className="w-5 h-5" />,
basic: <Cog className="tw-size-5" />,
model: <Cpu className="tw-size-5" />,
QA: <Database className="tw-size-5" />,
command: <Command className="tw-size-5" />,
plus: <Sparkles className="tw-size-5" />,
advanced: <Wrench className="tw-size-5" />,
};
// tab components
@ -48,8 +48,8 @@ const SettingsContent: React.FC<{ plugin: CopilotPlugin }> = ({ plugin }) => {
const { selectedTab, setSelectedTab } = useTab();
return (
<div className="flex flex-col">
<div className="inline-flex rounded-lg">
<div className="tw-flex tw-flex-col">
<div className="tw-inline-flex tw-rounded-lg">
{tabs.map((tab, index) => (
<TabItem
key={tab.id}
@ -61,7 +61,7 @@ const SettingsContent: React.FC<{ plugin: CopilotPlugin }> = ({ plugin }) => {
/>
))}
</div>
<div className="w-[100%] border border-solid" />
<div className="tw-w-full tw-border tw-border-solid" />
<div>
{TAB_IDS.map((id) => {
@ -98,12 +98,12 @@ const SettingsMainV2: React.FC<SettingsMainV2Props> = ({ plugin }) => {
return (
<TabProvider>
<div>
<div className="flex flex-col gap-2">
<h1 className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2">
<div className="flex items-center gap-2">
<div className="tw-flex tw-flex-col tw-gap-2">
<h1 className="tw-flex tw-flex-col tw-gap-2 sm:tw-flex-row sm:tw-items-center sm:tw-justify-between">
<div className="tw-flex tw-items-center tw-gap-2">
<span>Copilot Settings</span>
<div className="flex items-center gap-1">
<span className="text-xs text-muted">v{plugin.manifest.version}</span>
<div className="tw-flex tw-items-center tw-gap-1">
<span className="tw-text-xs tw-text-muted">v{plugin.manifest.version}</span>
{latestVersion && (
<>
{hasUpdate ? (
@ -111,18 +111,18 @@ const SettingsMainV2: React.FC<SettingsMainV2Props> = ({ plugin }) => {
href="obsidian://show-plugin?id=copilot"
target="_blank"
rel="noopener noreferrer"
className="text-xs text-accent hover:underline"
className="tw-text-xs tw-text-accent hover:tw-underline"
>
(Update to v{latestVersion})
</a>
) : (
<span className="text-xs text-normal"> (up to date)</span>
<span className="tw-text-xs tw-text-normal"> (up to date)</span>
)}
</>
)}
</div>
</div>
<div className="self-end sm:self-auto">
<div className="tw-self-end sm:tw-self-auto">
<Button variant="secondary" size="sm" onClick={handleReset}>
Reset Settings
</Button>

View file

@ -7,7 +7,7 @@ export const AdvancedSettings: React.FC = () => {
const settings = useSettingsValue();
return (
<div className="space-y-4">
<div className="tw-space-y-4">
{/* Privacy Settings Section */}
<section>
<SettingItem
@ -19,7 +19,7 @@ export const AdvancedSettings: React.FC = () => {
placeholder="Enter your system prompt here..."
/>
<div className="space-y-4">
<div className="tw-space-y-4">
<SettingItem
type="switch"
title="Custom Prompt Templating"

View file

@ -266,46 +266,48 @@ function ApiKeyModalContent({ onClose }: ApiKeyModalContentProps) {
};
return (
<div className="sm:max-w-[500px] max-h-[600px] overflow-y-auto p-4">
<div className="mb-4">
<h2 className="text-xl font-bold">AI Provider Settings</h2>
<p className="text-sm text-muted">Configure your AI providers by adding their API keys.</p>
<div className="tw-max-h-[600px] tw-overflow-y-auto tw-p-4 sm:tw-max-w-[500px]">
<div className="tw-mb-4">
<h2 className="tw-text-xl tw-font-bold">AI Provider Settings</h2>
<p className="tw-text-sm tw-text-muted">
Configure your AI providers by adding their API keys.
</p>
</div>
<div className="space-y-6 py-4">
<div className="space-y-4">
<div className="tw-space-y-6 tw-py-4">
<div className="tw-space-y-4">
{providers.map((item: ProviderKeyItem) => (
<React.Fragment key={item.provider}>
<div className="flex flex-col gap-2">
<div className="flex items-end gap-2 font-medium">
<div className="truncate">{getProviderLabel(item.provider)}</div>
<div className="tw-flex tw-flex-col tw-gap-2">
<div className="tw-flex tw-items-end tw-gap-2 tw-font-medium">
<div className="tw-truncate">{getProviderLabel(item.provider)}</div>
</div>
<div className="flex flex-row items-center gap-2">
<div className="flex-1">
<div className="tw-flex tw-flex-row tw-items-center tw-gap-2">
<div className="tw-flex-1">
<PasswordInput
className="max-w-full"
className="tw-max-w-full"
value={item.apiKey}
onChange={(v) => handleApiKeyChange(item.provider, v)}
disabled={verifyingProviders.has(item.provider)}
/>
</div>
<div className="w-[72px]">
<div className="tw-w-[72px]">
{!item.isVerified ? (
<Button
onClick={() => verifyApiKey(item.provider, item.apiKey)}
disabled={!item.apiKey || verifyingProviders.size > 0}
variant="secondary"
size="sm"
className="w-full whitespace-nowrap"
className="tw-w-full tw-whitespace-nowrap"
>
{verifyingProviders.has(item.provider) ? (
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
<Loader2 className="tw-mr-2 tw-size-4 tw-animate-spin" />
) : (
"Verify"
)}
</Button>
) : (
<span className="text-success text-sm flex items-center justify-center h-9">
<span className="tw-flex tw-h-9 tw-items-center tw-justify-center tw-text-sm tw-text-success">
Verified
</span>
)}
@ -329,13 +331,13 @@ function ApiKeyModalContent({ onClose }: ApiKeyModalContentProps) {
disabled={!item.apiKey || verifyingProviders.size > 0}
variant="secondary"
size="sm"
className="w-full whitespace-nowrap px-0.5 py-0.5 flex items-center justify-center gap-1"
className="tw-flex tw-w-full tw-items-center tw-justify-center tw-gap-1 tw-whitespace-nowrap tw-p-0.5"
>
Add Model
{expandedProvider === item.provider ? (
<ChevronUp className="h-4 w-4 ml-1" />
<ChevronUp className="tw-ml-1 tw-size-4" />
) : (
<ChevronDown className="h-4 w-4 ml-1" />
<ChevronDown className="tw-ml-1 tw-size-4" />
)}
</Button>
</div>
@ -346,23 +348,23 @@ function ApiKeyModalContent({ onClose }: ApiKeyModalContentProps) {
href={getProviderInfo(item.provider).keyManagementURL}
target="_blank"
rel="noopener noreferrer"
className="text-[10px] sm:text-xs text-accent hover:text-accent-hover"
className="tw-text-[10px] tw-text-accent hover:tw-text-accent-hover sm:tw-text-xs"
>
Get {getProviderLabel(item.provider)} Key
</a>
)}
</div>
</div>
<Collapsible open={expandedProvider === item.provider} className="mt-2">
<CollapsibleContent className="p-3 rounded-md">
<div className="flex flex-col gap-2">
<Collapsible open={expandedProvider === item.provider} className="tw-mt-2">
<CollapsibleContent className="tw-rounded-md tw-p-3">
<div className="tw-flex tw-flex-col tw-gap-2">
<FormField
label="Model"
description="Add the currently selected model to model List. After adding, please check the Model Tab."
>
<div>
<div className="flex items-center gap-2">
<div className="flex-1">
<div className="tw-flex tw-items-center tw-gap-2">
<div className="tw-flex-1">
<ObsidianNativeSelect
options={
modelsByProvider[item.provider]
@ -407,7 +409,7 @@ function ApiKeyModalContent({ onClose }: ApiKeyModalContentProps) {
}
/>
</div>
<div className="w-[72px]">
<div className="tw-w-[72px]">
<Button
onClick={verifyModel}
disabled={
@ -417,22 +419,22 @@ function ApiKeyModalContent({ onClose }: ApiKeyModalContentProps) {
}
variant="secondary"
size="sm"
className="w-full whitespace-nowrap"
className="tw-w-full tw-whitespace-nowrap"
>
{verifyingModel ? (
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
<Loader2 className="tw-mr-2 tw-size-4 tw-animate-spin" />
) : (
"Add"
)}
</Button>
</div>
</div>
<div className="mt-1 text-xs">
<div className="tw-mt-1 tw-text-xs">
{loadingProvider === item.provider && (
<div className="text-muted p-1">Loading models...</div>
<div className="tw-p-1 tw-text-muted">Loading models...</div>
)}
{errorProvider === item.provider && (
<div className="text-error p-1">
<div className="tw-p-1 tw-text-error">
Failed to load models.
{modelsByProvider[item.provider] === null &&
" Check API Key or network."}
@ -440,14 +442,14 @@ function ApiKeyModalContent({ onClose }: ApiKeyModalContentProps) {
)}
{modelsByProvider[item.provider] &&
modelsByProvider[item.provider]!.length === 0 && (
<div className="text-muted p-1">
<div className="tw-p-1 tw-text-muted">
No models available for this provider.
</div>
)}
{modelsByProvider[item.provider] === undefined &&
errorProvider !== item.provider &&
loadingProvider !== item.provider && (
<div className="text-muted p-1">
<div className="tw-p-1 tw-text-muted">
Click to load models or expand to try again if API key was changed.
</div>
)}
@ -462,7 +464,7 @@ function ApiKeyModalContent({ onClose }: ApiKeyModalContentProps) {
</div>
</div>
<div className="flex justify-end mt-4">
<div className="tw-mt-4 tw-flex tw-justify-end">
<Button onClick={onClose}>Close</Button>
</div>
</div>

View file

@ -15,6 +15,7 @@ import { HelpCircle, Key, Loader2 } from "lucide-react";
import { Notice } from "obsidian";
import React, { useState } from "react";
import { ApiKeyDialog } from "./ApiKeyDialog";
import { cn } from "@/lib/utils";
const ChainType2Label: Record<ChainType, string> = {
[ChainType.LLM_CHAIN]: "Chat",
@ -97,33 +98,33 @@ export const BasicSettings: React.FC = () => {
}));
return (
<div className="space-y-4">
<div className="tw-space-y-4">
<PlusSettings />
{/* General Section */}
<section>
<div className="text-xl font-bold mb-3">General</div>
<div className="space-y-4">
<div className="space-y-4">
<div className="tw-mb-3 tw-text-xl tw-font-bold">General</div>
<div className="tw-space-y-4">
<div className="tw-space-y-4">
{/* API Key Section */}
<SettingItem
type="custom"
title="API Keys"
description={
<div className="flex items-center gap-1.5">
<span className="leading-none">
<div className="tw-flex tw-items-center tw-gap-1.5">
<span className="tw-leading-none">
Configure API keys for different AI providers
</span>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<HelpCircle className="size-4" />
<HelpCircle className="tw-size-4" />
</TooltipTrigger>
<TooltipContent className="max-w-96 flex flex-col gap-2 py-4">
<div className="text-sm font-medium text-accent">
<TooltipContent className="tw-flex tw-max-w-96 tw-flex-col tw-gap-2 tw-py-4">
<div className="tw-text-sm tw-font-medium tw-text-accent">
API key required for chat and QA features
</div>
<div className="text-xs text-muted">
<div className="tw-text-xs tw-text-muted">
To enable chat and QA functionality, please provide an API key from your
selected provider.
</div>
@ -138,10 +139,10 @@ export const BasicSettings: React.FC = () => {
new ApiKeyDialog(app).open();
}}
variant="secondary"
className="flex items-center gap-2 w-full sm:w-auto justify-center sm:justify-start"
className="tw-flex tw-w-full tw-items-center tw-justify-center tw-gap-2 sm:tw-w-auto sm:tw-justify-start"
>
Set Keys
<Key className="h-4 w-4" />
<Key className="tw-size-4" />
</Button>
</SettingItem>
</div>
@ -175,22 +176,22 @@ export const BasicSettings: React.FC = () => {
type="select"
title="Embedding Model"
description={
<div className="space-y-2">
<div className="flex items-center gap-1.5">
<span className="leading-none font-medium text-accent">
<div className="tw-space-y-2">
<div className="tw-flex tw-items-center tw-gap-1.5">
<span className="tw-font-medium tw-leading-none tw-text-accent">
Core Feature: Powers Semantic Search & QA
</span>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<HelpCircle className="size-4" />
<HelpCircle className="tw-size-4" />
</TooltipTrigger>
<TooltipContent className="max-w-96 flex flex-col gap-2">
<div className="text-sm text-muted pt-2">
<TooltipContent className="tw-flex tw-max-w-96 tw-flex-col tw-gap-2">
<div className="tw-pt-2 tw-text-sm tw-text-muted">
This model converts text into vector representations, essential for
semantic search and QA functionality. Changing the embedding model will:
</div>
<ul className="text-sm text-muted pl-4">
<ul className="tw-pl-4 tw-text-sm tw-text-muted">
<li>Require rebuilding your vault&#39;s vector index</li>
<li>Affect semantic search quality</li>
<li>Impact QA feature performance</li>
@ -215,15 +216,15 @@ export const BasicSettings: React.FC = () => {
type="select"
title="Default Mode"
description={
<div className="flex items-center gap-1.5">
<span className="leading-none">Select the default chat mode</span>
<div className="tw-flex tw-items-center tw-gap-1.5">
<span className="tw-leading-none">Select the default chat mode</span>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<HelpCircle className="size-4" />
<HelpCircle className="tw-size-4" />
</TooltipTrigger>
<TooltipContent className="max-w-96 flex flex-col gap-2">
<ul className="text-sm text-muted pl-4">
<TooltipContent className="tw-flex tw-max-w-96 tw-flex-col tw-gap-2">
<ul className="tw-pl-4 tw-text-sm tw-text-muted">
<li>
<strong>Chat:</strong> Regular chat mode for general conversations and
tasks. <i>Free to use with your own API key.</i>
@ -240,7 +241,7 @@ export const BasicSettings: React.FC = () => {
href={createPlusPageUrl(PLUS_UTM_MEDIUMS.MODE_SELECT_TOOLTIP)}
target="_blank"
rel="noopener noreferrer"
className="text-accent hover:text-accent-hover"
className="tw-text-accent hover:tw-text-accent-hover"
>
obsidiancopilot.com
</a>{" "}
@ -303,22 +304,24 @@ export const BasicSettings: React.FC = () => {
type="custom"
title="Conversation Filename Template"
description={
<div className="flex items-start gap-1.5 ">
<span className="leading-none">
<div className="tw-flex tw-items-start tw-gap-1.5 ">
<span className="tw-leading-none">
Customize the format of saved conversation note names.
</span>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<HelpCircle className="size-4" />
<HelpCircle className="tw-size-4" />
</TooltipTrigger>
<TooltipContent className="max-w-96 flex flex-col gap-2 py-4">
<div className="text-sm font-medium text-accent">
<TooltipContent className="tw-flex tw-max-w-96 tw-flex-col tw-gap-2 tw-py-4">
<div className="tw-text-sm tw-font-medium tw-text-accent">
Note: All the following variables must be included in the template.
</div>
<div>
<div className="text-sm font-medium text-muted">Available variables:</div>
<ul className="text-sm text-muted pl-4">
<div className="tw-text-sm tw-font-medium tw-text-muted">
Available variables:
</div>
<ul className="tw-pl-4 tw-text-sm tw-text-muted">
<li>
<strong>{"{$date}"}</strong>: Date in YYYYMMDD format
</li>
@ -329,7 +332,7 @@ export const BasicSettings: React.FC = () => {
<strong>{"{$topic}"}</strong>: Chat conversation topic
</li>
</ul>
<i className="text-sm text-muted mt-2">
<i className="tw-mt-2 tw-text-sm tw-text-muted">
Example: {"{$date}_{$time}__{$topic}"}
20250114_153232__polish_this_article_[[Readme]]
</i>
@ -340,10 +343,13 @@ export const BasicSettings: React.FC = () => {
</div>
}
>
<div className="flex items-center gap-1.5 w-[320px]">
<div className="tw-flex tw-w-[320px] tw-items-center tw-gap-1.5">
<Input
type="text"
className={`transition-all duration-200 flex-grow min-w-[80px] ${isChecking ? "w-[80px]" : "w-[120px]"}`}
className={cn(
"tw-min-w-[80px] tw-grow tw-transition-all tw-duration-200",
isChecking ? "tw-w-[80px]" : "tw-w-[120px]"
)}
placeholder="{$date}_{$time}__{$topic}"
value={conversationNoteName}
onChange={(e) => setConversationNoteName(e.target.value)}
@ -357,7 +363,7 @@ export const BasicSettings: React.FC = () => {
>
{isChecking ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
<Loader2 className="tw-mr-2 tw-size-4 tw-animate-spin" />
Apply
</>
) : (

View file

@ -69,22 +69,22 @@ const SortableTableRow: React.FC<{
ref={setNodeRef}
style={style}
className={cn(
"transition-colors",
"tw-transition-colors",
isDragging &&
"shadow-lg bg-background/90 backdrop-blur-sm relative z-[100] cursor-grabbing border-2 border-accent/50"
"tw-relative tw-z-[100] tw-cursor-grabbing tw-shadow-lg tw-backdrop-blur-sm tw-border-accent/50"
)}
>
<TableCell className="w-10">
<TableCell className="tw-w-10">
<div
{...attributes}
{...listeners}
className="flex items-center justify-center cursor-grab"
className="tw-flex tw-cursor-grab tw-items-center tw-justify-center"
>
<GripVertical className="h-4 w-4 text-muted-foreground" />
<GripVertical className="tw-size-4" />
</div>
</TableCell>
<TableCell>{command.name}</TableCell>
<TableCell className="text-center">
<TableCell className="tw-text-center">
<Checkbox
checked={command.showInContextMenu}
onCheckedChange={(checked) =>
@ -93,11 +93,11 @@ const SortableTableRow: React.FC<{
showInContextMenu: checked === true,
})
}
className="mx-auto"
className="tw-mx-auto"
/>
</TableCell>
<TableCell className="text-center">
<div className="flex justify-center space-x-1">
<TableCell className="tw-text-center">
<div className="tw-flex tw-justify-center tw-space-x-1">
<Button
variant="ghost"
size="icon"
@ -110,12 +110,12 @@ const SortableTableRow: React.FC<{
).open()
}
>
<PencilLine className="h-4 w-4" />
<PencilLine className="tw-size-4" />
</Button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon">
<MoreVertical className="h-4 w-4" />
<MoreVertical className="tw-size-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" container={container}>
@ -129,15 +129,15 @@ const SortableTableRow: React.FC<{
).open()
}
>
<PencilLine className="h-4 w-4 mr-2" />
<PencilLine className="tw-mr-2 tw-size-4" />
Edit
</DropdownMenuItem>
<DropdownMenuItem onClick={() => onDuplicate(command)}>
<Copy className="h-4 w-4 mr-2" />
<Copy className="tw-mr-2 tw-size-4" />
Copy
</DropdownMenuItem>
<DropdownMenuItem onClick={() => onRemove(command)} className="text-error">
<Trash2 className="h-4 w-4 mr-2" />
<DropdownMenuItem onClick={() => onRemove(command)} className="tw-text-error">
<Trash2 className="tw-mr-2 tw-size-4" />
Delete
</DropdownMenuItem>
</DropdownMenuContent>
@ -214,23 +214,23 @@ export const CommandSettings: React.FC = () => {
};
return (
<div className="space-y-4">
<div className="tw-space-y-4">
<section>
<div className="flex flex-col mb-4 gap-2">
<div className="text-xl font-bold">Custom Commands</div>
<div className="text-sm text-muted">
<div className="tw-mb-4 tw-flex tw-flex-col tw-gap-2">
<div className="tw-text-xl tw-font-bold">Custom Commands</div>
<div className="tw-text-sm tw-text-muted">
To trigger a custom command, highlight text in the editor and select it from the command
palette, or right-click and choose it from the context menu if configured.
</div>
</div>
{!hasModifiedCommand() && (
<div className="border border-border border-solid p-4 rounded-md text-muted flex items-start gap-2">
<Lightbulb className="size-5" /> Take control of your inline edit commands! You can now
create your own or edit built-in ones to tailor functionality to your needs.
<div className="tw-flex tw-items-start tw-gap-2 tw-rounded-md tw-border tw-border-solid tw-border-border tw-p-4 tw-text-muted">
<Lightbulb className="tw-size-5" /> Take control of your inline edit commands! You can
now create your own or edit built-in ones to tailor functionality to your needs.
</div>
)}
<div className="flex flex-col gap-4">
<div className="tw-flex tw-flex-col tw-gap-4">
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
@ -239,10 +239,10 @@ export const CommandSettings: React.FC = () => {
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-10"></TableHead>
<TableHead className="tw-w-10"></TableHead>
<TableHead>Name</TableHead>
<TableHead className="text-center w-20">In Menu</TableHead>
<TableHead className="w-10"></TableHead>
<TableHead className="tw-w-20 tw-text-center">In Menu</TableHead>
<TableHead className="tw-w-10"></TableHead>
</TableRow>
</TableHeader>
<SortableContext
@ -263,7 +263,7 @@ export const CommandSettings: React.FC = () => {
</SortableContext>
</Table>
</DndContext>
<div className="flex w-full justify-end">
<div className="tw-flex tw-w-full tw-justify-end">
<Button
variant="secondary"
onClick={() =>
@ -278,7 +278,7 @@ export const CommandSettings: React.FC = () => {
).open()
}
>
<Plus className="h-4 w-4" /> Add Command
<Plus className="tw-size-4" /> Add Command
</Button>
</div>
</div>

View file

@ -1,5 +1,6 @@
import { AcceptKeyOption } from "@/autocomplete/codemirrorIntegration";
import { WordCompletionManager } from "@/autocomplete/wordCompletion";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
Select,
@ -11,6 +12,7 @@ import {
import { SettingItem } from "@/components/ui/setting-item";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { AUTOCOMPLETE_CONFIG } from "@/constants";
import { cn } from "@/lib/utils";
import { updateSetting, useSettingsValue } from "@/settings/model";
import { HelpCircle, RefreshCw } from "lucide-react";
import { Notice } from "obsidian";
@ -67,14 +69,14 @@ export const CopilotPlusSettings: React.FC = () => {
};
return (
<div className="flex flex-col gap-4">
<section className="flex flex-col gap-4">
<div className="flex items-center py-4">
<span className="rounded-full bg-accent/90 px-3 py-1 text-sm font-semibold text-accent-foreground">
Plus required
</span>
<div className="tw-flex tw-flex-col tw-gap-4">
<section className="tw-flex tw-flex-col tw-gap-4">
<div className="tw-flex tw-items-center tw-py-4">
<Badge variant="secondary" className="tw-text-accent">
Plus Required
</Badge>
</div>
<div className="flex flex-col gap-6">
<div className="tw-flex tw-flex-col tw-gap-4">
<SettingItem
type="switch"
title="Include Current Note in Context Menu"
@ -95,14 +97,14 @@ export const CopilotPlusSettings: React.FC = () => {
}}
/>
<h2 className="text-lg font-semibold pt-4">Autocomplete</h2>
<div className="tw-pt-4 tw-text-xl tw-font-semibold">Autocomplete</div>
<SettingItem
type="switch"
title="Sentence Autocomplete"
description={
<div className="flex items-center gap-1.5">
<span className="leading-none">
<div className="tw-flex tw-items-center tw-gap-1.5">
<span className="tw-leading-none">
Enable AI-powered sentence autocomplete suggestions while typing
</span>
</div>
@ -129,9 +131,9 @@ export const CopilotPlusSettings: React.FC = () => {
<Button
onClick={handleRefreshWordIndex}
disabled={isRefreshing}
className="flex items-center gap-2"
className="tw-flex tw-items-center tw-gap-2"
>
<RefreshCw className={`size-4 ${isRefreshing ? "animate-spin" : ""}`} />
<RefreshCw className={cn("tw-size-4", isRefreshing && "tw-animate-spin")} />
{isRefreshing ? "Rebuilding..." : "Refresh Word Index"}
</Button>
</SettingItem>
@ -140,17 +142,17 @@ export const CopilotPlusSettings: React.FC = () => {
type="custom"
title="Autocomplete Accept Suggestion Key"
description={
<div className="flex items-center gap-1.5">
<span className="leading-none">
<div className="tw-flex tw-items-center tw-gap-1.5">
<span className="tw-leading-none">
The key used to accept autocomplete suggestions
</span>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<HelpCircle className="size-4" />
<HelpCircle className="tw-size-4" />
</TooltipTrigger>
<TooltipContent className="max-w-96 flex flex-col gap-2">
<div className="text-sm text-muted pt-2">
<TooltipContent className="tw-flex tw-max-w-96 tw-flex-col tw-gap-2">
<div className="tw-text-sm tw-text-muted">
Select the key you want to use for accepting suggestions. Default is
&quot;Tab&quot;.
</div>
@ -160,9 +162,9 @@ export const CopilotPlusSettings: React.FC = () => {
</div>
}
>
<div className="flex items-center gap-2">
<div className="tw-flex tw-items-center tw-gap-2">
<Select value={currentShortcut} onValueChange={handleKeyChange}>
<SelectTrigger className="w-[180px]">
<SelectTrigger className="tw-w-[180px]">
<SelectValue placeholder="Select key" />
</SelectTrigger>
<SelectContent>
@ -175,7 +177,7 @@ export const CopilotPlusSettings: React.FC = () => {
</Select>
{currentShortcut && currentShortcut !== AUTOCOMPLETE_CONFIG.KEYBIND && (
<Button variant="ghost" onClick={resetToDefault} className="text-xs h-8">
<Button variant="ghost" onClick={resetToDefault} className="tw-h-8 tw-text-xs">
Reset to Default
</Button>
)}

View file

@ -346,18 +346,18 @@ export const ModelAddDialog: React.FC<ModelAddDialogProps> = ({
<Collapsible
open={isOpen}
onOpenChange={setIsOpen}
className="space-y-2 border rounded-lg pt-4"
className="tw-space-y-2 tw-rounded-lg tw-border tw-pt-4"
>
<div className="flex items-center justify-between">
<div className="tw-flex tw-items-center tw-justify-between">
<Label>Additional {getProviderLabel(model.provider)} Settings</Label>
<CollapsibleTrigger asChild>
<Button variant="ghost" size="sm" className="w-9 p-0">
<ChevronDown className="h-4 w-4" />
<span className="sr-only">Toggle</span>
<Button variant="ghost" size="sm" className="tw-w-9 tw-p-0">
<ChevronDown className="tw-size-4" />
<span className="tw-sr-only">Toggle</span>
</Button>
</CollapsibleTrigger>
</div>
<CollapsibleContent className="space-y-4 max-h-[200px] overflow-y-auto pl-0.5 pr-2 pb-0.5">
<CollapsibleContent className="tw-max-h-[200px] tw-space-y-4 tw-overflow-y-auto tw-pb-0.5 tw-pl-0.5 tw-pr-2">
{content}
</CollapsibleContent>
</Collapsible>
@ -388,7 +388,7 @@ export const ModelAddDialog: React.FC<ModelAddDialogProps> = ({
return (
<Dialog open={open} onOpenChange={handleOpenChange}>
<DialogContent
className="sm:max-w-[425px]"
className="sm:tw-max-w-[425px]"
container={modalContainer}
ref={(el) => setDialogElement(el)}
>
@ -397,7 +397,7 @@ export const ModelAddDialog: React.FC<ModelAddDialogProps> = ({
<DialogDescription>Add a new model to your collection.</DialogDescription>
</DialogHeader>
<div className="space-y-3">
<div className="tw-space-y-3">
<FormField
label="Model Name"
required
@ -419,18 +419,18 @@ export const ModelAddDialog: React.FC<ModelAddDialogProps> = ({
<FormField
label={
<div className="flex items-center gap-1.5">
<span className="leading-none">Display Name</span>
<div className="tw-flex tw-items-center tw-gap-1.5">
<span className="tw-leading-none">Display Name</span>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<HelpCircle className="size-4" />
<HelpCircle className="tw-size-4" />
</TooltipTrigger>
<TooltipContent align="start" className="max-w-96" side="bottom">
<div className="text-sm text-muted flex flex-col gap-0.5">
<div className="text-[12px] font-bold">Suggested format:</div>
<div className="text-accent">[Source]-[Payment]:[Pretty Model Name]</div>
<div className="text-[12px]">
<TooltipContent align="start" className="tw-max-w-96" side="bottom">
<div className="tw-flex tw-flex-col tw-gap-0.5 tw-text-sm tw-text-muted">
<div className="tw-text-[12px] tw-font-bold">Suggested format:</div>
<div className="tw-text-accent">[Source]-[Payment]:[Pretty Model Name]</div>
<div className="tw-text-[12px]">
Example:
<li>Direct-Paid:Ds-r1</li>
<li>OpenRouter-Paid:Ds-r1</li>
@ -488,7 +488,7 @@ export const ModelAddDialog: React.FC<ModelAddDialogProps> = ({
onChange={(value) => setModel({ ...model, apiKey: value })}
/>
{providerInfo.keyManagementURL && (
<p className="text-xs text-muted">
<p className="tw-text-xs tw-text-muted">
<a href={providerInfo.keyManagementURL} target="_blank" rel="noopener noreferrer">
Get {providerInfo.label} API Key
</a>
@ -498,15 +498,15 @@ export const ModelAddDialog: React.FC<ModelAddDialogProps> = ({
<FormField
label={
<div className="flex items-center gap-1.5">
<span className="leading-none">Model Capabilities</span>
<div className="tw-flex tw-items-center tw-gap-1.5">
<span className="tw-leading-none">Model Capabilities</span>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<HelpCircle className="size-4" />
<HelpCircle className="tw-size-4" />
</TooltipTrigger>
<TooltipContent align="start" className="max-w-96" side="bottom">
<div className="text-sm text-muted">
<TooltipContent align="start" className="tw-max-w-96" side="bottom">
<div className="tw-text-sm tw-text-muted">
Only used to display model capabilities, does not affect model functionality
</div>
</TooltipContent>
@ -515,9 +515,9 @@ export const ModelAddDialog: React.FC<ModelAddDialogProps> = ({
</div>
}
>
<div className="flex gap-4 items-center">
<div className="tw-flex tw-items-center tw-gap-4">
{capabilityOptions.map(({ id, label, description }) => (
<div key={id} className="flex items-center gap-2">
<div key={id} className="tw-flex tw-items-center tw-gap-2">
<Checkbox
id={id}
checked={model.capabilities?.includes(id)}
@ -531,7 +531,7 @@ export const ModelAddDialog: React.FC<ModelAddDialogProps> = ({
});
}}
/>
<Label htmlFor={id} className="text-sm">
<Label htmlFor={id} className="tw-text-sm">
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
@ -549,23 +549,23 @@ export const ModelAddDialog: React.FC<ModelAddDialogProps> = ({
{renderProviderSpecificFields()}
</div>
<div className="flex justify-end gap-4 items-center">
<div className="flex items-center gap-2">
<div className="tw-flex tw-items-center tw-justify-end tw-gap-4">
<div className="tw-flex tw-items-center tw-gap-2">
<Checkbox
id="enable-cors"
checked={model.enableCors || false}
onCheckedChange={(checked: boolean) => setModel({ ...model, enableCors: checked })}
/>
<Label htmlFor="enable-cors" className="text-sm">
<div className="flex items-center gap-1.5">
<Label htmlFor="enable-cors" className="tw-text-sm">
<div className="tw-flex tw-items-center tw-gap-1.5">
<span>Enable CORS</span>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<HelpCircle className="size-4" />
<HelpCircle className="tw-size-4" />
</TooltipTrigger>
<TooltipContent align="start" className="max-w-96" side="bottom">
<div className="text-sm text-muted">
<TooltipContent align="start" className="tw-max-w-96" side="bottom">
<div className="tw-text-sm tw-text-muted">
Only check this option when prompted that CORS is needed
</div>
</TooltipContent>
@ -574,14 +574,14 @@ export const ModelAddDialog: React.FC<ModelAddDialogProps> = ({
</div>
</Label>
</div>
<div className="flex gap-2">
<div className="tw-flex tw-gap-2">
<Button variant="secondary" onClick={handleAdd} disabled={isButtonDisabled()}>
Add Model
</Button>
<Button variant="secondary" onClick={handleVerify} disabled={isButtonDisabled()}>
{isVerifying ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
<Loader2 className="tw-mr-2 tw-size-4 tw-animate-spin" />
Verify
</>
) : (

View file

@ -113,13 +113,13 @@ export const ModelEditDialog: React.FC<ModelEditDialogProps> = ({
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-[425px]" container={modalContainer}>
<DialogContent className="sm:tw-max-w-[425px]" container={modalContainer}>
<DialogHeader>
<DialogTitle>Model Settings - {localModel.name}</DialogTitle>
<DialogDescription>Customize model parameters.</DialogDescription>
</DialogHeader>
<div className="space-y-3">
<div className="tw-space-y-3">
<FormField label="Model Name" required>
<Input
type="text"
@ -132,18 +132,18 @@ export const ModelEditDialog: React.FC<ModelEditDialogProps> = ({
<FormField
label={
<div className="flex items-center gap-1.5">
<span className="leading-none">Display Name</span>
<div className="tw-flex tw-items-center tw-gap-1.5">
<span className="tw-leading-none">Display Name</span>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<HelpCircle className="size-4" />
<HelpCircle className="tw-size-4" />
</TooltipTrigger>
<TooltipContent align="start" className="max-w-96" side="bottom">
<div className="text-sm text-muted flex flex-col gap-0.5">
<div className="text-[12px] font-bold">Suggested format:</div>
<div className="text-accent">[Source]-[Payment]:[Pretty Model Name]</div>
<div className="text-[12px]">
<TooltipContent align="start" className="tw-max-w-96" side="bottom">
<div className="tw-flex tw-flex-col tw-gap-0.5 tw-text-sm tw-text-muted">
<div className="tw-text-[12px] tw-font-bold">Suggested format:</div>
<div className="tw-text-accent">[Source]-[Payment]:[Pretty Model Name]</div>
<div className="tw-text-[12px]">
Example:
<li>Direct-Paid:Ds-r1</li>
<li>OpenRouter-Paid:Ds-r1</li>
@ -165,12 +165,7 @@ export const ModelEditDialog: React.FC<ModelEditDialogProps> = ({
</FormField>
<FormField label="Provider">
<Input
type="text"
value={getProviderLabel(localModel.provider)}
disabled
className="bg-muted"
/>
<Input type="text" value={getProviderLabel(localModel.provider)} disabled />
</FormField>
<FormField label="Base URL" description="Leave it blank, unless you are using a proxy.">
@ -189,7 +184,7 @@ export const ModelEditDialog: React.FC<ModelEditDialogProps> = ({
onChange={(value) => handleLocalUpdate("apiKey", value)}
/>
{providerInfo.keyManagementURL && (
<p className="text-xs text-muted">
<p className="tw-text-xs tw-text-muted">
<a href={providerInfo.keyManagementURL} target="_blank" rel="noopener noreferrer">
Get {providerInfo.label} API Key
</a>
@ -199,15 +194,15 @@ export const ModelEditDialog: React.FC<ModelEditDialogProps> = ({
<FormField
label={
<div className="flex items-center gap-1.5">
<span className="leading-none">Model Capabilities</span>
<div className="tw-flex tw-items-center tw-gap-1.5">
<span className="tw-leading-none">Model Capabilities</span>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<HelpCircle className="size-4" />
<HelpCircle className="tw-size-4" />
</TooltipTrigger>
<TooltipContent align="start" className="max-w-96" side="bottom">
<div className="text-sm text-muted">
<TooltipContent align="start" className="tw-max-w-96" side="bottom">
<div className="tw-text-sm tw-text-muted">
Only used to display model capabilities, does not affect model functionality
</div>
</TooltipContent>
@ -216,9 +211,9 @@ export const ModelEditDialog: React.FC<ModelEditDialogProps> = ({
</div>
}
>
<div className="flex gap-4 items-center">
<div className="tw-flex tw-items-center tw-gap-4">
{capabilityOptions.map(({ id, label, description }) => (
<div key={id} className="flex items-center gap-2">
<div key={id} className="tw-flex tw-items-center tw-gap-2">
<Checkbox
id={id}
checked={localModel.capabilities?.includes(id)}
@ -230,7 +225,7 @@ export const ModelEditDialog: React.FC<ModelEditDialogProps> = ({
handleLocalUpdate("capabilities", value);
}}
/>
<Label htmlFor={id} className="text-sm">
<Label htmlFor={id} className="tw-text-sm">
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
@ -247,12 +242,12 @@ export const ModelEditDialog: React.FC<ModelEditDialogProps> = ({
{/*<FormField
label={
<div className="flex items-center gap-2">
<div className="tw-flex tw-items-center tw-gap-2">
Temperature
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<HelpCircle className="h-4 w-4 text-muted" />
<HelpCircle className="tw-h-4 tw-w-4 tw-text-muted" />
</TooltipTrigger>
<TooltipContent side="bottom">
Controls randomness: 0 is focused and deterministic, 2 is more creative
@ -273,12 +268,12 @@ export const ModelEditDialog: React.FC<ModelEditDialogProps> = ({
<FormField
label={
<div className="flex items-center gap-2">
<div className="tw-flex tw-items-center tw-gap-2">
Context
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<HelpCircle className="h-4 w-4 text-muted" />
<HelpCircle className="tw-h-4 tw-w-4 tw-text-muted" />
</TooltipTrigger>
<TooltipContent side="bottom">
Maximum number of tokens to use for context
@ -297,13 +292,13 @@ export const ModelEditDialog: React.FC<ModelEditDialogProps> = ({
/>
</FormField>
<div className="flex items-center justify-between py-2">
<div className="flex items-center gap-2">
<span className="text-sm font-medium">Stream output</span>
<div className="tw-flex tw-items-center tw-justify-between tw-py-2">
<div className="tw-flex tw-items-center tw-gap-2">
<span className="tw-text-sm tw-font-medium">Stream output</span>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<HelpCircle className="h-4 w-4 text-muted" />
<HelpCircle className="tw-h-4 tw-w-4 tw-text-muted" />
</TooltipTrigger>
<TooltipContent side="bottom">
Enable streaming responses from the model

View file

@ -107,9 +107,9 @@ export const ModelSettings: React.FC = () => {
};
return (
<div className="space-y-4">
<div className="tw-space-y-4">
<section>
<div className="text-xl font-bold mb-3">Chat Models</div>
<div className="tw-mb-3 tw-text-xl tw-font-bold">Chat Models</div>
<ModelTable
models={settings.activeModels}
onEdit={setEditingModel}
@ -142,7 +142,7 @@ export const ModelSettings: React.FC = () => {
}
/>
<div className="space-y-4">
<div className="tw-space-y-4">
<SettingItem
type="slider"
title="Temperature"
@ -189,7 +189,7 @@ export const ModelSettings: React.FC = () => {
</section>
<section>
<div className="text-xl font-bold mb-3">Embedding Models</div>
<div className="tw-mb-3 tw-text-xl tw-font-bold">Embedding Models</div>
<ModelTable
models={settings.activeEmbeddingModels}
onDelete={onDeleteEmbeddingModel}

View file

@ -58,17 +58,17 @@ const CAPABILITY_ICONS: Record<
> = {
[ModelCapability.REASONING]: {
icon: Lightbulb,
color: "text-model-capabilities-blue",
color: "tw-text-model-capabilities-blue",
tooltip: MODEL_CAPABILITIES.reasoning,
},
[ModelCapability.VISION]: {
icon: Eye,
color: "text-model-capabilities-green",
color: "tw-text-model-capabilities-green",
tooltip: MODEL_CAPABILITIES.vision,
},
[ModelCapability.WEB_SEARCH]: {
icon: Globe,
color: "text-model-capabilities-blue",
color: "tw-text-model-capabilities-blue",
tooltip: MODEL_CAPABILITIES.websearch,
},
} as const;
@ -82,10 +82,10 @@ const CAPABILITY_ORDER = [
const renderCapabilities = (model: CustomModel) => {
return (
<TooltipProvider delayDuration={0}>
<div className="grid grid-cols-3 gap-1 w-16 mx-auto">
<div className="tw-mx-auto tw-grid tw-w-16 tw-grid-cols-3 tw-gap-1">
{CAPABILITY_ORDER.map((capability) => {
const config = CAPABILITY_ICONS[capability];
if (!config) return <div key={capability} className="w-4" />;
if (!config) return <div key={capability} className="tw-w-4" />;
const Icon = config.icon;
const hasCapability = model.capabilities?.includes(capability);
@ -93,15 +93,15 @@ const renderCapabilities = (model: CustomModel) => {
return hasCapability ? (
<Tooltip key={capability}>
<TooltipTrigger asChild>
<div className="flex items-center justify-center">
<Icon className={`h-4 w-4 ${config.color}`} />
<div className="tw-flex tw-items-center tw-justify-center">
<Icon className={cn("tw-size-4", config.color)} />
</div>
</TooltipTrigger>
<TooltipContent side="bottom">{config.tooltip}</TooltipContent>
</Tooltip>
) : (
<div key={capability} className="flex items-center justify-center">
<div className="w-4 h-4" />
<div key={capability} className="tw-flex tw-items-center tw-justify-center">
<div className="tw-size-4" />
</div>
);
})}
@ -147,51 +147,50 @@ const ModelCard: React.FC<ModelCardProps> = ({ model, onEdit, onDelete, onUpdate
ref={setNodeRef}
style={style}
className={cn(
"mb-2",
"tw-mb-2",
isDragging && [
"opacity-90",
"shadow-lg",
"border-2",
"border-accent/50",
"relative",
"z-[9999]",
"bg-primary",
"rounded-lg",
"transform-gpu",
"tw-opacity-90",
"tw-shadow-lg",
"tw-border-accent/50",
"tw-relative",
"tw-z-[9999]",
"tw-bg-primary",
"tw-rounded-lg",
"tw-transform-gpu",
],
!model.core && "touch-none"
!model.core && "tw-touch-none"
)}
>
<CardHeader className="p-3">
<div className="flex items-center justify-between">
<CardHeader className="tw-p-3">
<div className="tw-flex tw-items-center tw-justify-between">
{!model.core && (
<div
className="mr-2 touch-none cursor-grab active:cursor-grabbing"
className="tw-mr-2 tw-cursor-grab tw-touch-none active:tw-cursor-grabbing"
{...attributes}
{...listeners}
>
<GripVertical className="h-4 w-4 text-muted-foreground" />
<GripVertical className="tw-size-4" />
</div>
)}
<div className="flex-1 cursor-pointer" onClick={() => setIsExpanded(!isExpanded)}>
<div className="flex items-center gap-2">
<div className="w-3 h-3 flex items-center justify-center">
<div className="tw-flex-1 tw-cursor-pointer" onClick={() => setIsExpanded(!isExpanded)}>
<div className="tw-flex tw-items-center tw-gap-2">
<div className="tw-flex tw-size-3 tw-items-center tw-justify-center">
{isExpanded ? (
<ChevronDown className="h-3 w-3 stroke-[7]" />
<ChevronDown className="tw-size-3 tw-stroke-[7]" />
) : (
<ChevronRight className="h-3 w-3 stroke-[7]" />
<ChevronRight className="tw-size-3 tw-stroke-[7]" />
)}
</div>
<div>
<div className="flex items-center gap-1">
<span className="font-medium">{model.displayName || model.name}</span>
<div className="tw-flex tw-items-center tw-gap-1">
<span className="tw-font-medium">{model.displayName || model.name}</span>
{model.capabilities && model.capabilities.length > 0 && (
<ModelCapabilityIcons capabilities={model.capabilities} iconSize={14} />
)}
</div>
<div className="flex items-center gap-2">
<span className="text-sm text-muted bg-secondary">
<div className="tw-flex tw-items-center tw-gap-2">
<span className="tw-bg-secondary tw-text-sm tw-text-muted">
{getProviderLabel(model.provider, model)}
</span>
</div>
@ -199,7 +198,7 @@ const ModelCard: React.FC<ModelCardProps> = ({ model, onEdit, onDelete, onUpdate
</div>
</div>
<div className="flex items-center gap-2">
<div className="tw-flex tw-items-center tw-gap-2">
{onEdit && (
<Button
variant="ghost"
@ -209,7 +208,7 @@ const ModelCard: React.FC<ModelCardProps> = ({ model, onEdit, onDelete, onUpdate
onEdit();
}}
>
<Pencil className="h-4 w-4" />
<Pencil className="tw-size-4" />
</Button>
)}
{onDelete && !model.core && (
@ -221,7 +220,7 @@ const ModelCard: React.FC<ModelCardProps> = ({ model, onEdit, onDelete, onUpdate
onDelete();
}}
>
<Trash2 className="h-4 w-4" />
<Trash2 className="tw-size-4" />
</Button>
)}
</div>
@ -229,15 +228,15 @@ const ModelCard: React.FC<ModelCardProps> = ({ model, onEdit, onDelete, onUpdate
</CardHeader>
<div
className={cn(
"transition-all duration-300 ease-in-out",
isExpanded ? "max-h-20 opacity-100" : "max-h-0 opacity-0 overflow-hidden"
"tw-transition-all tw-duration-300 tw-ease-in-out",
isExpanded ? "tw-max-h-20 tw-opacity-100" : "tw-max-h-0 tw-overflow-hidden tw-opacity-0"
)}
>
<CardContent className="p-3 pt-0">
<div className="flex justify-around">
<CardContent className="tw-p-3 tw-pt-0">
<div className="tw-flex tw-justify-around">
{!model.isEmbeddingModel && (
<div className="flex items-center gap-2">
<span className="text-sm">Enabled</span>
<div className="tw-flex tw-items-center tw-gap-2">
<span className="tw-text-sm">Enabled</span>
<Checkbox
checked={model.enabled}
onCheckedChange={(checked: boolean) =>
@ -246,8 +245,8 @@ const ModelCard: React.FC<ModelCardProps> = ({ model, onEdit, onDelete, onUpdate
/>
</div>
)}
<div className="flex items-center gap-2">
<span className="text-sm">CORS</span>
<div className="tw-flex tw-items-center tw-gap-2">
<span className="tw-text-sm">CORS</span>
<Checkbox
checked={model.enableCors}
onCheckedChange={(checked: boolean) =>
@ -284,56 +283,55 @@ const SortableTableRow: React.FC<{
ref={setNodeRef}
style={style}
className={cn(
"hover:bg-interactive-accent/10 transition-colors duration-200",
"tw-transition-colors tw-duration-200 hover:tw-bg-interactive-accent/10",
isDragging &&
"shadow-lg bg-primary/90 backdrop-blur-sm relative z-[100] cursor-grabbing border-2 border-accent/50",
!isDragging && "z-auto"
"tw-relative tw-z-[100] tw-cursor-grabbing tw-shadow-lg tw-backdrop-blur-sm tw-border-accent/50 tw-bg-primary/90"
)}
>
<TableCell className="w-6 px-2">
<TableCell className="tw-w-6 tw-px-2">
{!model.core && (
<Button
variant="ghost"
size="icon"
className="cursor-grab touch-none hover:cursor-grab active:cursor-grabbing p-0 h-6 w-6"
className="tw-size-6 tw-cursor-grab tw-touch-none tw-p-0 hover:tw-cursor-grab active:tw-cursor-grabbing"
{...attributes}
{...listeners}
>
<GripVertical className="h-4 w-4 text-muted-foreground hover:text-foreground transition-colors" />
<GripVertical className="tw-size-4 tw-transition-colors" />
</Button>
)}
</TableCell>
<TableCell className="pl-0">{model.displayName || model.name}</TableCell>
<TableCell className="tw-pl-0">{model.displayName || model.name}</TableCell>
<TableCell>{getProviderLabel(model.provider, model)}</TableCell>
<TableCell className="text-center flex justify-center">{renderCapabilities(model)}</TableCell>
<TableCell>{renderCapabilities(model)}</TableCell>
{!isEmbeddingModel && (
<TableCell className="text-center">
<TableCell className="tw-text-center">
<Checkbox
id={`${getModelKeyFromModel(model)}-enabled`}
checked={model.enabled}
onCheckedChange={(checked: boolean) => onUpdateModel({ ...model, enabled: checked })}
className="mx-auto"
className="tw-mx-auto"
/>
</TableCell>
)}
<TableCell className="text-center">
<TableCell className="tw-text-center">
<Checkbox
id={`${getModelKeyFromModel(model)}-enableCors`}
checked={model.enableCors}
onCheckedChange={(checked: boolean) => onUpdateModel({ ...model, enableCors: checked })}
className="mx-auto"
className="tw-mx-auto"
/>
</TableCell>
<TableCell className="text-center">
<div className="flex justify-center gap-2">
<TableCell className="tw-text-center">
<div className="tw-flex tw-justify-center tw-gap-2">
{onEdit && (
<Button
variant="ghost"
size="icon"
onClick={() => onEdit(model)}
className="shadow-sm hover:shadow-md transition-shadow"
className="tw-shadow-sm tw-transition-shadow hover:tw-shadow-md"
>
<Pencil className="h-4 w-4" />
<Pencil className="tw-size-4" />
</Button>
)}
{!model.core && (
@ -341,9 +339,9 @@ const SortableTableRow: React.FC<{
variant="ghost"
size="icon"
onClick={() => onDelete(getModelKeyFromModel(model))}
className="shadow-sm hover:shadow-md transition-shadow"
className="tw-shadow-sm tw-transition-shadow hover:tw-shadow-md"
>
<Trash2 className="h-4 w-4" />
<Trash2 className="tw-size-4" />
</Button>
)}
</div>
@ -442,7 +440,7 @@ export const ModelTable: React.FC<ModelTableProps> = ({
// Mobile view rendering
const renderMobileView = () => (
<div className="copilot-model-table-mobile relative">
<div className="tw-relative md:tw-hidden">
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
@ -453,7 +451,7 @@ export const ModelTable: React.FC<ModelTableProps> = ({
items={models.map((model) => getModelKeyFromModel(model))}
strategy={verticalListSortingStrategy}
>
<div className="space-y-2 relative">
<div className="tw-relative tw-space-y-2">
{models.map((model) => (
<ModelCard
key={getModelKeyFromModel(model)}
@ -471,29 +469,29 @@ export const ModelTable: React.FC<ModelTableProps> = ({
);
return (
<div className="mb-4">
<div className="tw-mb-4">
{/* Desktop view */}
<div className="copilot-model-table-desktop">
<div className="tw-hidden md:tw-block">
<DndContext
sensors={sensors}
collisionDetection={closestCenter}
onDragEnd={handleDragEnd}
modifiers={[createDragModifier(false)]}
>
<div className="relative overflow-hidden">
<div className="tw-relative tw-overflow-hidden">
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-6 px-2"></TableHead>
<TableHead className="pl-0">Model</TableHead>
<TableHead className="tw-w-6 tw-px-2"></TableHead>
<TableHead className="tw-pl-0">Model</TableHead>
<TableHead>Provider</TableHead>
<TableHead className="text-center">Capabilities</TableHead>
{!isEmbeddingModel && <TableHead className="text-center">Enable</TableHead>}
<TableHead className="text-center">CORS</TableHead>
<TableHead className="w-[100px] text-center">Actions</TableHead>
<TableHead className="tw-text-center">Capabilities</TableHead>
{!isEmbeddingModel && <TableHead className="tw-text-center">Enable</TableHead>}
<TableHead className="tw-text-center">CORS</TableHead>
<TableHead className="tw-w-[100px] tw-text-center">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody className="relative">
<TableBody className="tw-relative">
<SortableContext
items={models.map((model) => getModelKeyFromModel(model))}
strategy={verticalListSortingStrategy}
@ -518,15 +516,19 @@ export const ModelTable: React.FC<ModelTableProps> = ({
{/* Mobile view */}
{renderMobileView()}
<div className="mt-4 flex justify-end gap-2">
<div className="tw-mt-4 tw-flex tw-justify-end tw-gap-2">
{onRefresh && (
<Button onClick={onRefresh} variant="secondary" className="flex items-center gap-2">
<RefreshCw className="h-4 w-4" />
<Button
onClick={onRefresh}
variant="secondary"
className="tw-flex tw-items-center tw-gap-2"
>
<RefreshCw className="tw-size-4" />
Refresh Built-in Models
</Button>
)}
<Button onClick={onAdd} variant="secondary" className="flex items-center gap-2">
<Plus className="h-4 w-4" />
<Button onClick={onAdd} variant="secondary" className="tw-flex tw-items-center tw-gap-2">
<Plus className="tw-size-4" />
Add Custom Model
</Button>
</div>

View file

@ -19,16 +19,16 @@ export function PlusSettings() {
}, [settings.plusLicenseKey]);
return (
<section className="flex flex-col gap-4 bg-secondary p-4 rounded-lg">
<div className="text-xl font-bold flex items-center gap-2 justify-between">
<section className="tw-flex tw-flex-col tw-gap-4 tw-rounded-lg tw-bg-secondary tw-p-4">
<div className="tw-flex tw-items-center tw-justify-between tw-gap-2 tw-text-xl tw-font-bold">
<span>Copilot Plus (beta)</span>
{isPlusUser && (
<Badge variant="outline" className="text-success">
<Badge variant="outline" className="tw-text-success">
Active
</Badge>
)}
</div>
<div className="text-sm text-muted flex flex-col gap-2">
<div className="tw-flex tw-flex-col tw-gap-2 tw-text-sm tw-text-muted">
<div>
Copilot Plus takes your Obsidian experience to the next level with cutting-edge AI
capabilities. This premium tier unlocks advanced features, including chat context, PDF and
@ -39,9 +39,9 @@ export function PlusSettings() {
rolling out regularly. Join now to secure the lowest price and get early access!
</div>
</div>
<div className="flex items-center gap-2">
<div className="tw-flex tw-items-center tw-gap-2">
<PasswordInput
className="w-full"
className="tw-w-full"
placeholder="Enter your license key"
value={localLicenseKey}
onChange={(value) => {
@ -62,15 +62,15 @@ export function PlusSettings() {
new CopilotPlusWelcomeModal(app).open();
}
}}
className="min-w-20"
className="tw-min-w-20"
>
{isChecking ? <Loader2 className="h-4 w-4 animate-spin" /> : "Apply"}
{isChecking ? <Loader2 className="tw-size-4 tw-animate-spin" /> : "Apply"}
</Button>
<Button variant="secondary" onClick={() => navigateToPlusPage(PLUS_UTM_MEDIUMS.SETTINGS)}>
Join Now <ExternalLink className="size-4" />
Join Now <ExternalLink className="tw-size-4" />
</Button>
</div>
<div className="text-error">{error}</div>
<div className="tw-text-error">{error}</div>
</section>
);
}

View file

@ -23,43 +23,49 @@ export const QASettings: React.FC = () => {
};
return (
<div className="space-y-4">
<div className="tw-space-y-4">
<section>
<div className="space-y-4">
<div className="tw-space-y-4">
{/* Auto-Index Strategy */}
<SettingItem
type="select"
title="Auto-Index Strategy"
description={
<div className="flex items-center gap-1.5">
<span className="leading-none">Decide when you want the vault to be indexed.</span>
<div className="tw-flex tw-items-center tw-gap-1.5">
<span className="tw-leading-none">
Decide when you want the vault to be indexed.
</span>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<HelpCircle className="size-4" />
<HelpCircle className="tw-size-4" />
</TooltipTrigger>
<TooltipContent>
<div className="space-y-2 py-2">
<div className="space-y-1">
<div className="text-muted text-sm">Choose when to index your vault:</div>
<ul className="space-y-1 pl-2 list-disc text-sm">
<div className="tw-space-y-2 tw-py-2">
<div className="tw-space-y-1">
<div className="tw-text-sm tw-text-muted">
Choose when to index your vault:
</div>
<ul className="tw-list-disc tw-space-y-1 tw-pl-2 tw-text-sm">
<li>
<div className="flex items-center gap-1">
<strong className="inline-block whitespace-nowrap">NEVER:</strong>
<div className="tw-flex tw-items-center tw-gap-1">
<strong className="tw-inline-block tw-whitespace-nowrap">
NEVER:
</strong>
<span>Manual indexing via command or refresh only</span>
</div>
</li>
<li>
<div className="flex items-center gap-1">
<strong className="inline-block whitespace-nowrap">
<div className="tw-flex tw-items-center tw-gap-1">
<strong className="tw-inline-block tw-whitespace-nowrap">
ON STARTUP:
</strong>
<span>Index updates when plugin loads or reloads</span>
</div>
</li>
<li>
<div className="flex items-center gap-1">
<strong className="inline-block whitespace-nowrap">
<div className="tw-flex tw-items-center tw-gap-1">
<strong className="tw-inline-block tw-whitespace-nowrap">
ON MODE SWITCH:
</strong>
<span>Updates when entering QA mode (Recommended)</span>
@ -67,7 +73,7 @@ export const QASettings: React.FC = () => {
</li>
</ul>
</div>
<p className="text-callout-warning text-sm">
<p className="tw-text-sm tw-text-callout-warning">
Warning: Cost implications for large vaults with paid models
</p>
</div>

View file

@ -440,12 +440,6 @@ If your plugin does not need CSS, delete this file.
margin: 10px;
}
.message-content-items {
display: flex;
flex-direction: column;
gap: 12px;
}
.message-image-content {
width: 100%;
max-width: 400px;
@ -854,23 +848,3 @@ If your plugin does not need CSS, delete this file.
color: var(--text-error);
opacity: 1;
}
/* Custom classes for ModelTable responsiveness */
.copilot-model-table-desktop {
display: none;
}
.copilot-model-table-mobile {
display: block;
}
@media (min-width: 768px) {
/* md breakpoint */
.copilot-model-table-desktop {
display: block; /* Shown on medium screens and up */
}
.copilot-model-table-mobile {
display: none; /* Hidden on medium screens and up */
}
}

View file

@ -1,15 +1,17 @@
// tailwind.config.js
import tailwindcssAnimate from "tailwindcss-animate";
import { colorOpacityPlugin } from "./src/lib/plugins/colorOpacityPlugin";
import colors from "tailwindcss/colors";
import containerQueries from "@tailwindcss/container-queries";
/** @type {import("tailwindcss").Config} */
module.exports = {
prefix: "tw-",
content: ["./src/**/*.{js,ts,jsx,tsx}", "./src/styles/tailwind.css"],
darkMode: ["class"],
plugins: [tailwindcssAnimate, colorOpacityPlugin, containerQueries],
// tailwindcss-animate doesn't work with linter when using import statement.
// eslint-disable-next-line @typescript-eslint/no-require-imports
plugins: [require("tailwindcss-animate"), colorOpacityPlugin, containerQueries],
corePlugins: {
preflight: false,
},