diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..81f3ec3 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +# top-most EditorConfig file +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = tab +indent_size = 4 +tab_width = 4 diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..e019f3c --- /dev/null +++ b/.eslintignore @@ -0,0 +1,3 @@ +node_modules/ + +main.js diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..0807290 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,23 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "env": { "node": true }, + "plugins": [ + "@typescript-eslint" + ], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" + ], + "parserOptions": { + "sourceType": "module" + }, + "rules": { + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": ["error", { "args": "none" }], + "@typescript-eslint/ban-ts-comment": "off", + "no-prototype-builtins": "off", + "@typescript-eslint/no-empty-function": "off" + } + } \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..386ac2b --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +# vscode +.vscode + +# Intellij +*.iml +.idea + +# npm +node_modules + +# Don't include the compiled main.js file in the repo. +# They should be uploaded to GitHub releases instead. +main.js + +# Exclude sourcemaps +*.map + +# obsidian +data.json + +# Exclude macOS Finder (System Explorer) View States +.DS_Store diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..b973752 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +tag-version-prefix="" \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..6261481 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,26 @@ +{ + "tabWidth": 2, + "semi": false, + "overrides": [ + { + "files": ["*.js", "*.ts"], + "options": { + "tabWidth": 4, + "semi": true, + "singleQuote": true, + "bracketSameLine": false, + "trailingComma": "none", + "arrowParens": "always", + "printWidth": 140 + } + }, + { + "files": ["*.css", "*.scss"], + "options": { + "tabWidth": 1, + "semi": false, + "singleQuote": true + } + } + ] +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..3f4274a --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,251 @@ +# Obsidian community plugin + +## Project overview + +- Target: Obsidian Community Plugin (TypeScript → bundled JavaScript). +- Entry point: `main.ts` compiled to `main.js` and loaded by Obsidian. +- Required release artifacts: `main.js`, `manifest.json`, and optional `styles.css`. + +## Environment & tooling + +- Node.js: use current LTS (Node 18+ recommended). +- **Package manager: npm** (required for this sample - `package.json` defines npm scripts and dependencies). +- **Bundler: esbuild** (required for this sample - `esbuild.config.mjs` and build scripts depend on it). Alternative bundlers like Rollup or webpack are acceptable for other projects if they bundle all external dependencies into `main.js`. +- Types: `obsidian` type definitions. + +**Note**: This sample project has specific technical dependencies on npm and esbuild. If you're creating a plugin from scratch, you can choose different tools, but you'll need to replace the build configuration accordingly. + +### Install + +```bash +npm install +``` + +### Dev (watch) + +```bash +npm run dev +``` + +### Production build + +```bash +npm run build +``` + +## Linting + +- To use eslint install eslint from terminal: `npm install -g eslint` +- To use eslint to analyze this project use this command: `eslint main.ts` +- eslint will then create a report with suggestions for code improvement by file and line number. +- If your source code is in a folder, such as `src`, you can use eslint with this command to analyze all files in that folder: `eslint ./src/` + +## File & folder conventions + +- **Organize code into multiple files**: Split functionality across separate modules rather than putting everything in `main.ts`. +- Source lives in `src/`. Keep `main.ts` small and focused on plugin lifecycle (loading, unloading, registering commands). +- **Example file structure**: + ``` + src/ + main.ts # Plugin entry point, lifecycle management + settings.ts # Settings interface and defaults + commands/ # Command implementations + command1.ts + command2.ts + ui/ # UI components, modals, views + modal.ts + view.ts + utils/ # Utility functions, helpers + helpers.ts + constants.ts + types.ts # TypeScript interfaces and types + ``` +- **Do not commit build artifacts**: Never commit `node_modules/`, `main.js`, or other generated files to version control. +- Keep the plugin small. Avoid large dependencies. Prefer browser-compatible packages. +- Generated output should be placed at the plugin root or `dist/` depending on your build setup. Release artifacts must end up at the top level of the plugin folder in the vault (`main.js`, `manifest.json`, `styles.css`). + +## Manifest rules (`manifest.json`) + +- Must include (non-exhaustive): + - `id` (plugin ID; for local dev it should match the folder name) + - `name` + - `version` (Semantic Versioning `x.y.z`) + - `minAppVersion` + - `description` + - `isDesktopOnly` (boolean) + - Optional: `author`, `authorUrl`, `fundingUrl` (string or map) +- Never change `id` after release. Treat it as stable API. +- Keep `minAppVersion` accurate when using newer APIs. +- Canonical requirements are coded here: https://github.com/obsidianmd/obsidian-releases/blob/master/.github/workflows/validate-plugin-entry.yml + +## Testing + +- Manual install for testing: copy `main.js`, `manifest.json`, `styles.css` (if any) to: + ``` + /.obsidian/plugins// + ``` +- Reload Obsidian and enable the plugin in **Settings → Community plugins**. + +## Commands & settings + +- Any user-facing commands should be added via `this.addCommand(...)`. +- If the plugin has configuration, provide a settings tab and sensible defaults. +- Persist settings using `this.loadData()` / `this.saveData()`. +- Use stable command IDs; avoid renaming once released. + +## Versioning & releases + +- Bump `version` in `manifest.json` (SemVer) and update `versions.json` to map plugin version → minimum app version. +- Create a GitHub release whose tag exactly matches `manifest.json`'s `version`. Do not use a leading `v`. +- Attach `manifest.json`, `main.js`, and `styles.css` (if present) to the release as individual assets. +- After the initial release, follow the process to add/update your plugin in the community catalog as required. + +## Security, privacy, and compliance + +Follow Obsidian's **Developer Policies** and **Plugin Guidelines**. In particular: + +- Default to local/offline operation. Only make network requests when essential to the feature. +- No hidden telemetry. If you collect optional analytics or call third-party services, require explicit opt-in and document clearly in `README.md` and in settings. +- Never execute remote code, fetch and eval scripts, or auto-update plugin code outside of normal releases. +- Minimize scope: read/write only what's necessary inside the vault. Do not access files outside the vault. +- Clearly disclose any external services used, data sent, and risks. +- Respect user privacy. Do not collect vault contents, filenames, or personal information unless absolutely necessary and explicitly consented. +- Avoid deceptive patterns, ads, or spammy notifications. +- Register and clean up all DOM, app, and interval listeners using the provided `register*` helpers so the plugin unloads safely. + +## UX & copy guidelines (for UI text, commands, settings) + +- Prefer sentence case for headings, buttons, and titles. +- Use clear, action-oriented imperatives in step-by-step copy. +- Use **bold** to indicate literal UI labels. Prefer "select" for interactions. +- Use arrow notation for navigation: **Settings → Community plugins**. +- Keep in-app strings short, consistent, and free of jargon. + +## Performance + +- Keep startup light. Defer heavy work until needed. +- Avoid long-running tasks during `onload`; use lazy initialization. +- Batch disk access and avoid excessive vault scans. +- Debounce/throttle expensive operations in response to file system events. + +## Coding conventions + +- TypeScript with `"strict": true` preferred. +- **Keep `main.ts` minimal**: Focus only on plugin lifecycle (onload, onunload, addCommand calls). Delegate all feature logic to separate modules. +- **Split large files**: If any file exceeds ~200-300 lines, consider breaking it into smaller, focused modules. +- **Use clear module boundaries**: Each file should have a single, well-defined responsibility. +- Bundle everything into `main.js` (no unbundled runtime deps). +- Avoid Node/Electron APIs if you want mobile compatibility; set `isDesktopOnly` accordingly. +- Prefer `async/await` over promise chains; handle errors gracefully. + +## Mobile + +- Where feasible, test on iOS and Android. +- Don't assume desktop-only behavior unless `isDesktopOnly` is `true`. +- Avoid large in-memory structures; be mindful of memory and storage constraints. + +## Agent do/don't + +**Do** +- Add commands with stable IDs (don't rename once released). +- Provide defaults and validation in settings. +- Write idempotent code paths so reload/unload doesn't leak listeners or intervals. +- Use `this.register*` helpers for everything that needs cleanup. + +**Don't** +- Introduce network calls without an obvious user-facing reason and documentation. +- Ship features that require cloud services without clear disclosure and explicit opt-in. +- Store or transmit vault contents unless essential and consented. + +## Common tasks + +### Organize code across multiple files + +**main.ts** (minimal, lifecycle only): +```ts +import { Plugin } from "obsidian"; +import { MySettings, DEFAULT_SETTINGS } from "./settings"; +import { registerCommands } from "./commands"; + +export default class MyPlugin extends Plugin { + settings: MySettings; + + async onload() { + this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); + registerCommands(this); + } +} +``` + +**settings.ts**: +```ts +export interface MySettings { + enabled: boolean; + apiKey: string; +} + +export const DEFAULT_SETTINGS: MySettings = { + enabled: true, + apiKey: "", +}; +``` + +**commands/index.ts**: +```ts +import { Plugin } from "obsidian"; +import { doSomething } from "./my-command"; + +export function registerCommands(plugin: Plugin) { + plugin.addCommand({ + id: "do-something", + name: "Do something", + callback: () => doSomething(plugin), + }); +} +``` + +### Add a command + +```ts +this.addCommand({ + id: "your-command-id", + name: "Do the thing", + callback: () => this.doTheThing(), +}); +``` + +### Persist settings + +```ts +interface MySettings { enabled: boolean } +const DEFAULT_SETTINGS: MySettings = { enabled: true }; + +async onload() { + this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); + await this.saveData(this.settings); +} +``` + +### Register listeners safely + +```ts +this.registerEvent(this.app.workspace.on("file-open", f => { /* ... */ })); +this.registerDomEvent(window, "resize", () => { /* ... */ }); +this.registerInterval(window.setInterval(() => { /* ... */ }, 1000)); +``` + +## Troubleshooting + +- Plugin doesn't load after build: ensure `main.js` and `manifest.json` are at the top level of the plugin folder under `/.obsidian/plugins//`. +- Build issues: if `main.js` is missing, run `npm run build` or `npm run dev` to compile your TypeScript source code. +- Commands not appearing: verify `addCommand` runs after `onload` and IDs are unique. +- Settings not persisting: ensure `loadData`/`saveData` are awaited and you re-render the UI after changes. +- Mobile-only issues: confirm you're not using desktop-only APIs; check `isDesktopOnly` and adjust. + +## References + +- Obsidian sample plugin: https://github.com/obsidianmd/obsidian-sample-plugin +- API documentation: https://docs.obsidian.md +- Developer policies: https://docs.obsidian.md/Developer+policies +- Plugin guidelines: https://docs.obsidian.md/Plugins/Releasing/Plugin+guidelines +- Style guide: https://help.obsidian.md/style-guide diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..287f37a --- /dev/null +++ b/LICENSE @@ -0,0 +1,5 @@ +Copyright (C) 2020-2025 by Dynalist Inc. + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. \ No newline at end of file diff --git a/README-Basic.md b/README-Basic.md new file mode 100644 index 0000000..23d9526 --- /dev/null +++ b/README-Basic.md @@ -0,0 +1,94 @@ +# Obsidian Sample Plugin + +This is a sample plugin for Obsidian (https://obsidian.md). + +This project uses TypeScript to provide type checking and documentation. +The repo depends on the latest plugin API (obsidian.d.ts) in TypeScript Definition format, which contains TSDoc comments describing what it does. + +This sample plugin demonstrates some of the basic functionality the plugin API can do. +- Adds a ribbon icon, which shows a Notice when clicked. +- Adds a command "Open Sample Modal" which opens a Modal. +- Adds a plugin setting tab to the settings page. +- Registers a global click event and output 'click' to the console. +- Registers a global interval which logs 'setInterval' to the console. + +## First time developing plugins? + +Quick starting guide for new plugin devs: + +- Check if [someone already developed a plugin for what you want](https://obsidian.md/plugins)! There might be an existing plugin similar enough that you can partner up with. +- Make a copy of this repo as a template with the "Use this template" button (login to GitHub if you don't see it). +- Clone your repo to a local development folder. For convenience, you can place this folder in your `.obsidian/plugins/your-plugin-name` folder. +- Install NodeJS, then run `npm i` in the command line under your repo folder. +- Run `npm run dev` to compile your plugin from `main.ts` to `main.js`. +- Make changes to `main.ts` (or create new `.ts` files). Those changes should be automatically compiled into `main.js`. +- Reload Obsidian to load the new version of your plugin. +- Enable plugin in settings window. +- For updates to the Obsidian API run `npm update` in the command line under your repo folder. + +## Releasing new releases + +- Update your `manifest.json` with your new version number, such as `1.0.1`, and the minimum Obsidian version required for your latest release. +- Update your `versions.json` file with `"new-plugin-version": "minimum-obsidian-version"` so older versions of Obsidian can download an older version of your plugin that's compatible. +- Create new GitHub release using your new version number as the "Tag version". Use the exact version number, don't include a prefix `v`. See here for an example: https://github.com/obsidianmd/obsidian-sample-plugin/releases +- Upload the files `manifest.json`, `main.js`, `styles.css` as binary attachments. Note: The manifest.json file must be in two places, first the root path of your repository and also in the release. +- Publish the release. + +> You can simplify the version bump process by running `npm version patch`, `npm version minor` or `npm version major` after updating `minAppVersion` manually in `manifest.json`. +> The command will bump version in `manifest.json` and `package.json`, and add the entry for the new version to `versions.json` + +## Adding your plugin to the community plugin list + +- Check the [plugin guidelines](https://docs.obsidian.md/Plugins/Releasing/Plugin+guidelines). +- Publish an initial version. +- Make sure you have a `README.md` file in the root of your repo. +- Make a pull request at https://github.com/obsidianmd/obsidian-releases to add your plugin. + +## How to use + +- Clone this repo. +- Make sure your NodeJS is at least v16 (`node --version`). +- `npm i` or `yarn` to install dependencies. +- `npm run dev` to start compilation in watch mode. + +## Manually installing the plugin + +- Copy over `main.js`, `styles.css`, `manifest.json` to your vault `VaultFolder/.obsidian/plugins/your-plugin-id/`. + +## Improve code quality with eslint (optional) +- [ESLint](https://eslint.org/) is a tool that analyzes your code to quickly find problems. You can run ESLint against your plugin to find common bugs and ways to improve your code. +- To use eslint with this project, make sure to install eslint from terminal: + - `npm install -g eslint` +- To use eslint to analyze this project use this command: + - `eslint main.ts` + - eslint will then create a report with suggestions for code improvement by file and line number. +- If your source code is in a folder, such as `src`, you can use eslint with this command to analyze all files in that folder: + - `eslint ./src/` + +## Funding URL + +You can include funding URLs where people who use your plugin can financially support it. + +The simple way is to set the `fundingUrl` field to your link in your `manifest.json` file: + +```json +{ + "fundingUrl": "https://buymeacoffee.com" +} +``` + +If you have multiple URLs, you can also do: + +```json +{ + "fundingUrl": { + "Buy Me a Coffee": "https://buymeacoffee.com", + "GitHub Sponsor": "https://github.com/sponsors", + "Patreon": "https://www.patreon.com/" + } +} +``` + +## API Documentation + +See https://github.com/obsidianmd/obsidian-api diff --git a/esbuild.config.mjs b/esbuild.config.mjs new file mode 100644 index 0000000..a5de8b8 --- /dev/null +++ b/esbuild.config.mjs @@ -0,0 +1,49 @@ +import esbuild from "esbuild"; +import process from "process"; +import builtins from "builtin-modules"; + +const banner = +`/* +THIS IS A GENERATED/BUNDLED FILE BY ESBUILD +if you want to view the source, please visit the github repository of this plugin +*/ +`; + +const prod = (process.argv[2] === "production"); + +const context = await esbuild.context({ + banner: { + js: banner, + }, + entryPoints: ["main.ts"], + bundle: true, + external: [ + "obsidian", + "electron", + "@codemirror/autocomplete", + "@codemirror/collab", + "@codemirror/commands", + "@codemirror/language", + "@codemirror/lint", + "@codemirror/search", + "@codemirror/state", + "@codemirror/view", + "@lezer/common", + "@lezer/highlight", + "@lezer/lr", + ...builtins], + format: "cjs", + target: "es2018", + logLevel: "info", + sourcemap: prod ? false : "inline", + treeShaking: true, + outfile: "main.js", + minify: prod, +}); + +if (prod) { + await context.rebuild(); + process.exit(0); +} else { + await context.watch(); +} diff --git a/main.js b/main.js index 52d1d9f..f6e0c21 100644 --- a/main.js +++ b/main.js @@ -1,105 +1,697 @@ -const { Plugin, ItemView, WorkspaceLeaf } = require('obsidian'); +/* +THIS IS A GENERATED/BUNDLED FILE BY ESBUILD +if you want to view the source, please visit the github repository of this plugin +*/ -const { GraphView, GRAPH_VIEW_TYPE } = require('/src/views/GraphView.js'); -const { ChatView, CHAT_VIEW_TYPE } = require('./src/views/ChatView.js'); +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); -const { EventBus } = require('./src/utils/EventBus.js'); +// main.ts +var main_exports = {}; +__export(main_exports, { + default: () => LLMAgentPlugin +}); +module.exports = __toCommonJS(main_exports); +var import_obsidian3 = require("obsidian"); -// ----------------------------------------------- Settings --------------------------------------------------------------- - -const DEFAULT_SETTINGS = { - apiBaseUrl: 'http://localhost:5000/api' +// src/utils/EventBus.ts +var EventBus = class { + constructor() { + this.events = {}; + } + on(eventName, callback) { + if (!this.events[eventName]) { + this.events[eventName] = []; + } + this.events[eventName].push(callback); + } + off(eventName, callback) { + if (this.events[eventName]) { + this.events[eventName] = this.events[eventName].filter((cb) => cb !== callback); + } + } + emit(eventName, data) { + if (this.events[eventName]) { + this.events[eventName].forEach((callback) => callback(data)); + } + } }; -// ----------------------------------------------- Main Plugin Class --------------------------------------------------------------- +// src/views/GraphView.ts +var import_obsidian = require("obsidian"); -class LLMAgentPlugin extends Plugin { - constructor() { - super(); - this.eventBus = new EventBus(); - this.settings = DEFAULT_SETTINGS; +// src/components/GraphPanel.ts +var GraphPanel = class { + constructor(container, props) { + this.container = container; + this.props = props; + this.nodes = []; + this.edges = []; + this.init(); + } + init() { + this.container.innerHTML = ` +
+
+
+ `; + this.initReactFlow(); + } + async initReactFlow() { + try { + this.createSimpleGraph(); + } catch (error) { + console.error("\u041E\u0448\u0438\u0431\u043A\u0430 \u0438\u043D\u0438\u0446\u0438\u0430\u043B\u0438\u0437\u0430\u0446\u0438\u0438 React Flow:", error); + this.createFallbackGraph(); } - - async onload() { - await this.loadSettings(); - - // Регистрируем кастомные views - this.registerView(GRAPH_VIEW_TYPE, (leaf) => new GraphView(leaf, this)); - this.registerView(CHAT_VIEW_TYPE, (leaf) => new ChatView(leaf, this)); - - // Добавляем команды - this.addCommand({ - id: 'open-llm-agent-graph', - name: 'Открыть граф диалогов LLM Agent', - callback: () => this.activateGraphView(), - }); - - this.addCommand({ - id: 'open-llm-agent-chat', - name: 'Открыть чат LLM Agent', - callback: () => this.activateChatView(), - }); - - this.addCommand({ - id: 'new-llm-agent-chat', - name: 'Новый чат LLM Agent', - callback: () => this.handleNewChat(), - }); - - // Добавляем иконку в ribbon - this.addRibbonIcon('brain-circuit', 'LLM Agent', () => { - this.activateGraphView(); - this.activateChatView(); - }); - - console.log('LLM Agent plugin загружен'); + } + createSimpleGraph() { + const container = this.container.querySelector("#reactflow-container"); + container.innerHTML = ` +
+
+ +
+ `; + this.renderNodes(); + this.renderEdges(); + } + renderNodes() { + const nodesContainer = this.container.querySelector("#graph-nodes"); + nodesContainer.innerHTML = ""; + this.nodes.forEach((node, index) => { + var _a; + const nodeEl = document.createElement("div"); + nodeEl.className = `graph-node node-${node.type || "default"}`; + nodeEl.style.cssText = ` + position: absolute; + left: ${50 + index * 150}px; + top: ${50 + Math.floor(index / 5) * 80}px; + padding: 8px 12px; + border: 2px solid #333; + border-radius: 4px; + background: #fff; + cursor: pointer; + max-width: 120px; + word-wrap: break-word; + `; + nodeEl.textContent = ((_a = node.data) == null ? void 0 : _a.label) || `Node ${node.id}`; + nodeEl.dataset.nodeId = node.id; + nodeEl.addEventListener("click", () => { + this.handleNodeClick(node.id); + }); + nodesContainer.appendChild(nodeEl); + }); + } + renderEdges() { + const edgesContainer = this.container.querySelector("#graph-edges"); + edgesContainer.innerHTML = ""; + } + createFallbackGraph() { + const container = this.container.querySelector("#reactflow-container"); + container.innerHTML = ` +
+

\u0423\u043F\u0440\u043E\u0449\u0435\u043D\u043D\u043E\u0435 \u043F\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u0433\u0440\u0430\u0444\u0430

+
+
+ `; + this.renderFallbackNodes(); + } + renderFallbackNodes() { + const nodesList = this.container.querySelector("#nodes-list"); + nodesList.innerHTML = ""; + this.nodes.forEach((node) => { + var _a; + const nodeEl = document.createElement("div"); + nodeEl.className = "fallback-node"; + nodeEl.textContent = `${node.id}: ${((_a = node.data) == null ? void 0 : _a.label) || "No label"}`; + nodeEl.addEventListener("click", () => this.handleNodeClick(node.id)); + nodesList.appendChild(nodeEl); + }); + } + updateData(graphData) { + this.nodes = graphData.nodes || []; + this.edges = graphData.edges || []; + this.renderNodes(); + this.renderEdges(); + } + handleNodeClick(nodeId) { + if (this.props.onNodeClick) { + this.props.onNodeClick(nodeId); } + } + destroy() { + this.container.innerHTML = ""; + } +}; - onunload() { - console.log('LLM Agent plugin выгружен'); +// src/views/GraphView.ts +var GRAPH_VIEW_TYPE = "llm-agent-graph-view"; +var GraphView = class extends import_obsidian.ItemView { + constructor(leaf, plugin) { + super(leaf); + this.plugin = plugin; + this.graphData = { nodes: [], edges: [] }; + this.currentNode = null; + this.graphPanel = null; + } + getViewType() { + return GRAPH_VIEW_TYPE; + } + getDisplayText() { + return "LLM Agent Graph"; + } + getIcon() { + return "brain-circuit"; + } + async onOpen() { + const container = this.containerEl.children[1]; + container.empty(); + this.reactContainer = container.createDiv(); + this.reactContainer.addClass("llm-agent-graph-container"); + this.graphPanel = new GraphPanel(this.reactContainer, { + graphData: this.graphData, + onNodeClick: this.handleNodeClick.bind(this) + }); + this.plugin.eventBus.on("graph-updated", this.handleGraphUpdate.bind(this)); + this.plugin.eventBus.on("new-chat", this.handleNewChat.bind(this)); + } + async onClose() { + if (this.graphPanel) { + this.graphPanel.destroy(); + this.graphPanel = null; } - - // ----------------------------------------------- Settings Management --------------------------------------------------------------- - - async loadSettings() { - this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); + this.plugin.eventBus.off("graph-updated", this.handleGraphUpdate); + this.plugin.eventBus.off("new-chat", this.handleNewChat); + } + // ----------------------------------------------- Event Handlers --------------------------------------------------------------- + handleNodeClick(nodeId) { + this.currentNode = nodeId; + this.plugin.eventBus.emit("node-selected", { nodeId }); + } + handleGraphUpdate(data) { + this.graphData = data.graphData; + this.currentNode = data.currentNode; + if (this.graphPanel) { + this.graphPanel.updateData(this.graphData); } - - async saveSettings() { - await this.saveData(this.settings); + } + handleNewChat() { + this.graphData = { nodes: [], edges: [] }; + this.currentNode = null; + if (this.graphPanel) { + this.graphPanel.updateData(this.graphData); } + } +}; - // ----------------------------------------------- View Management --------------------------------------------------------------- +// src/views/ChatView.ts +var import_obsidian2 = require("obsidian"); - async activateGraphView() { - const existingLeaf = this.app.workspace.getLeavesOfType(GRAPH_VIEW_TYPE)[0]; - if (existingLeaf) { - this.app.workspace.revealLeaf(existingLeaf); - } else { - await this.app.workspace.getLeaf('tab').setViewState({ - type: GRAPH_VIEW_TYPE, - active: true, - }); +// src/components/ChatPanel.ts +var ChatPanel = class { + constructor(container, props) { + this.container = container; + this.props = props; + this.chatHistory = props.chatHistory || []; + this.init(); + } + init() { + this.container.innerHTML = ` +
+
+
+
+ + +
+ +
+
+ `; + this.messageInput = this.container.querySelector("#message-input"); + this.sendButton = this.container.querySelector("#send-button"); + this.suggestionsContainer = this.container.querySelector("#suggestions"); + this.setupEventListeners(); + this.renderHistory(); + } + setupEventListeners() { + this.sendButton.addEventListener("click", () => this.handleSend()); + this.messageInput.addEventListener("keydown", (e) => { + if (e.key === "Enter") { + if (this.suggestionsContainer.style.display === "block") { + const firstSuggestion = this.suggestionsContainer.querySelector(".suggestion-item"); + if (firstSuggestion) { + this.messageInput.value = firstSuggestion.textContent || ""; + this.hideSuggestions(); + e.preventDefault(); + return; + } } + this.handleSend(); + e.preventDefault(); + } + }); + this.messageInput.addEventListener("input", (e) => { + this.handleInput(e.target.value); + }); + } + handleInput(value) { + const commands = ["/help", "/imagine", "/analyze", "/subtitles_meet", "/subtitles_teams", "/summarize", "/chat"]; + if (value.startsWith("/")) { + const filteredCommands = commands.filter((cmd) => cmd.startsWith(value.toLowerCase())); + this.showSuggestions(filteredCommands); + } else { + this.hideSuggestions(); } + } + showSuggestions(suggestions) { + if (suggestions.length === 0) { + this.hideSuggestions(); + return; + } + this.suggestionsContainer.innerHTML = suggestions.map((suggestion) => `
${suggestion}
`).join(""); + this.suggestionsContainer.style.display = "block"; + this.suggestionsContainer.querySelectorAll(".suggestion-item").forEach((item) => { + item.addEventListener("click", () => { + this.messageInput.value = item.textContent || ""; + this.hideSuggestions(); + this.messageInput.focus(); + }); + }); + } + hideSuggestions() { + this.suggestionsContainer.style.display = "none"; + } + handleSend() { + const message = this.messageInput.value.trim(); + if (message && this.props.onSendMessage) { + this.props.onSendMessage(message); + this.messageInput.value = ""; + this.hideSuggestions(); + } + } + renderHistory() { + const historyContainer = this.container.querySelector("#chat-history"); + historyContainer.innerHTML = this.chatHistory.map( + (msg) => ` +
+
${this.processMessageContent(msg.content)}
+
+ ` + ).join(""); + historyContainer.scrollTop = historyContainer.scrollHeight; + } + processMessageContent(content) { + return content.replace(/]*?)>/g, '').replace(/\n/g, "
"); + } + updateHistory(newHistory) { + this.chatHistory = newHistory; + this.renderHistory(); + } + destroy() { + this.container.innerHTML = ""; + } +}; - async activateChatView() { - const existingLeaf = this.app.workspace.getLeavesOfType(CHAT_VIEW_TYPE)[0]; - if (existingLeaf) { - this.app.workspace.revealLeaf(existingLeaf); - } else { - await this.app.workspace.getRightLeaf(false).setViewState({ - type: CHAT_VIEW_TYPE, - active: true, - }); +// src/components/HistoryPanel.ts +var HistoryPanel = class { + constructor(container, props) { + this.container = container; + this.props = props; + this.graphs = props.graphs || []; + this.init(); + } + init() { + this.container.innerHTML = ` +
+
+

