diff --git a/package-lock.json b/package-lock.json index 1a414357..9669bf70 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "twinny", - "version": "3.17.5", + "version": "3.17.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "twinny", - "version": "3.17.5", + "version": "3.17.6", "cpu": [ "x64", "arm64" diff --git a/package.json b/package.json index 39b7d8bd..ecb04640 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "twinny", "displayName": "twinny - AI Code Completion and Chat", "description": "Locally hosted AI code completion plugin for vscode", - "version": "3.17.5", + "version": "3.17.6", "icon": "assets/icon.png", "keywords": [ "code-inference", @@ -118,18 +118,27 @@ "group": "navigation@6" }, { - "command": "twinny.newConversation", + "command": "twinny.openPanelChat", "when": "view == twinny.sidebar", "group": "navigation@7" }, { - "command": "twinny.settings", + "command": "twinny.newConversation", "when": "view == twinny.sidebar", "group": "navigation@8" + }, + { + "command": "twinny.settings", + "when": "view == twinny.sidebar", + "group": "navigation@9" } ] }, "commands": [ + { + "command": "twinny.openPanelChat", + "title": "Twinny - Open full screen panel" + }, { "command": "twinny.explain", "title": "Twinny - Explain" @@ -201,6 +210,12 @@ "title": "Open twinny conversation history", "icon": "$(history)" }, + { + "command": "twinny.openPanelChat", + "shortTitle": "Open twinny panel chat", + "title": "Open twinny panel chat", + "icon": "$(screen-full)" + }, { "command": "twinny.newConversation", "shortTitle": "New chat", diff --git a/src/common/constants.ts b/src/common/constants.ts index 948492a9..0ec1fe58 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -34,8 +34,8 @@ export const defaultChunkOptions = { } export const EVENT_NAME = { - twinngAddMessage: 'twinny-add-message', twinnyAcceptSolution: 'twinny-accept-solution', + twinnyAddMessage: 'twinny-add-message', twinnyChat: 'twinny-chat', twinnyChatMessage: 'twinny-chat-message', twinnyClickSuggestion: 'twinny-click-suggestion', @@ -50,10 +50,13 @@ export const EVENT_NAME = { twinnyFileListResponse: 'twinny-file-list-response', twinnyGetConfigValue: 'twinny-get-config-value', twinnyGetGitChanges: 'twinny-get-git-changes', + twinnyGetWorkspaceContext: 'twinny-workspace-context', + twinnyGithhubReview: 'twinny-githhub-review', twinnyGlobalContext: 'twinny-global-context', twinnyHideBackButton: 'twinny-hide-back-button', twinnyListTemplates: 'twinny-list-templates', twinnyManageTemplates: 'twinny-manage-templates', + twinnyNewConversation: 'twinny-new-conversation', twinnyNewDocument: 'twinny-new-document', twinnyNotification: 'twinny-notification', twinnyOnCompletion: 'twinny-on-completion', @@ -62,24 +65,22 @@ export const EVENT_NAME = { twinnyOpenDiff: 'twinny-open-diff', twinnyRerankThresholdChanged: 'twinny-rerank-threshold-changed', twinnySendLanguage: 'twinny-send-language', - twinnySymmetryModeles: 'twinny-symmetry-models', twinnySendLoader: 'twinny-send-loader', twinnySendSymmetryMessage: 'twinny-send-symmetry-message', twinnySendSystemMessage: 'twinny-send-system-message', twinnySendTheme: 'twinny-send-theme', twinnySessionContext: 'twinny-session-context', - twinnyStartSymmetryProvider: 'twinny-start-symmetry-provider', - twinnyStopSymmetryProvider: 'twinny-stop-symmetry-provider', twinnySetConfigValue: 'twinny-set-config-value', twinnySetGlobalContext: 'twinny-set-global-context', twinnySetOllamaModel: 'twinny-set-ollama-model', twinnySetSessionContext: 'twinny-set-session-context', twinnySetTab: 'twinny-set-tab', twinnySetWorkspaceContext: 'twinny-set-workspace-context', + twinnyStartSymmetryProvider: 'twinny-start-symmetry-provider', twinnyStopGeneration: 'twinny-stop-generation', + twinnyStopSymmetryProvider: 'twinny-stop-symmetry-provider', + twinnySymmetryModeles: 'twinny-symmetry-models', twinnyTextSelection: 'twinny-text-selection', - twinnyGetWorkspaceContext: 'twinny-workspace-context', - twinnyGithhubReview: 'twinny-githhub-review' } export const TWINNY_COMMAND_NAME = { diff --git a/src/common/types.ts b/src/common/types.ts index 3c2a0937..00444172 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -294,3 +294,9 @@ export interface MentionType { name: string path: string } + +export interface GitHubPr { + number: number + title: string + html_url: string +} diff --git a/src/extension/chat-service.ts b/src/extension/chat-service.ts index 69fa5dfe..0bad714f 100644 --- a/src/extension/chat-service.ts +++ b/src/extension/chat-service.ts @@ -665,7 +665,7 @@ export class ChatService { type: EVENT_NAME.twinnyOnLoading }) this._webView?.postMessage({ - type: EVENT_NAME.twinngAddMessage, + type: EVENT_NAME.twinnyAddMessage, value: { completion: kebabToSentence(template) + '\n\n' + '```\n' + selection, data: getLanguage() diff --git a/src/extension/conversation-history.ts b/src/extension/conversation-history.ts index 1f58c729..04ec1662 100644 --- a/src/extension/conversation-history.ts +++ b/src/extension/conversation-history.ts @@ -23,7 +23,6 @@ import { SYMMETRY_DATA_MESSAGE, TITLE_GENERATION_PROMPT_MESAGE, USER, - ASSISTANT } from '../common/constants' import { SessionManager } from './session-manager' import { SymmetryService } from './symmetry-service' @@ -159,13 +158,6 @@ export class ConversationHistory { const requestOptions = this.getRequestOptions(provider) - if (messages.length === 1 && messages[0].role === ASSISTANT) { - messages.unshift({ - role: USER, - content: 'Request to review code.' - }) - } - const requestBody = createStreamRequestBody(provider.provider, { model: provider.modelName, numPredictChat: this.config.numPredictChat, diff --git a/src/extension/providers/base.ts b/src/extension/providers/base.ts index e8c8ad15..9b8b99fa 100644 --- a/src/extension/providers/base.ts +++ b/src/extension/providers/base.ts @@ -171,6 +171,7 @@ export class BaseProvider { [EVENT_NAME.twinnyStopSymmetryProvider]: this.stopSymmetryProvider, [EVENT_NAME.twinnyTextSelection]: this.getSelectedText, [EVENT_NAME.twinnyFileListRequest]: this.fileListRequest, + [EVENT_NAME.twinnyNewConversation]: this.twinnyNewConversation, [TWINNY_COMMAND_NAME.settings]: this.openSettings } eventHandlers[message.type as string]?.(message) @@ -183,6 +184,14 @@ export class BaseProvider { ) } + private twinnyNewConversation = () => { + this.conversationHistory?.resetConversation() + this.newConversation() + this.webView?.postMessage({ + type: EVENT_NAME.twinnyStopGeneration + } as ServerMessage) + } + public destroyStream = () => { this._chatService?.destroyStream() this.webView?.postMessage({ diff --git a/src/extension/review-service.ts b/src/extension/review-service.ts index eb47e653..f9608ea4 100644 --- a/src/extension/review-service.ts +++ b/src/extension/review-service.ts @@ -149,6 +149,14 @@ export class GithubService extends ConversationHistory { this.resetConversation() setTimeout(async () => { + + this.webView?.postMessage({ + type: EVENT_NAME.twinnyAddMessage, + value: { + completion: prompt + } + }) + this.webView?.postMessage({ type: EVENT_NAME.twinnyOnLoading }) diff --git a/src/index.ts b/src/index.ts index 3972f3d9..28f11bfa 100644 --- a/src/index.ts +++ b/src/index.ts @@ -32,6 +32,7 @@ import { TemplateProvider } from './extension/template-provider' import { ServerMessage } from './common/types' import { FileInteractionCache } from './extension/file-interaction' import { getLineBreakCount } from './webview/utils' +import { FullScreenProvider } from './extension/providers/panel' export async function activate(context: ExtensionContext) { setContext(context) @@ -41,6 +42,11 @@ export async function activate(context: ExtensionContext) { const templateProvider = new TemplateProvider(templateDir) const fileInteractionCache = new FileInteractionCache() const sessionManager = new SessionManager() + const fullScreenProvider = new FullScreenProvider( + context, + templateDir, + statusBarItem + ) const homeDir = os.homedir() const dbDir = path.join(homeDir, '.twinny/embeddings') @@ -262,6 +268,10 @@ export async function activate(context: ExtensionContext) { type: EVENT_NAME.twinnyStopGeneration } as ServerMessage) }), + commands.registerCommand(TWINNY_COMMAND_NAME.openPanelChat, () => { + commands.executeCommand('workbench.action.closeSidebar'); + fullScreenProvider.createOrShowPanel() + }), workspace.onDidCloseTextDocument((document) => { const filePath = document.uri.fsPath fileInteractionCache.endSession() diff --git a/src/webview/chat.tsx b/src/webview/chat.tsx index 7cb94711..a59bec2a 100644 --- a/src/webview/chat.tsx +++ b/src/webview/chat.tsx @@ -1,5 +1,5 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react' - +import cn from 'classnames' import { VSCodeButton, VSCodePanelView, @@ -49,9 +49,14 @@ import { EmbeddingOptions } from './embedding-options' import ChatLoader from './loader' import styles from './styles/index.module.css' +interface ChatProps { + fullScreen?: boolean +} + // eslint-disable-next-line @typescript-eslint/no-explicit-any const global = globalThis as any -export const Chat = () => { +export const Chat = (props: ChatProps): JSX.Element => { + const { fullScreen } = props const generatingRef = useRef(false) const editorRef = useRef(null) const stopRef = useRef(false) @@ -158,7 +163,7 @@ export const Chat = () => { const messageEventHandler = (event: MessageEvent) => { const message: ServerMessage = event.data switch (message.type) { - case EVENT_NAME.twinngAddMessage: { + case EVENT_NAME.twinnyAddMessage: { handleAddTemplateMessage(message) break } @@ -398,7 +403,10 @@ export const Chat = () => { const { suggestion, filePaths } = useSuggestion() - const memoizedSuggestion = useMemo(() => suggestion, [JSON.stringify(filePaths)]) + const memoizedSuggestion = useMemo( + () => suggestion, + [JSON.stringify(filePaths)] + ) const editor = useEditor( { @@ -422,7 +430,7 @@ export const Chat = () => { }), Placeholder.configure({ placeholder: 'How can twinny help you today?' - }), + }) ] }, [memoizedSuggestion] @@ -445,15 +453,38 @@ export const Chat = () => { } }, [memoizedSuggestion]) + const handleNewConversation = () => { + global.vscode.postMessage({ + type: EVENT_NAME.twinnyNewConversation + }) + } + return (
+ {!!fullScreen && ( +
+ + + +
+ )}

