mirror of
https://github.com/voideditor/void
synced 2026-05-23 17:38:23 +00:00
progress
This commit is contained in:
parent
3fd4357503
commit
932e9fe9c6
14 changed files with 179 additions and 213 deletions
|
|
@ -30,6 +30,12 @@ class MetricsService extends Disposable implements IMetricsService {
|
|||
console.debug('deviceId', deviceId)
|
||||
|
||||
posthog.identify(deviceId)
|
||||
|
||||
|
||||
// export const captureEvent = (eventId: string, properties: object) => {
|
||||
// posthog.capture(eventId, properties)
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -205,8 +205,14 @@ export type VoidConfig = {
|
|||
|
||||
const VOID_CONFIG_KEY = 'void.partialVoidConfig'
|
||||
|
||||
type setFieldType = <K extends VoidConfigField>(field: K, param: keyof VoidConfigInfo[K], newVal: string) => Promise<void>;
|
||||
|
||||
export interface IVoidSettingsService {
|
||||
readonly _serviceBrand: undefined;
|
||||
onDidChange: Event<void>;
|
||||
getPartialVoidConfig(): Promise<PartialVoidConfig>;
|
||||
getVoidConfig(): Promise<VoidConfig>;
|
||||
setField: setFieldType;
|
||||
}
|
||||
|
||||
export const IVoidSettingsService = createDecorator<IVoidSettingsService>('voidSettingsService');
|
||||
|
|
@ -248,7 +254,7 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService {
|
|||
|
||||
|
||||
// Set field on PartialVoidConfig
|
||||
async setField<K extends VoidConfigField>(field: K, param: keyof VoidConfigInfo[K], newVal: string) {
|
||||
setField: setFieldType = async <K extends VoidConfigField>(field: K, param: keyof VoidConfigInfo[K], newVal: string) => {
|
||||
const partialVoidConfig = await this.getPartialVoidConfig()
|
||||
|
||||
const newPartialConfig: PartialVoidConfig = {
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ import { IOpenerService } from '../../../../platform/opener/common/opener.js';
|
|||
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';
|
||||
import { IHoverService } from '../../../../platform/hover/browser/hover.js';
|
||||
import { IVoidSettingsService } from './registerSettings.js';
|
||||
import { IEditorService } from '../../../services/editor/common/editorService.js';
|
||||
|
||||
|
||||
|
||||
// compare against search.contribution.ts and https://app.greptile.com/chat/w1nsmt3lauwzculipycpn?repo=github%3Amain%3Amicrosoft%2Fvscode
|
||||
|
|
@ -78,20 +80,40 @@ class VoidSidebarViewPane extends ViewPane {
|
|||
protected override renderBody(parent: HTMLElement): void {
|
||||
super.renderBody(parent);
|
||||
|
||||
const { root, history, chat, settings } = dom.h('div@root', [
|
||||
dom.h('div@history', []),
|
||||
// <div className={`flex flex-col h-screen w-full`}>
|
||||
|
||||
const { root, chat, history, settings } = dom.h('div@root', [
|
||||
dom.h('div@chat', []),
|
||||
dom.h('div@history', []),
|
||||
dom.h('div@settings', []),
|
||||
])
|
||||
root.style.display = 'flex';
|
||||
root.style.flexDirection = 'column';
|
||||
root.style.height = '100vh';
|
||||
root.style.width = '100%';
|
||||
|
||||
|
||||
dom.append(parent, root);
|
||||
|
||||
this._renderChat(chat);
|
||||
this._renderHistory(history);
|
||||
this._renderSettings(settings);
|
||||
}
|
||||
|
||||
|
||||
private _renderChat(element: HTMLElement) {
|
||||
// <div className={`${tab !== 'chat' && tab !== 'threadSelector' ? 'hidden' : ''}`}>
|
||||
// <SidebarChat chatInputRef={chatInputRef} />
|
||||
// </div>
|
||||
|
||||
|
||||
|
||||
this._voidSidebarStateService.onDidChange(() => {
|
||||
})
|
||||
|
||||
|
||||
this._voidSidebarStateService.onFocusChat(() => {
|
||||
})
|
||||
this._voidSidebarStateService.onBlurChat(() => {
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -116,12 +138,6 @@ class VoidSidebarViewPane extends ViewPane {
|
|||
|
||||
}
|
||||
|
||||
private _renderChat(element: HTMLElement) {
|
||||
// <div className={`${tab !== 'chat' && tab !== 'threadSelector' ? 'hidden' : ''}`}>
|
||||
// <SidebarChat chatInputRef={chatInputRef} />
|
||||
// </div>
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -209,7 +225,7 @@ class VoidSidebarStateService extends Disposable implements IVoidSidebarStateSer
|
|||
@IViewsService private readonly _viewsService: IViewsService,
|
||||
) {
|
||||
super()
|
||||
// auto open the view on mount (can view this as initializing state...)
|
||||
// auto open the view on mount (if it bothers you this is here, this is technically just initializing the state of the view)
|
||||
this._viewsService.openView(SIDEBAR_VIEW_ID);
|
||||
}
|
||||
|
||||
|
|
@ -247,27 +263,26 @@ registerAction2(class extends Action2 {
|
|||
const stateService = accessor.get(IVoidSidebarStateService)
|
||||
stateService.setState({ isHistoryOpen: false, currentTab: 'chat' })
|
||||
stateService.focusChat()
|
||||
|
||||
const selection = accessor.get(IEditorService).activeTextEditorControl?.getSelection()
|
||||
|
||||
|
||||
// chat state:
|
||||
// // if user pressed ctrl+l, add their selection to the sidebar
|
||||
// useOnVSCodeMessage('ctrl+l', (m) => {
|
||||
// setSelection(m.selection)
|
||||
// const filepath = m.selection.filePath
|
||||
|
||||
// // add current file to the context if it's not already in the files array
|
||||
// if (!files.find(f => f.fsPath === filepath.fsPath))
|
||||
// setFiles(files => [...files, filepath])
|
||||
// })
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// History menu button
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'void.historyAction',
|
||||
title: 'View past chats',
|
||||
icon: { id: 'history' },
|
||||
menu: [{ id: MenuId.ViewTitle, group: 'navigation', when: ContextKeyExpr.equals('view', SIDEBAR_VIEW_ID), }]
|
||||
});
|
||||
}
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const stateService = accessor.get(IVoidSidebarStateService)
|
||||
stateService.setState({ isHistoryOpen: !stateService.state.isHistoryOpen })
|
||||
stateService.blurChat()
|
||||
}
|
||||
})
|
||||
|
||||
// New chat menu button
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
|
|
@ -288,6 +303,23 @@ registerAction2(class extends Action2 {
|
|||
}
|
||||
})
|
||||
|
||||
// History menu button
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'void.historyAction',
|
||||
title: 'View past chats',
|
||||
icon: { id: 'history' },
|
||||
menu: [{ id: MenuId.ViewTitle, group: 'navigation', when: ContextKeyExpr.equals('view', SIDEBAR_VIEW_ID), }]
|
||||
});
|
||||
}
|
||||
async run(accessor: ServicesAccessor): Promise<void> {
|
||||
const stateService = accessor.get(IVoidSidebarStateService)
|
||||
stateService.setState({ isHistoryOpen: !stateService.state.isHistoryOpen })
|
||||
stateService.blurChat()
|
||||
}
|
||||
})
|
||||
|
||||
// Settings (API config) menu button
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ type ChatMessage =
|
|||
|
||||
// a "thread" means a chat message history
|
||||
|
||||
const createNewThread = () => {
|
||||
const newThreadObject = () => {
|
||||
const now = new Date().toISOString()
|
||||
return {
|
||||
id: new Date().getTime().toString(),
|
||||
|
|
@ -91,8 +91,17 @@ class ThreadHistoryService extends Disposable implements IThreadHistoryService {
|
|||
|
||||
|
||||
startNewThread() {
|
||||
const newThread = createNewThread()
|
||||
|
||||
// if a thread with 0 messages already exists, switch to it
|
||||
const currentThreads = this.getAllThreads()
|
||||
for (let threadId in currentThreads) {
|
||||
if (currentThreads[threadId].messages.length === 0) {
|
||||
this.switchToThread(threadId)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const newThread = newThreadObject()
|
||||
this._storeAllThreads({
|
||||
...currentThreads,
|
||||
[newThread.id]: newThread
|
||||
|
|
@ -110,7 +119,7 @@ class ThreadHistoryService extends Disposable implements IThreadHistoryService {
|
|||
currentThread = allThreads[this._currentThreadId]
|
||||
}
|
||||
else {
|
||||
currentThread = createNewThread()
|
||||
currentThread = newThreadObject()
|
||||
this._currentThreadId = currentThread.id
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"ms-vscode.extension-test-runner"
|
||||
]
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
// A launch configuration that compiles the extension and then opens it inside a new window
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Run Extension",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}",
|
||||
"--enable-proposed-api=void.void",
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "${defaultBuildTask}"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
"files.exclude": {
|
||||
"**/.git": true,
|
||||
"**/.svn": true,
|
||||
"**/.hg": true,
|
||||
"**/CVS": true,
|
||||
"**/.DS_Store": true,
|
||||
"**/Thumbs.db": true,
|
||||
"out": false,
|
||||
"**/node_modules": true
|
||||
},
|
||||
"search.exclude": {
|
||||
"out": true // set this to false to include "out" folder in search results
|
||||
},
|
||||
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
|
||||
"typescript.tsc.autoDetect": "off",
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "watch",
|
||||
"problemMatcher": "$tsc-watch",
|
||||
"isBackground": true,
|
||||
"presentation": {
|
||||
"reveal": "never"
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,63 +1,63 @@
|
|||
import React, { useState, useRef } from '../void-imports/react.js'
|
||||
import { SidebarThreadSelector } from './SidebarThreadSelector.js';
|
||||
import { SidebarChat } from './SidebarChat.js';
|
||||
import { SidebarSettings } from './SidebarSettings.js';
|
||||
// import React, { useState, useRef } from '../void-imports/react.js'
|
||||
// import { SidebarThreadSelector } from './SidebarThreadSelector.js';
|
||||
// import { SidebarChat } from './SidebarChat.js';
|
||||
// import { SidebarSettings } from './SidebarSettings.js';
|
||||
|
||||
|
||||
const Sidebar = () => {
|
||||
// const Sidebar = () => {
|
||||
|
||||
const chatInputRef = useRef<HTMLTextAreaElement | null>(null)
|
||||
// const chatInputRef = useRef<HTMLTextAreaElement | null>(null)
|
||||
|
||||
const [tab, setTab] = useState<'threadSelector' | 'chat' | 'settings'>('chat')
|
||||
// const [tab, setTab] = useState<'threadSelector' | 'chat' | 'settings'>('chat')
|
||||
|
||||
// // if they pressed the + to add a new chat
|
||||
// useOnVSCodeMessage('startNewThread', (m) => {
|
||||
// setTab('chat');
|
||||
// chatInputRef.current?.focus();
|
||||
// })
|
||||
// // // if they pressed the + to add a new chat
|
||||
// // useOnVSCodeMessage('startNewThread', (m) => {
|
||||
// // setTab('chat');
|
||||
// // chatInputRef.current?.focus();
|
||||
// // })
|
||||
|
||||
// // ctrl+l should switch back to chat
|
||||
// useOnVSCodeMessage('ctrl+l', (m) => {
|
||||
// setTab('chat');
|
||||
// chatInputRef.current?.focus();
|
||||
// })
|
||||
// // // ctrl+l should switch back to chat
|
||||
// // useOnVSCodeMessage('ctrl+l', (m) => {
|
||||
// // setTab('chat');
|
||||
// // chatInputRef.current?.focus();
|
||||
// // })
|
||||
|
||||
// // if they toggled thread selector
|
||||
// useOnVSCodeMessage('toggleThreadSelector', (m) => {
|
||||
// if (tab === 'threadSelector') {
|
||||
// setTab('chat')
|
||||
// chatInputRef.current?.blur();
|
||||
// } else
|
||||
// setTab('threadSelector')
|
||||
// })
|
||||
// // // if they toggled thread selector
|
||||
// // useOnVSCodeMessage('toggleThreadSelector', (m) => {
|
||||
// // if (tab === 'threadSelector') {
|
||||
// // setTab('chat')
|
||||
// // chatInputRef.current?.blur();
|
||||
// // } else
|
||||
// // setTab('threadSelector')
|
||||
// // })
|
||||
|
||||
// // if they toggled settings
|
||||
// useOnVSCodeMessage('toggleSettings', (m) => {
|
||||
// if (tab === 'settings') {
|
||||
// setTab('chat')
|
||||
// chatInputRef.current?.blur();
|
||||
// } else
|
||||
// setTab('settings')
|
||||
// })
|
||||
// // // if they toggled settings
|
||||
// // useOnVSCodeMessage('toggleSettings', (m) => {
|
||||
// // if (tab === 'settings') {
|
||||
// // setTab('chat')
|
||||
// // chatInputRef.current?.blur();
|
||||
// // } else
|
||||
// // setTab('settings')
|
||||
// // })
|
||||
|
||||
return <>
|
||||
<div className={`flex flex-col h-screen w-full`}>
|
||||
// return <>
|
||||
// <div className={`flex flex-col h-screen w-full`}>
|
||||
|
||||
<div className={`mb-2 h-[30vh] ${tab !== 'threadSelector' ? 'hidden' : ''}`}>
|
||||
<SidebarThreadSelector onClose={() => setTab('chat')} />
|
||||
</div>
|
||||
// <div className={`mb-2 h-[30vh] ${tab !== 'threadSelector' ? 'hidden' : ''}`}>
|
||||
// <SidebarThreadSelector onClose={() => setTab('chat')} />
|
||||
// </div>
|
||||
|
||||
<div className={`${tab !== 'chat' && tab !== 'threadSelector' ? 'hidden' : ''}`}>
|
||||
<SidebarChat chatInputRef={chatInputRef} />
|
||||
</div>
|
||||
// <div className={`${tab !== 'chat' && tab !== 'threadSelector' ? 'hidden' : ''}`}>
|
||||
// <SidebarChat chatInputRef={chatInputRef} />
|
||||
// </div>
|
||||
|
||||
<div className={`${tab !== 'settings' ? 'hidden' : ''}`}>
|
||||
<SidebarSettings />
|
||||
</div>
|
||||
// <div className={`${tab !== 'settings' ? 'hidden' : ''}`}>
|
||||
// <SidebarSettings />
|
||||
// </div>
|
||||
|
||||
</div>
|
||||
</>
|
||||
// </div>
|
||||
// </>
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
export default Sidebar
|
||||
// export default Sidebar
|
||||
|
|
|
|||
|
|
@ -147,6 +147,29 @@ const ChatBubble = ({ chatMessage }: { chatMessage: ChatMessage }) => {
|
|||
|
||||
export const SidebarChat = ({ chatInputRef }: { chatInputRef: React.RefObject<HTMLTextAreaElement> }) => {
|
||||
|
||||
// // if they pressed the + to add a new chat
|
||||
// useOnVSCodeMessage('startNewThread', (m) => {
|
||||
// const allThreads = getAllThreads()
|
||||
// // find a thread with 0 messages and switch to it
|
||||
// for (let threadId in allThreads) {
|
||||
// if (allThreads[threadId].messages.length === 0) {
|
||||
// switchToThread(threadId)
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
// // start a new thread
|
||||
// startNewThread()
|
||||
// })
|
||||
|
||||
// // if user pressed ctrl+l, add their selection to the sidebar
|
||||
// useOnVSCodeMessage('ctrl+l', (m) => {
|
||||
// setSelection(m.selection)
|
||||
// const filepath = m.selection.filePath
|
||||
|
||||
// // add current file to the context if it's not already in the files array
|
||||
// if (!files.find(f => f.fsPath === filepath.fsPath))
|
||||
// setFiles(files => [...files, filepath])
|
||||
// })
|
||||
|
||||
// state of current message
|
||||
const [selection, setSelection] = useState<CodeSelection | null>(null) // the code the user is selecting
|
||||
|
|
@ -167,29 +190,6 @@ export const SidebarChat = ({ chatInputRef }: { chatInputRef: React.RefObject<HT
|
|||
|
||||
|
||||
|
||||
// if they pressed the + to add a new chat
|
||||
useOnVSCodeMessage('startNewThread', (m) => {
|
||||
const allThreads = getAllThreads()
|
||||
// find a thread with 0 messages and switch to it
|
||||
for (let threadId in allThreads) {
|
||||
if (allThreads[threadId].messages.length === 0) {
|
||||
switchToThread(threadId)
|
||||
return
|
||||
}
|
||||
}
|
||||
// start a new thread
|
||||
startNewThread()
|
||||
})
|
||||
|
||||
// if user pressed ctrl+l, add their selection to the sidebar
|
||||
useOnVSCodeMessage('ctrl+l', (m) => {
|
||||
setSelection(m.selection)
|
||||
const filepath = m.selection.filePath
|
||||
|
||||
// add current file to the context if it's not already in the files array
|
||||
if (!files.find(f => f.fsPath === filepath.fsPath))
|
||||
setFiles(files => [...files, filepath])
|
||||
})
|
||||
|
||||
|
||||
const isDisabled = !instructions
|
||||
|
|
|
|||
|
|
@ -1,30 +1,30 @@
|
|||
// renders the code from `src/sidebar`
|
||||
// // renders the code from `src/sidebar`
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { updateWebviewHTML as _updateWebviewHTML } from './src/extension/extensionLib/updateWebviewHTML';
|
||||
// import * as vscode from 'vscode';
|
||||
// import { updateWebviewHTML as _updateWebviewHTML } from './src/extension/extensionLib/updateWebviewHTML';
|
||||
|
||||
export class SidebarWebviewProvider implements vscode.WebviewViewProvider {
|
||||
public static readonly viewId = 'void.viewnumberone';
|
||||
// export class SidebarWebviewProvider implements vscode.WebviewViewProvider {
|
||||
// public static readonly viewId = 'void.viewnumberone';
|
||||
|
||||
public webview: Promise<vscode.Webview> // used to send messages to the webview, resolved by _res in resolveWebviewView
|
||||
private _res: (c: vscode.Webview) => void // used to resolve the webview
|
||||
// public webview: Promise<vscode.Webview> // used to send messages to the webview, resolved by _res in resolveWebviewView
|
||||
// private _res: (c: vscode.Webview) => void // used to resolve the webview
|
||||
|
||||
private readonly _extensionUri: vscode.Uri
|
||||
// private readonly _extensionUri: vscode.Uri
|
||||
|
||||
constructor(context: vscode.ExtensionContext) {
|
||||
// const extensionPath = context.extensionPath // the directory where the extension is installed, might be useful later... was included in webviewProvider code
|
||||
this._extensionUri = context.extensionUri
|
||||
// constructor(context: vscode.ExtensionContext) {
|
||||
// // const extensionPath = context.extensionPath // the directory where the extension is installed, might be useful later... was included in webviewProvider code
|
||||
// this._extensionUri = context.extensionUri
|
||||
|
||||
let temp_res: typeof this._res | undefined = undefined
|
||||
this.webview = new Promise((res, rej) => { temp_res = res })
|
||||
if (!temp_res) throw new Error("Void sidebar provider: resolver was undefined")
|
||||
this._res = temp_res
|
||||
}
|
||||
// let temp_res: typeof this._res | undefined = undefined
|
||||
// this.webview = new Promise((res, rej) => { temp_res = res })
|
||||
// if (!temp_res) throw new Error("Void sidebar provider: resolver was undefined")
|
||||
// this._res = temp_res
|
||||
// }
|
||||
|
||||
// called internally by vscode
|
||||
resolveWebviewView(webviewView: vscode.WebviewView, context: vscode.WebviewViewResolveContext, token: vscode.CancellationToken,) {
|
||||
const webview = webviewView.webview;
|
||||
_updateWebviewHTML(webview, this._extensionUri, { jsOutLocation: 'dist/webviews/sidebar/index.js', cssOutLocation: 'dist/webviews/styles.css' })
|
||||
this._res(webview); // resolve webview and _webviewView
|
||||
}
|
||||
}
|
||||
// // called internally by vscode
|
||||
// resolveWebviewView(webviewView: vscode.WebviewView, context: vscode.WebviewViewResolveContext, token: vscode.CancellationToken,) {
|
||||
// const webview = webviewView.webview;
|
||||
// _updateWebviewHTML(webview, this._extensionUri, { jsOutLocation: 'dist/webviews/sidebar/index.js', cssOutLocation: 'dist/webviews/styles.css' })
|
||||
// this._res(webview); // resolve webview and _webviewView
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
import posthog from '../void-imports/posthog-js.js'
|
||||
|
||||
export const identifyUser = (id: string) => {
|
||||
posthog.identify(id)
|
||||
}
|
||||
|
||||
export const captureEvent = (eventId: string, properties: object) => {
|
||||
posthog.capture(eventId, properties)
|
||||
}
|
||||
|
||||
export const initPosthog = () => {
|
||||
// We send absolutely no code to the server. We only track usage metrics like button clicks, etc. This might change and we might eventually add an opt-in or opt-out.
|
||||
posthog.init('phc_UanIdujHiLp55BkUTjB1AuBXcasVkdqRwgnwRlWESH2',
|
||||
{
|
||||
api_host: 'https://us.i.posthog.com',
|
||||
person_profiles: 'identified_only' // we only track events from identified users. We identify them in Sidebar
|
||||
}
|
||||
)
|
||||
}
|
||||
Loading…
Reference in a new issue