\u0418\u0441\u0442\u043E\u0440\u0438\u044F \u0434\u0438\u0430\u043B\u043E\u0433\u043E\u0432

+ +
+
+ +
+ `; + this.newChatButton = this.container.querySelector("#new-chat-button"); + this.chatList = this.container.querySelector("#chat-list"); + this.contextMenu = this.container.querySelector("#context-menu"); + this.deleteButton = this.container.querySelector("#delete-graph-button"); + this.setupEventListeners(); + this.renderGraphs(); + } + setupEventListeners() { + this.newChatButton.addEventListener("click", () => { + if (this.props.onNewChat) { + this.props.onNewChat(); + } + }); + this.deleteButton.addEventListener("click", () => { + if (this.contextMenuGraphId) { + this.handleDeleteGraph(this.contextMenuGraphId); + } + }); + document.addEventListener("click", (e) => { + if (!this.contextMenu.contains(e.target)) { + this.contextMenu.style.display = "none"; + } + }); + } + renderGraphs() { + this.chatList.innerHTML = this.graphs.map( + (graph) => ` +
+
+
+ + ${graph.first_message || `\u0413\u0440\u0430\u0444 ${graph.id}`} + +
+
+ +
+
+
+ ` + ).join(""); + this.chatList.querySelectorAll(".chat-list-item").forEach((item) => { + item.addEventListener("click", (e) => { + if (!e.target.classList.contains("chat-item-options-button")) { + const graphId = item.dataset.graphId; + if (graphId && this.props.onGraphSelect) { + this.props.onGraphSelect(graphId); + } } + }); + }); + this.chatList.querySelectorAll(".chat-item-options-button").forEach((button) => { + button.addEventListener("click", (e) => { + e.stopPropagation(); + this.showContextMenu(e, button.dataset.graphId || ""); + }); + }); + } + showContextMenu(event, graphId) { + this.contextMenuGraphId = graphId; + this.contextMenu.style.display = "block"; + this.contextMenu.style.left = `${event.clientX}px`; + this.contextMenu.style.top = `${event.clientY}px`; + } + async handleDeleteGraph(graphId) { + try { + const response = await fetch(`http://localhost:5000/api/graphs/${graphId}`, { + method: "DELETE" + }); + if (!response.ok) { + throw new Error(`HTTP \u043E\u0448\u0438\u0431\u043A\u0430! \u0441\u0442\u0430\u0442\u0443\u0441: ${response.status}`); + } + this.graphs = this.graphs.filter((graph) => graph.id !== graphId); + this.renderGraphs(); + this.contextMenu.style.display = "none"; + } catch (error) { + console.error("\u041E\u0448\u0438\u0431\u043A\u0430 \u0443\u0434\u0430\u043B\u0435\u043D\u0438\u044F \u0433\u0440\u0430\u0444\u0430:", error); } + } + updateGraphs(newGraphs) { + this.graphs = newGraphs; + this.renderGraphs(); + } + destroy() { + this.container.innerHTML = ""; + } +}; - // ----------------------------------------------- Event Handlers --------------------------------------------------------------- - - handleNewChat() { - this.eventBus.emit('new-chat'); +// src/views/ChatView.ts +var CHAT_VIEW_TYPE = "llm-agent-chat-view"; +var ChatView = class extends import_obsidian2.ItemView { + constructor(leaf, plugin) { + super(leaf); + this.plugin = plugin; + this.chatHistory = []; + this.graphsList = []; + this.selectedGraphId = null; + this.currentNode = null; + this.chatPanel = null; + this.historyPanel = null; + } + getViewType() { + return CHAT_VIEW_TYPE; + } + getDisplayText() { + return "LLM Agent Chat"; + } + getIcon() { + return "message-circle"; + } + async onOpen() { + const container = this.containerEl.children[1]; + container.empty(); + this.chatContainer = container.createDiv(); + this.chatContainer.addClass("llm-agent-chat-container"); + this.historyContainer = container.createDiv(); + this.historyContainer.addClass("llm-agent-history-container"); + this.chatPanel = new ChatPanel(this.chatContainer, { + chatHistory: this.chatHistory, + onSendMessage: this.handleSendMessage.bind(this) + }); + this.historyPanel = new HistoryPanel(this.historyContainer, { + graphs: this.graphsList, + onGraphSelect: this.handleGraphSelect.bind(this), + onNewChat: this.handleNewChat.bind(this) + }); + this.plugin.eventBus.on("node-selected", this.handleNodeSelected.bind(this)); + this.plugin.eventBus.on("new-chat", this.handleNewChat.bind(this)); + this.fetchGraphsList(); + } + async onClose() { + if (this.chatPanel) { + this.chatPanel.destroy(); + this.chatPanel = null; } -} + if (this.historyPanel) { + this.historyPanel.destroy(); + this.historyPanel = null; + } + this.plugin.eventBus.off("node-selected", this.handleNodeSelected); + this.plugin.eventBus.off("new-chat", this.handleNewChat); + } + // ----------------------------------------------- API Methods --------------------------------------------------------------- + async fetchGraphsList() { + try { + const response = await fetch(`${this.plugin.settings.apiBaseUrl}/graphs`); + const data = await response.json(); + this.graphsList = data; + if (this.historyPanel) { + this.historyPanel.updateGraphs(this.graphsList); + } + } catch (error) { + console.error("\u041E\u0448\u0438\u0431\u043A\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0438 \u0441\u043F\u0438\u0441\u043A\u0430 \u0433\u0440\u0430\u0444\u043E\u0432:", error); + } + } + async fetchGraphData(graphId) { + try { + const response = await fetch(`${this.plugin.settings.apiBaseUrl}/graphs/${graphId}`); + if (!response.ok) { + if (response.status === 404) { + console.error(`\u0413\u0440\u0430\u0444 \u0441 ID ${graphId} \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D.`); + } + throw new Error(`HTTP \u043E\u0448\u0438\u0431\u043A\u0430! \u0441\u0442\u0430\u0442\u0443\u0441: ${response.status}`); + } + const data = await response.json(); + this.plugin.eventBus.emit("graph-updated", { + graphData: { + nodes: data.graph_nodes || [], + edges: data.graph_edges || [] + }, + currentNode: data.current_node_id + }); + this.chatHistory = data.messages || []; + if (this.chatPanel) { + this.chatPanel.updateHistory(this.chatHistory); + } + } catch (error) { + console.error("\u041E\u0448\u0438\u0431\u043A\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0438 \u0434\u0430\u043D\u043D\u044B\u0445 \u0433\u0440\u0430\u0444\u0430:", error); + } + } + async fetchMessagesFromRootToNode(nodeId) { + try { + const response = await fetch(`${this.plugin.settings.apiBaseUrl}/messages/${this.selectedGraphId}/${nodeId}`); + const data = await response.json(); + this.chatHistory = data; + if (this.chatPanel) { + this.chatPanel.updateHistory(this.chatHistory); + } + } catch (error) { + console.error("\u041E\u0448\u0438\u0431\u043A\u0430 \u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0438 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439:", error); + } + } + // ----------------------------------------------- Event Handlers --------------------------------------------------------------- + async handleSendMessage(message) { + try { + const response = await fetch(`${this.plugin.settings.apiBaseUrl}/chat`, { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ + message, + graph_id: this.selectedGraphId, + parent_node_id: this.currentNode + }) + }); + if (!response.ok) { + throw new Error(`HTTP \u043E\u0448\u0438\u0431\u043A\u0430! \u0441\u0442\u0430\u0442\u0443\u0441: ${response.status}`); + } + const result = await response.json(); + this.selectedGraphId = result.graph_id; + const newCurrentNodeId = result.graph_visualization_data.current_node_id; + this.currentNode = newCurrentNodeId; + this.plugin.eventBus.emit("graph-updated", { + graphData: { + nodes: result.graph_visualization_data.nodes || [], + edges: result.graph_visualization_data.edges || [] + }, + currentNode: newCurrentNodeId + }); + if (newCurrentNodeId) { + await this.fetchMessagesFromRootToNode(newCurrentNodeId); + } else { + this.chatHistory = result.messages || []; + if (this.chatPanel) { + this.chatPanel.updateHistory(this.chatHistory); + } + } + this.fetchGraphsList(); + } catch (error) { + console.error("\u041E\u0448\u0438\u0431\u043A\u0430 \u043E\u0442\u043F\u0440\u0430\u0432\u043A\u0438 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F:", error); + } + } + handleNodeSelected(data) { + this.currentNode = data.nodeId; + if (this.selectedGraphId) { + this.fetchMessagesFromRootToNode(data.nodeId); + } + } + handleGraphSelect(graphId) { + this.selectedGraphId = graphId; + this.fetchGraphData(graphId); + } + handleNewChat() { + this.chatHistory = []; + this.currentNode = null; + this.selectedGraphId = null; + if (this.chatPanel) { + this.chatPanel.updateHistory(this.chatHistory); + } + } +}; -module.exports = LLMAgentPlugin; \ No newline at end of file +// main.ts +var DEFAULT_SETTINGS = { + apiBaseUrl: "http://localhost:5000/api" +}; +var LLMAgentPlugin = class extends import_obsidian3.Plugin { + async onload() { + await this.loadSettings(); + this.eventBus = new EventBus(); + this.registerView(GRAPH_VIEW_TYPE, (leaf) => new GraphView(leaf, this)); + this.registerView(CHAT_VIEW_TYPE, (leaf) => new ChatView(leaf, this)); + this.addCommand({ + id: "open-llm-agent-graph", + name: "\u041E\u0442\u043A\u0440\u044B\u0442\u044C \u0433\u0440\u0430\u0444 \u0434\u0438\u0430\u043B\u043E\u0433\u043E\u0432 LLM Agent", + callback: () => this.activateGraphView() + }); + this.addCommand({ + id: "open-llm-agent-chat", + name: "\u041E\u0442\u043A\u0440\u044B\u0442\u044C \u0447\u0430\u0442 LLM Agent", + callback: () => this.activateChatView() + }); + this.addCommand({ + id: "new-llm-agent-chat", + name: "\u041D\u043E\u0432\u044B\u0439 \u0447\u0430\u0442 LLM Agent", + callback: () => this.handleNewChat() + }); + this.addRibbonIcon("brain-circuit", "LLM Agent", () => { + this.activateGraphView(); + this.activateChatView(); + }); + console.log("LLM Agent plugin \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043D"); + this.addSettingTab(new SampleSettingTab(this.app, this)); + } + onunload() { + console.log("LLM Agent plugin \u0432\u044B\u0433\u0440\u0443\u0436\u0435\u043D"); + } + // ----------------------------------------------- Settings Management --------------------------------------------------------------- + async loadSettings() { + this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); + } + async saveSettings() { + await this.saveData(this.settings); + } + // ----------------------------------------------- View Management --------------------------------------------------------------- + async activateGraphView() { + const existingLeaf = this.app.workspace.getLeavesOfType(GRAPH_VIEW_TYPE)[0]; + if (existingLeaf) { + this.app.workspace.revealLeaf(existingLeaf); + } else { + await this.app.workspace.getLeaf("tab").setViewState({ + type: GRAPH_VIEW_TYPE, + active: true + }); + } + } + async activateChatView() { + const existingLeaf = this.app.workspace.getLeavesOfType(CHAT_VIEW_TYPE)[0]; + if (existingLeaf) { + this.app.workspace.revealLeaf(existingLeaf); + } else { + const rightLeaf = this.app.workspace.getRightLeaf(false); + if (rightLeaf) { + await rightLeaf.setViewState({ + type: CHAT_VIEW_TYPE, + active: true + }); + } + } + } + // ----------------------------------------------- Event Handlers --------------------------------------------------------------- + handleNewChat() { + this.eventBus.emit("new-chat"); + } +}; +var SampleSettingTab = class extends import_obsidian3.PluginSettingTab { + constructor(app, plugin) { + super(app, plugin); + this.plugin = plugin; + } + display() { + const { containerEl } = this; + containerEl.empty(); + new import_obsidian3.Setting(containerEl).setName("API Base URL").setDesc("\u0411\u0430\u0437\u043E\u0432\u044B\u0439 URL \u0434\u043B\u044F API LLM Agent").addText( + (text) => text.setPlaceholder("\u0412\u0432\u0435\u0434\u0438\u0442\u0435 URL").setValue(this.plugin.settings.apiBaseUrl).onChange(async (value) => { + this.plugin.settings.apiBaseUrl = value; + await this.plugin.saveSettings(); + }) + ); + } +}; +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsibWFpbi50cyIsICJzcmMvdXRpbHMvRXZlbnRCdXMudHMiLCAic3JjL3ZpZXdzL0dyYXBoVmlldy50cyIsICJzcmMvY29tcG9uZW50cy9HcmFwaFBhbmVsLnRzIiwgInNyYy92aWV3cy9DaGF0Vmlldy50cyIsICJzcmMvY29tcG9uZW50cy9DaGF0UGFuZWwudHMiLCAic3JjL2NvbXBvbmVudHMvSGlzdG9yeVBhbmVsLnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyJpbXBvcnQgeyBBcHAsIEVkaXRvciwgTWFya2Rvd25WaWV3LCBNb2RhbCwgTm90aWNlLCBQbHVnaW4sIFBsdWdpblNldHRpbmdUYWIsIFNldHRpbmcsIEl0ZW1WaWV3LCBXb3Jrc3BhY2VMZWFmIH0gZnJvbSAnb2JzaWRpYW4nO1xyXG5pbXBvcnQgeyBFdmVudEJ1cyB9IGZyb20gJy4vc3JjL3V0aWxzL0V2ZW50QnVzJztcclxuaW1wb3J0IHsgR3JhcGhWaWV3LCBHUkFQSF9WSUVXX1RZUEUgfSBmcm9tICcuL3NyYy92aWV3cy9HcmFwaFZpZXcnO1xyXG5pbXBvcnQgeyBDaGF0VmlldywgQ0hBVF9WSUVXX1RZUEUgfSBmcm9tICcuL3NyYy92aWV3cy9DaGF0Vmlldyc7XHJcblxyXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSBTZXR0aW5ncyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuXHJcbmludGVyZmFjZSBMTE1BZ2VudFNldHRpbmdzIHtcclxuXHRhcGlCYXNlVXJsOiBzdHJpbmc7XHJcbn1cclxuXHJcbmNvbnN0IERFRkFVTFRfU0VUVElOR1M6IExMTUFnZW50U2V0dGluZ3MgPSB7XHJcblx0YXBpQmFzZVVybDogJ2h0dHA6Ly9sb2NhbGhvc3Q6NTAwMC9hcGknXHJcbn07XHJcblxyXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSBNYWluIFBsdWdpbiBDbGFzcyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuXHJcbmV4cG9ydCBkZWZhdWx0IGNsYXNzIExMTUFnZW50UGx1Z2luIGV4dGVuZHMgUGx1Z2luIHtcclxuXHRzZXR0aW5nczogTExNQWdlbnRTZXR0aW5ncztcclxuXHRldmVudEJ1czogRXZlbnRCdXM7XHJcblxyXG5cdGFzeW5jIG9ubG9hZCgpIHtcclxuXHRcdGF3YWl0IHRoaXMubG9hZFNldHRpbmdzKCk7XHJcblxyXG5cdFx0dGhpcy5ldmVudEJ1cyA9IG5ldyBFdmVudEJ1cygpO1xyXG5cclxuXHRcdC8vIFx1MDQyMFx1MDQzNVx1MDQzM1x1MDQzOFx1MDQ0MVx1MDQ0Mlx1MDQ0MFx1MDQzOFx1MDQ0MFx1MDQ0M1x1MDQzNVx1MDQzQyBcdTA0M0FcdTA0MzBcdTA0NDFcdTA0NDJcdTA0M0VcdTA0M0NcdTA0M0RcdTA0NEJcdTA0MzUgdmlld3NcclxuXHRcdHRoaXMucmVnaXN0ZXJWaWV3KEdSQVBIX1ZJRVdfVFlQRSwgKGxlYWYpID0+IG5ldyBHcmFwaFZpZXcobGVhZiwgdGhpcykpO1xyXG5cdFx0dGhpcy5yZWdpc3RlclZpZXcoQ0hBVF9WSUVXX1RZUEUsIChsZWFmKSA9PiBuZXcgQ2hhdFZpZXcobGVhZiwgdGhpcykpO1xyXG5cclxuXHRcdC8vIFx1MDQxNFx1MDQzRVx1MDQzMVx1MDQzMFx1MDQzMlx1MDQzQlx1MDQ0Rlx1MDQzNVx1MDQzQyBcdTA0M0FcdTA0M0VcdTA0M0NcdTA0MzBcdTA0M0RcdTA0MzRcdTA0NEJcclxuXHRcdHRoaXMuYWRkQ29tbWFuZCh7XHJcblx0XHRcdGlkOiAnb3Blbi1sbG0tYWdlbnQtZ3JhcGgnLFxyXG5cdFx0XHRuYW1lOiAnXHUwNDFFXHUwNDQyXHUwNDNBXHUwNDQwXHUwNDRCXHUwNDQyXHUwNDRDIFx1MDQzM1x1MDQ0MFx1MDQzMFx1MDQ0NCBcdTA0MzRcdTA0MzhcdTA0MzBcdTA0M0JcdTA0M0VcdTA0MzNcdTA0M0VcdTA0MzIgTExNIEFnZW50JyxcclxuXHRcdFx0Y2FsbGJhY2s6ICgpID0+IHRoaXMuYWN0aXZhdGVHcmFwaFZpZXcoKVxyXG5cdFx0fSk7XHJcblxyXG5cdFx0dGhpcy5hZGRDb21tYW5kKHtcclxuXHRcdFx0aWQ6ICdvcGVuLWxsbS1hZ2VudC1jaGF0JyxcclxuXHRcdFx0bmFtZTogJ1x1MDQxRVx1MDQ0Mlx1MDQzQVx1MDQ0MFx1MDQ0Qlx1MDQ0Mlx1MDQ0QyBcdTA0NDdcdTA0MzBcdTA0NDIgTExNIEFnZW50JyxcclxuXHRcdFx0Y2FsbGJhY2s6ICgpID0+IHRoaXMuYWN0aXZhdGVDaGF0VmlldygpXHJcblx0XHR9KTtcclxuXHJcblx0XHR0aGlzLmFkZENvbW1hbmQoe1xyXG5cdFx0XHRpZDogJ25ldy1sbG0tYWdlbnQtY2hhdCcsXHJcblx0XHRcdG5hbWU6ICdcdTA0MURcdTA0M0VcdTA0MzJcdTA0NEJcdTA0MzkgXHUwNDQ3XHUwNDMwXHUwNDQyIExMTSBBZ2VudCcsXHJcblx0XHRcdGNhbGxiYWNrOiAoKSA9PiB0aGlzLmhhbmRsZU5ld0NoYXQoKVxyXG5cdFx0fSk7XHJcblxyXG5cdFx0Ly8gXHUwNDE0XHUwNDNFXHUwNDMxXHUwNDMwXHUwNDMyXHUwNDNCXHUwNDRGXHUwNDM1XHUwNDNDIFx1MDQzOFx1MDQzQVx1MDQzRVx1MDQzRFx1MDQzQVx1MDQ0MyBcdTA0MzIgcmliYm9uXHJcblx0XHR0aGlzLmFkZFJpYmJvbkljb24oJ2JyYWluLWNpcmN1aXQnLCAnTExNIEFnZW50JywgKCkgPT4ge1xyXG5cdFx0XHR0aGlzLmFjdGl2YXRlR3JhcGhWaWV3KCk7XHJcblx0XHRcdHRoaXMuYWN0aXZhdGVDaGF0VmlldygpO1xyXG5cdFx0fSk7XHJcblxyXG5cdFx0Y29uc29sZS5sb2coJ0xMTSBBZ2VudCBwbHVnaW4gXHUwNDM3XHUwNDMwXHUwNDMzXHUwNDQwXHUwNDQzXHUwNDM2XHUwNDM1XHUwNDNEJyk7XHJcblxyXG5cdFx0Ly8gVGhpcyBhZGRzIGEgc2V0dGluZ3MgdGFiIHNvIHRoZSB1c2VyIGNhbiBjb25maWd1cmUgdmFyaW91cyBhc3BlY3RzIG9mIHRoZSBwbHVnaW5cclxuXHRcdHRoaXMuYWRkU2V0dGluZ1RhYihuZXcgU2FtcGxlU2V0dGluZ1RhYih0aGlzLmFwcCwgdGhpcykpO1xyXG5cdH1cclxuXHJcblx0b251bmxvYWQoKSB7XHJcblx0XHRjb25zb2xlLmxvZygnTExNIEFnZW50IHBsdWdpbiBcdTA0MzJcdTA0NEJcdTA0MzNcdTA0NDBcdTA0NDNcdTA0MzZcdTA0MzVcdTA0M0QnKTtcclxuXHR9XHJcblxyXG5cdC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIFNldHRpbmdzIE1hbmFnZW1lbnQgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcblxyXG5cdGFzeW5jIGxvYWRTZXR0aW5ncygpIHtcclxuXHRcdHRoaXMuc2V0dGluZ3MgPSBPYmplY3QuYXNzaWduKHt9LCBERUZBVUxUX1NFVFRJTkdTLCBhd2FpdCB0aGlzLmxvYWREYXRhKCkpO1xyXG5cdH1cclxuXHJcblx0YXN5bmMgc2F2ZVNldHRpbmdzKCkge1xyXG5cdFx0YXdhaXQgdGhpcy5zYXZlRGF0YSh0aGlzLnNldHRpbmdzKTtcclxuXHR9XHJcblxyXG5cdC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIFZpZXcgTWFuYWdlbWVudCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuXHJcblx0YXN5bmMgYWN0aXZhdGVHcmFwaFZpZXcoKSB7XHJcblx0XHRjb25zdCBleGlzdGluZ0xlYWYgPSB0aGlzLmFwcC53b3Jrc3BhY2UuZ2V0TGVhdmVzT2ZUeXBlKEdSQVBIX1ZJRVdfVFlQRSlbMF07XHJcblx0XHRpZiAoZXhpc3RpbmdMZWFmKSB7XHJcblx0XHRcdHRoaXMuYXBwLndvcmtzcGFjZS5yZXZlYWxMZWFmKGV4aXN0aW5nTGVhZik7XHJcblx0XHR9IGVsc2Uge1xyXG5cdFx0XHRhd2FpdCB0aGlzLmFwcC53b3Jrc3BhY2UuZ2V0TGVhZigndGFiJykuc2V0Vmlld1N0YXRlKHtcclxuXHRcdFx0XHR0eXBlOiBHUkFQSF9WSUVXX1RZUEUsXHJcblx0XHRcdFx0YWN0aXZlOiB0cnVlXHJcblx0XHRcdH0pO1xyXG5cdFx0fVxyXG5cdH1cclxuXHJcblx0YXN5bmMgYWN0aXZhdGVDaGF0VmlldygpIHtcclxuXHRcdGNvbnN0IGV4aXN0aW5nTGVhZiA9IHRoaXMuYXBwLndvcmtzcGFjZS5nZXRMZWF2ZXNPZlR5cGUoQ0hBVF9WSUVXX1RZUEUpWzBdO1xyXG5cdFx0aWYgKGV4aXN0aW5nTGVhZikge1xyXG5cdFx0XHR0aGlzLmFwcC53b3Jrc3BhY2UucmV2ZWFsTGVhZihleGlzdGluZ0xlYWYpO1xyXG5cdFx0fSBlbHNlIHtcclxuXHRcdFx0Y29uc3QgcmlnaHRMZWFmID0gdGhpcy5hcHAud29ya3NwYWNlLmdldFJpZ2h0TGVhZihmYWxzZSk7XHJcblx0XHRcdGlmIChyaWdodExlYWYpIHtcclxuXHRcdFx0XHRhd2FpdCByaWdodExlYWYuc2V0Vmlld1N0YXRlKHtcclxuXHRcdFx0XHRcdHR5cGU6IENIQVRfVklFV19UWVBFLFxyXG5cdFx0XHRcdFx0YWN0aXZlOiB0cnVlXHJcblx0XHRcdFx0fSk7XHJcblx0XHRcdH1cclxuXHRcdH1cclxuXHR9XHJcblxyXG5cdC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIEV2ZW50IEhhbmRsZXJzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG5cclxuXHRoYW5kbGVOZXdDaGF0KCkge1xyXG5cdFx0dGhpcy5ldmVudEJ1cy5lbWl0KCduZXctY2hhdCcpO1xyXG5cdH1cclxufVxyXG5cclxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gU2FtcGxlIFNldHRpbmcgVGFiIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG5cclxuY2xhc3MgU2FtcGxlU2V0dGluZ1RhYiBleHRlbmRzIFBsdWdpblNldHRpbmdUYWIge1xyXG5cdHBsdWdpbjogTExNQWdlbnRQbHVnaW47XHJcblxyXG5cdGNvbnN0cnVjdG9yKGFwcDogQXBwLCBwbHVnaW46IExMTUFnZW50UGx1Z2luKSB7XHJcblx0XHRzdXBlcihhcHAsIHBsdWdpbik7XHJcblx0XHR0aGlzLnBsdWdpbiA9IHBsdWdpbjtcclxuXHR9XHJcblxyXG5cdGRpc3BsYXkoKTogdm9pZCB7XHJcblx0XHRjb25zdCB7IGNvbnRhaW5lckVsIH0gPSB0aGlzO1xyXG5cclxuXHRcdGNvbnRhaW5lckVsLmVtcHR5KCk7XHJcblxyXG5cdFx0bmV3IFNldHRpbmcoY29udGFpbmVyRWwpXHJcblx0XHRcdC5zZXROYW1lKCdBUEkgQmFzZSBVUkwnKVxyXG5cdFx0XHQuc2V0RGVzYygnXHUwNDExXHUwNDMwXHUwNDM3XHUwNDNFXHUwNDMyXHUwNDRCXHUwNDM5IFVSTCBcdTA0MzRcdTA0M0JcdTA0NEYgQVBJIExMTSBBZ2VudCcpXHJcblx0XHRcdC5hZGRUZXh0KCh0ZXh0KSA9PlxyXG5cdFx0XHRcdHRleHRcclxuXHRcdFx0XHRcdC5zZXRQbGFjZWhvbGRlcignXHUwNDEyXHUwNDMyXHUwNDM1XHUwNDM0XHUwNDM4XHUwNDQyXHUwNDM1IFVSTCcpXHJcblx0XHRcdFx0XHQuc2V0VmFsdWUodGhpcy5wbHVnaW4uc2V0dGluZ3MuYXBpQmFzZVVybClcclxuXHRcdFx0XHRcdC5vbkNoYW5nZShhc3luYyAodmFsdWUpID0+IHtcclxuXHRcdFx0XHRcdFx0dGhpcy5wbHVnaW4uc2V0dGluZ3MuYXBpQmFzZVVybCA9IHZhbHVlO1xyXG5cdFx0XHRcdFx0XHRhd2FpdCB0aGlzLnBsdWdpbi5zYXZlU2V0dGluZ3MoKTtcclxuXHRcdFx0XHRcdH0pXHJcblx0XHRcdCk7XHJcblx0fVxyXG59XHJcbiIsICIvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSBFdmVudCBCdXMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcblxyXG5leHBvcnQgY2xhc3MgRXZlbnRCdXMge1xyXG5cdHByaXZhdGUgZXZlbnRzOiB7IFtrZXk6IHN0cmluZ106ICgoZGF0YT86IGFueSkgPT4gdm9pZClbXSB9ID0ge307XHJcblxyXG5cdG9uKGV2ZW50TmFtZTogc3RyaW5nLCBjYWxsYmFjazogKGRhdGE/OiBhbnkpID0+IHZvaWQpOiB2b2lkIHtcclxuXHRcdGlmICghdGhpcy5ldmVudHNbZXZlbnROYW1lXSkge1xyXG5cdFx0XHR0aGlzLmV2ZW50c1tldmVudE5hbWVdID0gW107XHJcblx0XHR9XHJcblx0XHR0aGlzLmV2ZW50c1tldmVudE5hbWVdLnB1c2goY2FsbGJhY2spO1xyXG5cdH1cclxuXHJcblx0b2ZmKGV2ZW50TmFtZTogc3RyaW5nLCBjYWxsYmFjazogKGRhdGE/OiBhbnkpID0+IHZvaWQpOiB2b2lkIHtcclxuXHRcdGlmICh0aGlzLmV2ZW50c1tldmVudE5hbWVdKSB7XHJcblx0XHRcdHRoaXMuZXZlbnRzW2V2ZW50TmFtZV0gPSB0aGlzLmV2ZW50c1tldmVudE5hbWVdLmZpbHRlcigoY2IpID0+IGNiICE9PSBjYWxsYmFjayk7XHJcblx0XHR9XHJcblx0fVxyXG5cclxuXHRlbWl0KGV2ZW50TmFtZTogc3RyaW5nLCBkYXRhPzogYW55KTogdm9pZCB7XHJcblx0XHRpZiAodGhpcy5ldmVudHNbZXZlbnROYW1lXSkge1xyXG5cdFx0XHR0aGlzLmV2ZW50c1tldmVudE5hbWVdLmZvckVhY2goKGNhbGxiYWNrKSA9PiBjYWxsYmFjayhkYXRhKSk7XHJcblx0XHR9XHJcblx0fVxyXG59XHJcbiIsICJpbXBvcnQgeyBJdGVtVmlldywgV29ya3NwYWNlTGVhZiB9IGZyb20gJ29ic2lkaWFuJztcclxuaW1wb3J0IHsgR3JhcGhQYW5lbCB9IGZyb20gJy4uL2NvbXBvbmVudHMvR3JhcGhQYW5lbCc7XHJcbmltcG9ydCBMTE1BZ2VudFBsdWdpbiBmcm9tICdtYWluJztcclxuXHJcbmV4cG9ydCBjb25zdCBHUkFQSF9WSUVXX1RZUEUgPSAnbGxtLWFnZW50LWdyYXBoLXZpZXcnO1xyXG5cclxuLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gR3JhcGggVmlldyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuXHJcbmV4cG9ydCBjbGFzcyBHcmFwaFZpZXcgZXh0ZW5kcyBJdGVtVmlldyB7XHJcblx0cGx1Z2luOiBMTE1BZ2VudFBsdWdpbjtcclxuXHRncmFwaERhdGE6IHsgbm9kZXM6IGFueVtdOyBlZGdlczogYW55W10gfTtcclxuXHRjdXJyZW50Tm9kZTogc3RyaW5nIHwgbnVsbDtcclxuXHRncmFwaFBhbmVsOiBHcmFwaFBhbmVsIHwgbnVsbDtcclxuXHRyZWFjdENvbnRhaW5lcjogSFRNTERpdkVsZW1lbnQ7XHJcblxyXG5cdGNvbnN0cnVjdG9yKGxlYWY6IFdvcmtzcGFjZUxlYWYsIHBsdWdpbjogTExNQWdlbnRQbHVnaW4pIHtcclxuXHRcdHN1cGVyKGxlYWYpO1xyXG5cdFx0dGhpcy5wbHVnaW4gPSBwbHVnaW47XHJcblx0XHR0aGlzLmdyYXBoRGF0YSA9IHsgbm9kZXM6IFtdLCBlZGdlczogW10gfTtcclxuXHRcdHRoaXMuY3VycmVudE5vZGUgPSBudWxsO1xyXG5cdFx0dGhpcy5ncmFwaFBhbmVsID0gbnVsbDtcclxuXHR9XHJcblxyXG5cdGdldFZpZXdUeXBlKCk6IHN0cmluZyB7XHJcblx0XHRyZXR1cm4gR1JBUEhfVklFV19UWVBFO1xyXG5cdH1cclxuXHJcblx0Z2V0RGlzcGxheVRleHQoKTogc3RyaW5nIHtcclxuXHRcdHJldHVybiAnTExNIEFnZW50IEdyYXBoJztcclxuXHR9XHJcblxyXG5cdGdldEljb24oKTogc3RyaW5nIHtcclxuXHRcdHJldHVybiAnYnJhaW4tY2lyY3VpdCc7XHJcblx0fVxyXG5cclxuXHRhc3luYyBvbk9wZW4oKTogUHJvbWlzZTx2b2lkPiB7XHJcblx0XHRjb25zdCBjb250YWluZXIgPSB0aGlzLmNvbnRhaW5lckVsLmNoaWxkcmVuWzFdIGFzIEhUTUxFbGVtZW50O1xyXG5cdFx0Y29udGFpbmVyLmVtcHR5KCk7XHJcblxyXG5cdFx0Ly8gXHUwNDIxXHUwNDNFXHUwNDM3XHUwNDM0XHUwNDMwXHUwNDM1XHUwNDNDIFx1MDQzQVx1MDQzRVx1MDQzRFx1MDQ0Mlx1MDQzNVx1MDQzOVx1MDQzRFx1MDQzNVx1MDQ0MCBcdTA0MzRcdTA0M0JcdTA0NEYgUmVhY3QgXHUwNDNBXHUwNDNFXHUwNDNDXHUwNDNGXHUwNDNFXHUwNDNEXHUwNDM1XHUwNDNEXHUwNDQyXHUwNDMwXHJcblx0XHR0aGlzLnJlYWN0Q29udGFpbmVyID0gY29udGFpbmVyLmNyZWF0ZURpdigpIGFzIEhUTUxEaXZFbGVtZW50O1xyXG5cdFx0dGhpcy5yZWFjdENvbnRhaW5lci5hZGRDbGFzcygnbGxtLWFnZW50LWdyYXBoLWNvbnRhaW5lcicpO1xyXG5cclxuXHRcdC8vIFx1MDQxOFx1MDQzRFx1MDQzOFx1MDQ0Nlx1MDQzOFx1MDQzMFx1MDQzQlx1MDQzOFx1MDQzN1x1MDQzOFx1MDQ0MFx1MDQ0M1x1MDQzNVx1MDQzQyBSZWFjdCBcdTA0M0FcdTA0M0VcdTA0M0NcdTA0M0ZcdTA0M0VcdTA0M0RcdTA0MzVcdTA0M0RcdTA0NDJcclxuXHRcdHRoaXMuZ3JhcGhQYW5lbCA9IG5ldyBHcmFwaFBhbmVsKHRoaXMucmVhY3RDb250YWluZXIsIHtcclxuXHRcdFx0Z3JhcGhEYXRhOiB0aGlzLmdyYXBoRGF0YSxcclxuXHRcdFx0b25Ob2RlQ2xpY2s6IHRoaXMuaGFuZGxlTm9kZUNsaWNrLmJpbmQodGhpcylcclxuXHRcdH0pO1xyXG5cclxuXHRcdC8vIFx1MDQxRlx1MDQzRVx1MDQzNFx1MDQzRlx1MDQzOFx1MDQ0MVx1MDQ0Qlx1MDQzMlx1MDQzMFx1MDQzNVx1MDQzQ1x1MDQ0MVx1MDQ0RiBcdTA0M0RcdTA0MzAgXHUwNDQxXHUwNDNFXHUwNDMxXHUwNDRCXHUwNDQyXHUwNDM4XHUwNDRGXHJcblx0XHR0aGlzLnBsdWdpbi5ldmVudEJ1cy5vbignZ3JhcGgtdXBkYXRlZCcsIHRoaXMuaGFuZGxlR3JhcGhVcGRhdGUuYmluZCh0aGlzKSk7XHJcblx0XHR0aGlzLnBsdWdpbi5ldmVudEJ1cy5vbignbmV3LWNoYXQnLCB0aGlzLmhhbmRsZU5ld0NoYXQuYmluZCh0aGlzKSk7XHJcblx0fVxyXG5cclxuXHRhc3luYyBvbkNsb3NlKCk6IFByb21pc2U8dm9pZD4ge1xyXG5cdFx0aWYgKHRoaXMuZ3JhcGhQYW5lbCkge1xyXG5cdFx0XHR0aGlzLmdyYXBoUGFuZWwuZGVzdHJveSgpO1xyXG5cdFx0XHR0aGlzLmdyYXBoUGFuZWwgPSBudWxsO1xyXG5cdFx0fVxyXG5cdFx0dGhpcy5wbHVnaW4uZXZlbnRCdXMub2ZmKCdncmFwaC11cGRhdGVkJywgdGhpcy5oYW5kbGVHcmFwaFVwZGF0ZSk7XHJcblx0XHR0aGlzLnBsdWdpbi5ldmVudEJ1cy5vZmYoJ25ldy1jaGF0JywgdGhpcy5oYW5kbGVOZXdDaGF0KTtcclxuXHR9XHJcblxyXG5cdC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIEV2ZW50IEhhbmRsZXJzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG5cclxuXHRoYW5kbGVOb2RlQ2xpY2sobm9kZUlkOiBzdHJpbmcpOiB2b2lkIHtcclxuXHRcdHRoaXMuY3VycmVudE5vZGUgPSBub2RlSWQ7XHJcblx0XHR0aGlzLnBsdWdpbi5ldmVudEJ1cy5lbWl0KCdub2RlLXNlbGVjdGVkJywgeyBub2RlSWQgfSk7XHJcblx0fVxyXG5cclxuXHRoYW5kbGVHcmFwaFVwZGF0ZShkYXRhOiB7IGdyYXBoRGF0YTogeyBub2RlczogYW55W107IGVkZ2VzOiBhbnlbXSB9OyBjdXJyZW50Tm9kZTogc3RyaW5nIHwgbnVsbCB9KTogdm9pZCB7XHJcblx0XHR0aGlzLmdyYXBoRGF0YSA9IGRhdGEuZ3JhcGhEYXRhO1xyXG5cdFx0dGhpcy5jdXJyZW50Tm9kZSA9IGRhdGEuY3VycmVudE5vZGU7XHJcblx0XHRpZiAodGhpcy5ncmFwaFBhbmVsKSB7XHJcblx0XHRcdHRoaXMuZ3JhcGhQYW5lbC51cGRhdGVEYXRhKHRoaXMuZ3JhcGhEYXRhKTtcclxuXHRcdH1cclxuXHR9XHJcblxyXG5cdGhhbmRsZU5ld0NoYXQoKTogdm9pZCB7XHJcblx0XHR0aGlzLmdyYXBoRGF0YSA9IHsgbm9kZXM6IFtdLCBlZGdlczogW10gfTtcclxuXHRcdHRoaXMuY3VycmVudE5vZGUgPSBudWxsO1xyXG5cdFx0aWYgKHRoaXMuZ3JhcGhQYW5lbCkge1xyXG5cdFx0XHR0aGlzLmdyYXBoUGFuZWwudXBkYXRlRGF0YSh0aGlzLmdyYXBoRGF0YSk7XHJcblx0XHR9XHJcblx0fVxyXG59XHJcbiIsICJleHBvcnQgY2xhc3MgR3JhcGhQYW5lbCB7XHJcblx0Y29udGFpbmVyOiBIVE1MRWxlbWVudDtcclxuXHRwcm9wczoge1xyXG5cdFx0Z3JhcGhEYXRhOiB7IG5vZGVzOiBhbnlbXTsgZWRnZXM6IGFueVtdIH07XHJcblx0XHRvbk5vZGVDbGljazogKG5vZGVJZDogc3RyaW5nKSA9PiB2b2lkO1xyXG5cdH07XHJcblx0bm9kZXM6IGFueVtdO1xyXG5cdGVkZ2VzOiBhbnlbXTtcclxuXHJcblx0Y29uc3RydWN0b3IoXHJcblx0XHRjb250YWluZXI6IEhUTUxFbGVtZW50LFxyXG5cdFx0cHJvcHM6IHtcclxuXHRcdFx0Z3JhcGhEYXRhOiB7IG5vZGVzOiBhbnlbXTsgZWRnZXM6IGFueVtdIH07XHJcblx0XHRcdG9uTm9kZUNsaWNrOiAobm9kZUlkOiBzdHJpbmcpID0+IHZvaWQ7XHJcblx0XHR9XHJcblx0KSB7XHJcblx0XHR0aGlzLmNvbnRhaW5lciA9IGNvbnRhaW5lcjtcclxuXHRcdHRoaXMucHJvcHMgPSBwcm9wcztcclxuXHRcdHRoaXMubm9kZXMgPSBbXTtcclxuXHRcdHRoaXMuZWRnZXMgPSBbXTtcclxuXHJcblx0XHR0aGlzLmluaXQoKTtcclxuXHR9XHJcblxyXG5cdGluaXQoKTogdm9pZCB7XHJcblx0XHQvLyBcdTA0MjFcdTA0M0VcdTA0MzdcdTA0MzRcdTA0MzBcdTA0MzVcdTA0M0MgXHUwNDQxXHUwNDQyXHUwNDQwXHUwNDQzXHUwNDNBXHUwNDQyXHUwNDQzXHUwNDQwXHUwNDQzIFx1MDQzNFx1MDQzQlx1MDQ0RiBSZWFjdCBGbG93XHJcblx0XHR0aGlzLmNvbnRhaW5lci5pbm5lckhUTUwgPSBgXHJcbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJsbG0tYWdlbnQtZ3JhcGgtcGFuZWxcIj5cclxuICAgICAgICAgICAgICAgIDxkaXYgaWQ9XCJyZWFjdGZsb3ctY29udGFpbmVyXCIgc3R5bGU9XCJ3aWR0aDogMTAwJTsgaGVpZ2h0OiAxMDAlO1wiPjwvZGl2PlxyXG4gICAgICAgICAgICA8L2Rpdj5cclxuICAgICAgICBgO1xyXG5cclxuXHRcdC8vIFx1MDQxN1x1MDQzMFx1MDQzM1x1MDQ0MFx1MDQ0M1x1MDQzNlx1MDQzMFx1MDQzNVx1MDQzQyBSZWFjdCBGbG93IFx1MDQzNFx1MDQzOFx1MDQzRFx1MDQzMFx1MDQzQ1x1MDQzOFx1MDQ0N1x1MDQzNVx1MDQ0MVx1MDQzQVx1MDQzOCAoXHUwNDM1XHUwNDQxXHUwNDNCXHUwNDM4IFx1MDQzNFx1MDQzRVx1MDQ0MVx1MDQ0Mlx1MDQ0M1x1MDQzRlx1MDQzNVx1MDQzRClcclxuXHRcdHRoaXMuaW5pdFJlYWN0RmxvdygpO1xyXG5cdH1cclxuXHJcblx0YXN5bmMgaW5pdFJlYWN0RmxvdygpOiBQcm9taXNlPHZvaWQ+IHtcclxuXHRcdHRyeSB7XHJcblx0XHRcdC8vIFx1MDQxMiBcdTA0NDFcdTA0NDBcdTA0MzVcdTA0MzRcdTA0MzUgT2JzaWRpYW4gXHUwNDNEXHUwNDQzXHUwNDM2XHUwNDNEXHUwNDNFIFx1MDQzMVx1MDQ0M1x1MDQzNFx1MDQzNVx1MDQ0MiBcdTA0M0ZcdTA0M0VcdTA0MzRcdTA0M0FcdTA0M0JcdTA0NEVcdTA0NDdcdTA0MzhcdTA0NDJcdTA0NEMgUmVhY3QgRmxvdyBcdTA0NDdcdTA0MzVcdTA0NDBcdTA0MzVcdTA0MzcgQ0ROIFx1MDQzOFx1MDQzQlx1MDQzOCBidW5kbGVyXHJcblx0XHRcdC8vIFx1MDQxNFx1MDQzQlx1MDQ0RiBcdTA0MzRcdTA0MzVcdTA0M0NcdTA0M0VcdTA0M0RcdTA0NDFcdTA0NDJcdTA0NDBcdTA0MzBcdTA0NDZcdTA0MzhcdTA0MzggXHUwNDQxXHUwNDNFXHUwNDM3XHUwNDM0XHUwNDMwXHUwNDM1XHUwNDNDIFx1MDQ0M1x1MDQzRlx1MDQ0MFx1MDQzRVx1MDQ0OVx1MDQzNVx1MDQzRFx1MDQzRFx1MDQ0M1x1MDQ0RSBcdTA0MzJcdTA0MzhcdTA0MzdcdTA0NDNcdTA0MzBcdTA0M0JcdTA0MzhcdTA0MzdcdTA0MzBcdTA0NDZcdTA0MzhcdTA0NEVcclxuXHRcdFx0dGhpcy5jcmVhdGVTaW1wbGVHcmFwaCgpO1xyXG5cdFx0fSBjYXRjaCAoZXJyb3IpIHtcclxuXHRcdFx0Y29uc29sZS5lcnJvcignXHUwNDFFXHUwNDQ4XHUwNDM4XHUwNDMxXHUwNDNBXHUwNDMwIFx1MDQzOFx1MDQzRFx1MDQzOFx1MDQ0Nlx1MDQzOFx1MDQzMFx1MDQzQlx1MDQzOFx1MDQzN1x1MDQzMFx1MDQ0Nlx1MDQzOFx1MDQzOCBSZWFjdCBGbG93OicsIGVycm9yKTtcclxuXHRcdFx0dGhpcy5jcmVhdGVGYWxsYmFja0dyYXBoKCk7XHJcblx0XHR9XHJcblx0fVxyXG5cclxuXHRjcmVhdGVTaW1wbGVHcmFwaCgpOiB2b2lkIHtcclxuXHRcdGNvbnN0IGNvbnRhaW5lciA9IHRoaXMuY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3IoJyNyZWFjdGZsb3ctY29udGFpbmVyJykgYXMgSFRNTEVsZW1lbnQ7XHJcblx0XHRjb250YWluZXIuaW5uZXJIVE1MID0gYFxyXG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwic2ltcGxlLWdyYXBoXCI+XHJcbiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwiZ3JhcGgtbm9kZXNcIiBpZD1cImdyYXBoLW5vZGVzXCI+PC9kaXY+XHJcbiAgICAgICAgICAgICAgICA8c3ZnIGNsYXNzPVwiZ3JhcGgtZWRnZXNcIiBpZD1cImdyYXBoLWVkZ2VzXCI+PC9zdmc+XHJcbiAgICAgICAgICAgIDwvZGl2PlxyXG4gICAgICAgIGA7XHJcblxyXG5cdFx0dGhpcy5yZW5kZXJOb2RlcygpO1xyXG5cdFx0dGhpcy5yZW5kZXJFZGdlcygpO1xyXG5cdH1cclxuXHJcblx0cmVuZGVyTm9kZXMoKTogdm9pZCB7XHJcblx0XHRjb25zdCBub2Rlc0NvbnRhaW5lciA9IHRoaXMuY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3IoJyNncmFwaC1ub2RlcycpIGFzIEhUTUxFbGVtZW50O1xyXG5cdFx0bm9kZXNDb250YWluZXIuaW5uZXJIVE1MID0gJyc7XHJcblxyXG5cdFx0dGhpcy5ub2Rlcy5mb3JFYWNoKChub2RlLCBpbmRleCkgPT4ge1xyXG5cdFx0XHRjb25zdCBub2RlRWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcclxuXHRcdFx0bm9kZUVsLmNsYXNzTmFtZSA9IGBncmFwaC1ub2RlIG5vZGUtJHtub2RlLnR5cGUgfHwgJ2RlZmF1bHQnfWA7XHJcblx0XHRcdG5vZGVFbC5zdHlsZS5jc3NUZXh0ID0gYFxyXG4gICAgICAgICAgICAgICAgcG9zaXRpb246IGFic29sdXRlO1xyXG4gICAgICAgICAgICAgICAgbGVmdDogJHs1MCArIGluZGV4ICogMTUwfXB4O1xyXG4gICAgICAgICAgICAgICAgdG9wOiAkezUwICsgTWF0aC5mbG9vcihpbmRleCAvIDUpICogODB9cHg7XHJcbiAgICAgICAgICAgICAgICBwYWRkaW5nOiA4cHggMTJweDtcclxuICAgICAgICAgICAgICAgIGJvcmRlcjogMnB4IHNvbGlkICMzMzM7XHJcbiAgICAgICAgICAgICAgICBib3JkZXItcmFkaXVzOiA0cHg7XHJcbiAgICAgICAgICAgICAgICBiYWNrZ3JvdW5kOiAjZmZmO1xyXG4gICAgICAgICAgICAgICAgY3Vyc29yOiBwb2ludGVyO1xyXG4gICAgICAgICAgICAgICAgbWF4LXdpZHRoOiAxMjBweDtcclxuICAgICAgICAgICAgICAgIHdvcmQtd3JhcDogYnJlYWstd29yZDtcclxuICAgICAgICAgICAgYDtcclxuXHRcdFx0bm9kZUVsLnRleHRDb250ZW50ID0gbm9kZS5kYXRhPy5sYWJlbCB8fCBgTm9kZSAke25vZGUuaWR9YDtcclxuXHRcdFx0bm9kZUVsLmRhdGFzZXQubm9kZUlkID0gbm9kZS5pZDtcclxuXHJcblx0XHRcdG5vZGVFbC5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsICgpID0+IHtcclxuXHRcdFx0XHR0aGlzLmhhbmRsZU5vZGVDbGljayhub2RlLmlkKTtcclxuXHRcdFx0fSk7XHJcblxyXG5cdFx0XHRub2Rlc0NvbnRhaW5lci5hcHBlbmRDaGlsZChub2RlRWwpO1xyXG5cdFx0fSk7XHJcblx0fVxyXG5cclxuXHRyZW5kZXJFZGdlcygpOiB2b2lkIHtcclxuXHRcdGNvbnN0IGVkZ2VzQ29udGFpbmVyID0gdGhpcy5jb250YWluZXIucXVlcnlTZWxlY3RvcignI2dyYXBoLWVkZ2VzJykgYXMgSFRNTEVsZW1lbnQ7XHJcblx0XHRlZGdlc0NvbnRhaW5lci5pbm5lckhUTUwgPSAnJztcclxuXHRcdC8vIFx1MDQyM1x1MDQzRlx1MDQ0MFx1MDQzRVx1MDQ0OVx1MDQzNVx1MDQzRFx1MDQzRFx1MDQzMFx1MDQ0RiBcdTA0M0VcdTA0NDJcdTA0NDBcdTA0MzhcdTA0NDFcdTA0M0VcdTA0MzJcdTA0M0FcdTA0MzAgXHUwNDQxXHUwNDMyXHUwNDRGXHUwNDM3XHUwNDM1XHUwNDM5IC0gXHUwNDMyIFx1MDQ0MFx1MDQzNVx1MDQzMFx1MDQzQlx1MDQ0Q1x1MDQzRFx1MDQzRVx1MDQzOSBcdTA0NDBcdTA0MzVcdTA0MzBcdTA0M0JcdTA0MzhcdTA0MzdcdTA0MzBcdTA0NDZcdTA0MzhcdTA0MzggXHUwNDNEXHUwNDQzXHUwNDM2XHUwNDNEXHUwNDNFIFx1MDQzMVx1MDQ0M1x1MDQzNFx1MDQzNVx1MDQ0MiBcdTA0MzJcdTA0NEJcdTA0NDdcdTA0MzhcdTA0NDFcdTA0M0JcdTA0NEZcdTA0NDJcdTA0NEMgXHUwNDNGXHUwNDNFXHUwNDM3XHUwNDM4XHUwNDQ2XHUwNDM4XHUwNDM4XHJcblx0fVxyXG5cclxuXHRjcmVhdGVGYWxsYmFja0dyYXBoKCk6IHZvaWQge1xyXG5cdFx0Y29uc3QgY29udGFpbmVyID0gdGhpcy5jb250YWluZXIucXVlcnlTZWxlY3RvcignI3JlYWN0Zmxvdy1jb250YWluZXInKSBhcyBIVE1MRWxlbWVudDtcclxuXHRcdGNvbnRhaW5lci5pbm5lckhUTUwgPSBgXHJcbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJmYWxsYmFjay1ncmFwaFwiPlxyXG4gICAgICAgICAgICAgICAgPHA+XHUwNDIzXHUwNDNGXHUwNDQwXHUwNDNFXHUwNDQ5XHUwNDM1XHUwNDNEXHUwNDNEXHUwNDNFXHUwNDM1IFx1MDQzRlx1MDQ0MFx1MDQzNVx1MDQzNFx1MDQ0MVx1MDQ0Mlx1MDQzMFx1MDQzMlx1MDQzQlx1MDQzNVx1MDQzRFx1MDQzOFx1MDQzNSBcdTA0MzNcdTA0NDBcdTA0MzBcdTA0NDRcdTA0MzA8L3A+XHJcbiAgICAgICAgICAgICAgICA8ZGl2IGlkPVwibm9kZXMtbGlzdFwiPjwvZGl2PlxyXG4gICAgICAgICAgICA8L2Rpdj5cclxuICAgICAgICBgO1xyXG5cdFx0dGhpcy5yZW5kZXJGYWxsYmFja05vZGVzKCk7XHJcblx0fVxyXG5cclxuXHRyZW5kZXJGYWxsYmFja05vZGVzKCk6IHZvaWQge1xyXG5cdFx0Y29uc3Qgbm9kZXNMaXN0ID0gdGhpcy5jb250YWluZXIucXVlcnlTZWxlY3RvcignI25vZGVzLWxpc3QnKSBhcyBIVE1MRWxlbWVudDtcclxuXHRcdG5vZGVzTGlzdC5pbm5lckhUTUwgPSAnJztcclxuXHJcblx0XHR0aGlzLm5vZGVzLmZvckVhY2goKG5vZGUpID0+IHtcclxuXHRcdFx0Y29uc3Qgbm9kZUVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnZGl2Jyk7XHJcblx0XHRcdG5vZGVFbC5jbGFzc05hbWUgPSAnZmFsbGJhY2stbm9kZSc7XHJcblx0XHRcdG5vZGVFbC50ZXh0Q29udGVudCA9IGAke25vZGUuaWR9OiAke25vZGUuZGF0YT8ubGFiZWwgfHwgJ05vIGxhYmVsJ31gO1xyXG5cdFx0XHRub2RlRWwuYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCAoKSA9PiB0aGlzLmhhbmRsZU5vZGVDbGljayhub2RlLmlkKSk7XHJcblx0XHRcdG5vZGVzTGlzdC5hcHBlbmRDaGlsZChub2RlRWwpO1xyXG5cdFx0fSk7XHJcblx0fVxyXG5cclxuXHR1cGRhdGVEYXRhKGdyYXBoRGF0YTogeyBub2RlczogYW55W107IGVkZ2VzOiBhbnlbXSB9KTogdm9pZCB7XHJcblx0XHR0aGlzLm5vZGVzID0gZ3JhcGhEYXRhLm5vZGVzIHx8IFtdO1xyXG5cdFx0dGhpcy5lZGdlcyA9IGdyYXBoRGF0YS5lZGdlcyB8fCBbXTtcclxuXHRcdHRoaXMucmVuZGVyTm9kZXMoKTtcclxuXHRcdHRoaXMucmVuZGVyRWRnZXMoKTtcclxuXHR9XHJcblxyXG5cdGhhbmRsZU5vZGVDbGljayhub2RlSWQ6IHN0cmluZyk6IHZvaWQge1xyXG5cdFx0aWYgKHRoaXMucHJvcHMub25Ob2RlQ2xpY2spIHtcclxuXHRcdFx0dGhpcy5wcm9wcy5vbk5vZGVDbGljayhub2RlSWQpO1xyXG5cdFx0fVxyXG5cdH1cclxuXHJcblx0ZGVzdHJveSgpOiB2b2lkIHtcclxuXHRcdC8vIFx1MDQxRVx1MDQ0N1x1MDQzOFx1MDQ0MVx1MDQ0Mlx1MDQzQVx1MDQzMCBcdTA0NDBcdTA0MzVcdTA0NDFcdTA0NDNcdTA0NDBcdTA0NDFcdTA0M0VcdTA0MzJcclxuXHRcdHRoaXMuY29udGFpbmVyLmlubmVySFRNTCA9ICcnO1xyXG5cdH1cclxufVxyXG4iLCAiaW1wb3J0IHsgSXRlbVZpZXcsIFdvcmtzcGFjZUxlYWYgfSBmcm9tICdvYnNpZGlhbic7XHJcbmltcG9ydCB7IENoYXRQYW5lbCB9IGZyb20gJy4uL2NvbXBvbmVudHMvQ2hhdFBhbmVsJztcclxuaW1wb3J0IHsgSGlzdG9yeVBhbmVsIH0gZnJvbSAnLi4vY29tcG9uZW50cy9IaXN0b3J5UGFuZWwnO1xyXG5pbXBvcnQgTExNQWdlbnRQbHVnaW4gZnJvbSAnbWFpbic7XHJcblxyXG5leHBvcnQgY29uc3QgQ0hBVF9WSUVXX1RZUEUgPSAnbGxtLWFnZW50LWNoYXQtdmlldyc7XHJcblxyXG4vLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSBDaGF0IFZpZXcgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXHJcblxyXG5leHBvcnQgY2xhc3MgQ2hhdFZpZXcgZXh0ZW5kcyBJdGVtVmlldyB7XHJcblx0cGx1Z2luOiBMTE1BZ2VudFBsdWdpbjtcclxuXHRjaGF0SGlzdG9yeTogYW55W107XHJcblx0Z3JhcGhzTGlzdDogYW55W107XHJcblx0c2VsZWN0ZWRHcmFwaElkOiBzdHJpbmcgfCBudWxsO1xyXG5cdGN1cnJlbnROb2RlOiBzdHJpbmcgfCBudWxsO1xyXG5cdGNoYXRDb250YWluZXI6IEhUTUxEaXZFbGVtZW50O1xyXG5cdGhpc3RvcnlDb250YWluZXI6IEhUTUxEaXZFbGVtZW50O1xyXG5cdGNoYXRQYW5lbDogQ2hhdFBhbmVsIHwgbnVsbDtcclxuXHRoaXN0b3J5UGFuZWw6IEhpc3RvcnlQYW5lbCB8IG51bGw7XHJcblxyXG5cdGNvbnN0cnVjdG9yKGxlYWY6IFdvcmtzcGFjZUxlYWYsIHBsdWdpbjogTExNQWdlbnRQbHVnaW4pIHtcclxuXHRcdHN1cGVyKGxlYWYpO1xyXG5cdFx0dGhpcy5wbHVnaW4gPSBwbHVnaW47XHJcblx0XHR0aGlzLmNoYXRIaXN0b3J5ID0gW107XHJcblx0XHR0aGlzLmdyYXBoc0xpc3QgPSBbXTtcclxuXHRcdHRoaXMuc2VsZWN0ZWRHcmFwaElkID0gbnVsbDtcclxuXHRcdHRoaXMuY3VycmVudE5vZGUgPSBudWxsO1xyXG5cdFx0dGhpcy5jaGF0UGFuZWwgPSBudWxsO1xyXG5cdFx0dGhpcy5oaXN0b3J5UGFuZWwgPSBudWxsO1xyXG5cdH1cclxuXHJcblx0Z2V0Vmlld1R5cGUoKTogc3RyaW5nIHtcclxuXHRcdHJldHVybiBDSEFUX1ZJRVdfVFlQRTtcclxuXHR9XHJcblxyXG5cdGdldERpc3BsYXlUZXh0KCk6IHN0cmluZyB7XHJcblx0XHRyZXR1cm4gJ0xMTSBBZ2VudCBDaGF0JztcclxuXHR9XHJcblxyXG5cdGdldEljb24oKTogc3RyaW5nIHtcclxuXHRcdHJldHVybiAnbWVzc2FnZS1jaXJjbGUnO1xyXG5cdH1cclxuXHJcblx0YXN5bmMgb25PcGVuKCk6IFByb21pc2U8dm9pZD4ge1xyXG5cdFx0Y29uc3QgY29udGFpbmVyID0gdGhpcy5jb250YWluZXJFbC5jaGlsZHJlblsxXSBhcyBIVE1MRWxlbWVudDtcclxuXHRcdGNvbnRhaW5lci5lbXB0eSgpO1xyXG5cclxuXHRcdC8vIFx1MDQyMVx1MDQzRVx1MDQzN1x1MDQzNFx1MDQzMFx1MDQzNVx1MDQzQyBcdTA0M0FcdTA0M0VcdTA0M0RcdTA0NDJcdTA0MzVcdTA0MzlcdTA0M0RcdTA0MzVcdTA0NDBcdTA0NEIgXHUwNDM0XHUwNDNCXHUwNDRGIFJlYWN0IFx1MDQzQVx1MDQzRVx1MDQzQ1x1MDQzRlx1MDQzRVx1MDQzRFx1MDQzNVx1MDQzRFx1MDQ0Mlx1MDQzRVx1MDQzMlxyXG5cdFx0dGhpcy5jaGF0Q29udGFpbmVyID0gY29udGFpbmVyLmNyZWF0ZURpdigpIGFzIEhUTUxEaXZFbGVtZW50O1xyXG5cdFx0dGhpcy5jaGF0Q29udGFpbmVyLmFkZENsYXNzKCdsbG0tYWdlbnQtY2hhdC1jb250YWluZXInKTtcclxuXHJcblx0XHR0aGlzLmhpc3RvcnlDb250YWluZXIgPSBjb250YWluZXIuY3JlYXRlRGl2KCkgYXMgSFRNTERpdkVsZW1lbnQ7XHJcblx0XHR0aGlzLmhpc3RvcnlDb250YWluZXIuYWRkQ2xhc3MoJ2xsbS1hZ2VudC1oaXN0b3J5LWNvbnRhaW5lcicpO1xyXG5cclxuXHRcdC8vIFx1MDQxOFx1MDQzRFx1MDQzOFx1MDQ0Nlx1MDQzOFx1MDQzMFx1MDQzQlx1MDQzOFx1MDQzN1x1MDQzOFx1MDQ0MFx1MDQ0M1x1MDQzNVx1MDQzQyBSZWFjdCBcdTA0M0FcdTA0M0VcdTA0M0NcdTA0M0ZcdTA0M0VcdTA0M0RcdTA0MzVcdTA0M0RcdTA0NDJcdTA0NEJcclxuXHRcdHRoaXMuY2hhdFBhbmVsID0gbmV3IENoYXRQYW5lbCh0aGlzLmNoYXRDb250YWluZXIsIHtcclxuXHRcdFx0Y2hhdEhpc3Rvcnk6IHRoaXMuY2hhdEhpc3RvcnksXHJcblx0XHRcdG9uU2VuZE1lc3NhZ2U6IHRoaXMuaGFuZGxlU2VuZE1lc3NhZ2UuYmluZCh0aGlzKVxyXG5cdFx0fSk7XHJcblxyXG5cdFx0dGhpcy5oaXN0b3J5UGFuZWwgPSBuZXcgSGlzdG9yeVBhbmVsKHRoaXMuaGlzdG9yeUNvbnRhaW5lciwge1xyXG5cdFx0XHRncmFwaHM6IHRoaXMuZ3JhcGhzTGlzdCxcclxuXHRcdFx0b25HcmFwaFNlbGVjdDogdGhpcy5oYW5kbGVHcmFwaFNlbGVjdC5iaW5kKHRoaXMpLFxyXG5cdFx0XHRvbk5ld0NoYXQ6IHRoaXMuaGFuZGxlTmV3Q2hhdC5iaW5kKHRoaXMpXHJcblx0XHR9KTtcclxuXHJcblx0XHQvLyBcdTA0MUZcdTA0M0VcdTA0MzRcdTA0M0ZcdTA0MzhcdTA0NDFcdTA0NEJcdTA0MzJcdTA0MzBcdTA0MzVcdTA0M0NcdTA0NDFcdTA0NEYgXHUwNDNEXHUwNDMwIFx1MDQ0MVx1MDQzRVx1MDQzMVx1MDQ0Qlx1MDQ0Mlx1MDQzOFx1MDQ0RlxyXG5cdFx0dGhpcy5wbHVnaW4uZXZlbnRCdXMub24oJ25vZGUtc2VsZWN0ZWQnLCB0aGlzLmhhbmRsZU5vZGVTZWxlY3RlZC5iaW5kKHRoaXMpKTtcclxuXHRcdHRoaXMucGx1Z2luLmV2ZW50QnVzLm9uKCduZXctY2hhdCcsIHRoaXMuaGFuZGxlTmV3Q2hhdC5iaW5kKHRoaXMpKTtcclxuXHJcblx0XHQvLyBcdTA0MTdcdTA0MzBcdTA0MzNcdTA0NDBcdTA0NDNcdTA0MzZcdTA0MzBcdTA0MzVcdTA0M0MgXHUwNDQxXHUwNDNGXHUwNDM4XHUwNDQxXHUwNDNFXHUwNDNBIFx1MDQzM1x1MDQ0MFx1MDQzMFx1MDQ0NFx1MDQzRVx1MDQzMlxyXG5cdFx0dGhpcy5mZXRjaEdyYXBoc0xpc3QoKTtcclxuXHR9XHJcblxyXG5cdGFzeW5jIG9uQ2xvc2UoKTogUHJvbWlzZTx2b2lkPiB7XHJcblx0XHRpZiAodGhpcy5jaGF0UGFuZWwpIHtcclxuXHRcdFx0dGhpcy5jaGF0UGFuZWwuZGVzdHJveSgpO1xyXG5cdFx0XHR0aGlzLmNoYXRQYW5lbCA9IG51bGw7XHJcblx0XHR9XHJcblx0XHRpZiAodGhpcy5oaXN0b3J5UGFuZWwpIHtcclxuXHRcdFx0dGhpcy5oaXN0b3J5UGFuZWwuZGVzdHJveSgpO1xyXG5cdFx0XHR0aGlzLmhpc3RvcnlQYW5lbCA9IG51bGw7XHJcblx0XHR9XHJcblx0XHR0aGlzLnBsdWdpbi5ldmVudEJ1cy5vZmYoJ25vZGUtc2VsZWN0ZWQnLCB0aGlzLmhhbmRsZU5vZGVTZWxlY3RlZCk7XHJcblx0XHR0aGlzLnBsdWdpbi5ldmVudEJ1cy5vZmYoJ25ldy1jaGF0JywgdGhpcy5oYW5kbGVOZXdDaGF0KTtcclxuXHR9XHJcblxyXG5cdC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIEFQSSBNZXRob2RzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxyXG5cclxuXHRhc3luYyBmZXRjaEdyYXBoc0xpc3QoKTogUHJvbWlzZTx2b2lkPiB7XHJcblx0XHR0cnkge1xyXG5cdFx0XHRjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKGAke3RoaXMucGx1Z2luLnNldHRpbmdzLmFwaUJhc2VVcmx9L2dyYXBoc2ApO1xyXG5cdFx0XHRjb25zdCBkYXRhID0gYXdhaXQgcmVzcG9uc2UuanNvbigpO1xyXG5cdFx0XHR0aGlzLmdyYXBoc0xpc3QgPSBkYXRhO1xyXG5cdFx0XHRpZiAodGhpcy5oaXN0b3J5UGFuZWwpIHtcclxuXHRcdFx0XHR0aGlzLmhpc3RvcnlQYW5lbC51cGRhdGVHcmFwaHModGhpcy5ncmFwaHNMaXN0KTtcclxuXHRcdFx0fVxyXG5cdFx0fSBjYXRjaCAoZXJyb3IpIHtcclxuXHRcdFx0Y29uc29sZS5lcnJvcignXHUwNDFFXHUwNDQ4XHUwNDM4XHUwNDMxXHUwNDNBXHUwNDMwIFx1MDQzN1x1MDQzMFx1MDQzM1x1MDQ0MFx1MDQ0M1x1MDQzN1x1MDQzQVx1MDQzOCBcdTA0NDFcdTA0M0ZcdTA0MzhcdTA0NDFcdTA0M0FcdTA0MzAgXHUwNDMzXHUwNDQwXHUwNDMwXHUwNDQ0XHUwNDNFXHUwNDMyOicsIGVycm9yKTtcclxuXHRcdH1cclxuXHR9XHJcblxyXG5cdGFzeW5jIGZldGNoR3JhcGhEYXRhKGdyYXBoSWQ6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xyXG5cdFx0dHJ5IHtcclxuXHRcdFx0Y29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaChgJHt0aGlzLnBsdWdpbi5zZXR0aW5ncy5hcGlCYXNlVXJsfS9ncmFwaHMvJHtncmFwaElkfWApO1xyXG5cdFx0XHRpZiAoIXJlc3BvbnNlLm9rKSB7XHJcblx0XHRcdFx0aWYgKHJlc3BvbnNlLnN0YXR1cyA9PT0gNDA0KSB7XHJcblx0XHRcdFx0XHRjb25zb2xlLmVycm9yKGBcdTA0MTNcdTA0NDBcdTA0MzBcdTA0NDQgXHUwNDQxIElEICR7Z3JhcGhJZH0gXHUwNDNEXHUwNDM1IFx1MDQzRFx1MDQzMFx1MDQzOVx1MDQzNFx1MDQzNVx1MDQzRC5gKTtcclxuXHRcdFx0XHR9XHJcblx0XHRcdFx0dGhyb3cgbmV3IEVycm9yKGBIVFRQIFx1MDQzRVx1MDQ0OFx1MDQzOFx1MDQzMVx1MDQzQVx1MDQzMCEgXHUwNDQxXHUwNDQyXHUwNDMwXHUwNDQyXHUwNDQzXHUwNDQxOiAke3Jlc3BvbnNlLnN0YXR1c31gKTtcclxuXHRcdFx0fVxyXG5cdFx0XHRjb25zdCBkYXRhID0gYXdhaXQgcmVzcG9uc2UuanNvbigpO1xyXG5cclxuXHRcdFx0Ly8gXHUwNDFFXHUwNDMxXHUwNDNEXHUwNDNFXHUwNDMyXHUwNDNCXHUwNDRGXHUwNDM1XHUwNDNDIFx1MDQzM1x1MDQ0MFx1MDQzMFx1MDQ0NFxyXG5cdFx0XHR0aGlzLnBsdWdpbi5ldmVudEJ1cy5lbWl0KCdncmFwaC11cGRhdGVkJywge1xyXG5cdFx0XHRcdGdyYXBoRGF0YToge1xyXG5cdFx0XHRcdFx0bm9kZXM6IGRhdGEuZ3JhcGhfbm9kZXMgfHwgW10sXHJcblx0XHRcdFx0XHRlZGdlczogZGF0YS5ncmFwaF9lZGdlcyB8fCBbXVxyXG5cdFx0XHRcdH0sXHJcblx0XHRcdFx0Y3VycmVudE5vZGU6IGRhdGEuY3VycmVudF9ub2RlX2lkXHJcblx0XHRcdH0pO1xyXG5cclxuXHRcdFx0Ly8gXHUwNDFFXHUwNDMxXHUwNDNEXHUwNDNFXHUwNDMyXHUwNDNCXHUwNDRGXHUwNDM1XHUwNDNDIFx1MDQzOFx1MDQ0MVx1MDQ0Mlx1MDQzRVx1MDQ0MFx1MDQzOFx1MDQ0RSBcdTA0NDdcdTA0MzBcdTA0NDJcdTA0MzBcclxuXHRcdFx0dGhpcy5jaGF0SGlzdG9yeSA9IGRhdGEubWVzc2FnZXMgfHwgW107XHJcblx0XHRcdGlmICh0aGlzLmNoYXRQYW5lbCkge1xyXG5cdFx0XHRcdHRoaXMuY2hhdFBhbmVsLnVwZGF0ZUhpc3RvcnkodGhpcy5jaGF0SGlzdG9yeSk7XHJcblx0XHRcdH1cclxuXHRcdH0gY2F0Y2ggKGVycm9yKSB7XHJcblx0XHRcdGNvbnNvbGUuZXJyb3IoJ1x1MDQxRVx1MDQ0OFx1MDQzOFx1MDQzMVx1MDQzQVx1MDQzMCBcdTA0MzdcdTA0MzBcdTA0MzNcdTA0NDBcdTA0NDNcdTA0MzdcdTA0M0FcdTA0MzggXHUwNDM0XHUwNDMwXHUwNDNEXHUwNDNEXHUwNDRCXHUwNDQ1IFx1MDQzM1x1MDQ0MFx1MDQzMFx1MDQ0NFx1MDQzMDonLCBlcnJvcik7XHJcblx0XHR9XHJcblx0fVxyXG5cclxuXHRhc3luYyBmZXRjaE1lc3NhZ2VzRnJvbVJvb3RUb05vZGUobm9kZUlkOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcclxuXHRcdHRyeSB7XHJcblx0XHRcdGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2goYCR7dGhpcy5wbHVnaW4uc2V0dGluZ3MuYXBpQmFzZVVybH0vbWVzc2FnZXMvJHt0aGlzLnNlbGVjdGVkR3JhcGhJZH0vJHtub2RlSWR9YCk7XHJcblx0XHRcdGNvbnN0IGRhdGEgPSBhd2FpdCByZXNwb25zZS5qc29uKCk7XHJcblx0XHRcdHRoaXMuY2hhdEhpc3RvcnkgPSBkYXRhO1xyXG5cdFx0XHRpZiAodGhpcy5jaGF0UGFuZWwpIHtcclxuXHRcdFx0XHR0aGlzLmNoYXRQYW5lbC51cGRhdGVIaXN0b3J5KHRoaXMuY2hhdEhpc3RvcnkpO1xyXG5cdFx0XHR9XHJcblx0XHR9IGNhdGNoIChlcnJvcikge1xyXG5cdFx0XHRjb25zb2xlLmVycm9yKCdcdTA0MUVcdTA0NDhcdTA0MzhcdTA0MzFcdTA0M0FcdTA0MzAgXHUwNDM3XHUwNDMwXHUwNDMzXHUwNDQwXHUwNDQzXHUwNDM3XHUwNDNBXHUwNDM4IFx1MDQ0MVx1MDQzRVx1MDQzRVx1MDQzMVx1MDQ0OVx1MDQzNVx1MDQzRFx1MDQzOFx1MDQzOTonLCBlcnJvcik7XHJcblx0XHR9XHJcblx0fVxyXG5cclxuXHQvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSBFdmVudCBIYW5kbGVycyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cclxuXHJcblx0YXN5bmMgaGFuZGxlU2VuZE1lc3NhZ2UobWVzc2FnZTogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XHJcblx0XHR0cnkge1xyXG5cdFx0XHRjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKGAke3RoaXMucGx1Z2luLnNldHRpbmdzLmFwaUJhc2VVcmx9L2NoYXRgLCB7XHJcblx0XHRcdFx0bWV0aG9kOiAnUE9TVCcsXHJcblx0XHRcdFx0aGVhZGVyczoge1xyXG5cdFx0XHRcdFx0J0NvbnRlbnQtVHlwZSc6ICdhcHBsaWNhdGlvbi9qc29uJ1xyXG5cdFx0XHRcdH0sXHJcblx0XHRcdFx0Ym9keTogSlNPTi5zdHJpbmdpZnkoe1xyXG5cdFx0XHRcdFx0bWVzc2FnZTogbWVzc2FnZSxcclxuXHRcdFx0XHRcdGdyYXBoX2lkOiB0aGlzLnNlbGVjdGVkR3JhcGhJZCxcclxuXHRcdFx0XHRcdHBhcmVudF9ub2RlX2lkOiB0aGlzLmN1cnJlbnROb2RlXHJcblx0XHRcdFx0fSlcclxuXHRcdFx0fSk7XHJcblxyXG5cdFx0XHRpZiAoIXJlc3BvbnNlLm9rKSB7XHJcblx0XHRcdFx0dGhyb3cgbmV3IEVycm9yKGBIVFRQIFx1MDQzRVx1MDQ0OFx1MDQzOFx1MDQzMVx1MDQzQVx1MDQzMCEgXHUwNDQxXHUwNDQyXHUwNDMwXHUwNDQyXHUwNDQzXHUwNDQxOiAke3Jlc3BvbnNlLnN0YXR1c31gKTtcclxuXHRcdFx0fVxyXG5cclxuXHRcdFx0Y29uc3QgcmVzdWx0ID0gYXdhaXQgcmVzcG9uc2UuanNvbigpO1xyXG5cclxuXHRcdFx0Ly8gXHUwNDFFXHUwNDMxXHUwNDNEXHUwNDNFXHUwNDMyXHUwNDNCXHUwNDRGXHUwNDM1XHUwNDNDIFx1MDQzMlx1MDQ0Qlx1MDQzMVx1MDQ0MFx1MDQzMFx1MDQzRFx1MDQzRFx1MDQ0Qlx1MDQzOSBJRCBcdTA0MzNcdTA0NDBcdTA0MzBcdTA0NDRcdTA0MzBcclxuXHRcdFx0dGhpcy5zZWxlY3RlZEdyYXBoSWQgPSByZXN1bHQuZ3JhcGhfaWQ7XHJcblxyXG5cdFx0XHQvLyBcdTA0MjNcdTA0NDFcdTA0NDJcdTA0MzBcdTA0M0RcdTA0MzBcdTA0MzJcdTA0M0JcdTA0MzhcdTA0MzJcdTA0MzBcdTA0MzVcdTA0M0MgXHUwNDQyXHUwNDM1XHUwNDNBXHUwNDQzXHUwNDQ5XHUwNDM4XHUwNDM5IFx1MDQ0M1x1MDQzN1x1MDQzNVx1MDQzQlxyXG5cdFx0XHRjb25zdCBuZXdDdXJyZW50Tm9kZUlkID0gcmVzdWx0LmdyYXBoX3Zpc3VhbGl6YXRpb25fZGF0YS5jdXJyZW50X25vZGVfaWQ7XHJcblx0XHRcdHRoaXMuY3VycmVudE5vZGUgPSBuZXdDdXJyZW50Tm9kZUlkO1xyXG5cclxuXHRcdFx0Ly8gXHUwNDFFXHUwNDMxXHUwNDNEXHUwNDNFXHUwNDMyXHUwNDNCXHUwNDRGXHUwNDM1XHUwNDNDIFx1MDQzM1x1MDQ0MFx1MDQzMFx1MDQ0NFxyXG5cdFx0XHR0aGlzLnBsdWdpbi5ldmVudEJ1cy5lbWl0KCdncmFwaC11cGRhdGVkJywge1xyXG5cdFx0XHRcdGdyYXBoRGF0YToge1xyXG5cdFx0XHRcdFx0bm9kZXM6IHJlc3VsdC5ncmFwaF92aXN1YWxpemF0aW9uX2RhdGEubm9kZXMgfHwgW10sXHJcblx0XHRcdFx0XHRlZGdlczogcmVzdWx0LmdyYXBoX3Zpc3VhbGl6YXRpb25fZGF0YS5lZGdlcyB8fCBbXVxyXG5cdFx0XHRcdH0sXHJcblx0XHRcdFx0Y3VycmVudE5vZGU6IG5ld0N1cnJlbnROb2RlSWRcclxuXHRcdFx0fSk7XHJcblxyXG5cdFx0XHQvLyBcdTA0MTdcdTA0MzBcdTA0MzNcdTA0NDBcdTA0NDNcdTA0MzZcdTA0MzBcdTA0MzVcdTA0M0MgXHUwNDQxXHUwNDNFXHUwNDNFXHUwNDMxXHUwNDQ5XHUwNDM1XHUwNDNEXHUwNDM4XHUwNDRGIFx1MDQzRVx1MDQ0MiBcdTA0M0FcdTA0M0VcdTA0NDBcdTA0M0RcdTA0NEYgXHUwNDM0XHUwNDNFIFx1MDQzRFx1MDQzRVx1MDQzMlx1MDQzRVx1MDQzM1x1MDQzRSBcdTA0NDJcdTA0MzVcdTA0M0FcdTA0NDNcdTA0NDlcdTA0MzVcdTA0MzNcdTA0M0UgXHUwNDQzXHUwNDM3XHUwNDNCXHUwNDMwXHJcblx0XHRcdGlmIChuZXdDdXJyZW50Tm9kZUlkKSB7XHJcblx0XHRcdFx0YXdhaXQgdGhpcy5mZXRjaE1lc3NhZ2VzRnJvbVJvb3RUb05vZGUobmV3Q3VycmVudE5vZGVJZCk7XHJcblx0XHRcdH0gZWxzZSB7XHJcblx0XHRcdFx0dGhpcy5jaGF0SGlzdG9yeSA9IHJlc3VsdC5tZXNzYWdlcyB8fCBbXTtcclxuXHRcdFx0XHRpZiAodGhpcy5jaGF0UGFuZWwpIHtcclxuXHRcdFx0XHRcdHRoaXMuY2hhdFBhbmVsLnVwZGF0ZUhpc3RvcnkodGhpcy5jaGF0SGlzdG9yeSk7XHJcblx0XHRcdFx0fVxyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHQvLyBcdTA0MUVcdTA0MzFcdTA0M0RcdTA0M0VcdTA0MzJcdTA0M0JcdTA0NEZcdTA0MzVcdTA0M0MgXHUwNDQxXHUwNDNGXHUwNDM4XHUwNDQxXHUwNDNFXHUwNDNBIFx1MDQzM1x1MDQ0MFx1MDQzMFx1MDQ0NFx1MDQzRVx1MDQzMlxyXG5cdFx0XHR0aGlzLmZldGNoR3JhcGhzTGlzdCgpO1xyXG5cdFx0fSBjYXRjaCAoZXJyb3IpIHtcclxuXHRcdFx0Y29uc29sZS5lcnJvcignXHUwNDFFXHUwNDQ4XHUwNDM4XHUwNDMxXHUwNDNBXHUwNDMwIFx1MDQzRVx1MDQ0Mlx1MDQzRlx1MDQ0MFx1MDQzMFx1MDQzMlx1MDQzQVx1MDQzOCBcdTA0NDFcdTA0M0VcdTA0M0VcdTA0MzFcdTA0NDlcdTA0MzVcdTA0M0RcdTA0MzhcdTA0NEY6JywgZXJyb3IpO1xyXG5cdFx0fVxyXG5cdH1cclxuXHJcblx0aGFuZGxlTm9kZVNlbGVjdGVkKGRhdGE6IHsgbm9kZUlkOiBzdHJpbmcgfSk6IHZvaWQge1xyXG5cdFx0dGhpcy5jdXJyZW50Tm9kZSA9IGRhdGEubm9kZUlkO1xyXG5cdFx0aWYgKHRoaXMuc2VsZWN0ZWRHcmFwaElkKSB7XHJcblx0XHRcdHRoaXMuZmV0Y2hNZXNzYWdlc0Zyb21Sb290VG9Ob2RlKGRhdGEubm9kZUlkKTtcclxuXHRcdH1cclxuXHR9XHJcblxyXG5cdGhhbmRsZUdyYXBoU2VsZWN0KGdyYXBoSWQ6IHN0cmluZyk6IHZvaWQge1xyXG5cdFx0dGhpcy5zZWxlY3RlZEdyYXBoSWQgPSBncmFwaElkO1xyXG5cdFx0dGhpcy5mZXRjaEdyYXBoRGF0YShncmFwaElkKTtcclxuXHR9XHJcblxyXG5cdGhhbmRsZU5ld0NoYXQoKTogdm9pZCB7XHJcblx0XHR0aGlzLmNoYXRIaXN0b3J5ID0gW107XHJcblx0XHR0aGlzLmN1cnJlbnROb2RlID0gbnVsbDtcclxuXHRcdHRoaXMuc2VsZWN0ZWRHcmFwaElkID0gbnVsbDtcclxuXHRcdGlmICh0aGlzLmNoYXRQYW5lbCkge1xyXG5cdFx0XHR0aGlzLmNoYXRQYW5lbC51cGRhdGVIaXN0b3J5KHRoaXMuY2hhdEhpc3RvcnkpO1xyXG5cdFx0fVxyXG5cdH1cclxufVxyXG4iLCAiaW1wb3J0IHsgQ2hhdEhpc3RvcnlJdGVtIH0gZnJvbSAnLi9tb2RlbHMvQ2hhdEhpc3RvcnlJdGVtJztcclxuXHJcbmV4cG9ydCBjbGFzcyBDaGF0UGFuZWwge1xyXG5cdGNvbnRhaW5lcjogSFRNTEVsZW1lbnQ7XHJcblx0cHJvcHM6IHtcclxuXHRcdGNoYXRIaXN0b3J5OiBDaGF0SGlzdG9yeUl0ZW1bXTtcclxuXHRcdG9uU2VuZE1lc3NhZ2U6IChtZXNzYWdlOiBzdHJpbmcpID0+IHZvaWQ7XHJcblx0fTtcclxuXHRjaGF0SGlzdG9yeTogQ2hhdEhpc3RvcnlJdGVtW107XHJcblx0bWVzc2FnZUlucHV0OiBIVE1MSW5wdXRFbGVtZW50O1xyXG5cdHNlbmRCdXR0b246IEhUTUxCdXR0b25FbGVtZW50O1xyXG5cdHN1Z2dlc3Rpb25zQ29udGFpbmVyOiBIVE1MRGl2RWxlbWVudDtcclxuXHJcblx0Y29uc3RydWN0b3IoXHJcblx0XHRjb250YWluZXI6IEhUTUxFbGVtZW50LFxyXG5cdFx0cHJvcHM6IHtcclxuXHRcdFx0Y2hhdEhpc3Rvcnk6IENoYXRIaXN0b3J5SXRlbVtdO1xyXG5cdFx0XHRvblNlbmRNZXNzYWdlOiAobWVzc2FnZTogc3RyaW5nKSA9PiB2b2lkO1xyXG5cdFx0fVxyXG5cdCkge1xyXG5cdFx0dGhpcy5jb250YWluZXIgPSBjb250YWluZXI7XHJcblx0XHR0aGlzLnByb3BzID0gcHJvcHM7XHJcblx0XHR0aGlzLmNoYXRIaXN0b3J5ID0gcHJvcHMuY2hhdEhpc3RvcnkgfHwgW107XHJcblxyXG5cdFx0dGhpcy5pbml0KCk7XHJcblx0fVxyXG5cclxuXHRpbml0KCk6IHZvaWQge1xyXG5cdFx0dGhpcy5jb250YWluZXIuaW5uZXJIVE1MID0gYFxyXG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwibGxtLWFnZW50LWNoYXQtcGFuZWxcIj5cclxuICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJjaGF0LWhpc3RvcnlcIiBpZD1cImNoYXQtaGlzdG9yeVwiPjwvZGl2PlxyXG4gICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cImNoYXQtaW5wdXQtZm9ybVwiPlxyXG4gICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJjb21tYW5kLWlucHV0XCI+XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIDxpbnB1dCB0eXBlPVwidGV4dFwiIGlkPVwibWVzc2FnZS1pbnB1dFwiIHBsYWNlaG9sZGVyPVwiXHUwNDEyXHUwNDMyXHUwNDM1XHUwNDM0XHUwNDM4XHUwNDQyXHUwNDM1IFx1MDQ0MVx1MDQzRVx1MDQzRVx1MDQzMVx1MDQ0OVx1MDQzNVx1MDQzRFx1MDQzOFx1MDQzNSBcdTA0MzhcdTA0M0JcdTA0MzggXHUwNDNBXHUwNDNFXHUwNDNDXHUwNDMwXHUwNDNEXHUwNDM0XHUwNDQzICgvKS4uLlwiIC8+XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJzdWdnZXN0aW9uc1wiIGlkPVwic3VnZ2VzdGlvbnNcIiBzdHlsZT1cImRpc3BsYXk6IG5vbmU7XCI+PC9kaXY+XHJcbiAgICAgICAgICAgICAgICAgICAgPC9kaXY+XHJcbiAgICAgICAgICAgICAgICAgICAgPGJ1dHRvbiB0eXBlPVwiYnV0dG9uXCIgaWQ9XCJzZW5kLWJ1dHRvblwiPlx1MDQxRVx1MDQ0Mlx1MDQzRlx1MDQ0MFx1MDQzMFx1MDQzMlx1MDQzOFx1MDQ0Mlx1MDQ0QzwvYnV0dG9uPlxyXG4gICAgICAgICAgICAgICAgPC9kaXY+XHJcbiAgICAgICAgICAgIDwvZGl2PlxyXG4gICAgICAgIGA7XHJcblxyXG5cdFx0dGhpcy5tZXNzYWdlSW5wdXQgPSB0aGlzLmNvbnRhaW5lci5xdWVyeVNlbGVjdG9yKCcjbWVzc2FnZS1pbnB1dCcpIGFzIEhUTUxJbnB1dEVsZW1lbnQ7XHJcblx0XHR0aGlzLnNlbmRCdXR0b24gPSB0aGlzLmNvbnRhaW5lci5xdWVyeVNlbGVjdG9yKCcjc2VuZC1idXR0b24nKSBhcyBIVE1MQnV0dG9uRWxlbWVudDtcclxuXHRcdHRoaXMuc3VnZ2VzdGlvbnNDb250YWluZXIgPSB0aGlzLmNvbnRhaW5lci5xdWVyeVNlbGVjdG9yKCcjc3VnZ2VzdGlvbnMnKSBhcyBIVE1MRGl2RWxlbWVudDtcclxuXHJcblx0XHR0aGlzLnNldHVwRXZlbnRMaXN0ZW5lcnMoKTtcclxuXHRcdHRoaXMucmVuZGVySGlzdG9yeSgpO1xyXG5cdH1cclxuXHJcblx0c2V0dXBFdmVudExpc3RlbmVycygpOiB2b2lkIHtcclxuXHRcdHRoaXMuc2VuZEJ1dHRvbi5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsICgpID0+IHRoaXMuaGFuZGxlU2VuZCgpKTtcclxuXHJcblx0XHR0aGlzLm1lc3NhZ2VJbnB1dC5hZGRFdmVudExpc3RlbmVyKCdrZXlkb3duJywgKGUpID0+IHtcclxuXHRcdFx0aWYgKGUua2V5ID09PSAnRW50ZXInKSB7XHJcblx0XHRcdFx0aWYgKHRoaXMuc3VnZ2VzdGlvbnNDb250YWluZXIuc3R5bGUuZGlzcGxheSA9PT0gJ2Jsb2NrJykge1xyXG5cdFx0XHRcdFx0Y29uc3QgZmlyc3RTdWdnZXN0aW9uID0gdGhpcy5zdWdnZXN0aW9uc0NvbnRhaW5lci5xdWVyeVNlbGVjdG9yKCcuc3VnZ2VzdGlvbi1pdGVtJyk7XHJcblx0XHRcdFx0XHRpZiAoZmlyc3RTdWdnZXN0aW9uKSB7XHJcblx0XHRcdFx0XHRcdHRoaXMubWVzc2FnZUlucHV0LnZhbHVlID0gZmlyc3RTdWdnZXN0aW9uLnRleHRDb250ZW50IHx8ICcnO1xyXG5cdFx0XHRcdFx0XHR0aGlzLmhpZGVTdWdnZXN0aW9ucygpO1xyXG5cdFx0XHRcdFx0XHRlLnByZXZlbnREZWZhdWx0KCk7XHJcblx0XHRcdFx0XHRcdHJldHVybjtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHR9XHJcblx0XHRcdFx0dGhpcy5oYW5kbGVTZW5kKCk7XHJcblx0XHRcdFx0ZS5wcmV2ZW50RGVmYXVsdCgpO1xyXG5cdFx0XHR9XHJcblx0XHR9KTtcclxuXHJcblx0XHR0aGlzLm1lc3NhZ2VJbnB1dC5hZGRFdmVudExpc3RlbmVyKCdpbnB1dCcsIChlKSA9PiB7XHJcblx0XHRcdHRoaXMuaGFuZGxlSW5wdXQoKGUudGFyZ2V0IGFzIEhUTUxJbnB1dEVsZW1lbnQpLnZhbHVlKTtcclxuXHRcdH0pO1xyXG5cdH1cclxuXHJcblx0aGFuZGxlSW5wdXQodmFsdWU6IHN0cmluZyk6IHZvaWQge1xyXG5cdFx0Y29uc3QgY29tbWFuZHMgPSBbJy9oZWxwJywgJy9pbWFnaW5lJywgJy9hbmFseXplJywgJy9zdWJ0aXRsZXNfbWVldCcsICcvc3VidGl0bGVzX3RlYW1zJywgJy9zdW1tYXJpemUnLCAnL2NoYXQnXTtcclxuXHJcblx0XHRpZiAodmFsdWUuc3RhcnRzV2l0aCgnLycpKSB7XHJcblx0XHRcdGNvbnN0IGZpbHRlcmVkQ29tbWFuZHMgPSBjb21tYW5kcy5maWx0ZXIoKGNtZCkgPT4gY21kLnN0YXJ0c1dpdGgodmFsdWUudG9Mb3dlckNhc2UoKSkpO1xyXG5cdFx0XHR0aGlzLnNob3dTdWdnZXN0aW9ucyhmaWx0ZXJlZENvbW1hbmRzKTtcclxuXHRcdH0gZWxzZSB7XHJcblx0XHRcdHRoaXMuaGlkZVN1Z2dlc3Rpb25zKCk7XHJcblx0XHR9XHJcblx0fVxyXG5cclxuXHRzaG93U3VnZ2VzdGlvbnMoc3VnZ2VzdGlvbnM6IHN0cmluZ1tdKTogdm9pZCB7XHJcblx0XHRpZiAoc3VnZ2VzdGlvbnMubGVuZ3RoID09PSAwKSB7XHJcblx0XHRcdHRoaXMuaGlkZVN1Z2dlc3Rpb25zKCk7XHJcblx0XHRcdHJldHVybjtcclxuXHRcdH1cclxuXHJcblx0XHR0aGlzLnN1Z2dlc3Rpb25zQ29udGFpbmVyLmlubmVySFRNTCA9IHN1Z2dlc3Rpb25zLm1hcCgoc3VnZ2VzdGlvbikgPT4gYDxkaXYgY2xhc3M9XCJzdWdnZXN0aW9uLWl0ZW1cIj4ke3N1Z2dlc3Rpb259PC9kaXY+YCkuam9pbignJyk7XHJcblxyXG5cdFx0dGhpcy5zdWdnZXN0aW9uc0NvbnRhaW5lci5zdHlsZS5kaXNwbGF5ID0gJ2Jsb2NrJztcclxuXHJcblx0XHQvLyBcdTA0MTRcdTA0M0VcdTA0MzFcdTA0MzBcdTA0MzJcdTA0M0JcdTA0NEZcdTA0MzVcdTA0M0MgXHUwNDNFXHUwNDMxXHUwNDQwXHUwNDMwXHUwNDMxXHUwNDNFXHUwNDQyXHUwNDQ3XHUwNDM4XHUwNDNBXHUwNDM4IFx1MDQzQVx1MDQzQlx1MDQzOFx1MDQzQVx1MDQzRVx1MDQzMiBcdTA0M0RcdTA0MzAgXHUwNDNGXHUwNDQwXHUwNDM1XHUwNDM0XHUwNDNCXHUwNDNFXHUwNDM2XHUwNDM1XHUwNDNEXHUwNDM4XHUwNDRGXHJcblx0XHR0aGlzLnN1Z2dlc3Rpb25zQ29udGFpbmVyLnF1ZXJ5U2VsZWN0b3JBbGwoJy5zdWdnZXN0aW9uLWl0ZW0nKS5mb3JFYWNoKChpdGVtKSA9PiB7XHJcblx0XHRcdGl0ZW0uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCAoKSA9PiB7XHJcblx0XHRcdFx0dGhpcy5tZXNzYWdlSW5wdXQudmFsdWUgPSBpdGVtLnRleHRDb250ZW50IHx8ICcnO1xyXG5cdFx0XHRcdHRoaXMuaGlkZVN1Z2dlc3Rpb25zKCk7XHJcblx0XHRcdFx0dGhpcy5tZXNzYWdlSW5wdXQuZm9jdXMoKTtcclxuXHRcdFx0fSk7XHJcblx0XHR9KTtcclxuXHR9XHJcblxyXG5cdGhpZGVTdWdnZXN0aW9ucygpOiB2b2lkIHtcclxuXHRcdHRoaXMuc3VnZ2VzdGlvbnNDb250YWluZXIuc3R5bGUuZGlzcGxheSA9ICdub25lJztcclxuXHR9XHJcblxyXG5cdGhhbmRsZVNlbmQoKTogdm9pZCB7XHJcblx0XHRjb25zdCBtZXNzYWdlID0gdGhpcy5tZXNzYWdlSW5wdXQudmFsdWUudHJpbSgpO1xyXG5cdFx0aWYgKG1lc3NhZ2UgJiYgdGhpcy5wcm9wcy5vblNlbmRNZXNzYWdlKSB7XHJcblx0XHRcdHRoaXMucHJvcHMub25TZW5kTWVzc2FnZShtZXNzYWdlKTtcclxuXHRcdFx0dGhpcy5tZXNzYWdlSW5wdXQudmFsdWUgPSAnJztcclxuXHRcdFx0dGhpcy5oaWRlU3VnZ2VzdGlvbnMoKTtcclxuXHRcdH1cclxuXHR9XHJcblxyXG5cdHJlbmRlckhpc3RvcnkoKTogdm9pZCB7XHJcblx0XHRjb25zdCBoaXN0b3J5Q29udGFpbmVyID0gdGhpcy5jb250YWluZXIucXVlcnlTZWxlY3RvcignI2NoYXQtaGlzdG9yeScpIGFzIEhUTUxFbGVtZW50O1xyXG5cdFx0aGlzdG9yeUNvbnRhaW5lci5pbm5lckhUTUwgPSB0aGlzLmNoYXRIaXN0b3J5XHJcblx0XHRcdC5tYXAoXHJcblx0XHRcdFx0KG1zZykgPT4gYFxyXG4gICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cIm1lc3NhZ2UgJHttc2cucm9sZX1cIj5cclxuICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwibWVzc2FnZS1jb250ZW50XCI+JHt0aGlzLnByb2Nlc3NNZXNzYWdlQ29udGVudChtc2cuY29udGVudCl9PC9kaXY+XHJcbiAgICAgICAgICAgICAgICA8L2Rpdj5cclxuICAgICAgICAgICAgYFxyXG5cdFx0XHQpXHJcblx0XHRcdC5qb2luKCcnKTtcclxuXHJcblx0XHQvLyBcdTA0MUZcdTA0NDBcdTA0M0VcdTA0M0FcdTA0NDBcdTA0NDNcdTA0NDdcdTA0MzhcdTA0MzJcdTA0MzBcdTA0MzVcdTA0M0MgXHUwNDNBIFx1MDQzQVx1MDQzRVx1MDQzRFx1MDQ0Nlx1MDQ0M1xyXG5cdFx0aGlzdG9yeUNvbnRhaW5lci5zY3JvbGxUb3AgPSBoaXN0b3J5Q29udGFpbmVyLnNjcm9sbEhlaWdodDtcclxuXHR9XHJcblxyXG5cdHByb2Nlc3NNZXNzYWdlQ29udGVudChjb250ZW50OiBzdHJpbmcpOiBzdHJpbmcge1xyXG5cdFx0Ly8gXHUwNDFGXHUwNDQwXHUwNDNFXHUwNDQxXHUwNDQyXHUwNDM1XHUwNDM5XHUwNDQ4XHUwNDMwXHUwNDRGIFx1MDQzRVx1MDQzMVx1MDQ0MFx1MDQzMFx1MDQzMVx1MDQzRVx1MDQ0Mlx1MDQzQVx1MDQzMCBNYXJrZG93biBcdTA0MzRcdTA0M0JcdTA0NEYgXHUwNDM4XHUwNDM3XHUwNDNFXHUwNDMxXHUwNDQwXHUwNDMwXHUwNDM2XHUwNDM1XHUwNDNEXHUwNDM4XHUwNDM5XHJcblx0XHRyZXR1cm4gY29udGVudC5yZXBsYWNlKC88aW1nKFtePl0qPyk+L2csICc8aW1nJDEgc3R5bGU9XCJtYXgtd2lkdGg6IDEwMCU7IGhlaWdodDogYXV0bztcIj4nKS5yZXBsYWNlKC9cXG4vZywgJzxicj4nKTtcclxuXHR9XHJcblxyXG5cdHVwZGF0ZUhpc3RvcnkobmV3SGlzdG9yeTogQ2hhdEhpc3RvcnlJdGVtW10pOiB2b2lkIHtcclxuXHRcdHRoaXMuY2hhdEhpc3RvcnkgPSBuZXdIaXN0b3J5O1xyXG5cdFx0dGhpcy5yZW5kZXJIaXN0b3J5KCk7XHJcblx0fVxyXG5cclxuXHRkZXN0cm95KCk6IHZvaWQge1xyXG5cdFx0dGhpcy5jb250YWluZXIuaW5uZXJIVE1MID0gJyc7XHJcblx0fVxyXG59XHJcbiIsICJleHBvcnQgY2xhc3MgSGlzdG9yeVBhbmVsIHtcclxuXHRjb250YWluZXI6IEhUTUxFbGVtZW50O1xyXG5cdHByb3BzOiB7XHJcblx0XHRncmFwaHM6IGFueVtdO1xyXG5cdFx0b25HcmFwaFNlbGVjdDogKGdyYXBoSWQ6IHN0cmluZykgPT4gdm9pZDtcclxuXHRcdG9uTmV3Q2hhdDogKCkgPT4gdm9pZDtcclxuXHR9O1xyXG5cdGdyYXBoczogYW55W107XHJcblx0bmV3Q2hhdEJ1dHRvbjogSFRNTEJ1dHRvbkVsZW1lbnQ7XHJcblx0Y2hhdExpc3Q6IEhUTUxFbGVtZW50O1xyXG5cdGNvbnRleHRNZW51OiBIVE1MRWxlbWVudDtcclxuXHRkZWxldGVCdXR0b246IEhUTUxCdXR0b25FbGVtZW50O1xyXG5cdGNvbnRleHRNZW51R3JhcGhJZDogc3RyaW5nIHwgbnVsbDtcclxuXHJcblx0Y29uc3RydWN0b3IoXHJcblx0XHRjb250YWluZXI6IEhUTUxFbGVtZW50LFxyXG5cdFx0cHJvcHM6IHtcclxuXHRcdFx0Z3JhcGhzOiBhbnlbXTtcclxuXHRcdFx0b25HcmFwaFNlbGVjdDogKGdyYXBoSWQ6IHN0cmluZykgPT4gdm9pZDtcclxuXHRcdFx0b25OZXdDaGF0OiAoKSA9PiB2b2lkO1xyXG5cdFx0fVxyXG5cdCkge1xyXG5cdFx0dGhpcy5jb250YWluZXIgPSBjb250YWluZXI7XHJcblx0XHR0aGlzLnByb3BzID0gcHJvcHM7XHJcblx0XHR0aGlzLmdyYXBocyA9IHByb3BzLmdyYXBocyB8fCBbXTtcclxuXHJcblx0XHR0aGlzLmluaXQoKTtcclxuXHR9XHJcblxyXG5cdGluaXQoKTogdm9pZCB7XHJcblx0XHR0aGlzLmNvbnRhaW5lci5pbm5lckhUTUwgPSBgXHJcbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJsbG0tYWdlbnQtaGlzdG9yeS1wYW5lbFwiPlxyXG4gICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cImhpc3RvcnktaGVhZGVyXCI+XHJcbiAgICAgICAgICAgICAgICAgICAgPGgzPlx1MDQxOFx1MDQ0MVx1MDQ0Mlx1MDQzRVx1MDQ0MFx1MDQzOFx1MDQ0RiBcdTA0MzRcdTA0MzhcdTA0MzBcdTA0M0JcdTA0M0VcdTA0MzNcdTA0M0VcdTA0MzI8L2gzPlxyXG4gICAgICAgICAgICAgICAgICAgIDxidXR0b24gaWQ9XCJuZXctY2hhdC1idXR0b25cIiBjbGFzcz1cIm5ldy1jaGF0LWJ1dHRvblwiPisgXHUwNDFEXHUwNDNFXHUwNDMyXHUwNDRCXHUwNDM5IFx1MDQ0N1x1MDQzMFx1MDQ0MjwvYnV0dG9uPlxyXG4gICAgICAgICAgICAgICAgPC9kaXY+XHJcbiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwiY2hhdC1saXN0XCIgaWQ9XCJjaGF0LWxpc3RcIj48L2Rpdj5cclxuICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJjb250ZXh0LW1lbnVcIiBpZD1cImNvbnRleHQtbWVudVwiIHN0eWxlPVwiZGlzcGxheTogbm9uZTtcIj5cclxuICAgICAgICAgICAgICAgICAgICA8YnV0dG9uIGlkPVwiZGVsZXRlLWdyYXBoLWJ1dHRvblwiPlx1MDQyM1x1MDQzNFx1MDQzMFx1MDQzQlx1MDQzOFx1MDQ0Mlx1MDQ0QzwvYnV0dG9uPlxyXG4gICAgICAgICAgICAgICAgPC9kaXY+XHJcbiAgICAgICAgICAgIDwvZGl2PlxyXG4gICAgICAgIGA7XHJcblxyXG5cdFx0dGhpcy5uZXdDaGF0QnV0dG9uID0gdGhpcy5jb250YWluZXIucXVlcnlTZWxlY3RvcignI25ldy1jaGF0LWJ1dHRvbicpIGFzIEhUTUxCdXR0b25FbGVtZW50O1xyXG5cdFx0dGhpcy5jaGF0TGlzdCA9IHRoaXMuY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3IoJyNjaGF0LWxpc3QnKSBhcyBIVE1MRWxlbWVudDtcclxuXHRcdHRoaXMuY29udGV4dE1lbnUgPSB0aGlzLmNvbnRhaW5lci5xdWVyeVNlbGVjdG9yKCcjY29udGV4dC1tZW51JykgYXMgSFRNTEVsZW1lbnQ7XHJcblx0XHR0aGlzLmRlbGV0ZUJ1dHRvbiA9IHRoaXMuY29udGFpbmVyLnF1ZXJ5U2VsZWN0b3IoJyNkZWxldGUtZ3JhcGgtYnV0dG9uJykgYXMgSFRNTEJ1dHRvbkVsZW1lbnQ7XHJcblxyXG5cdFx0dGhpcy5zZXR1cEV2ZW50TGlzdGVuZXJzKCk7XHJcblx0XHR0aGlzLnJlbmRlckdyYXBocygpO1xyXG5cdH1cclxuXHJcblx0c2V0dXBFdmVudExpc3RlbmVycygpOiB2b2lkIHtcclxuXHRcdHRoaXMubmV3Q2hhdEJ1dHRvbi5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsICgpID0+IHtcclxuXHRcdFx0aWYgKHRoaXMucHJvcHMub25OZXdDaGF0KSB7XHJcblx0XHRcdFx0dGhpcy5wcm9wcy5vbk5ld0NoYXQoKTtcclxuXHRcdFx0fVxyXG5cdFx0fSk7XHJcblxyXG5cdFx0dGhpcy5kZWxldGVCdXR0b24uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCAoKSA9PiB7XHJcblx0XHRcdGlmICh0aGlzLmNvbnRleHRNZW51R3JhcGhJZCkge1xyXG5cdFx0XHRcdHRoaXMuaGFuZGxlRGVsZXRlR3JhcGgodGhpcy5jb250ZXh0TWVudUdyYXBoSWQpO1xyXG5cdFx0XHR9XHJcblx0XHR9KTtcclxuXHJcblx0XHQvLyBcdTA0MTdcdTA0MzBcdTA0M0FcdTA0NDBcdTA0NEJcdTA0NDJcdTA0MzhcdTA0MzUgXHUwNDNBXHUwNDNFXHUwNDNEXHUwNDQyXHUwNDM1XHUwNDNBXHUwNDQxXHUwNDQyXHUwNDNEXHUwNDNFXHUwNDMzXHUwNDNFIFx1MDQzQ1x1MDQzNVx1MDQzRFx1MDQ0RSBcdTA0M0ZcdTA0NDBcdTA0MzggXHUwNDNBXHUwNDNCXHUwNDM4XHUwNDNBXHUwNDM1IFx1MDQzMlx1MDQzRFx1MDQzNSBcdTA0MzVcdTA0MzNcdTA0M0VcclxuXHRcdGRvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgKGUpID0+IHtcclxuXHRcdFx0aWYgKCF0aGlzLmNvbnRleHRNZW51LmNvbnRhaW5zKGUudGFyZ2V0IGFzIE5vZGUpKSB7XHJcblx0XHRcdFx0dGhpcy5jb250ZXh0TWVudS5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnO1xyXG5cdFx0XHR9XHJcblx0XHR9KTtcclxuXHR9XHJcblxyXG5cdHJlbmRlckdyYXBocygpOiB2b2lkIHtcclxuXHRcdHRoaXMuY2hhdExpc3QuaW5uZXJIVE1MID0gdGhpcy5ncmFwaHNcclxuXHRcdFx0Lm1hcChcclxuXHRcdFx0XHQoZ3JhcGgpID0+IGBcclxuICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJjaGF0LWxpc3QtaXRlbVwiIGRhdGEtZ3JhcGgtaWQ9XCIke2dyYXBoLmlkfVwiPlxyXG4gICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJjaGF0LWl0ZW0tY29udGVudFwiPlxyXG4gICAgICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwiY2hhdC1pdGVtLXRleHRcIj5cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzPVwidHJ1bmNhdGVcIj5cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAke2dyYXBoLmZpcnN0X21lc3NhZ2UgfHwgYFx1MDQxM1x1MDQ0MFx1MDQzMFx1MDQ0NCAke2dyYXBoLmlkfWB9XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3NwYW4+XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIDwvZGl2PlxyXG4gICAgICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwiY2hhdC1pdGVtLWFjdGlvbnNcIj5cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxidXR0b24gY2xhc3M9XCJjaGF0LWl0ZW0tb3B0aW9ucy1idXR0b25cIiBkYXRhLWdyYXBoLWlkPVwiJHtncmFwaC5pZH1cIj5cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcdTIyRUVcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYnV0dG9uPlxyXG4gICAgICAgICAgICAgICAgICAgICAgICA8L2Rpdj5cclxuICAgICAgICAgICAgICAgICAgICA8L2Rpdj5cclxuICAgICAgICAgICAgICAgIDwvZGl2PlxyXG4gICAgICAgICAgICBgXHJcblx0XHRcdClcclxuXHRcdFx0LmpvaW4oJycpO1xyXG5cclxuXHRcdC8vIFx1MDQxNFx1MDQzRVx1MDQzMVx1MDQzMFx1MDQzMlx1MDQzQlx1MDQ0Rlx1MDQzNVx1MDQzQyBcdTA0M0VcdTA0MzFcdTA0NDBcdTA0MzBcdTA0MzFcdTA0M0VcdTA0NDJcdTA0NDdcdTA0MzhcdTA0M0FcdTA0MzggXHUwNDQxXHUwNDNFXHUwNDMxXHUwNDRCXHUwNDQyXHUwNDM4XHUwNDM5XHJcblx0XHR0aGlzLmNoYXRMaXN0LnF1ZXJ5U2VsZWN0b3JBbGwoJy5jaGF0LWxpc3QtaXRlbScpLmZvckVhY2goKGl0ZW0pID0+IHtcclxuXHRcdFx0aXRlbS5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIChlKSA9PiB7XHJcblx0XHRcdFx0aWYgKCEoZS50YXJnZXQgYXMgSFRNTEVsZW1lbnQpLmNsYXNzTGlzdC5jb250YWlucygnY2hhdC1pdGVtLW9wdGlvbnMtYnV0dG9uJykpIHtcclxuXHRcdFx0XHRcdGNvbnN0IGdyYXBoSWQgPSAoaXRlbSBhcyBIVE1MRWxlbWVudCkuZGF0YXNldC5ncmFwaElkO1xyXG5cdFx0XHRcdFx0aWYgKGdyYXBoSWQgJiYgdGhpcy5wcm9wcy5vbkdyYXBoU2VsZWN0KSB7XHJcblx0XHRcdFx0XHRcdHRoaXMucHJvcHMub25HcmFwaFNlbGVjdChncmFwaElkKTtcclxuXHRcdFx0XHRcdH1cclxuXHRcdFx0XHR9XHJcblx0XHRcdH0pO1xyXG5cdFx0fSk7XHJcblxyXG5cdFx0dGhpcy5jaGF0TGlzdC5xdWVyeVNlbGVjdG9yQWxsKCcuY2hhdC1pdGVtLW9wdGlvbnMtYnV0dG9uJykuZm9yRWFjaCgoYnV0dG9uKSA9PiB7XHJcblx0XHRcdGJ1dHRvbi5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsIChlKSA9PiB7XHJcblx0XHRcdFx0ZS5zdG9wUHJvcGFnYXRpb24oKTtcclxuXHRcdFx0XHR0aGlzLnNob3dDb250ZXh0TWVudShlLCAoYnV0dG9uIGFzIEhUTUxFbGVtZW50KS5kYXRhc2V0LmdyYXBoSWQgfHwgJycpO1xyXG5cdFx0XHR9KTtcclxuXHRcdH0pO1xyXG5cdH1cclxuXHJcblx0c2hvd0NvbnRleHRNZW51KGV2ZW50OiBFdmVudCwgZ3JhcGhJZDogc3RyaW5nKTogdm9pZCB7XHJcblx0XHR0aGlzLmNvbnRleHRNZW51R3JhcGhJZCA9IGdyYXBoSWQ7XHJcblx0XHR0aGlzLmNvbnRleHRNZW51LnN0eWxlLmRpc3BsYXkgPSAnYmxvY2snO1xyXG5cdFx0dGhpcy5jb250ZXh0TWVudS5zdHlsZS5sZWZ0ID0gYCR7KGV2ZW50IGFzIE1vdXNlRXZlbnQpLmNsaWVudFh9cHhgO1xyXG5cdFx0dGhpcy5jb250ZXh0TWVudS5zdHlsZS50b3AgPSBgJHsoZXZlbnQgYXMgTW91c2VFdmVudCkuY2xpZW50WX1weGA7XHJcblx0fVxyXG5cclxuXHRhc3luYyBoYW5kbGVEZWxldGVHcmFwaChncmFwaElkOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcclxuXHRcdHRyeSB7XHJcblx0XHRcdGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2goYGh0dHA6Ly9sb2NhbGhvc3Q6NTAwMC9hcGkvZ3JhcGhzLyR7Z3JhcGhJZH1gLCB7XHJcblx0XHRcdFx0bWV0aG9kOiAnREVMRVRFJ1xyXG5cdFx0XHR9KTtcclxuXHJcblx0XHRcdGlmICghcmVzcG9uc2Uub2spIHtcclxuXHRcdFx0XHR0aHJvdyBuZXcgRXJyb3IoYEhUVFAgXHUwNDNFXHUwNDQ4XHUwNDM4XHUwNDMxXHUwNDNBXHUwNDMwISBcdTA0NDFcdTA0NDJcdTA0MzBcdTA0NDJcdTA0NDNcdTA0NDE6ICR7cmVzcG9uc2Uuc3RhdHVzfWApO1xyXG5cdFx0XHR9XHJcblxyXG5cdFx0XHQvLyBcdTA0MUVcdTA0MzFcdTA0M0RcdTA0M0VcdTA0MzJcdTA0M0JcdTA0NEZcdTA0MzVcdTA0M0MgXHUwNDQxXHUwNDNGXHUwNDM4XHUwNDQxXHUwNDNFXHUwNDNBIFx1MDQzM1x1MDQ0MFx1MDQzMFx1MDQ0NFx1MDQzRVx1MDQzMlxyXG5cdFx0XHR0aGlzLmdyYXBocyA9IHRoaXMuZ3JhcGhzLmZpbHRlcigoZ3JhcGgpID0+IGdyYXBoLmlkICE9PSBncmFwaElkKTtcclxuXHRcdFx0dGhpcy5yZW5kZXJHcmFwaHMoKTtcclxuXHRcdFx0dGhpcy5jb250ZXh0TWVudS5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnO1xyXG5cdFx0fSBjYXRjaCAoZXJyb3IpIHtcclxuXHRcdFx0Y29uc29sZS5lcnJvcignXHUwNDFFXHUwNDQ4XHUwNDM4XHUwNDMxXHUwNDNBXHUwNDMwIFx1MDQ0M1x1MDQzNFx1MDQzMFx1MDQzQlx1MDQzNVx1MDQzRFx1MDQzOFx1MDQ0RiBcdTA0MzNcdTA0NDBcdTA0MzBcdTA0NDRcdTA0MzA6JywgZXJyb3IpO1xyXG5cdFx0fVxyXG5cdH1cclxuXHJcblx0dXBkYXRlR3JhcGhzKG5ld0dyYXBoczogYW55W10pOiB2b2lkIHtcclxuXHRcdHRoaXMuZ3JhcGhzID0gbmV3R3JhcGhzO1xyXG5cdFx0dGhpcy5yZW5kZXJHcmFwaHMoKTtcclxuXHR9XHJcblxyXG5cdGRlc3Ryb3koKTogdm9pZCB7XHJcblx0XHR0aGlzLmNvbnRhaW5lci5pbm5lckhUTUwgPSAnJztcclxuXHR9XHJcbn1cclxuIl0sCiAgIm1hcHBpbmdzIjogIjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLElBQUFBLG1CQUFxSDs7O0FDRTlHLElBQU0sV0FBTixNQUFlO0FBQUEsRUFBZjtBQUNOLFNBQVEsU0FBc0QsQ0FBQztBQUFBO0FBQUEsRUFFL0QsR0FBRyxXQUFtQixVQUFzQztBQUMzRCxRQUFJLENBQUMsS0FBSyxPQUFPLFNBQVMsR0FBRztBQUM1QixXQUFLLE9BQU8sU0FBUyxJQUFJLENBQUM7QUFBQSxJQUMzQjtBQUNBLFNBQUssT0FBTyxTQUFTLEVBQUUsS0FBSyxRQUFRO0FBQUEsRUFDckM7QUFBQSxFQUVBLElBQUksV0FBbUIsVUFBc0M7QUFDNUQsUUFBSSxLQUFLLE9BQU8sU0FBUyxHQUFHO0FBQzNCLFdBQUssT0FBTyxTQUFTLElBQUksS0FBSyxPQUFPLFNBQVMsRUFBRSxPQUFPLENBQUMsT0FBTyxPQUFP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QSxFQUN4RDtBQUFBLEVBRUEsV0FBVztBQUNWLFlBQVEsSUFBSSxtRUFBMkI7QUFBQSxFQUN4QztBQUFBO0FBQUEsRUFJQSxNQUFNLGVBQWU7QUFDcEIsU0FBSyxXQUFXLE9BQU8sT0FBTyxDQUFDLEdBQUcsa0JBQWtCLE1BQU0sS0FBSyxTQUFTLENBQUM7QUFBQSxFQUMxRTtBQUFBLEVBRUEsTUFBTSxlQUFlO0FBQ3BCLFVBQU0sS0FBSyxTQUFTLEtBQUssUUFBUTtBQUFBLEVBQ2xDO0FBQUE7QUFBQSxFQUlBLE1BQU0sb0JBQW9CO0FBQ3pCLFVBQU0sZUFBZSxLQUFLLElBQUksVUFBVSxnQkFBZ0IsZUFBZSxFQUFFLENBQUM7QUFDMUUsUUFBSSxjQUFjO0FBQ2pCLFdBQUssSUFBSSxVQUFVLFdBQVcsWUFBWTtBQUFBLElBQzNDLE9BQU87QUFDTixZQUFNLEtBQUssSUFBSSxVQUFVLFFBQVEsS0FBSyxFQUFFLGFBQWE7QUFBQSxRQUNwRCxNQUFNO0FBQUEsUUFDTixRQUFRO0FBQUEsTUFDVCxDQUFDO0FBQUEsSUFDRjtBQUFBLEVBQ0Q7QUFBQSxFQUVBLE1BQU0sbUJBQW1CO0FBQ3hCLFVBQU0sZUFBZSxLQUFLLElBQUksVUFBVSxnQkFBZ0IsY0FBYyxFQUFFLENBQUM7QUFDekUsUUFBSSxjQUFjO0FBQ2pCLFdBQUssSUFBSSxVQUFVLFdBQVcsWUFBWTtBQUFBLElBQzNDLE9BQU87QUFDTixZQUFNLFlBQVksS0FBSyxJQUFJLFVBQVUsYUFBYSxLQUFLO0FBQ3ZELFVBQUksV0FBVztBQUNkLGNBQU0sVUFBVSxhQUFhO0FBQUEsVUFDNUIsTUFBTTtBQUFBLFVBQ04sUUFBUTtBQUFBLFFBQ1QsQ0FBQztBQUFBLE1BQ0Y7QUFBQSxJQUNEO0FBQUEsRUFDRDtBQUFBO0FBQUEsRUFJQSxnQkFBZ0I7QUFDZixTQUFLLFNBQVMsS0FBSyxVQUFVO0FBQUEsRUFDOUI7QUFDRDtBQUlBLElBQU0sbUJBQU4sY0FBK0Isa0NBQWlCO0FBQUEsRUFHL0MsWUFBWSxLQUFVLFFBQXdCO0FBQzdDLFVBQU0sS0FBSyxNQUFNO0FBQ2pCLFNBQUssU0FBUztBQUFBLEVBQ2Y7QUFBQSxFQUVBLFVBQWdCO0FBQ2YsVUFBTSxFQUFFLFlBQVksSUFBSTtBQUV4QixnQkFBWSxNQUFNO0FBRWxCLFFBQUkseUJBQVEsV0FBVyxFQUNyQixRQUFRLGNBQWMsRUFDdEIsUUFBUSxpRkFBK0IsRUFDdkM7QUFBQSxNQUFRLENBQUMsU0FDVCxLQUNFLGVBQWUsZ0RBQWEsRUFDNUIsU0FBUyxLQUFLLE9BQU8sU0FBUyxVQUFVLEVBQ3hDLFNBQVMsT0FBTyxVQUFVO0FBQzFCLGFBQUssT0FBTyxTQUFTLGFBQWE7QUFDbEMsY0FBTSxLQUFLLE9BQU8sYUFBYTtBQUFBLE1BQ2hDLENBQUM7QUFBQSxJQUNIO0FBQUEsRUFDRjtBQUNEOyIsCiAgIm5hbWVzIjogWyJpbXBvcnRfb2JzaWRpYW4iLCAiaW1wb3J0X29ic2lkaWFuIl0KfQo= diff --git a/main.ts b/main.ts new file mode 100644 index 0000000..8dbbbba --- /dev/null +++ b/main.ts @@ -0,0 +1,140 @@ +import { App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting, ItemView, WorkspaceLeaf } from 'obsidian'; +import { EventBus } from './src/utils/EventBus'; +import { GraphView, GRAPH_VIEW_TYPE } from './src/views/GraphView'; +import { ChatView, CHAT_VIEW_TYPE } from './src/views/ChatView'; + +// ----------------------------------------------- Settings --------------------------------------------------------------- + +interface LLMAgentSettings { + apiBaseUrl: string; +} + +const DEFAULT_SETTINGS: LLMAgentSettings = { + apiBaseUrl: 'http://localhost:5000/api' +}; + +// ----------------------------------------------- Main Plugin Class --------------------------------------------------------------- + +export default class LLMAgentPlugin extends Plugin { + settings: LLMAgentSettings; + eventBus: EventBus; + + async onload() { + await this.loadSettings(); + + this.eventBus = new EventBus(); + + // Регистрируем кастомные views + this.registerView(GRAPH_VIEW_TYPE, (leaf) => new GraphView(leaf, this)); + this.registerView(CHAT_VIEW_TYPE, (leaf) => new ChatView(leaf, this)); + + // Добавляем команды + this.addCommand({ + id: 'open-llm-agent-graph', + name: 'Открыть граф диалогов LLM Agent', + callback: () => this.activateGraphView() + }); + + this.addCommand({ + id: 'open-llm-agent-chat', + name: 'Открыть чат LLM Agent', + callback: () => this.activateChatView() + }); + + this.addCommand({ + id: 'new-llm-agent-chat', + name: 'Новый чат LLM Agent', + callback: () => this.handleNewChat() + }); + + // Добавляем иконку в ribbon + this.addRibbonIcon('brain-circuit', 'LLM Agent', () => { + this.activateGraphView(); + this.activateChatView(); + }); + + console.log('LLM Agent plugin загружен'); + + // This adds a settings tab so the user can configure various aspects of the plugin + this.addSettingTab(new SampleSettingTab(this.app, this)); + } + + onunload() { + console.log('LLM Agent plugin выгружен'); + } + + // ----------------------------------------------- Settings Management --------------------------------------------------------------- + + async loadSettings() { + this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); + } + + async saveSettings() { + await this.saveData(this.settings); + } + + // ----------------------------------------------- View Management --------------------------------------------------------------- + + async activateGraphView() { + const existingLeaf = this.app.workspace.getLeavesOfType(GRAPH_VIEW_TYPE)[0]; + if (existingLeaf) { + this.app.workspace.revealLeaf(existingLeaf); + } else { + await this.app.workspace.getLeaf('tab').setViewState({ + type: GRAPH_VIEW_TYPE, + active: true + }); + } + } + + async activateChatView() { + const existingLeaf = this.app.workspace.getLeavesOfType(CHAT_VIEW_TYPE)[0]; + if (existingLeaf) { + this.app.workspace.revealLeaf(existingLeaf); + } else { + const rightLeaf = this.app.workspace.getRightLeaf(false); + if (rightLeaf) { + await rightLeaf.setViewState({ + type: CHAT_VIEW_TYPE, + active: true + }); + } + } + } + + // ----------------------------------------------- Event Handlers --------------------------------------------------------------- + + handleNewChat() { + this.eventBus.emit('new-chat'); + } +} + +// ----------------------------------------------- Sample Setting Tab --------------------------------------------------------------- + +class SampleSettingTab extends PluginSettingTab { + plugin: LLMAgentPlugin; + + constructor(app: App, plugin: LLMAgentPlugin) { + super(app, plugin); + this.plugin = plugin; + } + + display(): void { + const { containerEl } = this; + + containerEl.empty(); + + new Setting(containerEl) + .setName('API Base URL') + .setDesc('Базовый URL для API LLM Agent') + .addText((text) => + text + .setPlaceholder('Введите URL') + .setValue(this.plugin.settings.apiBaseUrl) + .onChange(async (value) => { + this.plugin.settings.apiBaseUrl = value; + await this.plugin.saveSettings(); + }) + ); + } +} diff --git a/mainBasic.ts b/mainBasic.ts new file mode 100644 index 0000000..2d07212 --- /dev/null +++ b/mainBasic.ts @@ -0,0 +1,134 @@ +import { App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting } from 'obsidian'; + +// Remember to rename these classes and interfaces! + +interface MyPluginSettings { + mySetting: string; +} + +const DEFAULT_SETTINGS: MyPluginSettings = { + mySetting: 'default' +} + +export default class MyPlugin extends Plugin { + settings: MyPluginSettings; + + async onload() { + await this.loadSettings(); + + // This creates an icon in the left ribbon. + const ribbonIconEl = this.addRibbonIcon('dice', 'Sample Plugin', (evt: MouseEvent) => { + // Called when the user clicks the icon. + new Notice('This is a notice!'); + }); + // Perform additional things with the ribbon + ribbonIconEl.addClass('my-plugin-ribbon-class'); + + // This adds a status bar item to the bottom of the app. Does not work on mobile apps. + const statusBarItemEl = this.addStatusBarItem(); + statusBarItemEl.setText('Status Bar Text'); + + // This adds a simple command that can be triggered anywhere + this.addCommand({ + id: 'open-sample-modal-simple', + name: 'Open sample modal (simple)', + callback: () => { + new SampleModal(this.app).open(); + } + }); + // This adds an editor command that can perform some operation on the current editor instance + this.addCommand({ + id: 'sample-editor-command', + name: 'Sample editor command', + editorCallback: (editor: Editor, view: MarkdownView) => { + console.log(editor.getSelection()); + editor.replaceSelection('Sample Editor Command'); + } + }); + // This adds a complex command that can check whether the current state of the app allows execution of the command + this.addCommand({ + id: 'open-sample-modal-complex', + name: 'Open sample modal (complex)', + checkCallback: (checking: boolean) => { + // Conditions to check + const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView); + if (markdownView) { + // If checking is true, we're simply "checking" if the command can be run. + // If checking is false, then we want to actually perform the operation. + if (!checking) { + new SampleModal(this.app).open(); + } + + // This command will only show up in Command Palette when the check function returns true + return true; + } + } + }); + + // This adds a settings tab so the user can configure various aspects of the plugin + this.addSettingTab(new SampleSettingTab(this.app, this)); + + // If the plugin hooks up any global DOM events (on parts of the app that doesn't belong to this plugin) + // Using this function will automatically remove the event listener when this plugin is disabled. + this.registerDomEvent(document, 'click', (evt: MouseEvent) => { + console.log('click', evt); + }); + + // When registering intervals, this function will automatically clear the interval when the plugin is disabled. + this.registerInterval(window.setInterval(() => console.log('setInterval'), 5 * 60 * 1000)); + } + + onunload() { + + } + + async loadSettings() { + this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); + } + + async saveSettings() { + await this.saveData(this.settings); + } +} + +class SampleModal extends Modal { + constructor(app: App) { + super(app); + } + + onOpen() { + const {contentEl} = this; + contentEl.setText('Woah!'); + } + + onClose() { + const {contentEl} = this; + contentEl.empty(); + } +} + +class SampleSettingTab extends PluginSettingTab { + plugin: MyPlugin; + + constructor(app: App, plugin: MyPlugin) { + super(app, plugin); + this.plugin = plugin; + } + + display(): void { + const {containerEl} = this; + + containerEl.empty(); + + new Setting(containerEl) + .setName('Setting #1') + .setDesc('It\'s a secret') + .addText(text => text + .setPlaceholder('Enter your secret') + .setValue(this.plugin.settings.mySetting) + .onChange(async (value) => { + this.plugin.settings.mySetting = value; + await this.plugin.saveSettings(); + })); + } +} diff --git a/manifest.json b/manifest.json index f831909..d2419e6 100644 --- a/manifest.json +++ b/manifest.json @@ -1,10 +1,11 @@ { - "id": "llm-agent-plugin", - "name": "LLM Agent", - "version": "1.0.0", - "minAppVersion": "0.15.0", - "description": "LLM Agent with graph visualization and chat interface", - "author": "Your Name", - "authorUrl": "", - "isDesktopOnly": false -} \ No newline at end of file + "id": "llm-agent-plugin", + "name": "LLM Agent", + "version": "1.0.0", + "minAppVersion": "0.15.0", + "description": "LLM Agent with graph visualization and chat interface", + "author": "tylerfg", + "authorUrl": "", + "fundingUrl": "", + "isDesktopOnly": false +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..e958d17 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2235 @@ +{ + "name": "obsidian-sample-plugin", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "obsidian-sample-plugin", + "version": "1.0.0", + "license": "MIT", + "devDependencies": { + "@types/node": "^16.11.6", + "@typescript-eslint/eslint-plugin": "5.29.0", + "@typescript-eslint/parser": "5.29.0", + "builtin-modules": "3.3.0", + "esbuild": "0.17.3", + "obsidian": "latest", + "tslib": "^2.4.0", + "typescript": "4.7.4" + } + }, + "node_modules/@codemirror/state": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz", + "integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==", + "dev": true, + "peer": true, + "dependencies": { + "@marijn/find-cluster-break": "^1.0.0" + } + }, + "node_modules/@codemirror/view": { + "version": "6.38.2", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.2.tgz", + "integrity": "sha512-bTWAJxL6EOFLPzTx+O5P5xAO3gTqpatQ2b/ARQ8itfU/v2LlpS3pH2fkL0A3E/Fx8Y2St2KES7ZEV0sHTsSW/A==", + "dev": true, + "peer": true, + "dependencies": { + "@codemirror/state": "^6.5.0", + "crelt": "^1.0.6", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.3.tgz", + "integrity": "sha512-1Mlz934GvbgdDmt26rTLmf03cAgLg5HyOgJN+ZGCeP3Q9ynYTNMn2/LQxIl7Uy+o4K6Rfi2OuLsr12JQQR8gNg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.3.tgz", + "integrity": "sha512-XvJsYo3dO3Pi4kpalkyMvfQsjxPWHYjoX4MDiB/FUM4YMfWcXa5l4VCwFWVYI1+92yxqjuqrhNg0CZg3gSouyQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.3.tgz", + "integrity": "sha512-nuV2CmLS07Gqh5/GrZLuqkU9Bm6H6vcCspM+zjp9TdQlxJtIe+qqEXQChmfc7nWdyr/yz3h45Utk1tUn8Cz5+A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.3.tgz", + "integrity": "sha512-01Hxaaat6m0Xp9AXGM8mjFtqqwDjzlMP0eQq9zll9U85ttVALGCGDuEvra5Feu/NbP5AEP1MaopPwzsTcUq1cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.3.tgz", + "integrity": "sha512-Eo2gq0Q/er2muf8Z83X21UFoB7EU6/m3GNKvrhACJkjVThd0uA+8RfKpfNhuMCl1bKRfBzKOk6xaYKQZ4lZqvA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.3.tgz", + "integrity": "sha512-CN62ESxaquP61n1ZjQP/jZte8CE09M6kNn3baos2SeUfdVBkWN5n6vGp2iKyb/bm/x4JQzEvJgRHLGd5F5b81w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.3.tgz", + "integrity": "sha512-feq+K8TxIznZE+zhdVurF3WNJ/Sa35dQNYbaqM/wsCbWdzXr5lyq+AaTUSER2cUR+SXPnd/EY75EPRjf4s1SLg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.3.tgz", + "integrity": "sha512-CLP3EgyNuPcg2cshbwkqYy5bbAgK+VhyfMU7oIYyn+x4Y67xb5C5ylxsNUjRmr8BX+MW3YhVNm6Lq6FKtRTWHQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.3.tgz", + "integrity": "sha512-JHeZXD4auLYBnrKn6JYJ0o5nWJI9PhChA/Nt0G4MvLaMrvXuWnY93R3a7PiXeJQphpL1nYsaMcoV2QtuvRnF/g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.3.tgz", + "integrity": "sha512-FyXlD2ZjZqTFh0sOQxFDiWG1uQUEOLbEh9gKN/7pFxck5Vw0qjWSDqbn6C10GAa1rXJpwsntHcmLqydY9ST9ZA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.3.tgz", + "integrity": "sha512-OrDGMvDBI2g7s04J8dh8/I7eSO+/E7nMDT2Z5IruBfUO/RiigF1OF6xoH33Dn4W/OwAWSUf1s2nXamb28ZklTA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.3.tgz", + "integrity": "sha512-DcnUpXnVCJvmv0TzuLwKBC2nsQHle8EIiAJiJ+PipEVC16wHXaPEKP0EqN8WnBe0TPvMITOUlP2aiL5YMld+CQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.3.tgz", + "integrity": "sha512-BDYf/l1WVhWE+FHAW3FzZPtVlk9QsrwsxGzABmN4g8bTjmhazsId3h127pliDRRu5674k1Y2RWejbpN46N9ZhQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.3.tgz", + "integrity": "sha512-WViAxWYMRIi+prTJTyV1wnqd2mS2cPqJlN85oscVhXdb/ZTFJdrpaqm/uDsZPGKHtbg5TuRX/ymKdOSk41YZow==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.3.tgz", + "integrity": "sha512-Iw8lkNHUC4oGP1O/KhumcVy77u2s6+KUjieUqzEU3XuWJqZ+AY7uVMrrCbAiwWTkpQHkr00BuXH5RpC6Sb/7Ug==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.3.tgz", + "integrity": "sha512-0AGkWQMzeoeAtXQRNB3s4J1/T2XbigM2/Mn2yU1tQSmQRmHIZdkGbVq2A3aDdNslPyhb9/lH0S5GMTZ4xsjBqg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.3.tgz", + "integrity": "sha512-4+rR/WHOxIVh53UIQIICryjdoKdHsFZFD4zLSonJ9RRw7bhKzVyXbnRPsWSfwybYqw9sB7ots/SYyufL1mBpEg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.3.tgz", + "integrity": "sha512-cVpWnkx9IYg99EjGxa5Gc0XmqumtAwK3aoz7O4Dii2vko+qXbkHoujWA68cqXjhh6TsLaQelfDO4MVnyr+ODeA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.3.tgz", + "integrity": "sha512-RxmhKLbTCDAY2xOfrww6ieIZkZF+KBqG7S2Ako2SljKXRFi+0863PspK74QQ7JpmWwncChY25JTJSbVBYGQk2Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.3.tgz", + "integrity": "sha512-0r36VeEJ4efwmofxVJRXDjVRP2jTmv877zc+i+Pc7MNsIr38NfsjkQj23AfF7l0WbB+RQ7VUb+LDiqC/KY/M/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.3.tgz", + "integrity": "sha512-wgO6rc7uGStH22nur4aLFcq7Wh86bE9cOFmfTr/yxN3BXvDEdCSXyKkO+U5JIt53eTOgC47v9k/C1bITWL/Teg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.3.tgz", + "integrity": "sha512-FdVl64OIuiKjgXBjwZaJLKp0eaEckifbhn10dXWhysMJkWblg3OEEGKSIyhiD5RSgAya8WzP3DNkngtIg3Nt7g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "dev": true, + "peer": true, + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "peer": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "peer": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "peer": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "peer": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "peer": true + }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", + "dev": true, + "peer": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@types/codemirror": { + "version": "5.60.8", + "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.8.tgz", + "integrity": "sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw==", + "dev": true, + "dependencies": { + "@types/tern": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "16.18.126", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.126.tgz", + "integrity": "sha512-OTcgaiwfGFBKacvfwuHzzn1KLxH/er8mluiy8/uM3sGXHaRe73RrSIj01jow9t4kJEW633Ov+cOexXeiApTyAw==", + "dev": true + }, + "node_modules/@types/tern": { + "version": "0.23.9", + "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz", + "integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==", + "dev": true, + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.29.0.tgz", + "integrity": "sha512-kgTsISt9pM53yRFQmLZ4npj99yGl3x3Pl7z4eA66OuTzAGC4bQB5H5fuLwPnqTKU3yyrrg4MIhjF17UYnL4c0w==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.29.0", + "@typescript-eslint/type-utils": "5.29.0", + "@typescript-eslint/utils": "5.29.0", + "debug": "^4.3.4", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.2.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.29.0.tgz", + "integrity": "sha512-ruKWTv+x0OOxbzIw9nW5oWlUopvP/IQDjB5ZqmTglLIoDTctLlAJpAQFpNPJP/ZI7hTT9sARBosEfaKbcFuECw==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.29.0", + "@typescript-eslint/types": "5.29.0", + "@typescript-eslint/typescript-estree": "5.29.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.29.0.tgz", + "integrity": "sha512-etbXUT0FygFi2ihcxDZjz21LtC+Eps9V2xVx09zFoN44RRHPrkMflidGMI+2dUs821zR1tDS6Oc9IXxIjOUZwA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.29.0", + "@typescript-eslint/visitor-keys": "5.29.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.29.0.tgz", + "integrity": "sha512-JK6bAaaiJozbox3K220VRfCzLa9n0ib/J+FHIwnaV3Enw/TO267qe0pM1b1QrrEuy6xun374XEAsRlA86JJnyg==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "5.29.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.29.0.tgz", + "integrity": "sha512-X99VbqvAXOMdVyfFmksMy3u8p8yoRGITgU1joBJPzeYa0rhdf5ok9S56/itRoUSh99fiDoMtarSIJXo7H/SnOg==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.29.0.tgz", + "integrity": "sha512-mQvSUJ/JjGBdvo+1LwC+GY2XmSYjK1nAaVw2emp/E61wEVYEyibRHCqm1I1vEKbXCpUKuW4G7u9ZCaZhJbLoNQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.29.0", + "@typescript-eslint/visitor-keys": "5.29.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.29.0.tgz", + "integrity": "sha512-3Eos6uP1nyLOBayc/VUdKZikV90HahXE5Dx9L5YlSd/7ylQPXhLk1BYb29SDgnBnTp+jmSZUU0QxUiyHgW4p7A==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.29.0", + "@typescript-eslint/types": "5.29.0", + "@typescript-eslint/typescript-estree": "5.29.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.29.0.tgz", + "integrity": "sha512-Hpb/mCWsjILvikMQoZIE3voc9wtQcS0A9FUw3h8bhr9UxBdtI/tw1ZDZUOXHXLOVMedKCH5NxyzATwnU78bWCQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.29.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "peer": true + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peer": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "peer": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "peer": true + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "peer": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "peer": true + }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", + "dev": true, + "peer": true + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "peer": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "peer": true + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "peer": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/esbuild": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.3.tgz", + "integrity": "sha512-9n3AsBRe6sIyOc6kmoXg2ypCLgf3eZSraWFRpnkto+svt8cZNuKTkb1bhQcitBcvIqjNiK7K0J3KPmwGSfkA8g==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.17.3", + "@esbuild/android-arm64": "0.17.3", + "@esbuild/android-x64": "0.17.3", + "@esbuild/darwin-arm64": "0.17.3", + "@esbuild/darwin-x64": "0.17.3", + "@esbuild/freebsd-arm64": "0.17.3", + "@esbuild/freebsd-x64": "0.17.3", + "@esbuild/linux-arm": "0.17.3", + "@esbuild/linux-arm64": "0.17.3", + "@esbuild/linux-ia32": "0.17.3", + "@esbuild/linux-loong64": "0.17.3", + "@esbuild/linux-mips64el": "0.17.3", + "@esbuild/linux-ppc64": "0.17.3", + "@esbuild/linux-riscv64": "0.17.3", + "@esbuild/linux-s390x": "0.17.3", + "@esbuild/linux-x64": "0.17.3", + "@esbuild/netbsd-x64": "0.17.3", + "@esbuild/openbsd-x64": "0.17.3", + "@esbuild/sunos-x64": "0.17.3", + "@esbuild/win32-arm64": "0.17.3", + "@esbuild/win32-ia32": "0.17.3", + "@esbuild/win32-x64": "0.17.3" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "peer": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "peer": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "peer": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "peer": true + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "peer": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "peer": true + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "peer": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "peer": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "peer": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "peer": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "peer": true + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "peer": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "peer": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "peer": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "peer": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "peer": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "peer": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "peer": true + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "peer": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "peer": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "peer": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "peer": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "peer": true + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "peer": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "peer": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "peer": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "peer": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "peer": true + }, + "node_modules/obsidian": { + "version": "1.8.7", + "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.8.7.tgz", + "integrity": "sha512-h4bWwNFAGRXlMlMAzdEiIM2ppTGlrh7uGOJS6w4gClrsjc+ei/3YAtU2VdFUlCiPuTHpY4aBpFJJW75S1Tl/JA==", + "dev": true, + "dependencies": { + "@types/codemirror": "5.60.8", + "moment": "2.29.4" + }, + "peerDependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "peer": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "peer": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "peer": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "peer": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "peer": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "peer": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "peer": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-mod": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", + "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==", + "dev": true, + "peer": true + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "peer": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "peer": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", + "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "peer": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "dev": true, + "peer": true + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "peer": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "peer": true + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..72541e1 --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "obsidian-sample-plugin", + "version": "1.0.0", + "description": "This is a sample plugin for Obsidian (https://obsidian.md)", + "main": "main.js", + "scripts": { + "dev": "node esbuild.config.mjs", + "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production", + "version": "node version-bump.mjs && git add manifest.json versions.json" + }, + "keywords": [], + "author": "", + "license": "MIT", + "devDependencies": { + "@types/node": "^16.11.6", + "@typescript-eslint/eslint-plugin": "5.29.0", + "@typescript-eslint/parser": "5.29.0", + "builtin-modules": "3.3.0", + "esbuild": "0.17.3", + "obsidian": "latest", + "tslib": "^2.4.0", + "typescript": "4.7.4" + } +} diff --git a/src/components/ChatPanel.js b/src/components/ChatPanel.js deleted file mode 100644 index 8a25be5..0000000 --- a/src/components/ChatPanel.js +++ /dev/null @@ -1,135 +0,0 @@ -class ChatPanel { - constructor(container, props) { - this.container = container; - this.props = props; - this.chatHistory = props.chatHistory || []; - - this.init(); - } - - init() { - this.container.innerHTML = ` -
-
-
-
- - -
- -
-
- `; - - this.messageInput = this.container.querySelector('#message-input'); - this.sendButton = this.container.querySelector('#send-button'); - this.suggestionsContainer = this.container.querySelector('#suggestions'); - - this.setupEventListeners(); - this.renderHistory(); - } - - setupEventListeners() { - this.sendButton.addEventListener('click', () => this.handleSend()); - - this.messageInput.addEventListener('keydown', (e) => { - if (e.key === 'Enter') { - if (this.suggestionsContainer.style.display === 'block') { - const firstSuggestion = this.suggestionsContainer.querySelector('.suggestion-item'); - if (firstSuggestion) { - this.messageInput.value = firstSuggestion.textContent; - this.hideSuggestions(); - e.preventDefault(); - return; - } - } - this.handleSend(); - e.preventDefault(); - } - }); - - this.messageInput.addEventListener('input', (e) => { - this.handleInput(e.target.value); - }); - } - - handleInput(value) { - const commands = ['/help', '/imagine', '/analyze', '/subtitles_meet', '/subtitles_teams', '/summarize', '/chat']; - - if (value.startsWith('/')) { - const filteredCommands = commands.filter(cmd => - cmd.startsWith(value.toLowerCase()) - ); - this.showSuggestions(filteredCommands); - } else { - this.hideSuggestions(); - } - } - - showSuggestions(suggestions) { - if (suggestions.length === 0) { - this.hideSuggestions(); - return; - } - - this.suggestionsContainer.innerHTML = suggestions - .map(suggestion => `
${suggestion}
`) - .join(''); - - this.suggestionsContainer.style.display = 'block'; - - // Добавляем обработчики кликов на предложения - this.suggestionsContainer.querySelectorAll('.suggestion-item').forEach(item => { - item.addEventListener('click', () => { - this.messageInput.value = item.textContent; - this.hideSuggestions(); - this.messageInput.focus(); - }); - }); - } - - hideSuggestions() { - this.suggestionsContainer.style.display = 'none'; - } - - handleSend() { - const message = this.messageInput.value.trim(); - if (message && this.props.onSendMessage) { - this.props.onSendMessage(message); - this.messageInput.value = ''; - this.hideSuggestions(); - } - } - - renderHistory() { - const historyContainer = this.container.querySelector('#chat-history'); - historyContainer.innerHTML = this.chatHistory - .map(msg => ` -
-
${this.processMessageContent(msg.content)}
-
- `) - .join(''); - - // Прокручиваем к концу - historyContainer.scrollTop = historyContainer.scrollHeight; - } - - processMessageContent(content) { - // Простейшая обработка Markdown для изображений - return content - .replace(/]*?)>/g, '') - .replace(/\n/g, '
'); - } - - updateHistory(newHistory) { - this.chatHistory = newHistory; - this.renderHistory(); - } - - destroy() { - this.container.innerHTML = ''; - } -} - -module.exports = { ChatPanel }; \ No newline at end of file diff --git a/src/components/ChatPanel.ts b/src/components/ChatPanel.ts new file mode 100644 index 0000000..7cd443c --- /dev/null +++ b/src/components/ChatPanel.ts @@ -0,0 +1,147 @@ +import { ChatHistoryItem } from './models/ChatHistoryItem'; + +export class ChatPanel { + container: HTMLElement; + props: { + chatHistory: ChatHistoryItem[]; + onSendMessage: (message: string) => void; + }; + chatHistory: ChatHistoryItem[]; + messageInput: HTMLInputElement; + sendButton: HTMLButtonElement; + suggestionsContainer: HTMLDivElement; + + constructor( + container: HTMLElement, + props: { + chatHistory: ChatHistoryItem[]; + onSendMessage: (message: string) => void; + } + ) { + this.container = container; + this.props = props; + this.chatHistory = props.chatHistory || []; + + this.init(); + } + + init(): void { + this.container.innerHTML = ` +
+
+
+
+ + +
+ +
+
+ `; + + this.messageInput = this.container.querySelector('#message-input') as HTMLInputElement; + this.sendButton = this.container.querySelector('#send-button') as HTMLButtonElement; + this.suggestionsContainer = this.container.querySelector('#suggestions') as HTMLDivElement; + + this.setupEventListeners(); + this.renderHistory(); + } + + setupEventListeners(): void { + this.sendButton.addEventListener('click', () => this.handleSend()); + + this.messageInput.addEventListener('keydown', (e) => { + if (e.key === 'Enter') { + if (this.suggestionsContainer.style.display === 'block') { + const firstSuggestion = this.suggestionsContainer.querySelector('.suggestion-item'); + if (firstSuggestion) { + this.messageInput.value = firstSuggestion.textContent || ''; + this.hideSuggestions(); + e.preventDefault(); + return; + } + } + this.handleSend(); + e.preventDefault(); + } + }); + + this.messageInput.addEventListener('input', (e) => { + this.handleInput((e.target as HTMLInputElement).value); + }); + } + + handleInput(value: string): void { + const commands = ['/help', '/imagine', '/analyze', '/subtitles_meet', '/subtitles_teams', '/summarize', '/chat']; + + if (value.startsWith('/')) { + const filteredCommands = commands.filter((cmd) => cmd.startsWith(value.toLowerCase())); + this.showSuggestions(filteredCommands); + } else { + this.hideSuggestions(); + } + } + + showSuggestions(suggestions: string[]): void { + if (suggestions.length === 0) { + this.hideSuggestions(); + return; + } + + this.suggestionsContainer.innerHTML = suggestions.map((suggestion) => `
${suggestion}
`).join(''); + + this.suggestionsContainer.style.display = 'block'; + + // Добавляем обработчики кликов на предложения + this.suggestionsContainer.querySelectorAll('.suggestion-item').forEach((item) => { + item.addEventListener('click', () => { + this.messageInput.value = item.textContent || ''; + this.hideSuggestions(); + this.messageInput.focus(); + }); + }); + } + + hideSuggestions(): void { + this.suggestionsContainer.style.display = 'none'; + } + + handleSend(): void { + const message = this.messageInput.value.trim(); + if (message && this.props.onSendMessage) { + this.props.onSendMessage(message); + this.messageInput.value = ''; + this.hideSuggestions(); + } + } + + renderHistory(): void { + const historyContainer = this.container.querySelector('#chat-history') as HTMLElement; + historyContainer.innerHTML = this.chatHistory + .map( + (msg) => ` +
+
${this.processMessageContent(msg.content)}
+
+ ` + ) + .join(''); + + // Прокручиваем к концу + historyContainer.scrollTop = historyContainer.scrollHeight; + } + + processMessageContent(content: string): string { + // Простейшая обработка Markdown для изображений + return content.replace(/]*?)>/g, '').replace(/\n/g, '
'); + } + + updateHistory(newHistory: ChatHistoryItem[]): void { + this.chatHistory = newHistory; + this.renderHistory(); + } + + destroy(): void { + this.container.innerHTML = ''; + } +} diff --git a/src/components/GraphPanel.js b/src/components/GraphPanel.js deleted file mode 100644 index aaf3c21..0000000 --- a/src/components/GraphPanel.js +++ /dev/null @@ -1,128 +0,0 @@ -// Адаптация оригинального GraphPanel для работы в Obsidian - -class GraphPanel { - constructor(container, props) { - this.container = container; - this.props = props; - this.nodes = []; - this.edges = []; - - this.init(); - } - - init() { - // Создаем структуру для React Flow - this.container.innerHTML = ` -
-
-
- `; - - // Загружаем React Flow динамически (если доступен) - this.initReactFlow(); - } - - async initReactFlow() { - try { - // В среде Obsidian нужно будет подключить React Flow через CDN или bundler - // Для демонстрации создаем упрощенную визуализацию - this.createSimpleGraph(); - } catch (error) { - console.error('Ошибка инициализации React Flow:', error); - this.createFallbackGraph(); - } - } - - createSimpleGraph() { - const container = this.container.querySelector('#reactflow-container'); - container.innerHTML = ` -
-
- -
- `; - - this.renderNodes(); - this.renderEdges(); - } - - renderNodes() { - const nodesContainer = this.container.querySelector('#graph-nodes'); - nodesContainer.innerHTML = ''; - - this.nodes.forEach((node, index) => { - const nodeEl = document.createElement('div'); - nodeEl.className = `graph-node node-${node.type || 'default'}`; - nodeEl.style.cssText = ` - position: absolute; - left: ${50 + index * 150}px; - top: ${50 + Math.floor(index / 5) * 80}px; - padding: 8px 12px; - border: 2px solid #333; - border-radius: 4px; - background: #fff; - cursor: pointer; - max-width: 120px; - word-wrap: break-word; - `; - nodeEl.textContent = node.data?.label || `Node ${node.id}`; - nodeEl.dataset.nodeId = node.id; - - nodeEl.addEventListener('click', () => { - this.handleNodeClick(node.id); - }); - - nodesContainer.appendChild(nodeEl); - }); - } - - renderEdges() { - const edgesContainer = this.container.querySelector('#graph-edges'); - edgesContainer.innerHTML = ''; - // Упрощенная отрисовка связей - в реальной реализации нужно будет вычислять позиции - } - - createFallbackGraph() { - const container = this.container.querySelector('#reactflow-container'); - container.innerHTML = ` -
-

