diff --git a/package-lock.json b/package-lock.json index b21c406d..b6133384 100644 --- a/package-lock.json +++ b/package-lock.json @@ -152,6 +152,7 @@ "path-browserify": "^1.0.1", "postcss": "^8.4.33", "postcss-nesting": "^12.0.2", + "posthog-js": "^1.184.2", "pump": "^1.0.1", "rcedit": "^1.1.0", "react": "^18.3.1", @@ -6503,6 +6504,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/core-js": { + "version": "3.39.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz", + "integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -8397,6 +8410,13 @@ } } }, + "node_modules/fflate": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz", + "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==", + "dev": true, + "license": "MIT" + }, "node_modules/figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", @@ -17455,6 +17475,30 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/posthog-js": { + "version": "1.184.2", + "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.184.2.tgz", + "integrity": "sha512-n7OxFntH7m4sw/GsuUAliWGqBDV7g7IemvjwGISCQFQpf5cSqZgmvT2V2O3GHXpONiEgwL/Ud/+zWmhZbWLExg==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-js": "^3.38.1", + "fflate": "^0.4.8", + "preact": "^10.19.3", + "web-vitals": "^4.2.0" + } + }, + "node_modules/preact": { + "version": "10.24.3", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.24.3.tgz", + "integrity": "sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, "node_modules/prebuild-install": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", @@ -22143,6 +22187,13 @@ "integrity": "sha512-weOVgZ3aAARgdnb220GqYuh7+rZU0Ka9k9yfKtGAzEYMa6GgiCzW9JjQRJyCJakvibQW+dfjJdihjInKuuCAUQ==", "dev": true }, + "node_modules/web-vitals": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz", + "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", diff --git a/package.json b/package.json index b765c4fa..57f850dc 100644 --- a/package.json +++ b/package.json @@ -214,6 +214,7 @@ "path-browserify": "^1.0.1", "postcss": "^8.4.33", "postcss-nesting": "^12.0.2", + "posthog-js": "^1.184.2", "pump": "^1.0.1", "rcedit": "^1.1.0", "react": "^18.3.1", diff --git a/src/vs/workbench/contrib/void/browser/react/src/markdown/MarkdownRender.tsx b/src/vs/workbench/contrib/void/browser/react/src/markdown/MarkdownRender.tsx index 3709c517..dee0a9e0 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/markdown/MarkdownRender.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/markdown/MarkdownRender.tsx @@ -1,12 +1,12 @@ -import React, { JSX, useCallback, useEffect, useState } from "react" -import { marked, MarkedToken, Token } from "marked" -import { BlockCode } from "./BlockCode.js" +import React, { JSX, useCallback, useEffect, useState } from 'react' +import { marked, MarkedToken, Token } from 'marked' +import { BlockCode } from './BlockCode.js' enum CopyButtonState { - Copy = "Copy", - Copied = "Copied!", - Error = "Could not copy", + Copy = 'Copy', + Copied = 'Copied!', + Error = 'Could not copy', } const COPY_FEEDBACK_TIMEOUT = 1000 // amount of time to say 'Copied!' diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/Sidebar.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/Sidebar.tsx index f1f6a073..8c71bf77 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/Sidebar.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/Sidebar.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react' -import { mountFnGenerator } from '../util/mountFnGenerator' +import { mountFnGenerator } from '../util/mountFnGenerator.js' import { SidebarSettings } from './SidebarSettings.js'; import { useSidebarState } from '../util/contextForServices.js'; diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarThreadSelector.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarThreadSelector.tsx index e0c2b519..b89bb1b6 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarThreadSelector.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarThreadSelector.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { useThreadsState } from '../util/contextForServices'; +import { useThreadsState } from '../util/contextForServices.js'; const truncate = (s: string) => { diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/mountFnGenerator.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/mountFnGenerator.tsx index a1ef75c0..421ee75a 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/mountFnGenerator.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/mountFnGenerator.tsx @@ -1,6 +1,6 @@ import React from 'react'; import * as ReactDOM from 'react-dom/client' -import { AccessorProvider } from './contextForServices'; +import { AccessorProvider } from './contextForServices.js'; import { ReactServicesType } from '../../../registerSidebar.js'; diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/posthog.ts b/src/vs/workbench/contrib/void/browser/react/src/util/posthog.ts new file mode 100644 index 00000000..4cdd75be --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/react/src/util/posthog.ts @@ -0,0 +1,6 @@ + +import posthog from 'posthog-js'; + + +export { posthog } + diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.ts b/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.ts index c895b04e..40b08fd1 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.ts +++ b/src/vs/workbench/contrib/void/browser/react/src/util/sendLLMMessage.ts @@ -2,10 +2,8 @@ import Anthropic from '@anthropic-ai/sdk'; import OpenAI from 'openai'; import { Ollama } from 'ollama/browser' import { Content, GoogleGenerativeAI, GoogleGenerativeAIFetchError } from '@google/generative-ai'; -import { VoidConfig } from '../../../registerConfig.js'; -// import { VoidConfig } from '../webviews/common/contextForConfig' -// import { captureEvent } from '../webviews/common/posthog'; -// import { ChatMessage } from './shared_types'; +import { posthog } from 'posthog-js' +import type { VoidConfig } from '../../../registerConfig.js'; export type AbortRef = { current: (() => void) | null } @@ -325,13 +323,13 @@ export const sendLLMMessage: SendLLMMessageFnTypeExternal = ({ // only captures number of messages and message "shape", no actual code, instructions, prompts, etc const captureChatEvent = (eventId: string, extras?: object) => { - // captureEvent(eventId, { - // whichApi: voidConfig.default['whichApi'], - // numMessages: messages?.length, - // messagesShape: messages?.map(msg => ({ role: msg.role, length: msg.content.length })), - // version: '2024-11-02', - // ...extras, - // }) + posthog.capture(eventId, { + whichApi: voidConfig.default['whichApi'], + numMessages: messages?.length, + messagesShape: messages?.map(msg => ({ role: msg.role, length: msg.content.length })), + version: '2024-11-14', + ...extras, + }) } const submit_time = new Date() diff --git a/src/vs/workbench/contrib/void/browser/react/tsup.config.js b/src/vs/workbench/contrib/void/browser/react/tsup.config.js index 484ded1c..a171e9dc 100644 --- a/src/vs/workbench/contrib/void/browser/react/tsup.config.js +++ b/src/vs/workbench/contrib/void/browser/react/tsup.config.js @@ -2,11 +2,13 @@ import { defineConfig } from 'tsup' export default defineConfig({ entry: [ - './src2/sidebar-tsx/Sidebar.tsx' + './src2/sidebar-tsx/Sidebar.tsx', + './src2/util/sendLLMMessage.ts', + './src2/util/posthog.ts', ], outDir: './out', format: ['esm'], - // dts: true, + dts: true, splitting: false, // sourcemap: true, clean: true, diff --git a/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts b/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts index 8da0674b..1971e9b3 100644 --- a/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts +++ b/src/vs/workbench/contrib/void/browser/registerInlineDiffs.ts @@ -6,8 +6,7 @@ import { ICodeEditor, IViewZone } from '../../../../editor/browser/editorBrowser import { IUndoRedoElement, IUndoRedoService, UndoRedoElementType, UndoRedoGroup } from '../../../../platform/undoRedo/common/undoRedo.js'; import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; -import { IBulkEditService, ResourceTextEdit } from '../../../../editor/browser/services/bulkEditService.js'; -import { sendLLMMessage } from './out/util/sendLLMMessage.js'; +import { sendLLMMessage } from './react/out/util/sendLLMMessage.js'; import { throttle } from '../../../../base/common/decorators.js'; import { IFileService } from '../../../../platform/files/common/files.js'; import { URI } from '../../../../base/common/uri.js'; @@ -127,7 +126,6 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { @IVoidConfigStateService private readonly _voidConfigStateService: IVoidConfigStateService, @ICodeEditorService private readonly _editorService: ICodeEditorService, @IUndoRedoService private readonly _undoRedoService: IUndoRedoService, // undoRedo service is the history of pressing ctrl+z - @IBulkEditService private readonly _bulkEditService: IBulkEditService, @IFileService private readonly _fileService: IFileService, ) { @@ -239,7 +237,9 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { - private _addToHistory(uri: URI, editGroup?: UndoRedoGroup) { + private _addToHistory(model: ITextModel) { + + const uri = model.uri const beforeSnapshot: HistorySnapshot = { diffAreaOfId: structuredClone(this.diffAreaOfId), @@ -257,26 +257,29 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { undo: () => { // when the user undoes this element, revert to oldSnapshot this.diffAreaOfId = structuredClone(beforeSnapshot.diffAreaOfId) - // TODO refresh diffs + this._refreshAllDiffsAndStyles(model) + this._refreshSweepStyles(model) }, // called when restoring this state redo: () => { if (afterSnapshot === null) return this.diffAreaOfId = structuredClone(afterSnapshot.diffAreaOfId) - // TODO refresh diffs + this._refreshAllDiffsAndStyles(model) + this._refreshSweepStyles(model) } } + const editGroup = new UndoRedoGroup() this._undoRedoService.pushElement(elt, editGroup) - const finishHistorySnapshot = () => () => { + const onFinishEdit = () => () => { if (afterSnapshot !== null) return afterSnapshot = { diffAreaOfId: structuredClone(this.diffAreaOfId), type: 'ctrl+l', } } - return { finishHistorySnapshot } + return { onFinishEdit, editGroup } } @@ -345,45 +348,43 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { } private _refreshSweepStyles(model: ITextModel) { + const modelid = model.id; - // const model = editor.getModel() - // if (!model) return - // const modelid = model.id + // Create decorations for each diffArea + for (const diffareaid of this.diffAreasOfModelId[modelid] || new Set()) { + const diffArea = this.diffAreaOfId[diffareaid]; + if (!diffArea._sweepLine) continue; - // const lightGrayDecoration = vscode.window.createTextEditorDecorationType({ - // backgroundColor: 'rgba(218 218 218 / .2)', - // isWholeLine: true, - // }) - // const darkGrayDecoration = vscode.window.createTextEditorDecorationType({ - // backgroundColor: 'rgb(148 148 148 / .2)', - // isWholeLine: true, - // }) + const lightGrayDecoration: IModelDeltaDecoration[] = [{ + range: { + startLineNumber: diffArea._sweepLine + 1, + startColumn: 0, + endLineNumber: diffArea.endLine, + endColumn: Number.MAX_SAFE_INTEGER + }, + options: { + className: 'sweep-light-gray', + description: 'sweep-light-gray', + isWholeLine: true + } + }]; + const darkGrayDecoration: IModelDeltaDecoration[] = [{ + range: { + startLineNumber: diffArea._sweepLine, + startColumn: 0, + endLineNumber: diffArea._sweepLine, + endColumn: Number.MAX_SAFE_INTEGER + }, + options: { + className: 'sweep-dark-gray', + description: 'sweep-dark-gray', + isWholeLine: true + } + }]; - - // // for each diffArea, highlight its sweepIndex in dark gray - // editor.setDecorations( - // darkGrayDecoration, - // (this._diffAreasOfDocument[modelid] - // .filter(diffArea => diffArea.sweepIndex !== null) - // .map(diffArea => { - // let s = diffArea.sweepIndex! - // return new vscode.Range(s, 0, s, 0) - // }) - // ) - // ) - - // // for each diffArea, highlight sweepIndex+1...end in light gray - // editor.setDecorations( - // lightGrayDecoration, - // (this._diffAreasOfDocument[modelid] - // .filter(diffArea => diffArea.sweepIndex !== null) - // .map(diffArea => { - // return new vscode.Range(diffArea.sweepIndex! + 1, 0, diffArea.endLine, 0) - // }) - // ) - // ) - + model.deltaDecorations([], [...lightGrayDecoration, ...darkGrayDecoration]); + } } @@ -467,8 +468,15 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { + private _writeToModel(model: ITextModel, text: string, range: IRange, editorGroup: UndoRedoGroup) { + if (!model.isDisposed()) + model.pushEditOperations(null, [{ range, text }], () => null, editorGroup) + } + + + @throttle(100) - private async _updateDiffAreaText(diffArea: DiffArea, llmCodeSoFar: string) { + private async _updateDiffAreaText(diffArea: DiffArea, llmCodeSoFar: string, editorGroup: UndoRedoGroup) { // clear all diffs in this diffarea and recompute them const modelid = diffArea._model.id @@ -518,12 +526,23 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { // applies edits without adding them to undo/redo stack + // model.applyEdits([{ + // range: { startLineNumber: diffArea.startLine, startColumn: 0, endLineNumber: diffArea.endLine, endColumn: Number.MAX_SAFE_INTEGER, }, + // text: newCode + // }]) + // this._bulkEditService.apply([new ResourceTextEdit(model.uri, { + // range: { startLineNumber: diffArea.startLine, startColumn: 0, endLineNumber: diffArea.endLine, endColumn: Number.MAX_SAFE_INTEGER, }, + // text: newCode + // })], { undoRedoGroupId: editorGroup.id }); // count all changes towards the group const model = diffArea._model - if (!model.isDisposed()) - model.applyEdits([{ - range: { startLineNumber: diffArea.startLine, startColumn: 0, endLineNumber: diffArea.endLine, endColumn: Number.MAX_SAFE_INTEGER, }, - text: newCode - }]) + this._writeToModel( + model, + newCode, + { startLineNumber: diffArea.startLine, startColumn: 0, endLineNumber: diffArea.endLine, endColumn: Number.MAX_SAFE_INTEGER, }, + editorGroup + ) + + // TODO resize diffAreas?? Or is this handled already by the listener? } @@ -531,7 +550,8 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { - private async _initializeStream(model: ITextModel, diffRepr: string, streamingGroup: UndoRedoGroup) { + private async _initializeStream(model: ITextModel, diffRepr: string) { + const uri = model.uri const modelid = uri.toString() @@ -559,7 +579,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService { this._registerTextChangeListener(model) // add to history - const { finishHistorySnapshot } = this._addToHistory(model.uri, streamingGroup) + const { onFinishEdit, editGroup } = this._addToHistory(model) // create a diffArea for the stream const diffareaid = this._diffareaidPool++ @@ -613,26 +633,26 @@ Please finish writing the new file by applying the diff to the original file. Re // TODO include more context too { role: 'user', content: promptContent, } ], - onText: (newText, fullText) => { - this._updateDiffAreaText(diffArea, fullText) + onText: (newText: string, fullText: string) => { + this._updateDiffAreaText(diffArea, fullText, editGroup) this._refreshAllDiffsAndStyles(model) this._refreshSweepStyles(model) }, - onFinalMessage: (fullText) => { - this._updateDiffAreaText(diffArea, fullText) + onFinalMessage: (fullText: string) => { + this._updateDiffAreaText(diffArea, fullText, editGroup) this._refreshAllDiffsAndStyles(model) this._refreshSweepStyles(model) resolve(); }, - onError: (e) => { + onError: (e: any) => { console.error('Error rewriting file with diff', e); resolve(); }, voidConfig, - abortRef, + abortRef: { current: null }, }) }) - finishHistorySnapshot() + onFinishEdit() } @@ -650,16 +670,19 @@ Please finish writing the new file by applying the diff to the original file. Re if (!model) return // update streaming state - const streamingGroup = new UndoRedoGroup() const streamingState: StreamingState = { type: 'streaming' } this.streamingStateOfModelId[model.id] = streamingState // initialize stream - this._initializeStream(model, userMessage, streamingGroup) + this._initializeStream(model, userMessage) } + interruptStreaming() { + // TODO add abort + } + @@ -688,7 +711,7 @@ Please finish writing the new file by applying the diff to the original file. Re if (currentFile === null) return // add to history - const { finishHistorySnapshot } = this._addToHistory(uri) + const { onFinishEdit, editGroup: _editGroup } = this._addToHistory(model) // Fixed: Handle newlines properly by splitting into lines and joining with proper newlines const originalLines = originalFile.split('\n'); @@ -722,7 +745,7 @@ Please finish writing the new file by applying the diff to the original file. Re this._refreshAllDiffsAndStyles(model) } - finishHistorySnapshot() + onFinishEdit() } @@ -748,14 +771,15 @@ Please finish writing the new file by applying the diff to the original file. Re // add to history - const { finishHistorySnapshot } = this._addToHistory(uri) + const { onFinishEdit, editGroup } = this._addToHistory(model) // Apply the rejection by replacing with original code (without putting it on the undo/redo stack, this is OK because we put it on the stack ourselves) - if (!model.isDisposed()) - model.applyEdits([{ - range: { startLineNumber: diffArea.startLine, startColumn: 0, endLineNumber: diffArea.endLine, endColumn: Number.MAX_SAFE_INTEGER, }, - text: diff.originalCode - }]) + this._writeToModel( + model, + diff.originalCode, + { startLineNumber: diffArea.startLine, startColumn: 0, endLineNumber: diffArea.endLine, endColumn: Number.MAX_SAFE_INTEGER, }, + editGroup + ) // Check if diffArea should be removed const currentLines = currentFile.split('\n'); @@ -771,7 +795,7 @@ Please finish writing the new file by applying the diff to the original file. Re if (editor?.getModel()?.id === modelid) this._refreshAllDiffsAndStyles(model) - finishHistorySnapshot() + onFinishEdit() } diff --git a/src/vs/workbench/contrib/void/browser/registerMetrics.ts b/src/vs/workbench/contrib/void/browser/registerMetrics.ts index c751f4f7..72307101 100644 --- a/src/vs/workbench/contrib/void/browser/registerMetrics.ts +++ b/src/vs/workbench/contrib/void/browser/registerMetrics.ts @@ -1,43 +1,45 @@ -// import { Disposable } from '../../../../base/common/lifecycle.js'; -// import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions'; -// import { createDecorator } from '../../../../platform/instantiation/common/instantiation'; -// import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry'; +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions'; +import { createDecorator } from '../../../../platform/instantiation/common/instantiation'; +import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry'; -// interface IMetricsService { -// readonly _serviceBrand: undefined; -// } +import { posthog } from './react/out/util/posthog.js' -// const IMetricsService = createDecorator('inlineDiffService'); -// class MetricsService extends Disposable implements IMetricsService { -// _serviceBrand: undefined; +interface IMetricsService { + readonly _serviceBrand: undefined; +} -// constructor( -// @ITelemetryService private readonly _telemetryService: ITelemetryService -// ) { -// super() -// } +const IMetricsService = createDecorator('inlineDiffService'); +class MetricsService extends Disposable implements IMetricsService { + _serviceBrand: undefined; -// init() { + constructor( + @ITelemetryService private readonly _telemetryService: ITelemetryService + ) { + super() + } -// 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 -// } -// ) + init() { -// const deviceId = this._telemetryService.devDeviceId -// console.debug('deviceId', deviceId) + 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 + } + ) -// posthog.identify(deviceId) + const deviceId = this._telemetryService.devDeviceId + console.debug('deviceId', deviceId) + + posthog.identify(deviceId) -// // export const captureEvent = (eventId: string, properties: object) => { -// // posthog.capture(eventId, properties) -// // } + // export const captureEvent = (eventId: string, properties: object) => { + // posthog.capture(eventId, properties) + // } -// } + } -// } +} -// registerSingleton(IMetricsService, MetricsService, InstantiationType.Eager); +registerSingleton(IMetricsService, MetricsService, InstantiationType.Eager);