{conversation?.title ? conversation?.title : generatingRef.current && New conversation}

-
+
{messages?.map((message, index) => ( { } export const useGithubPRs = () => { - const [prs, setPRs] = useState>([]) + const [prs, setPRs] = useState>([]) const [isLoading, setIsLoading] = useState(false) useEffect(() => { diff --git a/src/webview/index.tsx b/src/webview/index.tsx index b2356a0c..995bee39 100644 --- a/src/webview/index.tsx +++ b/src/webview/index.tsx @@ -6,8 +6,14 @@ import { Main } from './main' (globalThis as any).vscode = window.acquireVsCodeApi() const container = document.querySelector('#root') +const panelContainer = document.querySelector('#root-panel') if (container) { const root = createRoot(container) root.render(
) } + +if (panelContainer) { + const root = createRoot(panelContainer) + root.render(
) +} diff --git a/src/webview/main.tsx b/src/webview/main.tsx index a1588186..03c189a0 100644 --- a/src/webview/main.tsx +++ b/src/webview/main.tsx @@ -10,16 +10,23 @@ import { ConversationHistory } from './conversation-history' import { Review } from './review' const tabs: Record = { - [WEBUI_TABS.chat]: , [WEBUI_TABS.settings]: , [WEBUI_TABS.providers]: , [WEBUI_TABS.symmetry]: , [WEBUI_TABS.review]: } -export const Main = () => { +interface MainProps { + fullScreen?: boolean +} + +export const Main = ({ fullScreen }: MainProps) => { const [tab, setTab] = useState(WEBUI_TABS.chat) + const tabsWithProps = { + [WEBUI_TABS.chat]: , + } + const handler = (event: MessageEvent) => { const message: ServerMessage = event.data if (message?.type === EVENT_NAME.twinnySetTab) { @@ -39,7 +46,9 @@ export const Main = () => { return setTab(WEBUI_TABS.chat)} /> } - const element: JSX.Element = tabs[tab] + const allTabs = { ...tabs, ...tabsWithProps } + + const element: JSX.Element = allTabs[tab] return element || null } diff --git a/src/webview/review.tsx b/src/webview/review.tsx index 5cee5e28..44ae8f4b 100644 --- a/src/webview/review.tsx +++ b/src/webview/review.tsx @@ -75,7 +75,9 @@ export const Review = () => { {prs.map((pr) => (
  • - {pr.title} (#{pr.number}) + + {pr.title} (#{pr.number}) + handleStartReview(pr.number, pr.title)} diff --git a/src/webview/styles/index.module.css b/src/webview/styles/index.module.css index 46cb3376..023e7cb6 100644 --- a/src/webview/styles/index.module.css +++ b/src/webview/styles/index.module.css @@ -81,11 +81,12 @@ pre code { height: 95vh; } -.controlBar { +.fullScreenActions { display: flex; align-items: center; justify-content: flex-end; gap: 4px; + margin-top: 20px; } .markdown { @@ -97,6 +98,14 @@ pre code { margin-top: 20px; } +.markdownFullScreen { + flex-grow: 1; + overflow-y: auto; + overflow-x: hidden; + font-size: 14px; + margin-top: 20px; +} + .title { color: var(--vscode-textPreformat-foreground); display: block; @@ -457,3 +466,7 @@ pre code { flex-grow: 1; margin-right: 8px; } + +.prTitle a { + color: var(--vscode-textLink-foreground); +}