Упрощенное представление графа

-
-
- `; - this.renderFallbackNodes(); - } - - renderFallbackNodes() { - const nodesList = this.container.querySelector('#nodes-list'); - nodesList.innerHTML = ''; - - this.nodes.forEach(node => { - const nodeEl = document.createElement('div'); - nodeEl.className = 'fallback-node'; - nodeEl.textContent = `${node.id}: ${node.data?.label || 'No label'}`; - nodeEl.addEventListener('click', () => this.handleNodeClick(node.id)); - nodesList.appendChild(nodeEl); - }); - } - - updateData(graphData) { - this.nodes = graphData.nodes || []; - this.edges = graphData.edges || []; - this.renderNodes(); - this.renderEdges(); - } - - handleNodeClick(nodeId) { - if (this.props.onNodeClick) { - this.props.onNodeClick(nodeId); - } - } - - destroy() { - // Очистка ресурсов - this.container.innerHTML = ''; - } -} - -module.exports = { GraphPanel }; \ No newline at end of file diff --git a/src/components/GraphPanel.ts b/src/components/GraphPanel.ts new file mode 100644 index 0000000..923ea32 --- /dev/null +++ b/src/components/GraphPanel.ts @@ -0,0 +1,138 @@ +export class GraphPanel { + container: HTMLElement; + props: { + graphData: { nodes: any[]; edges: any[] }; + onNodeClick: (nodeId: string) => void; + }; + nodes: any[]; + edges: any[]; + + constructor( + container: HTMLElement, + props: { + graphData: { nodes: any[]; edges: any[] }; + onNodeClick: (nodeId: string) => void; + } + ) { + this.container = container; + this.props = props; + this.nodes = []; + this.edges = []; + + this.init(); + } + + init(): void { + // Создаем структуру для React Flow + this.container.innerHTML = ` +
+
+
+ `; + + // Загружаем React Flow динамически (если доступен) + this.initReactFlow(); + } + + async initReactFlow(): Promise { + try { + // В среде Obsidian нужно будет подключить React Flow через CDN или bundler + // Для демонстрации создаем упрощенную визуализацию + this.createSimpleGraph(); + } catch (error) { + console.error('Ошибка инициализации React Flow:', error); + this.createFallbackGraph(); + } + } + + createSimpleGraph(): void { + const container = this.container.querySelector('#reactflow-container') as HTMLElement; + container.innerHTML = ` +
+
+ +
+ `; + + this.renderNodes(); + this.renderEdges(); + } + + renderNodes(): void { + const nodesContainer = this.container.querySelector('#graph-nodes') as HTMLElement; + nodesContainer.innerHTML = ''; + + this.nodes.forEach((node, index) => { + const nodeEl = document.createElement('div'); + nodeEl.className = `graph-node node-${node.type || 'default'}`; + nodeEl.style.cssText = ` + position: absolute; + left: ${50 + index * 150}px; + top: ${50 + Math.floor(index / 5) * 80}px; + padding: 8px 12px; + border: 2px solid #333; + border-radius: 4px; + background: #fff; + cursor: pointer; + max-width: 120px; + word-wrap: break-word; + `; + nodeEl.textContent = node.data?.label || `Node ${node.id}`; + nodeEl.dataset.nodeId = node.id; + + nodeEl.addEventListener('click', () => { + this.handleNodeClick(node.id); + }); + + nodesContainer.appendChild(nodeEl); + }); + } + + renderEdges(): void { + const edgesContainer = this.container.querySelector('#graph-edges') as HTMLElement; + edgesContainer.innerHTML = ''; + // Упрощенная отрисовка связей - в реальной реализации нужно будет вычислять позиции + } + + createFallbackGraph(): void { + const container = this.container.querySelector('#reactflow-container') as HTMLElement; + container.innerHTML = ` +
+

