mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-07-05 09:21:39 +02:00
Added chatbot integration
This commit is contained in:
Generated
+1608
-11
File diff suppressed because it is too large
Load Diff
@@ -1868,6 +1868,11 @@
|
||||
"title": "Preview content",
|
||||
"category": "Front Matter"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.chatbot",
|
||||
"title": "Ask a bot to help you configure Front Matter",
|
||||
"category": "Front Matter"
|
||||
},
|
||||
{
|
||||
"command": "frontMatter.promoteSettings",
|
||||
"title": "Promote settings from local to team level",
|
||||
@@ -2270,6 +2275,7 @@
|
||||
"@headlessui/react": "1.5.0",
|
||||
"@heroicons/react": "1.0.4",
|
||||
"@iarna/toml": "2.2.3",
|
||||
"@microsoft/fetch-event-source": "^2.0.1",
|
||||
"@octokit/rest": "^18.12.0",
|
||||
"@popperjs/core": "^2.11.6",
|
||||
"@sentry/react": "^6.13.3",
|
||||
@@ -2300,6 +2306,7 @@
|
||||
"@webpack-cli/serve": "^1.6.0",
|
||||
"ajv": "^8.8.2",
|
||||
"array-move": "^4.0.0",
|
||||
"assert": "^2.0.0",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"chai": "^4.3.6",
|
||||
"css-loader": "5.2.7",
|
||||
@@ -2331,16 +2338,19 @@
|
||||
"postcss-loader": "^7.0.2",
|
||||
"prettier": "^2.8.3",
|
||||
"prettier-plugin-tailwindcss": "^0.2.2",
|
||||
"process": "^0.11.10",
|
||||
"react": "17.0.1",
|
||||
"react-datepicker": "4.2.1",
|
||||
"react-dom": "17.0.1",
|
||||
"react-dropzone": "^11.3.4",
|
||||
"react-markdown": "^8.0.5",
|
||||
"react-popper": "^2.3.0",
|
||||
"react-quill": "^2.0.0-beta.4",
|
||||
"react-router-dom": "^6.3.0",
|
||||
"react-sortable-hoc": "^2.0.0",
|
||||
"react-toastify": "^8.1.0",
|
||||
"recoil": "^0.4.1",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"semver": "^7.3.7",
|
||||
"simple-git": "^3.10.0",
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
import { processFmPlaceholders } from './../helpers/processFmPlaceholders';
|
||||
import { processPathPlaceholders } from './../helpers/processPathPlaceholders';
|
||||
import { Telemetry } from './../helpers/Telemetry';
|
||||
import {
|
||||
SETTING_PREVIEW_HOST,
|
||||
SETTING_PREVIEW_PATHNAME,
|
||||
CONTEXT,
|
||||
TelemetryEvent,
|
||||
PreviewCommands,
|
||||
SETTING_EXPERIMENTAL,
|
||||
SETTING_DATE_FORMAT
|
||||
} from './../constants';
|
||||
import { ArticleHelper } from './../helpers/ArticleHelper';
|
||||
import { join } from 'path';
|
||||
import { commands, env, Uri, ViewColumn, window } from 'vscode';
|
||||
import { Extension, parseWinPath, processKnownPlaceholders, Settings } from '../helpers';
|
||||
import { ContentFolder, ContentType, PreviewSettings } from '../models';
|
||||
import { format } from 'date-fns';
|
||||
import { DateHelper } from '../helpers/DateHelper';
|
||||
import { Article } from '.';
|
||||
import { urlJoin } from 'url-join-ts';
|
||||
import { WebviewHelper } from '@estruyf/vscode';
|
||||
import { Folders } from './Folders';
|
||||
|
||||
export class Chatbot {
|
||||
/**
|
||||
* Open the Chatbot in the editor
|
||||
*/
|
||||
public static async open(extensionPath: string) {
|
||||
// Create the preview webview
|
||||
const webView = window.createWebviewPanel(
|
||||
'frontMatterChatbot',
|
||||
'Front Matter - Ask me anything',
|
||||
{
|
||||
viewColumn: ViewColumn.Beside,
|
||||
preserveFocus: true
|
||||
},
|
||||
{
|
||||
enableScripts: true
|
||||
}
|
||||
);
|
||||
|
||||
webView.iconPath = {
|
||||
dark: Uri.file(join(extensionPath, 'assets/icons/frontmatter-short-dark.svg')),
|
||||
light: Uri.file(join(extensionPath, 'assets/icons/frontmatter-short-light.svg'))
|
||||
};
|
||||
|
||||
const cspSource = webView.webview.cspSource;
|
||||
|
||||
webView.webview.onDidReceiveMessage((message) => {
|
||||
switch (message.command) {
|
||||
case PreviewCommands.toVSCode.open:
|
||||
if (message.data) {
|
||||
commands.executeCommand('vscode.open', message.data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
const dashboardFile = 'dashboardWebView.js';
|
||||
const localPort = `9000`;
|
||||
const localServerUrl = `localhost:${localPort}`;
|
||||
|
||||
const nonce = WebviewHelper.getNonce();
|
||||
|
||||
const ext = Extension.getInstance();
|
||||
const isProd = ext.isProductionMode;
|
||||
const version = ext.getVersion();
|
||||
const isBeta = ext.isBetaVersion();
|
||||
const extensionUri = ext.extensionPath;
|
||||
|
||||
const csp = [
|
||||
`default-src 'none';`,
|
||||
`img-src ${cspSource} http: https:;`,
|
||||
`script-src ${
|
||||
isProd ? `'nonce-${nonce}'` : `http://${localServerUrl} http://0.0.0.0:${localPort}`
|
||||
} 'unsafe-eval'`,
|
||||
`style-src ${cspSource} 'self' 'unsafe-inline' http: https:`,
|
||||
`connect-src https://* ${
|
||||
isProd
|
||||
? ``
|
||||
: `ws://${localServerUrl} ws://0.0.0.0:${localPort} http://${localServerUrl} http://0.0.0.0:${localPort}`
|
||||
}`
|
||||
];
|
||||
|
||||
let scriptUri = '';
|
||||
if (isProd) {
|
||||
scriptUri = webView.webview
|
||||
.asWebviewUri(Uri.joinPath(extensionUri, 'dist', dashboardFile))
|
||||
.toString();
|
||||
} else {
|
||||
scriptUri = `http://${localServerUrl}/${dashboardFile}`;
|
||||
}
|
||||
|
||||
// Get experimental setting
|
||||
const experimental = Settings.get(SETTING_EXPERIMENTAL);
|
||||
|
||||
webView.webview.html = `
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" style="width:100%;height:100%;margin:0;padding:0;">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="${csp.join('; ')}">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<title>Front Matter Docs Chatbot</title>
|
||||
</head>
|
||||
<body style="width:100%;height:100%;margin:0;padding:0;overflow:hidden">
|
||||
<div id="app" data-type="chatbot" data-isProd="${isProd}" data-environment="${
|
||||
isBeta ? 'BETA' : 'main'
|
||||
}" data-version="${version.usedVersion}" ${
|
||||
experimental ? `data-experimental="${experimental}"` : ''
|
||||
} style="width:100%;height:100%;margin:0;padding:0;"></div>
|
||||
|
||||
<script ${isProd ? `nonce="${nonce}"` : ''} src="${scriptUri}"></script>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
Telemetry.send(TelemetryEvent.openChatbot);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
export * from './Article';
|
||||
export * from './Backers';
|
||||
export * from './Cache';
|
||||
export * from './Chatbot';
|
||||
export * from './Content';
|
||||
export * from './Dashboard';
|
||||
export * from './Diagnostics';
|
||||
|
||||
@@ -28,6 +28,7 @@ export const COMMAND_NAME = {
|
||||
initTemplate: getCommandName('initTemplate'),
|
||||
collapseSections: getCommandName('collapseSections'),
|
||||
preview: getCommandName('preview'),
|
||||
chatbot: getCommandName('chatbot'),
|
||||
dashboard: getCommandName('dashboard'),
|
||||
dashboardMedia: getCommandName('dashboard.media'),
|
||||
dashboardSnippets: getCommandName('dashboard.snippets'),
|
||||
|
||||
@@ -28,6 +28,9 @@ export const TelemetryEvent = {
|
||||
updateMediaMetadata: 'updateMediaMetadata',
|
||||
openExplorerView: 'openExplorerView',
|
||||
|
||||
// Chatbot
|
||||
openChatbot: 'openChatbot',
|
||||
|
||||
// Content types
|
||||
generateContentType: 'generateContentType',
|
||||
addMissingFields: 'addMissingFields',
|
||||
|
||||
@@ -0,0 +1,296 @@
|
||||
import * as React from 'react';
|
||||
import { InitResponse } from './models/InitResponse';
|
||||
import { NewConversationResponse } from './models/NewConversationResponse';
|
||||
import { fetchEventSource } from '@microsoft/fetch-event-source';
|
||||
import { ChatIcon, PaperAirplaneIcon, ThumbDownIcon, ThumbUpIcon } from '@heroicons/react/outline';
|
||||
import { ThumbDownIcon as ThumbDownSolidIcon, ThumbUpIcon as ThumbUpSolidIcon } from '@heroicons/react/solid';
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
import { useCallback, useEffect } from 'react';
|
||||
|
||||
export interface IChatbotProps { }
|
||||
|
||||
export const Chatbot: React.FunctionComponent<IChatbotProps> = (props: React.PropsWithChildren<IChatbotProps>) => {
|
||||
const [company, setCompany] = React.useState<string | undefined>(undefined);
|
||||
const [chatId, setChatId] = React.useState<number | undefined>(undefined);
|
||||
const [message, setMessage] = React.useState<string>("");
|
||||
const [questions, setQuestions] = React.useState<string[]>([]);
|
||||
const [answers, setAnswers] = React.useState<string[]>([]);
|
||||
const [answerIds, setAnswerIds] = React.useState<number[]>([]);
|
||||
const [sources, setSources] = React.useState<string[][]>([]);
|
||||
const [loading, setLoading] = React.useState<boolean>(false);
|
||||
|
||||
const [upVote, setUpVotes] = React.useState<number[]>([]);
|
||||
const [downVote, setDownvotes] = React.useState<number[]>([]);
|
||||
|
||||
const init = async () => {
|
||||
setLoading(true);
|
||||
const initResponse = await fetch('https://aijsplayground-production.up.railway.app/initializeMendable', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
anon_key: "466f5321-12d9-4d64-9e5b-ea5db41ed2ba"
|
||||
})
|
||||
});
|
||||
|
||||
if (!initResponse.ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
const initJson: InitResponse = await initResponse.json();
|
||||
|
||||
const newChatResponse = await fetch('https://aijsplayground-production.up.railway.app/newConversation', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
anon_key: "466f5321-12d9-4d64-9e5b-ea5db41ed2ba",
|
||||
messages: []
|
||||
})
|
||||
})
|
||||
|
||||
if (!newChatResponse.ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newChat: NewConversationResponse = await newChatResponse.json();
|
||||
|
||||
setCompany(initJson.company.name);
|
||||
setChatId(newChat.conversation_id);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const onUpVote = useCallback(async (index: number) => {
|
||||
setUpVotes(prev => [...prev, index])
|
||||
setDownvotes(prev => prev.filter(i => i !== index))
|
||||
callVote(index, true)
|
||||
}, []);
|
||||
|
||||
const onDownVote = useCallback(async (index: number) => {
|
||||
setDownvotes(prev => [...prev, index])
|
||||
setUpVotes(prev => prev.filter(i => i !== index))
|
||||
callVote(index, false)
|
||||
}, []);
|
||||
|
||||
const callVote = async (index: number, vote: boolean) => {
|
||||
await fetch(`https://aijsplayground-production.up.railway.app/updateMessageRating/${index}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
ratingValue: vote ? 1 : -1,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const callChatbot = useCallback(async () => {
|
||||
const nrOfQuestions = questions.length + 1;
|
||||
setLoading(true);
|
||||
|
||||
setQuestions(prev => [...prev, message])
|
||||
setAnswers(prev => [...prev, ""])
|
||||
setSources(prev => [...prev, []])
|
||||
setAnswerIds(prev => [...prev, 0])
|
||||
|
||||
setTimeout(() => {
|
||||
setMessage("")
|
||||
}, 0);
|
||||
|
||||
if (!company || !chatId) {
|
||||
return;
|
||||
}
|
||||
|
||||
await fetchEventSource('https://aijsplayground-production.up.railway.app/qaChat', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'accept': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
company: company,
|
||||
conversation_id: chatId,
|
||||
history: [{ prompt: "", response: "", sources: [] }],
|
||||
question: message,
|
||||
}),
|
||||
onmessage: (event) => {
|
||||
setLoading(false);
|
||||
const data = JSON.parse(event.data);
|
||||
const chunk = data.chunk;
|
||||
|
||||
if (chunk === "<|source|>") {
|
||||
setSources(prev => {
|
||||
const metadata = [...new Set(data.metadata.map((m: any) => m.link))] as string[]
|
||||
|
||||
const crntSources: string[][] = Object.assign([], prev)
|
||||
if (crntSources.length === nrOfQuestions) {
|
||||
crntSources[nrOfQuestions - 1] = metadata;
|
||||
} else {
|
||||
crntSources.push(metadata);
|
||||
}
|
||||
|
||||
return crntSources;
|
||||
});
|
||||
} else if (chunk === "<|message_id|>" && data.metadata) {
|
||||
setAnswerIds(prev => {
|
||||
const crntAnswerIds: number[] = Object.assign([], prev)
|
||||
if (crntAnswerIds.length === nrOfQuestions) {
|
||||
crntAnswerIds[nrOfQuestions - 1] = data.metadata;
|
||||
} else {
|
||||
crntAnswerIds.push(data.metadata);
|
||||
}
|
||||
|
||||
return crntAnswerIds;
|
||||
});
|
||||
} else {
|
||||
setAnswers(prev => {
|
||||
const crntAnswers: string[] = Object.assign([], prev)
|
||||
if (crntAnswers.length === nrOfQuestions) {
|
||||
crntAnswers[nrOfQuestions - 1] = crntAnswers[nrOfQuestions - 1] + chunk;
|
||||
} else {
|
||||
crntAnswers.push(chunk);
|
||||
}
|
||||
|
||||
return crntAnswers;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}, [company, chatId, message, questions]);
|
||||
|
||||
useEffect(() => {
|
||||
init();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={`flex flex-col overflow-x-hidden h-full w-full items-center overflow-hidden`}>
|
||||
<header className={`w-full max-w-xl m-4`}>
|
||||
<h1 className='text-2xl flex items-center space-x-4'>
|
||||
<ChatIcon className='h-6 w-6' />
|
||||
<span>Ask Font Matter AI</span>
|
||||
</h1>
|
||||
<h2
|
||||
className='mt-2 text-sm text-[var(--frontmatter-secondary-text)]'
|
||||
style={{
|
||||
fontFamily: "var(--vscode-editor-font-family)",
|
||||
}}
|
||||
>
|
||||
Our AI, powered by <a className={`text-[var(--vscode-textLink-foreground)] hover:text-[var(--vscode-textLink-activeForeground)]`} href={`https://www.mendable.ai/`} title={`mendable.ai`}>mendable.ai</a>, has processed the documentation and can assist you with any queries regarding Front Matter. Go ahead and ask away!
|
||||
</h2>
|
||||
</header>
|
||||
|
||||
<div className='w-full h-[1px] bg-[var(--frontmatter-border)] mb-4'></div>
|
||||
|
||||
<main className={`qa__bot flex-grow w-full max-w-xl overflow-y-auto overflow-x-hidden`}>
|
||||
<div>
|
||||
{
|
||||
questions.map((question, idx) => (
|
||||
<ul key={`question-${idx}`} className={`space-y-4`}>
|
||||
<li className='question'>{question}</li>
|
||||
{answers.length > 0 && answers[idx] && (
|
||||
<li className='answer'>
|
||||
<div className='text-lg flex justify-between'>
|
||||
<p>Answer</p>
|
||||
|
||||
{
|
||||
answerIds[idx] && (
|
||||
<div className='text-lg flex gap-4'>
|
||||
<button
|
||||
className='hover:text-[var(--vscode-textLink-activeForeground)]'
|
||||
onClick={() => onUpVote(answerIds[idx])}>
|
||||
{
|
||||
upVote.includes(answerIds[idx]) ? (
|
||||
<ThumbUpSolidIcon className='h-4 w-4 text-[var(--vscode-textLink-foreground)]' />
|
||||
) : (
|
||||
<ThumbUpIcon className='h-4 w-4' />
|
||||
)
|
||||
}
|
||||
</button>
|
||||
<button
|
||||
className='hover:text-[var(--vscode-textLink-activeForeground)]'
|
||||
onClick={() => onDownVote(answerIds[idx])}>
|
||||
{
|
||||
downVote.includes(answerIds[idx]) ? (
|
||||
<ThumbDownSolidIcon className='h-4 w-4 text-[var(--vscode-textLink-foreground)]' />
|
||||
) : (
|
||||
<ThumbDownIcon className='h-4 w-4' />
|
||||
)
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
<ReactMarkdown children={answers[idx]} remarkPlugins={[remarkGfm]} />
|
||||
|
||||
{
|
||||
sources[idx].length > 0 && sources[idx] && (
|
||||
<div>
|
||||
<p className='text-lg'>Resources</p>
|
||||
<ul className={`space-y-2 list-disc pl-4`}>
|
||||
{sources[idx].map((source, idx) => (
|
||||
<li key={`source-${idx}`} className={`text-sm`}>
|
||||
<a className={`text-[var(--vscode-textLink-foreground)] hover:text-[var(--vscode-textLink-activeForeground)]`} href={source} target="_blank" rel="noreferrer">{source}</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
<div className={`-mx-4 -mb-4 py-2 px-4 bg-[var(--vscode-sideBar-background)] text-[var(--vscode-sideBarTitle-foreground)] rounded-b`} style={{
|
||||
fontFamily: "var(--vscode-editor-font-family)",
|
||||
}}>
|
||||
Warning: Anwers might be wrong. In case of doubt, please consult the docs.
|
||||
</div>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
|
||||
{
|
||||
loading && (
|
||||
<div>
|
||||
<div className="mt-4 flex items-center justify-center space-x-2 animate-pulse">
|
||||
<div className="w-4 h-4 bg-[var(--frontmatter-button-background)] rounded-full"></div>
|
||||
<div className="w-4 h-4 bg-[var(--frontmatter-button-background)] rounded-full"></div>
|
||||
<div className="w-4 h-4 bg-[var(--frontmatter-button-background)] rounded-full"></div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</main>
|
||||
|
||||
<footer className={`w-full max-w-xl relative my-4`}>
|
||||
<textarea
|
||||
className={`resize-none w-full outline-none border-0`}
|
||||
placeholder='How should I configure Front Matter?'
|
||||
autoFocus={true}
|
||||
value={message}
|
||||
cols={30}
|
||||
onChange={(e) => setMessage(e.target.value)}
|
||||
onKeyPress={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
callChatbot();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<button
|
||||
className={`absolute right-3 top-3 text-[var(--frontmatter-button-background)] hover:text-[var(--frontmatter-button-hoverBackground)] disabled:opacity-50 disabled:text-[var(--vscode-disabledForeground)]`}
|
||||
type='button'
|
||||
disabled={message.trim().length === 0 || loading}
|
||||
onClick={callChatbot}
|
||||
>
|
||||
<PaperAirplaneIcon className='h-6 w-6 rotate-90' />
|
||||
</button>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
export interface InitResponse {
|
||||
company: Company;
|
||||
project: Project;
|
||||
}
|
||||
|
||||
export interface Project {
|
||||
id: number;
|
||||
created_at: string;
|
||||
name: string;
|
||||
company_id: number;
|
||||
}
|
||||
|
||||
export interface Company {
|
||||
company_id: number;
|
||||
name: string;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export interface NewConversationResponse {
|
||||
conversation_id: number;
|
||||
start_time: string;
|
||||
end_time?: any;
|
||||
project_id: number;
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import './styles.css';
|
||||
import { Preview } from './components/Preview';
|
||||
import { SettingsProvider } from './providers/SettingsProvider';
|
||||
import { CustomPanelViewResult } from '../models';
|
||||
import { Chatbot } from './components/Chatbot/Chatbot';
|
||||
|
||||
declare const acquireVsCodeApi: <T = unknown>() => {
|
||||
getState: () => T;
|
||||
@@ -129,6 +130,9 @@ if (elm) {
|
||||
<SettingsProvider experimental={experimental === 'true'} version={version || ""}>
|
||||
<Preview url={url} />
|
||||
</SettingsProvider>, elm);
|
||||
} else if (type === 'chatbot') {
|
||||
render(
|
||||
<Chatbot />, elm);
|
||||
} else {
|
||||
render(
|
||||
<RecoilRoot>
|
||||
|
||||
@@ -401,3 +401,43 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.qa__bot {
|
||||
> div {
|
||||
@apply space-y-4;
|
||||
}
|
||||
|
||||
li {
|
||||
@apply space-y-2;
|
||||
}
|
||||
|
||||
pre code {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.question {
|
||||
@apply relative ml-auto mr-3 w-5/6 rounded-full rounded-br-none bg-teal-900 py-2 px-4 text-whisper-500;
|
||||
|
||||
&:after {
|
||||
--size: 1rem;
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
height: var(--size);
|
||||
width: var(--size);
|
||||
z-index: 2;
|
||||
right: calc(var(--size) * -1);
|
||||
border-bottom-right-radius: 8rem;
|
||||
background: radial-gradient(
|
||||
circle at top right,
|
||||
rgba(0, 0, 0, 0) 0,
|
||||
rgba(0, 0, 0, 0) var(--size),
|
||||
rgb(0 154 163 / var(--tw-bg-opacity)) var(--size)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
.answer {
|
||||
@apply rounded border border-teal-100 border-opacity-20 p-4 pb-0;
|
||||
}
|
||||
}
|
||||
|
||||
+8
-2
@@ -26,7 +26,8 @@ import {
|
||||
Dashboard,
|
||||
Article,
|
||||
Settings,
|
||||
StatusListener
|
||||
StatusListener,
|
||||
Chatbot
|
||||
} from './commands';
|
||||
|
||||
let frontMatterStatusBar: vscode.StatusBarItem;
|
||||
@@ -309,6 +310,11 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
vscode.commands.registerCommand(COMMAND_NAME.preview, () => Preview.open(extensionPath))
|
||||
);
|
||||
|
||||
// Chat to the bot
|
||||
subscriptions.push(
|
||||
vscode.commands.registerCommand(COMMAND_NAME.chatbot, () => Chatbot.open(extensionPath))
|
||||
);
|
||||
|
||||
// Inserting an image in Markdown
|
||||
subscriptions.push(
|
||||
vscode.commands.registerCommand(COMMAND_NAME.insertMedia, Article.insertMedia)
|
||||
@@ -369,7 +375,7 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
createFolder
|
||||
);
|
||||
|
||||
console.log(`FRONT MATTER CMS activated!`)
|
||||
console.log(`FRONT MATTER CMS activated!`);
|
||||
}
|
||||
|
||||
export function deactivate() {
|
||||
|
||||
+59
-41
@@ -3,52 +3,70 @@
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const {
|
||||
ProvidePlugin
|
||||
} = require('webpack');
|
||||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
|
||||
|
||||
const config = [
|
||||
{
|
||||
name: 'dashboard',
|
||||
target: 'web',
|
||||
entry: './src/dashboardWebView/index.tsx',
|
||||
output: {
|
||||
filename: 'dashboardWebView.js',
|
||||
path: path.resolve(__dirname, '../dist')
|
||||
},
|
||||
devtool: 'source-map',
|
||||
resolve: {
|
||||
extensions: ['.ts', '.js', '.tsx', '.jsx'],
|
||||
fallback: { "path": require.resolve("path-browserify") }
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(ts|tsx)$/,
|
||||
exclude: /node_modules/,
|
||||
use: [{
|
||||
loader: 'ts-loader'
|
||||
}]
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: ['style-loader', 'css-loader', 'postcss-loader']
|
||||
const config = [{
|
||||
name: 'dashboard',
|
||||
target: 'web',
|
||||
entry: './src/dashboardWebView/index.tsx',
|
||||
output: {
|
||||
filename: 'dashboardWebView.js',
|
||||
path: path.resolve(__dirname, '../dist')
|
||||
},
|
||||
devtool: 'source-map',
|
||||
resolve: {
|
||||
extensions: ['.ts', '.js', '.tsx', '.jsx'],
|
||||
fallback: {
|
||||
"assert": require.resolve("assert"),
|
||||
"path": require.resolve("path-browserify")
|
||||
}
|
||||
},
|
||||
module: {
|
||||
rules: [{
|
||||
test: /\.(ts|tsx)$/,
|
||||
exclude: /node_modules/,
|
||||
use: [{
|
||||
loader: 'ts-loader'
|
||||
}]
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: ['style-loader', 'css-loader', 'postcss-loader']
|
||||
},
|
||||
{
|
||||
test: /\.mjs$/,
|
||||
include: /node_modules/,
|
||||
type: 'javascript/auto',
|
||||
resolve: {
|
||||
fullySpecified: false
|
||||
}
|
||||
]
|
||||
},
|
||||
performance: {
|
||||
hints: false
|
||||
},
|
||||
plugins: [],
|
||||
devServer: {
|
||||
compress: true,
|
||||
port: 9000,
|
||||
hot: true,
|
||||
allowedHosts: "all",
|
||||
headers: {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
}
|
||||
]
|
||||
},
|
||||
performance: {
|
||||
hints: false
|
||||
},
|
||||
plugins: [
|
||||
new ProvidePlugin({
|
||||
// Make a global `process` variable that points to the `process` package,
|
||||
// because the `util` package expects there to be a global variable named `process`.
|
||||
// Thanks to https://stackoverflow.com/a/65018686/14239942
|
||||
process: 'process/browser'
|
||||
})
|
||||
],
|
||||
devServer: {
|
||||
compress: true,
|
||||
port: 9000,
|
||||
hot: true,
|
||||
allowedHosts: "all",
|
||||
headers: {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
}
|
||||
}
|
||||
];
|
||||
}];
|
||||
|
||||
module.exports = (env, argv) => {
|
||||
for (const configItem of config) {
|
||||
@@ -56,7 +74,7 @@ module.exports = (env, argv) => {
|
||||
|
||||
if (argv.mode === 'production') {
|
||||
configItem.devtool = "hidden-source-map";
|
||||
|
||||
|
||||
configItem.plugins.push(new BundleAnalyzerPlugin({
|
||||
analyzerMode: 'static',
|
||||
reportFilename: "dashboard.html",
|
||||
|
||||
Reference in New Issue
Block a user