Упрощенное представление графа

+
+
+ `; + this.renderFallbackNodes(); + } + + renderFallbackNodes(): void { + const nodesList = this.container.querySelector('#nodes-list') as HTMLElement; + nodesList.innerHTML = ''; + + this.nodes.forEach((node) => { + const nodeEl = document.createElement('div'); + nodeEl.className = 'fallback-node'; + nodeEl.textContent = `${node.id}: ${node.data?.label || 'No label'}`; + nodeEl.addEventListener('click', () => this.handleNodeClick(node.id)); + nodesList.appendChild(nodeEl); + }); + } + + updateData(graphData: { nodes: any[]; edges: any[] }): void { + this.nodes = graphData.nodes || []; + this.edges = graphData.edges || []; + this.renderNodes(); + this.renderEdges(); + } + + handleNodeClick(nodeId: string): void { + if (this.props.onNodeClick) { + this.props.onNodeClick(nodeId); + } + } + + destroy(): void { + // Очистка ресурсов + this.container.innerHTML = ''; + } +} diff --git a/src/components/HistoryPanel.js b/src/components/HistoryPanel.js deleted file mode 100644 index 7278f67..0000000 --- a/src/components/HistoryPanel.js +++ /dev/null @@ -1,131 +0,0 @@ -class HistoryPanel { - constructor(container, props) { - this.container = container; - this.props = props; - this.graphs = props.graphs || []; - - this.init(); - } - - init() { - this.container.innerHTML = ` -
-
-

История диалогов

- -
-
- -
- `; - - this.newChatButton = this.container.querySelector('#new-chat-button'); - this.chatList = this.container.querySelector('#chat-list'); - this.contextMenu = this.container.querySelector('#context-menu'); - this.deleteButton = this.container.querySelector('#delete-graph-button'); - - this.setupEventListeners(); - this.renderGraphs(); - } - - setupEventListeners() { - this.newChatButton.addEventListener('click', () => { - if (this.props.onNewChat) { - this.props.onNewChat(); - } - }); - - this.deleteButton.addEventListener('click', () => { - if (this.contextMenuGraphId) { - this.handleDeleteGraph(this.contextMenuGraphId); - } - }); - - // Закрытие контекстного меню при клике вне его - document.addEventListener('click', (e) => { - if (!this.contextMenu.contains(e.target)) { - this.contextMenu.style.display = 'none'; - } - }); - } - - renderGraphs() { - this.chatList.innerHTML = this.graphs - .map(graph => ` -
-
-
- - ${graph.first_message || `Граф ${graph.id}`} - -
-
- -
-
-
- `) - .join(''); - - // Добавляем обработчики событий - this.chatList.querySelectorAll('.chat-list-item').forEach(item => { - item.addEventListener('click', (e) => { - if (!e.target.classList.contains('chat-item-options-button')) { - const graphId = item.dataset.graphId; - if (this.props.onGraphSelect) { - this.props.onGraphSelect(graphId); - } - } - }); - }); - - this.chatList.querySelectorAll('.chat-item-options-button').forEach(button => { - button.addEventListener('click', (e) => { - e.stopPropagation(); - this.showContextMenu(e, button.dataset.graphId); - }); - }); - } - - showContextMenu(event, graphId) { - this.contextMenuGraphId = graphId; - this.contextMenu.style.display = 'block'; - this.contextMenu.style.left = `${event.clientX}px`; - this.contextMenu.style.top = `${event.clientY}px`; - } - - async handleDeleteGraph(graphId) { - try { - const response = await fetch(`http://localhost:5000/api/graphs/${graphId}`, { - method: 'DELETE', - }); - - if (!response.ok) { - throw new Error(`HTTP ошибка! статус: ${response.status}`); - } - - // Обновляем список графов - this.graphs = this.graphs.filter(graph => graph.id !== graphId); - this.renderGraphs(); - this.contextMenu.style.display = 'none'; - - } catch (error) { - console.error('Ошибка удаления графа:', error); - } - } - - updateGraphs(newGraphs) { - this.graphs = newGraphs; - this.renderGraphs(); - } - - destroy() { - this.container.innerHTML = ''; - } -} - -module.exports = { HistoryPanel }; \ No newline at end of file diff --git a/src/components/HistoryPanel.ts b/src/components/HistoryPanel.ts new file mode 100644 index 0000000..a8caf13 --- /dev/null +++ b/src/components/HistoryPanel.ts @@ -0,0 +1,150 @@ +export class HistoryPanel { + container: HTMLElement; + props: { + graphs: any[]; + onGraphSelect: (graphId: string) => void; + onNewChat: () => void; + }; + graphs: any[]; + newChatButton: HTMLButtonElement; + chatList: HTMLElement; + contextMenu: HTMLElement; + deleteButton: HTMLButtonElement; + contextMenuGraphId: string | null; + + constructor( + container: HTMLElement, + props: { + graphs: any[]; + onGraphSelect: (graphId: string) => void; + onNewChat: () => void; + } + ) { + this.container = container; + this.props = props; + this.graphs = props.graphs || []; + + this.init(); + } + + init(): void { + this.container.innerHTML = ` +
+
+

История диалогов

+ +
+
+ +
+ `; + + this.newChatButton = this.container.querySelector('#new-chat-button') as HTMLButtonElement; + this.chatList = this.container.querySelector('#chat-list') as HTMLElement; + this.contextMenu = this.container.querySelector('#context-menu') as HTMLElement; + this.deleteButton = this.container.querySelector('#delete-graph-button') as HTMLButtonElement; + + this.setupEventListeners(); + this.renderGraphs(); + } + + setupEventListeners(): void { + this.newChatButton.addEventListener('click', () => { + if (this.props.onNewChat) { + this.props.onNewChat(); + } + }); + + this.deleteButton.addEventListener('click', () => { + if (this.contextMenuGraphId) { + this.handleDeleteGraph(this.contextMenuGraphId); + } + }); + + // Закрытие контекстного меню при клике вне его + document.addEventListener('click', (e) => { + if (!this.contextMenu.contains(e.target as Node)) { + this.contextMenu.style.display = 'none'; + } + }); + } + + renderGraphs(): void { + this.chatList.innerHTML = this.graphs + .map( + (graph) => ` +
+
+
+ + ${graph.first_message || `Граф ${graph.id}`} + +
+
+ +
+
+
+ ` + ) + .join(''); + + // Добавляем обработчики событий + this.chatList.querySelectorAll('.chat-list-item').forEach((item) => { + item.addEventListener('click', (e) => { + if (!(e.target as HTMLElement).classList.contains('chat-item-options-button')) { + const graphId = (item as HTMLElement).dataset.graphId; + if (graphId && this.props.onGraphSelect) { + this.props.onGraphSelect(graphId); + } + } + }); + }); + + this.chatList.querySelectorAll('.chat-item-options-button').forEach((button) => { + button.addEventListener('click', (e) => { + e.stopPropagation(); + this.showContextMenu(e, (button as HTMLElement).dataset.graphId || ''); + }); + }); + } + + showContextMenu(event: Event, graphId: string): void { + this.contextMenuGraphId = graphId; + this.contextMenu.style.display = 'block'; + this.contextMenu.style.left = `${(event as MouseEvent).clientX}px`; + this.contextMenu.style.top = `${(event as MouseEvent).clientY}px`; + } + + async handleDeleteGraph(graphId: string): Promise { + try { + const response = await fetch(`http://localhost:5000/api/graphs/${graphId}`, { + method: 'DELETE' + }); + + if (!response.ok) { + throw new Error(`HTTP ошибка! статус: ${response.status}`); + } + + // Обновляем список графов + this.graphs = this.graphs.filter((graph) => graph.id !== graphId); + this.renderGraphs(); + this.contextMenu.style.display = 'none'; + } catch (error) { + console.error('Ошибка удаления графа:', error); + } + } + + updateGraphs(newGraphs: any[]): void { + this.graphs = newGraphs; + this.renderGraphs(); + } + + destroy(): void { + this.container.innerHTML = ''; + } +} diff --git a/src/components/models/ChatHistoryItem.ts b/src/components/models/ChatHistoryItem.ts new file mode 100644 index 0000000..c4a92d9 --- /dev/null +++ b/src/components/models/ChatHistoryItem.ts @@ -0,0 +1,4 @@ +export interface ChatHistoryItem { + role: string; + content: string; +} diff --git a/src/utils/EventBus.js b/src/utils/EventBus.js deleted file mode 100644 index d2d3e49..0000000 --- a/src/utils/EventBus.js +++ /dev/null @@ -1,28 +0,0 @@ -// ----------------------------------------------- Event Bus --------------------------------------------------------------- - -class EventBus { - constructor() { - this.events = {}; - } - - on(eventName, callback) { - if (!this.events[eventName]) { - this.events[eventName] = []; - } - this.events[eventName].push(callback); - } - - off(eventName, callback) { - if (this.events[eventName]) { - this.events[eventName] = this.events[eventName].filter(cb => cb !== callback); - } - } - - emit(eventName, data) { - if (this.events[eventName]) { - this.events[eventName].forEach(callback => callback(data)); - } - } -} - -module.exports = { EventBus }; \ No newline at end of file diff --git a/src/utils/EventBus.ts b/src/utils/EventBus.ts new file mode 100644 index 0000000..bb186f5 --- /dev/null +++ b/src/utils/EventBus.ts @@ -0,0 +1,24 @@ +// ----------------------------------------------- Event Bus --------------------------------------------------------------- + +export class EventBus { + private events: { [key: string]: ((data?: any) => void)[] } = {}; + + on(eventName: string, callback: (data?: any) => void): void { + if (!this.events[eventName]) { + this.events[eventName] = []; + } + this.events[eventName].push(callback); + } + + off(eventName: string, callback: (data?: any) => void): void { + if (this.events[eventName]) { + this.events[eventName] = this.events[eventName].filter((cb) => cb !== callback); + } + } + + emit(eventName: string, data?: any): void { + if (this.events[eventName]) { + this.events[eventName].forEach((callback) => callback(data)); + } + } +} diff --git a/src/views/ChatView.js b/src/views/ChatView.js deleted file mode 100644 index 979199a..0000000 --- a/src/views/ChatView.js +++ /dev/null @@ -1,209 +0,0 @@ -const { ItemView } = require('obsidian'); -const { ChatPanel } = require('../components/ChatPanel'); -const { HistoryPanel } = require('../components/HistoryPanel'); - -const CHAT_VIEW_TYPE = 'llm-agent-chat-view'; - -// ----------------------------------------------- Chat View --------------------------------------------------------------- - -class ChatView extends ItemView { - constructor(leaf, plugin) { - super(leaf); - this.plugin = plugin; - this.chatHistory = []; - this.graphsList = []; - this.selectedGraphId = null; - this.currentNode = null; - } - - getViewType() { - return CHAT_VIEW_TYPE; - } - - getDisplayText() { - return 'LLM Agent Chat'; - } - - getIcon() { - return 'message-circle'; - } - - async onOpen() { - const container = this.containerEl.children[1]; - container.empty(); - - // Создаем контейнеры для React компонентов - this.chatContainer = container.createDiv(); - this.chatContainer.addClass('llm-agent-chat-container'); - - this.historyContainer = container.createDiv(); - this.historyContainer.addClass('llm-agent-history-container'); - - // Инициализируем React компоненты - this.chatPanel = new ChatPanel(this.chatContainer, { - chatHistory: this.chatHistory, - onSendMessage: this.handleSendMessage.bind(this) - }); - - this.historyPanel = new HistoryPanel(this.historyContainer, { - graphs: this.graphsList, - onGraphSelect: this.handleGraphSelect.bind(this), - onNewChat: this.handleNewChat.bind(this) - }); - - // Подписываемся на события - this.plugin.eventBus.on('node-selected', this.handleNodeSelected.bind(this)); - this.plugin.eventBus.on('new-chat', this.handleNewChat.bind(this)); - - // Загружаем список графов - this.fetchGraphsList(); - } - - async onClose() { - if (this.chatPanel) { - this.chatPanel.destroy(); - } - if (this.historyPanel) { - this.historyPanel.destroy(); - } - this.plugin.eventBus.off('node-selected', this.handleNodeSelected); - this.plugin.eventBus.off('new-chat', this.handleNewChat); - } - - // ----------------------------------------------- API Methods --------------------------------------------------------------- - - async fetchGraphsList() { - try { - const response = await fetch(`${this.plugin.settings.apiBaseUrl}/graphs`); - const data = await response.json(); - this.graphsList = data; - if (this.historyPanel) { - this.historyPanel.updateGraphs(this.graphsList); - } - } catch (error) { - console.error('Ошибка загрузки списка графов:', error); - } - } - - async fetchGraphData(graphId) { - try { - const response = await fetch(`${this.plugin.settings.apiBaseUrl}/graphs/${graphId}`); - if (!response.ok) { - if (response.status === 404) { - console.error(`Граф с ID ${graphId} не найден.`); - } - throw new Error(`HTTP ошибка! статус: ${response.status}`); - } - const data = await response.json(); - - // Обновляем граф - this.plugin.eventBus.emit('graph-updated', { - graphData: { - nodes: data.graph_nodes || [], - edges: data.graph_edges || [] - }, - currentNode: data.current_node_id - }); - - // Обновляем историю чата - this.chatHistory = data.messages || []; - if (this.chatPanel) { - this.chatPanel.updateHistory(this.chatHistory); - } - } catch (error) { - console.error('Ошибка загрузки данных графа:', error); - } - } - - async fetchMessagesFromRootToNode(nodeId) { - try { - const response = await fetch(`${this.plugin.settings.apiBaseUrl}/messages/${this.selectedGraphId}/${nodeId}`); - const data = await response.json(); - this.chatHistory = data; - if (this.chatPanel) { - this.chatPanel.updateHistory(this.chatHistory); - } - } catch (error) { - console.error('Ошибка загрузки сообщений:', error); - } - } - - // ----------------------------------------------- Event Handlers --------------------------------------------------------------- - - async handleSendMessage(message) { - try { - const response = await fetch(`${this.plugin.settings.apiBaseUrl}/chat`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - message: message, - graph_id: this.selectedGraphId, - parent_node_id: this.currentNode, - }), - }); - - if (!response.ok) { - throw new Error(`HTTP ошибка! статус: ${response.status}`); - } - - const result = await response.json(); - - // Обновляем выбранный ID графа - this.selectedGraphId = result.graph_id; - - // Устанавливаем текущий узел - const newCurrentNodeId = result.graph_visualization_data.current_node_id; - this.currentNode = newCurrentNodeId; - - // Обновляем граф - this.plugin.eventBus.emit('graph-updated', { - graphData: { - nodes: result.graph_visualization_data.nodes || [], - edges: result.graph_visualization_data.edges || [] - }, - currentNode: newCurrentNodeId - }); - - // Загружаем сообщения от корня до нового текущего узла - if (newCurrentNodeId) { - await this.fetchMessagesFromRootToNode(newCurrentNodeId); - } else { - this.chatHistory = result.messages || []; - if (this.chatPanel) { - this.chatPanel.updateHistory(this.chatHistory); - } - } - - // Обновляем список графов - this.fetchGraphsList(); - - } catch (error) { - console.error('Ошибка отправки сообщения:', error); - } - } - - handleNodeSelected(data) { - this.currentNode = data.nodeId; - if (this.selectedGraphId) { - this.fetchMessagesFromRootToNode(data.nodeId); - } - } - - handleGraphSelect(graphId) { - this.selectedGraphId = graphId; - this.fetchGraphData(graphId); - } - - handleNewChat() { - this.chatHistory = []; - this.currentNode = null; - this.selectedGraphId = null; - if (this.chatPanel) { - this.chatPanel.updateHistory(this.chatHistory); - } - } -} - -module.exports = { ChatView, CHAT_VIEW_TYPE }; \ No newline at end of file diff --git a/src/views/ChatView.ts b/src/views/ChatView.ts new file mode 100644 index 0000000..72d62f0 --- /dev/null +++ b/src/views/ChatView.ts @@ -0,0 +1,221 @@ +import { ItemView, WorkspaceLeaf } from 'obsidian'; +import { ChatPanel } from '../components/ChatPanel'; +import { HistoryPanel } from '../components/HistoryPanel'; +import LLMAgentPlugin from 'main'; + +export const CHAT_VIEW_TYPE = 'llm-agent-chat-view'; + +// ----------------------------------------------- Chat View --------------------------------------------------------------- + +export class ChatView extends ItemView { + plugin: LLMAgentPlugin; + chatHistory: any[]; + graphsList: any[]; + selectedGraphId: string | null; + currentNode: string | null; + chatContainer: HTMLDivElement; + historyContainer: HTMLDivElement; + chatPanel: ChatPanel | null; + historyPanel: HistoryPanel | null; + + constructor(leaf: WorkspaceLeaf, plugin: LLMAgentPlugin) { + super(leaf); + this.plugin = plugin; + this.chatHistory = []; + this.graphsList = []; + this.selectedGraphId = null; + this.currentNode = null; + this.chatPanel = null; + this.historyPanel = null; + } + + getViewType(): string { + return CHAT_VIEW_TYPE; + } + + getDisplayText(): string { + return 'LLM Agent Chat'; + } + + getIcon(): string { + return 'message-circle'; + } + + async onOpen(): Promise { + const container = this.containerEl.children[1] as HTMLElement; + container.empty(); + + // Создаем контейнеры для React компонентов + this.chatContainer = container.createDiv() as HTMLDivElement; + this.chatContainer.addClass('llm-agent-chat-container'); + + this.historyContainer = container.createDiv() as HTMLDivElement; + this.historyContainer.addClass('llm-agent-history-container'); + + // Инициализируем React компоненты + this.chatPanel = new ChatPanel(this.chatContainer, { + chatHistory: this.chatHistory, + onSendMessage: this.handleSendMessage.bind(this) + }); + + this.historyPanel = new HistoryPanel(this.historyContainer, { + graphs: this.graphsList, + onGraphSelect: this.handleGraphSelect.bind(this), + onNewChat: this.handleNewChat.bind(this) + }); + + // Подписываемся на события + this.plugin.eventBus.on('node-selected', this.handleNodeSelected.bind(this)); + this.plugin.eventBus.on('new-chat', this.handleNewChat.bind(this)); + + // Загружаем список графов + this.fetchGraphsList(); + } + + async onClose(): Promise { + if (this.chatPanel) { + this.chatPanel.destroy(); + this.chatPanel = null; + } + if (this.historyPanel) { + this.historyPanel.destroy(); + this.historyPanel = null; + } + this.plugin.eventBus.off('node-selected', this.handleNodeSelected); + this.plugin.eventBus.off('new-chat', this.handleNewChat); + } + + // ----------------------------------------------- API Methods --------------------------------------------------------------- + + async fetchGraphsList(): Promise { + try { + const response = await fetch(`${this.plugin.settings.apiBaseUrl}/graphs`); + const data = await response.json(); + this.graphsList = data; + if (this.historyPanel) { + this.historyPanel.updateGraphs(this.graphsList); + } + } catch (error) { + console.error('Ошибка загрузки списка графов:', error); + } + } + + async fetchGraphData(graphId: string): Promise { + try { + const response = await fetch(`${this.plugin.settings.apiBaseUrl}/graphs/${graphId}`); + if (!response.ok) { + if (response.status === 404) { + console.error(`Граф с ID ${graphId} не найден.`); + } + throw new Error(`HTTP ошибка! статус: ${response.status}`); + } + const data = await response.json(); + + // Обновляем граф + this.plugin.eventBus.emit('graph-updated', { + graphData: { + nodes: data.graph_nodes || [], + edges: data.graph_edges || [] + }, + currentNode: data.current_node_id + }); + + // Обновляем историю чата + this.chatHistory = data.messages || []; + if (this.chatPanel) { + this.chatPanel.updateHistory(this.chatHistory); + } + } catch (error) { + console.error('Ошибка загрузки данных графа:', error); + } + } + + async fetchMessagesFromRootToNode(nodeId: string): Promise { + try { + const response = await fetch(`${this.plugin.settings.apiBaseUrl}/messages/${this.selectedGraphId}/${nodeId}`); + const data = await response.json(); + this.chatHistory = data; + if (this.chatPanel) { + this.chatPanel.updateHistory(this.chatHistory); + } + } catch (error) { + console.error('Ошибка загрузки сообщений:', error); + } + } + + // ----------------------------------------------- Event Handlers --------------------------------------------------------------- + + async handleSendMessage(message: string): Promise { + try { + const response = await fetch(`${this.plugin.settings.apiBaseUrl}/chat`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + message: message, + graph_id: this.selectedGraphId, + parent_node_id: this.currentNode + }) + }); + + if (!response.ok) { + throw new Error(`HTTP ошибка! статус: ${response.status}`); + } + + const result = await response.json(); + + // Обновляем выбранный ID графа + this.selectedGraphId = result.graph_id; + + // Устанавливаем текущий узел + const newCurrentNodeId = result.graph_visualization_data.current_node_id; + this.currentNode = newCurrentNodeId; + + // Обновляем граф + this.plugin.eventBus.emit('graph-updated', { + graphData: { + nodes: result.graph_visualization_data.nodes || [], + edges: result.graph_visualization_data.edges || [] + }, + currentNode: newCurrentNodeId + }); + + // Загружаем сообщения от корня до нового текущего узла + if (newCurrentNodeId) { + await this.fetchMessagesFromRootToNode(newCurrentNodeId); + } else { + this.chatHistory = result.messages || []; + if (this.chatPanel) { + this.chatPanel.updateHistory(this.chatHistory); + } + } + + // Обновляем список графов + this.fetchGraphsList(); + } catch (error) { + console.error('Ошибка отправки сообщения:', error); + } + } + + handleNodeSelected(data: { nodeId: string }): void { + this.currentNode = data.nodeId; + if (this.selectedGraphId) { + this.fetchMessagesFromRootToNode(data.nodeId); + } + } + + handleGraphSelect(graphId: string): void { + this.selectedGraphId = graphId; + this.fetchGraphData(graphId); + } + + handleNewChat(): void { + this.chatHistory = []; + this.currentNode = null; + this.selectedGraphId = null; + if (this.chatPanel) { + this.chatPanel.updateHistory(this.chatHistory); + } + } +} diff --git a/src/views/GraphView.js b/src/views/GraphView.js deleted file mode 100644 index a177e45..0000000 --- a/src/views/GraphView.js +++ /dev/null @@ -1,79 +0,0 @@ -const { ItemView } = require('obsidian'); -const { GraphPanel } = require('../components/GraphPanel'); - -const GRAPH_VIEW_TYPE = 'llm-agent-graph-view'; - -// ----------------------------------------------- Graph View --------------------------------------------------------------- - -class GraphView extends ItemView { - constructor(leaf, plugin) { - super(leaf); - this.plugin = plugin; - this.graphData = { nodes: [], edges: [] }; - this.currentNode = null; - } - - getViewType() { - return GRAPH_VIEW_TYPE; - } - - getDisplayText() { - return 'LLM Agent Graph'; - } - - getIcon() { - return 'brain-circuit'; - } - - async onOpen() { - const container = this.containerEl.children[1]; - container.empty(); - - // Создаем контейнер для React компонента - this.reactContainer = container.createDiv(); - this.reactContainer.addClass('llm-agent-graph-container'); - - // Инициализируем React компонент - this.graphPanel = new GraphPanel(this.reactContainer, { - graphData: this.graphData, - onNodeClick: this.handleNodeClick.bind(this) - }); - - // Подписываемся на события - this.plugin.eventBus.on('graph-updated', this.handleGraphUpdate.bind(this)); - this.plugin.eventBus.on('new-chat', this.handleNewChat.bind(this)); - } - - async onClose() { - if (this.graphPanel) { - this.graphPanel.destroy(); - } - this.plugin.eventBus.off('graph-updated', this.handleGraphUpdate); - this.plugin.eventBus.off('new-chat', this.handleNewChat); - } - - // ----------------------------------------------- Event Handlers --------------------------------------------------------------- - - handleNodeClick(nodeId) { - this.currentNode = nodeId; - this.plugin.eventBus.emit('node-selected', { nodeId }); - } - - handleGraphUpdate(data) { - this.graphData = data.graphData; - this.currentNode = data.currentNode; - if (this.graphPanel) { - this.graphPanel.updateData(this.graphData); - } - } - - handleNewChat() { - this.graphData = { nodes: [], edges: [] }; - this.currentNode = null; - if (this.graphPanel) { - this.graphPanel.updateData(this.graphData); - } - } -} - -module.exports = { GraphView, GRAPH_VIEW_TYPE }; \ No newline at end of file diff --git a/src/views/GraphView.ts b/src/views/GraphView.ts new file mode 100644 index 0000000..f650925 --- /dev/null +++ b/src/views/GraphView.ts @@ -0,0 +1,86 @@ +import { ItemView, WorkspaceLeaf } from 'obsidian'; +import { GraphPanel } from '../components/GraphPanel'; +import LLMAgentPlugin from 'main'; + +export const GRAPH_VIEW_TYPE = 'llm-agent-graph-view'; + +// ----------------------------------------------- Graph View --------------------------------------------------------------- + +export class GraphView extends ItemView { + plugin: LLMAgentPlugin; + graphData: { nodes: any[]; edges: any[] }; + currentNode: string | null; + graphPanel: GraphPanel | null; + reactContainer: HTMLDivElement; + + constructor(leaf: WorkspaceLeaf, plugin: LLMAgentPlugin) { + super(leaf); + this.plugin = plugin; + this.graphData = { nodes: [], edges: [] }; + this.currentNode = null; + this.graphPanel = null; + } + + getViewType(): string { + return GRAPH_VIEW_TYPE; + } + + getDisplayText(): string { + return 'LLM Agent Graph'; + } + + getIcon(): string { + return 'brain-circuit'; + } + + async onOpen(): Promise { + const container = this.containerEl.children[1] as HTMLElement; + container.empty(); + + // Создаем контейнер для React компонента + this.reactContainer = container.createDiv() as HTMLDivElement; + this.reactContainer.addClass('llm-agent-graph-container'); + + // Инициализируем React компонент + this.graphPanel = new GraphPanel(this.reactContainer, { + graphData: this.graphData, + onNodeClick: this.handleNodeClick.bind(this) + }); + + // Подписываемся на события + this.plugin.eventBus.on('graph-updated', this.handleGraphUpdate.bind(this)); + this.plugin.eventBus.on('new-chat', this.handleNewChat.bind(this)); + } + + async onClose(): Promise { + if (this.graphPanel) { + this.graphPanel.destroy(); + this.graphPanel = null; + } + this.plugin.eventBus.off('graph-updated', this.handleGraphUpdate); + this.plugin.eventBus.off('new-chat', this.handleNewChat); + } + + // ----------------------------------------------- Event Handlers --------------------------------------------------------------- + + handleNodeClick(nodeId: string): void { + this.currentNode = nodeId; + this.plugin.eventBus.emit('node-selected', { nodeId }); + } + + handleGraphUpdate(data: { graphData: { nodes: any[]; edges: any[] }; currentNode: string | null }): void { + this.graphData = data.graphData; + this.currentNode = data.currentNode; + if (this.graphPanel) { + this.graphPanel.updateData(this.graphData); + } + } + + handleNewChat(): void { + this.graphData = { nodes: [], edges: [] }; + this.currentNode = null; + if (this.graphPanel) { + this.graphPanel.updateData(this.graphData); + } + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..c44b729 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "inlineSourceMap": true, + "inlineSources": true, + "module": "ESNext", + "target": "ES6", + "allowJs": true, + "noImplicitAny": true, + "moduleResolution": "node", + "importHelpers": true, + "isolatedModules": true, + "strictNullChecks": true, + "lib": [ + "DOM", + "ES5", + "ES6", + "ES7" + ] + }, + "include": [ + "**/*.ts" + ] +} diff --git a/version-bump.mjs b/version-bump.mjs new file mode 100644 index 0000000..d409fa0 --- /dev/null +++ b/version-bump.mjs @@ -0,0 +1,14 @@ +import { readFileSync, writeFileSync } from "fs"; + +const targetVersion = process.env.npm_package_version; + +// read minAppVersion from manifest.json and bump version to target version +let manifest = JSON.parse(readFileSync("manifest.json", "utf8")); +const { minAppVersion } = manifest; +manifest.version = targetVersion; +writeFileSync("manifest.json", JSON.stringify(manifest, null, "\t")); + +// update versions.json with target version and minAppVersion from manifest.json +let versions = JSON.parse(readFileSync("versions.json", "utf8")); +versions[targetVersion] = minAppVersion; +writeFileSync("versions.json", JSON.stringify(versions, null, "\t")); diff --git a/versions.json b/versions.json new file mode 100644 index 0000000..26382a1 --- /dev/null +++ b/versions.json @@ -0,0 +1,3 @@ +{ + "1.0.0": "0.15.0" +}