├── .github └── workflows │ └── release.yml ├── .gitignore ├── LICENSE ├── README.md ├── bmc_qr.png ├── brain.svg ├── currentNote-demo.gif ├── esbuild.config.mjs ├── fabric-logo-gif.gif ├── main.ts ├── manifest.json ├── package-lock.json ├── package.json ├── styles.css └── tsconfig.json /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release Obsidian plugin 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v3 14 | 15 | - name: Use Node.js 16 | uses: actions/setup-node@v3 17 | with: 18 | node-version: "18.x" 19 | 20 | - name: Build plugin 21 | run: | 22 | npm install 23 | npm run build 24 | 25 | - name: Create release 26 | env: 27 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 28 | run: | 29 | tag="${GITHUB_REF#refs/tags/}" 30 | 31 | gh release create "$tag" \ 32 | --title="$tag" \ 33 | --draft \ 34 | main.js manifest.json styles.css fabric-logo-gif.gif 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore built files 2 | main.js 3 | *.js.map 4 | 5 | # Ignore node_modules 6 | node_modules/ 7 | 8 | # Ignore macOS system files 9 | .DS_Store 10 | data.json -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2024] [Chase Elder] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fabric Plugin for Obsidian 2 | 3 | **Try out my new plugin, it accomplishes the same thing as this one without having to have fabric or fabric-connector installed. Install it from the community plugins repo in Obsidian. https://github.com/chasebank87/mesh-ai** 4 | 5 | ## Updated to work with Fabric 2.0 (Enable fabric 2.0 compatability in the plugin settings) 6 | If you like this plugin, feel free to support the development by buying a coffee: 7 |
8 | 9 | Buy Me A Coffee 10 |
11 | 12 | ## Overview 13 | 14 | The Fabric Plugin is an advanced integration tool for Obsidian, designed to enhance content creation and management within the Obsidian ecosystem. It connects to external APIs to fetch and manipulate data based on user-defined patterns and models. The plugin also supports custom pattern management and YouTube link detection. 15 | 16 | ## Features 17 | 18 | - **Custom Pattern Management**: Watch for changes in a designated folder and sync patterns. 19 | - **Community Custom Patterns**: Download and share custom patterns with other fabric users. 20 | - [Fork repo to submit your patterns](https://github.com/chasebank87/fabric-patterns) 21 | - **YouTube Link Detection**: Automatically detect YouTube links in notes. 22 | - **External API Integration**: Connect to Fabric Connector API and Tavily API for enhanced content manipulation. 23 | - **Dynamic Content Rendering**: Render content dynamically based on user interactions and API responses. 24 | - **Debugging Support**: Toggle debug mode for additional logging. 25 | 26 | ## Prerequisites 27 | 28 | 1. **Fabric**: Install Fabric from [danielmiessler/fabric](https://github.com/danielmiessler/fabric). 29 | 2. **Fabric Connector**: Install Fabric Connector from [chasebank87/fabric-connector](https://github.com/chasebank87/fabric-connector). 30 | 31 | ## Installation 32 | 33 | To install the Fabric Plugin, follow these steps: 34 | 35 | 1. Download the plugin from the official repository. 36 | 2. Place the plugin in your Obsidian's plugins folder. 37 | 3. Enable the plugin from Obsidian's settings under "Community Plugins". 38 | 39 | ## Configuration 40 | 41 | Configure the plugin by setting up the necessary API URLs and keys through the plugin settings tab in Obsidian. 42 | 43 | ### Settings 44 | 45 | - `Fabric Connector API URL`: URL to the Fabric Connector API. 46 | - `Fabric Connector API Key`: Authentication key for the Fabric Connector API. 47 | - `Output Folder`: Default folder path where output files will be saved. 48 | - `Custom Patterns Folder`: Folder path for storing and managing custom patterns. 49 | - `YouTube Autodetect Enabled`: Toggle to enable or disable automatic YouTube link detection. 50 | - `Default Model`: Default model used for data processing. 51 | - `Debug`: Enable or disable debug mode for logging. 52 | 53 | ### Usage 54 | 55 | 1. **Pattern Management**: Add or remove markdown files in the custom patterns folder to manage patterns. 56 | 2. **YouTube Transcription**: Autodetect youtube links in current note or clipboard and transcribe them using whisper and then running a pattern against. 57 | 3. **Tavily Search**: Use the Tavily API to search for relevant content, and process through results through the selected pattern. 58 | 4. **Input Sources**: 59 | 1. ***Current Note***: Uses the current active note as the source to be sent to fabric. 60 | 2. ***Clipboard***: Uses the clipboard as the source to be sent to fabric. 61 | 3. ***Tavily***: Uses Tavily Search results as the source to be sent to fabric. 62 | 5. **Pattern Selection**: Choose a pattern from the available custom or built in patterns to process the input data. 63 | 6. **Models**: Select a model from the available options to process the input data and pattern. 64 | 7. **Upload Patterns**: One way sync to fabric, will create if the custom pattern does not exist, and update if it does 65 | 8. **Update Patterns and Models**: Refreshes the models and patterns displayed in the dropdowns. 66 | 67 | ### Demonstration 68 | 69 | ![currentNote](https://github.com/chasebank87/unofficial-fabric-plugin/blob/main/currentNote-demo.gif) 70 | ### Debugging 71 | 72 | Toggle the debug mode in settings to view detailed logs in the console. This can help in tracing issues and understanding the flow of data. 73 | 74 | Contributions are welcome. Please fork the repository, make changes, and submit a pull request for review. 75 | 76 | ## License 77 | 78 | This project is licensed under the MIT License - see the LICENSE file for details. 79 | 80 | --- 81 | 82 | For more information on usage and configuration, refer to the detailed comments within the codebase or visit the [Fabric Plugin Documentation](#). 83 | -------------------------------------------------------------------------------- /bmc_qr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chasebank87/unofficial-fabric-plugin/1a44bb6ab12a234ec23c6f56ebafed069073ffb6/bmc_qr.png -------------------------------------------------------------------------------- /brain.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /currentNote-demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chasebank87/unofficial-fabric-plugin/1a44bb6ab12a234ec23c6f56ebafed069073ffb6/currentNote-demo.gif -------------------------------------------------------------------------------- /esbuild.config.mjs: -------------------------------------------------------------------------------- 1 | import esbuild from "esbuild"; 2 | import process from "process"; 3 | import builtins from "builtin-modules"; 4 | 5 | const banner = 6 | `/* 7 | THIS IS A GENERATED/BUNDLED FILE BY ESBUILD 8 | if you want to view the source, please visit the github repository of this plugin 9 | */ 10 | `; 11 | 12 | const prod = (process.argv[2] === "production"); 13 | 14 | esbuild.build({ 15 | banner: { 16 | js: banner, 17 | }, 18 | entryPoints: ["main.ts"], 19 | bundle: true, 20 | external: [ 21 | "obsidian", 22 | "electron", 23 | "@codemirror/autocomplete", 24 | "@codemirror/collab", 25 | "@codemirror/commands", 26 | "@codemirror/language", 27 | "@codemirror/lint", 28 | "@codemirror/search", 29 | "@codemirror/state", 30 | "@codemirror/view", 31 | "@lezer/common", 32 | "@lezer/highlight", 33 | "@lezer/lr", 34 | ...builtins], 35 | format: "cjs", 36 | target: "es2018", 37 | logLevel: "info", 38 | sourcemap: prod ? false : "inline", 39 | treeShaking: true, 40 | outfile: "main.js", 41 | }).catch(() => process.exit(1)); -------------------------------------------------------------------------------- /fabric-logo-gif.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chasebank87/unofficial-fabric-plugin/1a44bb6ab12a234ec23c6f56ebafed069073ffb6/fabric-logo-gif.gif -------------------------------------------------------------------------------- /main.ts: -------------------------------------------------------------------------------- 1 | import { App, Platform, Plugin, PluginSettingTab, Setting, WorkspaceLeaf, TFile, TFolder, TAbstractFile, Notice, ItemView, MarkdownRenderer, setIcon, Modal, ViewState, ButtonComponent} from 'obsidian'; 2 | import { exec } from 'child_process'; 3 | import { promisify } from 'util'; 4 | import * as path from 'path'; 5 | import * as os from 'os'; 6 | import * as fuzzaldrin from 'fuzzaldrin-plus'; 7 | 8 | const shellEscape = require('shell-escape'); 9 | const execAsync = promisify(exec); 10 | 11 | interface FabricPluginSettings { 12 | fabricConnectorApiUrl: string; 13 | fabricConnectorApiKey: string; 14 | outputFolder: string; 15 | customPatternsFolder: string; 16 | youtubeAutodetectEnabled: boolean; 17 | audioFileAutodetectEnabled: boolean; 18 | defaultModel: string; 19 | defaultPostProcessingPattern: string; 20 | debug: boolean; 21 | fabric2: boolean; 22 | tavilyApiKey: string; 23 | } 24 | 25 | const DEFAULT_SETTINGS: FabricPluginSettings = { 26 | fabricConnectorApiUrl: '', 27 | fabricConnectorApiKey: '', 28 | outputFolder: '', 29 | customPatternsFolder: '', 30 | youtubeAutodetectEnabled: true, 31 | audioFileAutodetectEnabled: true, 32 | defaultModel: 'gpt-4o', 33 | defaultPostProcessingPattern: '', 34 | debug: false, 35 | fabric2: false, 36 | tavilyApiKey: '' 37 | }; 38 | 39 | export default class FabricPlugin extends Plugin { 40 | settings: FabricPluginSettings; 41 | customPatternsFolder: TFolder | null = null; 42 | patterns: string[] = []; 43 | debug: boolean; 44 | 45 | async onload() { 46 | await this.loadSettings(); 47 | this.updateLogging(); 48 | await this.checkAndDownloadLogo(); 49 | await this.loadSettings(); 50 | this.registerCustomPatternsFolderWatcher(); 51 | 52 | this.app.workspace.onLayoutReady(() => { 53 | this.registerCustomPatternsFolderWatcher(); 54 | }); 55 | 56 | this.addCommand({ 57 | id: 'open-community-patterns', 58 | name: 'Open Community Patterns', 59 | callback: () => { 60 | new CommunityPatternsModal(this.app, this).open(); 61 | } 62 | }); 63 | 64 | this.addSettingTab(new FabricSettingTab(this.app, this)); 65 | 66 | this.registerView( 67 | 'fabric-view', 68 | (leaf) => new FabricView( 69 | leaf, 70 | this, 71 | this.settings.fabricConnectorApiUrl, 72 | this.settings.fabricConnectorApiKey 73 | ) 74 | ); 75 | 76 | if (this.app.workspace.layoutReady) { 77 | this.initLeaf(); 78 | } else { 79 | this.app.workspace.onLayoutReady(this.initLeaf.bind(this)); 80 | } 81 | 82 | this.addRibbonIcon('brain', 'Fabric', () => { 83 | this.activateView(); 84 | }); 85 | } 86 | 87 | private isLogging = false; 88 | 89 | log(message: string, ...args: any[]) { 90 | if (this.settings.debug && !this.isLogging) { 91 | this.isLogging = true; 92 | console.log(`[Fabric Debug] ${message}`, ...args); 93 | this.isLogging = false; 94 | } 95 | } 96 | 97 | initLeaf(): void { 98 | if (this.app.workspace.getLeavesOfType('fabric-view').length) { 99 | return; 100 | } 101 | const rightLeaf = this.app.workspace.getRightLeaf(false); 102 | if (rightLeaf) { 103 | rightLeaf.setViewState({ 104 | type: 'fabric-view', 105 | active: true, 106 | }); 107 | } 108 | } 109 | 110 | 111 | updateLogging() { 112 | if (this.settings.debug) { 113 | console.log('[Fabric] Debug mode enabled'); 114 | } else { 115 | console.log('[Fabric] Debug mode disabled'); 116 | } 117 | } 118 | 119 | async loadSettings() { 120 | this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); 121 | } 122 | 123 | async saveSettings() { 124 | await this.saveData(this.settings); 125 | } 126 | 127 | registerCustomPatternsFolderWatcher() { 128 | this.log("Registering custom patterns folder watcher"); 129 | // Unregister previous watcher if exists 130 | this.app.vault.off('delete', this.handleFileDeletion); 131 | 132 | if (this.settings.customPatternsFolder) { 133 | const folderPath = this.settings.customPatternsFolder.endsWith('/') 134 | ? this.settings.customPatternsFolder 135 | : this.settings.customPatternsFolder + '/'; 136 | 137 | this.log(`Watching for deletions in: ${folderPath}`); 138 | 139 | this.registerEvent( 140 | this.app.vault.on('delete', this.handleFileDeletion) 141 | ); 142 | } else { 143 | console.warn('Custom patterns folder path not set in settings'); 144 | } 145 | } 146 | 147 | 148 | handleFileDeletion = (file: TAbstractFile) => { 149 | this.log(`File deletion detected: ${file.path}`); 150 | this.log(`Custom patterns folder: ${this.settings.customPatternsFolder}`); 151 | 152 | if (!(file instanceof TFile)) { 153 | this.log("Deleted item is not a file"); 154 | return; 155 | } 156 | 157 | // Check if the file is directly in the custom patterns folder 158 | const customPatternsPath = this.settings.customPatternsFolder.endsWith('/') 159 | ? this.settings.customPatternsFolder 160 | : this.settings.customPatternsFolder + '/'; 161 | 162 | if (!file.path.startsWith(customPatternsPath)) { 163 | this.log("File is not in the custom patterns folder"); 164 | return; 165 | } 166 | 167 | if (file.extension !== 'md') { 168 | this.log("File is not a markdown file"); 169 | return; 170 | } 171 | 172 | this.log(`Markdown file deleted in custom patterns folder: ${file.path}`); 173 | this.handleCustomPatternDeletion(file.name); 174 | }; 175 | 176 | async handleCustomPatternDeletion(fileName: string) { 177 | this.log(`Handling custom pattern deletion for: ${fileName}`); 178 | const patternName = fileName.replace('.md', ''); 179 | const confirmDelete = await this.confirmPatternDeletion(patternName); 180 | if (confirmDelete) { 181 | await this.deletePatternFromFabric(patternName); 182 | } 183 | } 184 | 185 | async checkAndDownloadLogo() { 186 | if (!this.manifest.dir) { 187 | console.error('Plugin directory is undefined'); 188 | return; 189 | } 190 | 191 | const logoPath = path.join(this.manifest.dir, 'fabric-logo-gif.gif'); 192 | const logoExists = await this.app.vault.adapter.exists(logoPath); 193 | 194 | if (!logoExists) { 195 | try { 196 | const logoUrl = 'https://raw.githubusercontent.com/chasebank87/unofficial-fabric-plugin/main/fabric-logo-gif.gif'; 197 | const response = await fetch(logoUrl); 198 | if (!response.ok) throw new Error('Failed to fetch logo'); 199 | const arrayBuffer = await response.arrayBuffer(); 200 | const buffer = new Uint8Array(arrayBuffer); 201 | await this.app.vault.adapter.writeBinary(logoPath, buffer); 202 | this.log('Fabric logo downloaded successfully'); 203 | } catch (error) { 204 | console.error('Error downloading Fabric logo:', error); 205 | } 206 | } 207 | } 208 | 209 | async confirmPatternDeletion(patternName: string): Promise { 210 | return new Promise((resolve) => { 211 | const notice = new Notice('', 0); 212 | const container = notice.noticeEl.createDiv('fabric-confirm-deletion'); 213 | 214 | container.createEl('h3', { text: 'Confirm Pattern Deletion' }); 215 | container.createEl('p', { text: `Do you want to delete the pattern "${patternName}" and its folder from Fabric as well?` }); 216 | 217 | const buttonContainer = container.createDiv('fabric-confirm-buttons'); 218 | 219 | const yesButton = buttonContainer.createEl('button', { text: 'Yes' }); 220 | yesButton.onclick = () => { 221 | notice.hide(); 222 | resolve(true); 223 | }; 224 | 225 | const noButton = buttonContainer.createEl('button', { text: 'No' }); 226 | noButton.onclick = () => { 227 | notice.hide(); 228 | resolve(false); 229 | }; 230 | }); 231 | } 232 | 233 | async deletePatternFromFabric(patternName: string) { 234 | try { 235 | const response = await fetch(this.settings.fabricConnectorApiUrl + '/delete_pattern', { 236 | method: 'POST', 237 | headers: { 238 | 'Content-Type': 'application/json', 239 | 'X-API-Key': this.settings.fabricConnectorApiKey // Add the Fabric Connector API Key here 240 | }, 241 | body: JSON.stringify({ pattern: patternName }) 242 | }); 243 | 244 | if (!response.ok) { 245 | throw new Error(`HTTP error! status: ${response.status}`); 246 | } 247 | 248 | const result = await response.json(); 249 | new Notice(result.message); 250 | // Reload patterns after successful deletion 251 | await this.updateFabricView(); 252 | } catch (error) { 253 | console.error('Error deleting pattern from Fabric:', error); 254 | new Notice(`Failed to delete pattern "${patternName}" from Fabric.`); 255 | } 256 | } 257 | 258 | 259 | updateFabricView() { 260 | // Find and update the FabricView if it exists 261 | this.app.workspace.iterateAllLeaves((leaf) => { 262 | if (leaf.view instanceof FabricView) { 263 | (leaf.view as FabricView).loadPatterns(); 264 | } 265 | }); 266 | } 267 | 268 | async getDefaultShell(): Promise { 269 | // Use Platform.isWin instead of os.platform() to detect Windows 270 | if (Platform.isWin) { 271 | return 'cmd.exe'; 272 | } 273 | try { 274 | const { stdout } = await execAsync('echo $SHELL'); 275 | return stdout.trim(); 276 | } catch (error) { 277 | console.error('Failed to detect default shell:', error); 278 | return '/bin/sh'; // Fallback to /bin/sh 279 | } 280 | } 281 | 282 | async activateView() { 283 | this.app.workspace.detachLeavesOfType('fabric-view'); 284 | 285 | const rightLeaf = this.app.workspace.getRightLeaf(false); 286 | if (rightLeaf) { 287 | rightLeaf.setViewState({ 288 | type: 'fabric-view', 289 | active: true, 290 | }); 291 | } 292 | 293 | this.app.workspace.revealLeaf( 294 | this.app.workspace.getLeavesOfType('fabric-view')[0] 295 | ); 296 | } 297 | } 298 | 299 | 300 | class FabricView extends ItemView { 301 | plugin: FabricPlugin; 302 | patterns: string[] = []; 303 | patternDropdown: HTMLElement; 304 | searchInput: HTMLInputElement; 305 | outputNoteInput: HTMLInputElement; 306 | selectedOptionIndex: number = -1; 307 | buttonsContainer: HTMLElement; 308 | progressSpinner: HTMLElement; 309 | patternsSyncContainer: HTMLElement; 310 | patternsSyncButton: HTMLElement; 311 | containerEl: HTMLElement; 312 | refreshButton: HTMLElement; 313 | communityPatternsBtn: HTMLElement; 314 | logoContainer: HTMLElement; 315 | loadingText: HTMLElement; 316 | ytSwitch: HTMLInputElement; 317 | ytToggle: HTMLElement; 318 | tsToggle: HTMLElement; 319 | modelSearchInput: HTMLInputElement; 320 | modelDropdown: HTMLElement; 321 | models: string[] = []; 322 | selectedModelIndex: number = -1; 323 | defaultModelDisplay: HTMLElement; 324 | modelNameSpan: HTMLSpanElement; 325 | syncButton: HTMLElement; 326 | fabricConnectorApiUrl: string; 327 | fabricConnectorApiKey: string; 328 | selectedPatterns: string[] = []; 329 | 330 | loadingMessages: string[] = [ 331 | "reticulating splines...", 332 | "engaging warp drive...", 333 | "calibrating flux capacitor...", 334 | "compiling techno-babble...", 335 | "reversing the polarity...", 336 | "bypassing the mainframe...", 337 | "initializing neural network...", 338 | "decrypting alien transmissions...", 339 | "charging photon torpedoes...", 340 | "hacking the gibson...", 341 | "Warming flux capacitor...", 342 | "Downloading more RAM...", 343 | "Reversing neutron flow...", 344 | "Initializing sass protocol...", 345 | "Calibrating sarcasm sensors...", 346 | "Bypassing laws of physics...", 347 | "Generating witty message...", 348 | "Spinning hamster wheels...", 349 | "Charging sonic screwdriver...", 350 | "Aligning the stars...", 351 | "Dividing by zero...", 352 | "Upgrading to Windows 9...", 353 | "Searching for life's meaning...", 354 | "Awaiting the Singularity...", 355 | "Tuning alien frequencies...", 356 | "Bending space-time continuum...", 357 | "Compiling crash excuses...", 358 | "Calculating success probability...", 359 | "Rolling for initiative...", 360 | "Initiating self-destruct sequence...", 361 | "Summoning IT gods...", 362 | "Applying warp core tape...", 363 | "Translating binary to dance...", 364 | "Charging Arc Reactor..." 365 | ]; 366 | 367 | constructor(leaf: WorkspaceLeaf, plugin: FabricPlugin, fabricConnectorApiUrl: string, fabricConnectorApiKey: string) { 368 | super(leaf); 369 | this.plugin = plugin; 370 | this.fabricConnectorApiUrl = fabricConnectorApiUrl; 371 | this.fabricConnectorApiKey = fabricConnectorApiKey; 372 | } 373 | 374 | getViewType(): string { 375 | return 'fabric-view'; 376 | } 377 | 378 | getDisplayText(): string { 379 | return 'Fabric'; 380 | } 381 | 382 | showCommunityPatternsModal() { 383 | new CommunityPatternsModal(this.app, this.plugin).open(); 384 | } 385 | 386 | async onOpen() { 387 | this.containerEl = this.contentEl; 388 | this.containerEl.empty(); 389 | this.containerEl.addClass('fabric-view'); 390 | 391 | 392 | this.logoContainer = this.containerEl.createEl('div', { cls: 'fabric-logo-container' }); 393 | let logoPath: string; 394 | if (this.plugin.manifest && this.plugin.manifest.dir) { 395 | logoPath = path.join(this.plugin.manifest.dir, 'fabric-logo-gif.gif'); 396 | const logoSrc = this.app.vault.adapter.getResourcePath(logoPath); 397 | } else { 398 | // Fallback to the GitHub URL if the plugin directory is undefined 399 | logoPath = 'https://raw.githubusercontent.com/chasebank87/unofficial-fabric-plugin/main/fabric-logo-gif.gif'; 400 | } 401 | 402 | const logoSrc = this.app.vault.adapter.getResourcePath(logoPath); 403 | const logo = this.logoContainer.createEl('img', { 404 | cls: 'fabric-logo', 405 | attr: { src: logoSrc } 406 | }); 407 | this.loadingText = this.logoContainer.createEl('h6', { cls: 'fabric-loading-text' }); 408 | 409 | const contentContainer = this.containerEl.createEl('div', { cls: 'fabric-content' }); 410 | 411 | // Add YouTube toggle and icon 412 | const ytToggleContainer = contentContainer.createEl('div', { cls: 'fabric-yt-toggle-container' }); 413 | 414 | // Add YouTube toggle and icon 415 | const tsToggleContainer = contentContainer.createEl('div', { cls: 'fabric-ts-toggle-container' }); 416 | 417 | // Create toggle for yt 418 | this.ytToggle = ytToggleContainer.createEl('div', { 419 | cls: `fabric-yt-toggle ${this.plugin.settings.youtubeAutodetectEnabled ? 'active' : ''}` 420 | }); 421 | const toggleSlider = this.ytToggle.createEl('span', { cls: 'fabric-yt-toggle-slider' }); 422 | 423 | // Create text label 424 | const ytLabel = ytToggleContainer.createEl('span', { 425 | cls: 'fabric-yt-label', 426 | text: 'YouTube Links' 427 | }); 428 | 429 | // Create toggle for ts 430 | this.tsToggle = tsToggleContainer.createEl('div', { 431 | cls: `fabric-ts-toggle ${this.plugin.settings.audioFileAutodetectEnabled ? 'active' : ''}` 432 | }); 433 | const toggleSliderTS = this.tsToggle.createEl('span', { cls: 'fabric-ts-toggle-slider' }); 434 | 435 | // Create text label 436 | const tsLabel = tsToggleContainer.createEl('span', { 437 | cls: 'fabric-ts-label', 438 | text: 'Audio Files' 439 | }); 440 | 441 | 442 | contentContainer.createEl('h3', { text: 'fabric', cls: 'fabric-title' }); 443 | 444 | this.buttonsContainer = contentContainer.createEl('div', { cls: 'fabric-buttons' }); 445 | const currentNoteBtn = this.buttonsContainer.createEl('button', { text: 'Current note', cls: 'fabric-button current-note' }); 446 | const clipboardBtn = this.buttonsContainer.createEl('button', { text: 'Clipboard', cls: 'fabric-button clipboard' }); 447 | 448 | const tavilyBtn = this.buttonsContainer.createEl('button', { text: 'Tavily', cls: 'fabric-button tavily' }); 449 | 450 | tavilyBtn.onclick = () => this.showTavilySearchModal(); 451 | currentNoteBtn.onclick = () => this.runFabric('current'); 452 | clipboardBtn.onclick = () => this.runFabric('clipboard'); 453 | 454 | 455 | const inputsContainer = contentContainer.createEl('div', { cls: 'fabric-inputs-container' }); 456 | 457 | this.outputNoteInput = inputsContainer.createEl('input', { 458 | cls: 'fabric-input', 459 | attr: { type: 'text', placeholder: 'Output note name' } 460 | }); 461 | 462 | this.searchInput = inputsContainer.createEl('input', { 463 | cls: 'fabric-input', 464 | attr: { type: 'text', placeholder: 'Search patterns...' } 465 | }); 466 | 467 | this.modelSearchInput = inputsContainer.createEl('input', { 468 | cls: 'fabric-input', 469 | attr: { 470 | type: 'text', 471 | placeholder: 'Search models...', 472 | } 473 | 474 | }); 475 | 476 | this.patternDropdown = contentContainer.createEl('div', { cls: 'fabric-dropdown' , attr: { id: 'pattern-dropdown', multiple: 'true' }} ); 477 | this.modelDropdown = contentContainer.createEl('div', { cls: 'fabric-dropdown', attr: { id: 'model-dropdown' }}); 478 | 479 | this.searchInput.addEventListener('blur', () => { 480 | // Use setTimeout to allow click events on dropdown options to fire first 481 | setTimeout(() => { 482 | this.patternDropdown.empty(); 483 | this.selectedOptionIndex = -1; 484 | }, 200); 485 | }); 486 | 487 | this.searchInput.addEventListener('input', () => { 488 | const inputValue = this.searchInput.value; 489 | const patterns = inputValue.split(',').map(p => p.trim()).filter(p => p !== ''); 490 | 491 | // The last item might be a partial search term, so we exclude it from selected patterns 492 | const selectedPatterns = patterns.slice(0, -1); 493 | const searchTerm = patterns[patterns.length - 1] || ''; 494 | 495 | // Update selectedPatterns, ensuring no more than 5 are selected 496 | this.selectedPatterns = selectedPatterns.slice(0, 5); 497 | 498 | // Update the dropdown options based on the search term 499 | this.updatePatternOptions(searchTerm.toLowerCase()); 500 | }); 501 | 502 | this.modelSearchInput.addEventListener('input', () => { 503 | this.updateModelOptions(this.modelSearchInput.value.toLowerCase()); 504 | this.updatePoweredByText(this.modelSearchInput.value); 505 | }); 506 | 507 | this.searchInput.addEventListener('keydown', (event) => { 508 | this.handlePatternDropdownNavigation(event, this.patternDropdown, this.searchInput); 509 | }); 510 | 511 | this.modelSearchInput.addEventListener('keydown', (event) => { 512 | this.handleDropdownNavigation(event, this.modelDropdown, this.modelSearchInput); 513 | }); 514 | 515 | // Create the default model display 516 | this.defaultModelDisplay = contentContainer.createEl('div', { cls: 'fabric-default-model' }); 517 | this.defaultModelDisplay.createSpan({ text: 'Powered by ' }); 518 | this.modelNameSpan = this.defaultModelDisplay.createSpan({ cls: 'model-name' }); 519 | this.updatePoweredByText(this.plugin.settings.defaultModel || 'No default model set'); 520 | 521 | this.searchInput.addEventListener('focus', () => { 522 | this.searchInput.classList.add('active'); 523 | }); 524 | 525 | this.searchInput.addEventListener('blur', () => { 526 | this.searchInput.classList.remove('active'); 527 | }); 528 | 529 | this.outputNoteInput.addEventListener('focus', () => { 530 | this.outputNoteInput.classList.add('active'); 531 | }); 532 | 533 | this.outputNoteInput.addEventListener('blur', () => { 534 | this.outputNoteInput.classList.remove('active'); 535 | }); 536 | 537 | this.ytToggle.addEventListener('click', () => { 538 | this.ytToggle.classList.toggle('active'); 539 | this.plugin.settings.youtubeAutodetectEnabled = this.ytToggle.classList.contains('active'); 540 | this.plugin.saveSettings(); 541 | if (this.plugin.settings.youtubeAutodetectEnabled) { 542 | new Notice('YouTube link detection enabled'); 543 | } else { 544 | new Notice('YouTube link detection disabled'); 545 | } 546 | }); 547 | 548 | this.tsToggle.addEventListener('click', () => { 549 | this.tsToggle.classList.toggle('active'); 550 | this.plugin.settings.audioFileAutodetectEnabled = this.tsToggle.classList.contains('active'); 551 | this.plugin.saveSettings(); 552 | if (this.plugin.settings.audioFileAutodetectEnabled) { 553 | new Notice('Audio file detection enabled'); 554 | } else { 555 | new Notice('Audio file link detection disabled'); 556 | } 557 | }); 558 | 559 | // Modify the click handlers for currentNoteBtn and clipboardBtn 560 | currentNoteBtn.onclick = () => this.handleFabricRun('current'); 561 | clipboardBtn.onclick = () => this.handleFabricRun('clipboard'); 562 | 563 | 564 | const buttonContainer = contentContainer.createEl('div', { cls: 'fabric-button-container' }); 565 | 566 | this.refreshButton = buttonContainer.createEl('button', { 567 | cls: 'fabric-icon-button fabric-refresh-button', 568 | attr: { 569 | 'aria-label': 'Refresh patterns and models' 570 | } 571 | }); 572 | setIcon(this.refreshButton, 'refresh-cw'); 573 | this.refreshButton.onclick = async () => { 574 | await this.loadPatterns(); 575 | await this.loadModels(); 576 | new Notice('Patterns and models refreshed'); 577 | }; 578 | 579 | this.syncButton = buttonContainer.createEl('button', { 580 | cls: 'fabric-icon-button fabric-sync-button', 581 | attr: { 582 | 'aria-label': 'Sync custom patterns' 583 | } 584 | }); 585 | setIcon(this.syncButton, 'upload-cloud'); 586 | this.syncButton.onclick = async () => { 587 | await this.syncCustomPatterns(); 588 | }; 589 | 590 | this.communityPatternsBtn = contentContainer.createEl('button', { 591 | cls: 'fabric-icon-button community-patterns', 592 | attr: { 593 | 'aria-label': 'Download community patterns' 594 | } 595 | }); 596 | setIcon(this.communityPatternsBtn, 'download'); 597 | 598 | this.communityPatternsBtn.onclick = () => this.showCommunityPatternsModal(); 599 | 600 | 601 | 602 | this.progressSpinner = contentContainer.createEl('div', { cls: 'fabric-progress-spinner' }); 603 | 604 | 605 | await this.loadPatterns(); 606 | await this.loadModels(); 607 | this.updatePatternOptions(''); 608 | this.updateModelOptions(''); 609 | this.searchInput.focus(); 610 | } 611 | 612 | showTavilySearchModal() { 613 | const pattern = this.searchInput.value.trim(); 614 | const model = this.getCurrentModel(); 615 | 616 | if (!model) { 617 | new Notice('Please select a model or set a default model in settings before running.'); 618 | return; 619 | } 620 | 621 | if (!pattern) { 622 | new Notice('Please select a pattern first'); 623 | return; 624 | } 625 | 626 | const modal = new Modal(this.app); 627 | modal.titleEl.setText('Tavily Search'); 628 | const { contentEl } = modal; 629 | contentEl.addClass('fabric-tavily-modal'); 630 | const searchInput = contentEl.createEl('input', { 631 | type: 'text', 632 | placeholder: 'Enter your search query' 633 | }); 634 | searchInput.addClass('fabric-tavily-input'); 635 | 636 | const searchButton = contentEl.createEl('button', { 637 | text: 'Search', 638 | cls: 'mod-cta' 639 | }); 640 | 641 | searchButton.addClass('fabric-tavily-search-button'); 642 | 643 | searchButton.onclick = async () => { 644 | const query = searchInput.value.trim(); 645 | if (query) { 646 | modal.close(); 647 | await this.performTavilySearch(query); 648 | } else { 649 | new Notice('Please enter a search query'); 650 | } 651 | }; 652 | 653 | modal.open(); 654 | } 655 | 656 | async performTavilySearch(query: string) { 657 | this.logoContainer.addClass('loading'); 658 | this.loadingText.setText(''); 659 | this.animateLoadingText('Searching Tavily...'); 660 | 661 | try { 662 | const response = await fetch('https://api.tavily.com/search', { 663 | method: 'POST', 664 | headers: { 665 | 'Content-Type': 'application/json' 666 | }, 667 | body: JSON.stringify({ 668 | query: query, 669 | include_answer: true, 670 | max_results: 5, 671 | include_images: true, 672 | search_depth: "basic", 673 | api_key: this.plugin.settings.tavilyApiKey 674 | }) 675 | }); 676 | 677 | if (!response.ok) { 678 | throw new Error(`HTTP error! status: ${response.status}`); 679 | } 680 | 681 | const data = await response.json(); 682 | const searchResult = JSON.stringify(data) + '\n'; 683 | await this.runFabricWithTavilyResult(searchResult); 684 | } catch (error) { 685 | console.error('Failed to perform Tavily search:', error); 686 | new Notice('Failed to perform Tavily search. Please check your API key and try again.'); 687 | } finally { 688 | this.logoContainer.removeClass('loading'); 689 | this.loadingText.setText(''); 690 | } 691 | } 692 | 693 | async runFabricWithTavilyResult(searchResult: string) { 694 | if (this.plugin.settings.defaultPostProcessingPattern) { 695 | this.selectedPatterns.push(this.plugin.settings.defaultPostProcessingPattern); 696 | } 697 | const pattern = this.selectedPatterns; 698 | const model = this.getCurrentModel(); 699 | let outputNoteName = this.outputNoteInput.value.trim(); 700 | 701 | if (!model) { 702 | new Notice('Please select a model or set a default model in settings before running.'); 703 | return; 704 | } 705 | 706 | if (!pattern) { 707 | new Notice('Please select a pattern first'); 708 | return; 709 | } 710 | 711 | try { 712 | const response = await fetch(this.plugin.settings.fabricConnectorApiUrl + '/fabric', { 713 | method: 'POST', 714 | headers: { 715 | 'Content-Type': 'application/json', 716 | 'Accept': 'application/json', 717 | 'X-API-Key': this.plugin.settings.fabricConnectorApiKey 718 | }, 719 | body: JSON.stringify({ 720 | pattern: pattern, 721 | model: model, 722 | data: searchResult, 723 | stream: true, 724 | goCompatibility: this.plugin.settings.fabric2 725 | }) 726 | }); 727 | 728 | if (!response.ok) { 729 | throw new Error(`HTTP error! status: ${response.status}`); 730 | } 731 | 732 | const responseData = await response.json(); 733 | const output = responseData.output; 734 | 735 | const newFile = await this.createOutputNote(output, outputNoteName); 736 | new Notice('Fabric output generated successfully with Tavily search result'); 737 | } catch (error) { 738 | console.error('Failed to run fabric with Tavily result:', error); 739 | new Notice('Failed to run fabric with Tavily result. Please check your settings and try again.'); 740 | } 741 | } 742 | 743 | async runFabric(source: 'current' | 'clipboard' | 'pattern') { 744 | let data = ''; 745 | let pattern = this.selectedPatterns; 746 | let outputNoteName = this.outputNoteInput.value.trim(); 747 | const model = this.getCurrentModel(); 748 | 749 | if (!model) { 750 | new Notice('Please select a model or set a default model in settings before running.'); 751 | return; 752 | } 753 | 754 | if (source === 'current') { 755 | const activeFile = this.app.workspace.getActiveFile(); 756 | if (activeFile) { 757 | data = await this.app.vault.read(activeFile) + '\n'; 758 | } 759 | } else if (source === 'clipboard') { 760 | data = await navigator.clipboard.readText() + '\n'; 761 | } else if (source === 'pattern') { 762 | if (!pattern) { 763 | new Notice('Please select a pattern first'); 764 | return; 765 | } 766 | } 767 | 768 | this.logoContainer.addClass('loading'); 769 | this.loadingText.setText(''); 770 | this.animateLoadingText(this.getRandomLoadingMessage()); 771 | try { 772 | const response = await fetch(this.plugin.settings.fabricConnectorApiUrl + '/fabric', { 773 | method: 'POST', 774 | headers: { 775 | 'Content-Type': 'application/json', 776 | 'Accept': 'application/json', 777 | 'X-API-Key': this.plugin.settings.fabricConnectorApiKey 778 | }, 779 | body: JSON.stringify({ 780 | pattern: pattern, 781 | model: model, 782 | data: data, 783 | stream: true, 784 | goCompatibility: this.plugin.settings.fabric2 785 | }) 786 | }); 787 | 788 | if (!response.ok) { 789 | throw new Error(`HTTP error! status: ${response.status}`); 790 | } 791 | 792 | const responseData = await response.json(); 793 | const output = responseData.output; 794 | 795 | const newFile = await this.createOutputNote(output, outputNoteName); 796 | this.logoContainer.removeClass('loading'); 797 | this.loadingText.setText(''); 798 | new Notice('Fabric output generated successfully'); 799 | } catch (error) { 800 | console.error('Failed to run fabric:', error); 801 | this.logoContainer.removeClass('loading'); 802 | this.loadingText.setText(''); 803 | new Notice('Failed to run fabric. Please check your settings and try again.'); 804 | } 805 | } 806 | 807 | getRandomLoadingMessage(): string { 808 | return this.loadingMessages[Math.floor(Math.random() * this.loadingMessages.length)]; 809 | } 810 | 811 | animateLoadingText(text: string) { 812 | let i = 0; 813 | const intervalId = setInterval(() => { 814 | if (!this.logoContainer.hasClass('loading')) { 815 | clearInterval(intervalId); 816 | return; 817 | } 818 | if (i < text.length) { 819 | this.loadingText.setText(this.loadingText.getText() + text[i]); 820 | i++; 821 | } else { 822 | setTimeout(() => { 823 | this.loadingText.setText(''); 824 | i = 0; 825 | }, 1000); // Pause for a second before restarting 826 | } 827 | }, 100); // Adjust this value to change the typing speed 828 | } 829 | 830 | async createOutputNote(content: string, noteName: string): Promise { 831 | let fileName = `${noteName}.md`; 832 | let filePath = path.join(this.plugin.settings.outputFolder, fileName); 833 | let fileExists = await this.app.vault.adapter.exists(filePath); 834 | let counter = 1; 835 | 836 | while (fileExists) { 837 | fileName = `${noteName} (${counter}).md`; 838 | filePath = path.join(this.plugin.settings.outputFolder, fileName); 839 | fileExists = await this.app.vault.adapter.exists(filePath); 840 | counter++; 841 | } 842 | 843 | const newFile = await this.app.vault.create(filePath, content); 844 | 845 | // Get the most recently focused leaf in the main workspace 846 | const activeLeaf = this.app.workspace.getMostRecentLeaf(); 847 | 848 | if (activeLeaf && !activeLeaf.getViewState().pinned) { 849 | // If there's an active leaf and it's not pinned, create a new leaf in split 850 | const newLeaf = this.app.workspace.createLeafBySplit(activeLeaf, 'vertical'); 851 | await newLeaf.openFile(newFile); 852 | } else { 853 | // If there's no active leaf or it's pinned, create a new leaf 854 | const newLeaf = this.app.workspace.getLeaf('tab'); 855 | await newLeaf.openFile(newFile); 856 | } 857 | 858 | return newFile; 859 | } 860 | 861 | 862 | 863 | 864 | updatePatternOptions(query: string) { 865 | this.patternDropdown.empty(); 866 | this.selectedOptionIndex = -1; 867 | 868 | const filteredPatterns = query 869 | ? fuzzaldrin.filter(this.patterns, query) 870 | : this.patterns; 871 | 872 | if (filteredPatterns.length === 0) { 873 | this.patternDropdown.createEl('div', { 874 | cls: 'fabric-dropdown-option', 875 | text: 'No patterns found' 876 | }); 877 | } else { 878 | filteredPatterns.forEach((pattern, index) => { 879 | this.addPatternOption(pattern, index); 880 | }); 881 | } 882 | 883 | if (this.patternDropdown.children.length > 0) { 884 | this.selectedOptionIndex = 0; 885 | this.updateSelectedOption(this.patternDropdown.querySelectorAll('.fabric-dropdown-option')); 886 | } 887 | } 888 | 889 | 890 | private addPatternOption(pattern: string, index: number) { 891 | const option = this.patternDropdown.createEl('div', { 892 | cls: `fabric-dropdown-option ${index === 0 ? 'selected' : ''}`, 893 | text: pattern 894 | }); 895 | 896 | option.addEventListener('mousedown', (event) => { 897 | // Prevent the default behavior which would trigger the blur event 898 | event.preventDefault(); 899 | }); 900 | 901 | option.addEventListener('click', (event) => { 902 | event.preventDefault(); 903 | this.selectPattern(pattern); 904 | // Refocus on the input after selection 905 | this.searchInput.focus(); 906 | }); 907 | } 908 | 909 | private selectPattern(pattern: string) { 910 | if (this.selectedPatterns.length >= 5 && !this.selectedPatterns.includes(pattern)) { 911 | new Notice('You can only select up to 5 patterns.'); 912 | return; 913 | } 914 | 915 | if (!this.selectedPatterns.includes(pattern)) { 916 | this.selectedPatterns.push(pattern); 917 | } 918 | 919 | this.searchInput.value = this.selectedPatterns.join(', ') + (this.selectedPatterns.length > 0 ? ', ' : ''); 920 | this.updatePatternOptions(''); 921 | this.searchInput.focus(); 922 | 923 | // Set cursor position to the end 924 | const len = this.searchInput.value.length; 925 | this.searchInput.setSelectionRange(len, len); 926 | } 927 | 928 | private updateSelectedOption(options: NodeListOf) { 929 | options.forEach((option, index) => { 930 | if (index === this.selectedOptionIndex) { 931 | option.classList.add('selected'); 932 | option.scrollIntoView({ block: 'nearest' }); 933 | } else { 934 | option.classList.remove('selected'); 935 | } 936 | }); 937 | } 938 | 939 | updateModelOptions(query: string) { 940 | this.modelDropdown.empty(); 941 | this.selectedModelIndex = -1; 942 | 943 | if (query === '') return; 944 | 945 | const filteredModels = fuzzaldrin.filter(this.models, query); 946 | 947 | if (filteredModels.length === 0 && query !== '') { 948 | this.modelDropdown.createEl('div', { 949 | cls: 'fabric-dropdown-option', 950 | text: 'No models found' 951 | }); 952 | } else { 953 | filteredModels.forEach((model, index) => { 954 | const option = this.modelDropdown.createEl('div', { 955 | cls: `fabric-dropdown-option ${index === 0 ? 'selected' : ''}`, 956 | text: model 957 | }); 958 | option.addEventListener('click', () => { 959 | this.selectModel(model); 960 | }); 961 | }); 962 | this.selectedModelIndex = 0; 963 | } 964 | } 965 | 966 | selectModel(model: string) { 967 | this.modelSearchInput.value = model; 968 | this.modelDropdown.empty(); 969 | this.updatePoweredByText(model); 970 | } 971 | 972 | updatePoweredByText(model: string) { 973 | const displayModel = model || this.plugin.settings.defaultModel || 'No model selected'; 974 | 975 | if (this.modelNameSpan.textContent !== displayModel) { 976 | this.modelNameSpan.setText(displayModel); 977 | this.modelNameSpan.addClass('updating'); 978 | 979 | setTimeout(() => { 980 | this.modelNameSpan.removeClass('updating'); 981 | }, 500); // This should match the animation duration 982 | } 983 | } 984 | 985 | updateDefaultModelDisplay() { 986 | this.updatePoweredByText(this.modelSearchInput.value); 987 | } 988 | 989 | getCurrentModel(): string { 990 | return this.modelSearchInput.value || this.plugin.settings.defaultModel; 991 | } 992 | 993 | async loadModels() { 994 | try { 995 | const url = new URL(this.plugin.settings.fabricConnectorApiUrl + '/models'); 996 | url.searchParams.append('goCompatibility', this.plugin.settings.fabric2.toString()); 997 | const response = await fetch(url, { 998 | method: 'GET', 999 | headers: { 1000 | 'Content-Type': 'application/json', 1001 | 'X-API-Key': this.plugin.settings.fabricConnectorApiKey 1002 | } 1003 | }); 1004 | if (!response.ok) { 1005 | throw new Error(`HTTP error! status: ${response.status}`); 1006 | } 1007 | const data = await response.json(); 1008 | this.models = data.data.models.map((model: { name: any; }) => model.name); 1009 | this.plugin.log('Models loaded:', this.models); 1010 | this.updateDefaultModelDisplay(); 1011 | } catch (error) { 1012 | this.plugin.log('Failed to load models from API:', error); 1013 | new Notice('Failed to load models. Please check the API server.'); 1014 | } 1015 | } 1016 | 1017 | handleDropdownNavigation(event: KeyboardEvent, dropdown: HTMLElement, input: HTMLInputElement) { 1018 | switch (event.key) { 1019 | case 'ArrowDown': 1020 | event.preventDefault(); 1021 | this.navigateDropdownOptions(1, dropdown); 1022 | break; 1023 | case 'ArrowUp': 1024 | event.preventDefault(); 1025 | this.navigateDropdownOptions(-1, dropdown); 1026 | break; 1027 | case 'Enter': 1028 | event.preventDefault(); 1029 | this.selectCurrentOption(dropdown, input); 1030 | break; 1031 | } 1032 | } 1033 | 1034 | handlePatternDropdownNavigation(event: KeyboardEvent, dropdown: HTMLElement, input: HTMLInputElement) { 1035 | const options = dropdown.querySelectorAll('.fabric-dropdown-option'); 1036 | 1037 | switch (event.key) { 1038 | case 'ArrowDown': 1039 | event.preventDefault(); 1040 | this.selectedOptionIndex = Math.min(this.selectedOptionIndex + 1, options.length - 1); 1041 | this.updateSelectedOption(options); 1042 | break; 1043 | case 'ArrowUp': 1044 | event.preventDefault(); 1045 | this.selectedOptionIndex = Math.max(this.selectedOptionIndex - 1, 0); 1046 | this.updateSelectedOption(options); 1047 | break; 1048 | case 'Enter': 1049 | event.preventDefault(); 1050 | if (this.selectedOptionIndex >= 0 && this.selectedOptionIndex < options.length) { 1051 | const selectedPattern = options[this.selectedOptionIndex].textContent; 1052 | if (selectedPattern) { 1053 | this.selectPattern(selectedPattern); 1054 | // Keep the dropdown open 1055 | this.updatePatternOptions(''); 1056 | } 1057 | } 1058 | break; 1059 | case 'Escape': 1060 | event.preventDefault(); 1061 | dropdown.empty(); 1062 | this.selectedOptionIndex = -1; 1063 | break; 1064 | } 1065 | } 1066 | 1067 | 1068 | navigateDropdownOptions(direction: number, dropdown: HTMLElement) { 1069 | const options = Array.from(dropdown.children) as HTMLElement[]; 1070 | const optionsCount = options.length; 1071 | 1072 | if (optionsCount === 0) return; 1073 | 1074 | const currentIndex = dropdown === this.patternDropdown ? this.selectedOptionIndex : this.selectedModelIndex; 1075 | const newIndex = (currentIndex + direction + optionsCount) % optionsCount; 1076 | 1077 | options.forEach((option, index) => { 1078 | if (index === newIndex) { 1079 | option.classList.add('selected'); 1080 | } else { 1081 | option.classList.remove('selected'); 1082 | } 1083 | }); 1084 | 1085 | if (dropdown === this.patternDropdown) { 1086 | this.selectedOptionIndex = newIndex; 1087 | } else { 1088 | this.selectedModelIndex = newIndex; 1089 | } 1090 | } 1091 | 1092 | selectCurrentOption(dropdown: HTMLElement, input: HTMLInputElement) { 1093 | const index = dropdown === this.modelDropdown ? this.selectedModelIndex : this.selectedOptionIndex; 1094 | const selectedOption = dropdown.children[index] as HTMLElement; 1095 | if (selectedOption) { 1096 | const value = selectedOption.textContent!; 1097 | if (dropdown === this.modelDropdown) { 1098 | this.selectModel(value); 1099 | } else { 1100 | input.value = value; 1101 | dropdown.empty(); 1102 | } 1103 | } 1104 | } 1105 | 1106 | async loadPatterns() { 1107 | try { 1108 | const url = new URL(this.plugin.settings.fabricConnectorApiUrl + '/patterns'); 1109 | url.searchParams.append('goCompatibility', this.plugin.settings.fabric2.toString()); 1110 | const response = await fetch(url, { 1111 | method: 'GET', 1112 | headers: { 1113 | 'Content-Type': 'application/json', 1114 | 'X-API-Key': this.plugin.settings.fabricConnectorApiKey 1115 | }, 1116 | }); 1117 | if (!response.ok) { 1118 | throw new Error(`HTTP error! status: ${response.status}`); 1119 | } 1120 | const data = await response.json(); 1121 | // Extract pattern names from the JSON structure 1122 | this.patterns = data.data.patterns.map((pattern: { name: any; }) => pattern.name); 1123 | this.plugin.log('Patterns loaded:', this.patterns); 1124 | } catch (error) { 1125 | this.plugin.log('Failed to load patterns from API:', error); 1126 | new Notice('Failed to load patterns. Please check the API server.'); 1127 | } 1128 | } 1129 | 1130 | async handleFabricRun(source: 'current' | 'clipboard') { 1131 | if (this.plugin.settings.defaultPostProcessingPattern) { 1132 | this.selectedPatterns.push(this.plugin.settings.defaultPostProcessingPattern); 1133 | } 1134 | if (this.ytToggle.classList.contains('active') && this.tsToggle.classList.contains('active')) { 1135 | const links = await this.extractYouTubeLinks(source); 1136 | const paths = await this.extractAudioFiles(source); 1137 | if (links.length > 0 && paths.length > 0) { 1138 | new Notice('Both YouTube links and audio files found. This is not supported yet. Try using clipboard.'); 1139 | this.runFabric(source); 1140 | return; 1141 | } 1142 | if (links.length > 0) { 1143 | this.showYouTubeModal(links, source); 1144 | } else if (paths.length > 0) { 1145 | this.showAudioModal(paths, source); 1146 | } else { 1147 | new Notice('No YouTube links or audio files found. Running Fabric normally.'); 1148 | this.runFabric(source); 1149 | } 1150 | } else if (this.tsToggle.classList.contains('active')) { 1151 | const links = await this.extractAudioFiles(source); 1152 | if (links.length > 0) { 1153 | this.showAudioModal(links, source); 1154 | } else { 1155 | new Notice('No audio files found. Running Fabric normally.'); 1156 | this.runFabric(source); 1157 | } 1158 | } else if (this.ytToggle.classList.contains('active')) { 1159 | const links = await this.extractYouTubeLinks(source); 1160 | if (links.length > 0) { 1161 | this.showYouTubeModal(links, source); 1162 | } else { 1163 | new Notice('No YouTube links found. Running Fabric normally.'); 1164 | this.runFabric(source); 1165 | } 1166 | } else { 1167 | this.runFabric(source); 1168 | } 1169 | } 1170 | 1171 | async extractYouTubeLinks(source: 'current' | 'clipboard'): Promise { 1172 | let text = ''; 1173 | if (source === 'current') { 1174 | const activeFile = this.app.workspace.getActiveFile(); 1175 | if (activeFile) { 1176 | text = await this.app.vault.read(activeFile); 1177 | } 1178 | } else { 1179 | text = await navigator.clipboard.readText(); 1180 | } 1181 | const youtubeRegex = /(?:https?:\/\/)?(?:www\.)?(?:youtube\.com|youtu\.be)\/(?:watch\?v=)?(?:embed\/)?(?:v\/)?(?:shorts\/)?(?:\S+)/g; 1182 | return text.match(youtubeRegex) || []; 1183 | } 1184 | 1185 | async extractAudioFiles(source: 'current' | 'clipboard'): Promise { 1186 | let text = ''; 1187 | if (source === 'current') { 1188 | const activeFile = this.app.workspace.getActiveFile(); 1189 | if (activeFile) { 1190 | text = await this.app.vault.read(activeFile); 1191 | } 1192 | } else { 1193 | text = await navigator.clipboard.readText(); 1194 | } 1195 | const audioRegex = /(?:^|[\s(])?(?:")?(?:\/|[a-zA-Z]:[\\/])(?:[^"\n\r]*[\\/])*[^"\n\r]*\.(?:mp3|wav|ogg|aac|flac|mp4|mkv|webm|avi|mov|flv)(?:")?(?:\s|$)/gi; 1196 | return text.match(audioRegex) || []; 1197 | } 1198 | 1199 | showYouTubeModal(links: string[], source: 'current' | 'clipboard') { 1200 | const modal = new Modal(this.app); 1201 | modal.titleEl.setText('Select YouTube Link'); 1202 | const { contentEl } = modal; 1203 | 1204 | let selectedIndex = 0; 1205 | 1206 | const linkList = contentEl.createEl('div', { cls: 'fabric-yt-link-list' }); 1207 | 1208 | const updateSelection = () => { 1209 | linkList.querySelectorAll('.fabric-yt-link').forEach((el, index) => { 1210 | el.classList.toggle('is-selected', index === selectedIndex); 1211 | }); 1212 | }; 1213 | 1214 | links.forEach((link, index) => { 1215 | const linkEl = linkList.createEl('div', { cls: 'fabric-yt-link', text: link }); 1216 | linkEl.addEventListener('click', () => { 1217 | selectedIndex = index; 1218 | updateSelection(); 1219 | }); 1220 | }); 1221 | 1222 | const buttonContainer = contentEl.createEl('div', { cls: 'fabric-yt-modal-buttons' }); 1223 | const skipButton = buttonContainer.createEl('button', { text: 'Skip' }); 1224 | skipButton.addClass('skip-button'); 1225 | const runYTButton = buttonContainer.createEl('button', { text: 'Run' }); 1226 | runYTButton.addClass('run-button'); 1227 | 1228 | skipButton.addEventListener('click', () => { 1229 | modal.close(); 1230 | this.runFabric(source); 1231 | }); 1232 | 1233 | runYTButton.addEventListener('click', () => { 1234 | modal.close(); 1235 | if (links.length > 0) { 1236 | this.runYT(links[selectedIndex]); 1237 | } else { 1238 | new Notice('No YouTube links found'); 1239 | } 1240 | }); 1241 | 1242 | modal.onOpen = () => { 1243 | updateSelection(); 1244 | 1245 | const handleKeyDown = (event: KeyboardEvent) => { 1246 | switch (event.key) { 1247 | case 'ArrowUp': 1248 | selectedIndex = (selectedIndex - 1 + links.length) % links.length; 1249 | updateSelection(); 1250 | event.preventDefault(); 1251 | break; 1252 | case 'ArrowDown': 1253 | selectedIndex = (selectedIndex + 1) % links.length; 1254 | updateSelection(); 1255 | event.preventDefault(); 1256 | break; 1257 | case 'Enter': 1258 | modal.close(); 1259 | this.runYT(links[selectedIndex]); 1260 | event.preventDefault(); 1261 | break; 1262 | } 1263 | }; 1264 | 1265 | document.addEventListener('keydown', handleKeyDown); 1266 | modal.onClose = () => { 1267 | document.removeEventListener('keydown', handleKeyDown); 1268 | }; 1269 | }; 1270 | 1271 | modal.open(); 1272 | } 1273 | 1274 | showAudioModal(files: string[], source: 'current' | 'clipboard') { 1275 | const modal = new Modal(this.app); 1276 | modal.titleEl.setText('Select Audio File Path'); 1277 | const { contentEl } = modal; 1278 | 1279 | let selectedIndex = 0; 1280 | 1281 | const fileList = contentEl.createEl('div', { cls: 'fabric-ts-link-list' }); 1282 | 1283 | const updateSelection = () => { 1284 | fileList.querySelectorAll('.fabric-ts-link').forEach((el, index) => { 1285 | el.classList.toggle('is-selected', index === selectedIndex); 1286 | }); 1287 | }; 1288 | 1289 | files.forEach((file, index) => { 1290 | const linkEl = fileList.createEl('div', { cls: 'fabric-ts-link', text: file }); 1291 | linkEl.addEventListener('click', () => { 1292 | selectedIndex = index; 1293 | updateSelection(); 1294 | }); 1295 | }); 1296 | 1297 | const buttonContainer = contentEl.createEl('div', { cls: 'fabric-ts-modal-buttons' }); 1298 | const skipButton = buttonContainer.createEl('button', { text: 'Skip' }); 1299 | skipButton.addClass('skip-button'); 1300 | const runTSButton = buttonContainer.createEl('button', { text: 'Run' }); 1301 | runTSButton.addClass('run-button'); 1302 | 1303 | skipButton.addEventListener('click', () => { 1304 | modal.close(); 1305 | this.runTS(source); 1306 | }); 1307 | 1308 | runTSButton.addEventListener('click', () => { 1309 | modal.close(); 1310 | if (files.length > 0) { 1311 | this.runTS(files[selectedIndex]); 1312 | } else { 1313 | new Notice('No audio files found'); 1314 | } 1315 | }); 1316 | 1317 | modal.onOpen = () => { 1318 | updateSelection(); 1319 | 1320 | const handleKeyDown = (event: KeyboardEvent) => { 1321 | switch (event.key) { 1322 | case 'ArrowUp': 1323 | selectedIndex = (selectedIndex - 1 + files.length) % files.length; 1324 | updateSelection(); 1325 | event.preventDefault(); 1326 | break; 1327 | case 'ArrowDown': 1328 | selectedIndex = (selectedIndex + 1) % files.length; 1329 | updateSelection(); 1330 | event.preventDefault(); 1331 | break; 1332 | case 'Enter': 1333 | modal.close(); 1334 | this.runTS(files[selectedIndex]); 1335 | event.preventDefault(); 1336 | break; 1337 | } 1338 | }; 1339 | 1340 | document.addEventListener('keydown', handleKeyDown); 1341 | modal.onClose = () => { 1342 | document.removeEventListener('keydown', handleKeyDown); 1343 | }; 1344 | }; 1345 | 1346 | modal.open(); 1347 | } 1348 | 1349 | 1350 | async runYT(url: string) { 1351 | let outputNoteName = this.outputNoteInput.value.trim(); 1352 | const pattern = this.selectedPatterns 1353 | const model = this.getCurrentModel(); 1354 | 1355 | if (!model) { 1356 | new Notice('Please select a model or set a default model in settings before running.'); 1357 | return; 1358 | } 1359 | 1360 | if (!pattern) { 1361 | new Notice('Please select a pattern first'); 1362 | return; 1363 | } 1364 | 1365 | this.logoContainer.addClass('loading'); 1366 | this.loadingText.setText(''); 1367 | this.animateLoadingText(this.getRandomLoadingMessage()); 1368 | 1369 | 1370 | try { 1371 | const response = await fetch(this.plugin.settings.fabricConnectorApiUrl + '/yt', { 1372 | method: 'POST', 1373 | headers: { 1374 | 'Content-Type': 'application/json', 1375 | 'X-API-Key': this.plugin.settings.fabricConnectorApiKey 1376 | }, 1377 | body: JSON.stringify({ 1378 | pattern: pattern, 1379 | model: model, 1380 | url: url, 1381 | stream: true, 1382 | goCompatibility: this.plugin.settings.fabric2 1383 | }), 1384 | }); 1385 | 1386 | if (!response.ok) { 1387 | throw new Error(`HTTP error! status: ${response.status}`); 1388 | } 1389 | 1390 | const data = await response.json(); 1391 | const output = data.output; 1392 | 1393 | const newFile = await this.createOutputNote(output, outputNoteName); 1394 | this.logoContainer.removeClass('loading'); 1395 | this.loadingText.setText(''); 1396 | new Notice('YouTube Fabric output generated successfully'); 1397 | } catch (error) { 1398 | console.error('Failed to run YouTube Fabric:', error); 1399 | new Notice('Failed to run YouTube Fabric. Please check your settings and try again.'); 1400 | } 1401 | } 1402 | 1403 | async runTS(path: string) { 1404 | let outputNoteName = this.outputNoteInput.value.trim(); 1405 | const pattern = this.selectedPatterns 1406 | const model = this.getCurrentModel(); 1407 | 1408 | if (!model) { 1409 | new Notice('Please select a model or set a default model in settings before running.'); 1410 | return; 1411 | } 1412 | 1413 | if (!pattern) { 1414 | new Notice('Please select a pattern first'); 1415 | return; 1416 | } 1417 | 1418 | this.logoContainer.addClass('loading'); 1419 | this.loadingText.setText(''); 1420 | this.animateLoadingText(this.getRandomLoadingMessage()); 1421 | 1422 | try { 1423 | const response = await fetch(this.plugin.settings.fabricConnectorApiUrl + '/ts', { 1424 | method: 'POST', 1425 | headers: { 1426 | 'Content-Type': 'application/json', 1427 | 'X-API-Key': this.plugin.settings.fabricConnectorApiKey 1428 | }, 1429 | body: JSON.stringify({ 1430 | pattern: pattern, 1431 | model: model, 1432 | path: path, 1433 | stream: true, 1434 | goCompatibility: this.plugin.settings.fabric2 1435 | }), 1436 | }); 1437 | 1438 | if (!response.ok) { 1439 | throw new Error(`HTTP error! status: ${response.status}`); 1440 | } 1441 | 1442 | const data = await response.json(); 1443 | const output = data.output; 1444 | 1445 | const newFile = await this.createOutputNote(output, outputNoteName); 1446 | this.logoContainer.removeClass('loading'); 1447 | this.loadingText.setText(''); 1448 | new Notice('TS Fabric output generated successfully'); 1449 | } catch (error) { 1450 | console.error('Failed to run TS Fabric:', error); 1451 | new Notice('Failed to run TS Fabric. Please check your settings and try again.'); 1452 | } 1453 | } 1454 | 1455 | async syncCustomPatterns() { 1456 | const customPatternsFolder = this.plugin.settings.customPatternsFolder; 1457 | if (!customPatternsFolder) { 1458 | new Notice('Custom patterns folder not set. Please set it in the plugin settings.'); 1459 | return; 1460 | } 1461 | 1462 | const folderPath = this.app.vault.getAbstractFileByPath(customPatternsFolder); 1463 | if (!folderPath || !(folderPath instanceof TFolder)) { 1464 | new Notice('Custom patterns folder not found in the vault.'); 1465 | return; 1466 | } 1467 | 1468 | for (const file of folderPath.children) { 1469 | if (file instanceof TFile && file.extension === 'md') { 1470 | const content = await this.app.vault.read(file); 1471 | const patternName = file.basename; 1472 | 1473 | try { 1474 | await this.updatePattern(patternName, content); 1475 | new Notice(`Pattern "${patternName}" synced successfully.`); 1476 | } catch (error) { 1477 | new Notice(`Failed to sync pattern "${patternName}": ${error.message}`); 1478 | } 1479 | } 1480 | } 1481 | await this.loadPatterns() 1482 | } 1483 | 1484 | async updatePattern(name: string, content: string) { 1485 | const response = await fetch(this.plugin.settings.fabricConnectorApiUrl + '/update_pattern', { 1486 | method: 'POST', 1487 | headers: { 1488 | 'Content-Type': 'application/json', 1489 | 'X-API-Key': this.plugin.settings.fabricConnectorApiKey 1490 | }, 1491 | body: JSON.stringify({ 1492 | pattern: name, 1493 | content: content 1494 | }) 1495 | }); 1496 | 1497 | if (!response.ok) { 1498 | throw new Error(`HTTP error! status: ${response.status}`); 1499 | } 1500 | 1501 | 1502 | 1503 | 1504 | return await response.json(); 1505 | } 1506 | 1507 | } 1508 | 1509 | class FabricSettingTab extends PluginSettingTab { 1510 | plugin: FabricPlugin; 1511 | 1512 | constructor(app: App, plugin: FabricPlugin) { 1513 | super(app, plugin); 1514 | this.plugin = plugin; 1515 | } 1516 | 1517 | display(): void { 1518 | const { containerEl } = this; 1519 | 1520 | containerEl.empty(); 1521 | 1522 | // Instructions for Fabric Connector API URL and Key 1523 | containerEl.createEl('p', { 1524 | text: 'To set up the Fabric Connector:', 1525 | cls: 'fabric-settings-instruction' 1526 | }); 1527 | containerEl.createEl('ol', {cls: 'fabric-settings-list'}, (ol) => { 1528 | ol.createEl('li', {text: 'Click the Fabric Connector icon (brain icon) in your system tray'}); 1529 | ol.createEl('li', {text: 'For the API URL: Click "Open API Docs" and copy the URL from your browser, removing "/docs" from the end'}); 1530 | ol.createEl('li', {text: 'For the API Key: Click "Copy API Key"'}); 1531 | }); 1532 | 1533 | // Fabric Connector API URL input 1534 | new Setting(containerEl) 1535 | .setName('Fabric Connector API URL') 1536 | .setDesc('Enter the URL for the Fabric Connector API') 1537 | .addDropdown(dropdown => { 1538 | dropdown 1539 | .addOption('http://', 'http://') 1540 | .addOption('https://', 'https://') 1541 | .setValue(this.plugin.settings.fabricConnectorApiUrl.startsWith('https://') ? 'https://' : 'http://') 1542 | .onChange(async (value) => { 1543 | const domain = this.plugin.settings.fabricConnectorApiUrl.replace(/^https?:\/\//, ''); 1544 | this.plugin.settings.fabricConnectorApiUrl = value + domain; 1545 | await this.plugin.saveSettings(); 1546 | }); 1547 | }) 1548 | .addText(text => { 1549 | const domain = this.plugin.settings.fabricConnectorApiUrl.replace(/^https?:\/\//, ''); 1550 | text 1551 | .setPlaceholder('Enter domain') 1552 | .setValue(domain) 1553 | .onChange(async (value) => { 1554 | // Remove any slashes from the input 1555 | const cleanValue = value.replace(/\//g, ''); 1556 | const protocol = this.plugin.settings.fabricConnectorApiUrl.startsWith('https://') ? 'https://' : 'http://'; 1557 | this.plugin.settings.fabricConnectorApiUrl = protocol + cleanValue; 1558 | await this.plugin.saveSettings(); 1559 | // Update the text field to show the cleaned value 1560 | text.setValue(cleanValue); 1561 | }); 1562 | }); 1563 | 1564 | // Fabric Connector API Key input 1565 | new Setting(containerEl) 1566 | .setName('Fabric Connector API Key') 1567 | .setDesc('Enter your API key for the Fabric Connector') 1568 | .addText(text => text 1569 | .setPlaceholder('Enter API Key') 1570 | .setValue(this.plugin.settings.fabricConnectorApiKey || '') 1571 | .onChange(async (value) => { 1572 | this.plugin.settings.fabricConnectorApiKey = value; 1573 | await this.plugin.saveSettings(); 1574 | })) 1575 | .addButton(button => button 1576 | .setButtonText('Test API Key') 1577 | .onClick(async () => { 1578 | await this.testFabricConnectorApiKey(); 1579 | })); 1580 | 1581 | 1582 | new Setting(containerEl) 1583 | .setName('Tavily API Key') 1584 | .setDesc('Enter your Tavily API key') 1585 | .addText(text => text 1586 | .setPlaceholder('Enter API Key') 1587 | .setValue(this.plugin.settings.tavilyApiKey || '') 1588 | .onChange(async (value) => { 1589 | this.plugin.settings.tavilyApiKey = value; 1590 | await this.plugin.saveSettings(); 1591 | })) 1592 | .addButton(button => button 1593 | .setButtonText('Test API Key') 1594 | .onClick(async () => { 1595 | await this.testTavilyApiKey(); 1596 | })); 1597 | 1598 | new Setting(containerEl) 1599 | .setName('Output Folder') 1600 | .setDesc('Folder to save output files') 1601 | .addText(text => text 1602 | .setPlaceholder('Enter folder path') 1603 | .setValue(this.plugin.settings.outputFolder) 1604 | .onChange(async (value) => { 1605 | this.plugin.settings.outputFolder = value; 1606 | await this.plugin.saveSettings(); 1607 | })); 1608 | 1609 | new Setting(containerEl) 1610 | .setName('Custom Patterns Folder') 1611 | .setDesc('Folder to store custom patterns') 1612 | .addText(text => text 1613 | .setPlaceholder('Enter folder path') 1614 | .setValue(this.plugin.settings.customPatternsFolder) 1615 | .onChange(async (value) => { 1616 | this.plugin.settings.customPatternsFolder = value; 1617 | await this.plugin.saveSettings(); 1618 | this.plugin.registerCustomPatternsFolderWatcher(); 1619 | })); 1620 | 1621 | new Setting(containerEl) 1622 | .setName('Default Model') 1623 | .setDesc('The default model to use when running Fabric') 1624 | .addText(text => text 1625 | .setPlaceholder('Enter default model') 1626 | .setValue(this.plugin.settings.defaultModel) 1627 | .onChange(async (value) => { 1628 | this.plugin.settings.defaultModel = value; 1629 | await this.plugin.saveSettings(); 1630 | })); 1631 | 1632 | new Setting(containerEl) 1633 | .setName('Default Post Processing Pattern') 1634 | .setDesc('This pattern will be appended to selected patterns when running Fabric') 1635 | .addText(text => text 1636 | .setPlaceholder('Enter pattern name') 1637 | .setValue(this.plugin.settings.defaultPostProcessingPattern) 1638 | .onChange(async (value) => { 1639 | this.plugin.settings.defaultPostProcessingPattern = value; 1640 | await this.plugin.saveSettings(); 1641 | })); 1642 | 1643 | new Setting(containerEl) 1644 | .setName('Fabric 2.0 Compatibility Mode') 1645 | .setDesc('Enable comptaibility mode for Fabric 2.0') 1646 | .addToggle(toggle => toggle 1647 | .setValue(this.plugin.settings.fabric2) 1648 | .onChange(async (value) => { 1649 | this.plugin.settings.fabric2 = value; 1650 | await this.plugin.saveSettings(); 1651 | this.plugin.updateLogging(); 1652 | })); 1653 | 1654 | new Setting(containerEl) 1655 | .setName('Debug Mode') 1656 | .setDesc('Enable debug logging') 1657 | .addToggle(toggle => toggle 1658 | .setValue(this.plugin.settings.debug) 1659 | .onChange(async (value) => { 1660 | this.plugin.settings.debug = value; 1661 | await this.plugin.saveSettings(); 1662 | this.plugin.updateLogging(); 1663 | })); 1664 | } 1665 | 1666 | async testFabricConnectorApiKey() { 1667 | const fabricConnectorApiUrl = this.plugin.settings.fabricConnectorApiUrl; 1668 | const url = new URL(this.plugin.settings.fabricConnectorApiUrl + '/models'); 1669 | url.searchParams.append('goCompatibility', this.plugin.settings.fabric2.toString()); 1670 | const apiKey = this.plugin.settings.fabricConnectorApiKey; 1671 | 1672 | if (!fabricConnectorApiUrl || !apiKey) { 1673 | new Notice('Please enter both Fabric Connector API URL and API Key'); 1674 | return; 1675 | } 1676 | 1677 | try { 1678 | const response = await fetch(url, { 1679 | method: 'GET', 1680 | headers: { 1681 | 'Content-Type': 'application/json', 1682 | 'X-API-Key': apiKey 1683 | } 1684 | }); 1685 | 1686 | if (response.ok) { 1687 | new Notice('Fabric Connector API Key is valid'); 1688 | } else { 1689 | new Notice('Invalid Fabric Connector API Key'); 1690 | } 1691 | } catch (error) { 1692 | console.error('Error testing Fabric Connector API Key:', error); 1693 | new Notice('Error testing Fabric Connector API Key. Check console for details.'); 1694 | } 1695 | 1696 | } 1697 | 1698 | async testTavilyApiKey() { 1699 | const apiKey = this.plugin.settings.tavilyApiKey; 1700 | 1701 | if (!this.plugin.settings.tavilyApiKey) { 1702 | new Notice('Please enter a Tavily API Key'); 1703 | return; 1704 | } 1705 | 1706 | try { 1707 | const response = await fetch('https://api.tavily.com/search', { 1708 | method: 'POST', 1709 | headers: { 1710 | 'Content-Type': 'application/json' 1711 | }, 1712 | body: JSON.stringify({ 1713 | query: 'Test Query', 1714 | max_results: 1, 1715 | api_key: apiKey 1716 | }) 1717 | }); 1718 | 1719 | if (response.ok) { 1720 | new Notice('Tavily API Key is valid'); 1721 | } else { 1722 | new Notice('Invalid Tavily API Key'); 1723 | } 1724 | } catch (error) { 1725 | console.error('Error testing Tavily API Key:', error); 1726 | new Notice('Error testing Tavily API Key. Check console for details.'); 1727 | } 1728 | } 1729 | } 1730 | 1731 | 1732 | class CommunityPatternsModal extends Modal { 1733 | plugin: FabricPlugin; 1734 | patterns: any[]; 1735 | searchInput: HTMLInputElement; 1736 | resultsContainer: HTMLElement; 1737 | 1738 | constructor(app: App, plugin: FabricPlugin) { 1739 | super(app); 1740 | this.plugin = plugin; 1741 | } 1742 | 1743 | async onOpen() { 1744 | const { contentEl } = this; 1745 | contentEl.empty(); 1746 | 1747 | contentEl.createEl('h2', { text: 'Community Patterns', cls: 'community-patterns-title' }); 1748 | 1749 | // Add Update All button 1750 | 1751 | const buttonContainer = contentEl.createDiv({ cls: 'community-patterns-button-container' }); 1752 | 1753 | const updateAllButton = new ButtonComponent(buttonContainer) 1754 | .setButtonText('Update All') 1755 | .onClick(() => this.updateAllPatterns()); 1756 | //setIcon(updateAllButton.buttonEl, 'refresh-cw'); // Use an appropriate icon 1757 | updateAllButton.buttonEl.className = 'fabric-button community-patterns-update-all'; 1758 | 1759 | const refreshPatternsButton = new ButtonComponent(buttonContainer) 1760 | .setButtonText('Refresh Patterns') 1761 | .onClick(() => this.refreshPatterns()); 1762 | //setIcon(refreshPatternsButton.buttonEl, 'refresh-cw'); // Use an appropriate icon 1763 | refreshPatternsButton.buttonEl.className = 'fabric-button community-patterns-refresh'; 1764 | 1765 | 1766 | this.searchInput = contentEl.createEl('input', { 1767 | type: 'text', 1768 | placeholder: 'Search patterns...', 1769 | cls: 'community-patterns-search' 1770 | }); 1771 | this.searchInput.addEventListener('input', () => this.updateResults()); 1772 | 1773 | this.resultsContainer = contentEl.createEl('div', { cls: 'community-patterns-results' }); 1774 | 1775 | await this.fetchPatterns(); 1776 | this.updateResults(); 1777 | } 1778 | 1779 | async fetchPatterns() { 1780 | try { 1781 | const response = await fetch('https://raw.githubusercontent.com/chasebank87/fabric-patterns/main/patterns.json'); 1782 | if (!response.ok) { 1783 | throw new Error('Failed to fetch patterns'); 1784 | } 1785 | const data = await response.json(); 1786 | this.patterns = Object.keys(data.patterns).map(key => ({ 1787 | name: key, 1788 | ...data.patterns[key] 1789 | })); 1790 | } catch (error) { 1791 | console.error('Error fetching patterns:', error); 1792 | new Notice('Failed to fetch patterns'); 1793 | } 1794 | } 1795 | 1796 | async isPatternInstalled(patternName: string): Promise { 1797 | const file = `${this.plugin.settings.customPatternsFolder}/${patternName}.md`; 1798 | return await this.app.vault.adapter.exists(file); 1799 | } 1800 | 1801 | async doesPatternNeedUpdate(patternName: string, pattern_repo: string): Promise { 1802 | const installedPath = `${this.plugin.settings.customPatternsFolder}/${patternName}.md`; 1803 | const repoPath = 'https://raw.githubusercontent.com/'+ pattern_repo + '/main/patterns/' + patternName + '.md'; 1804 | 1805 | const installedContent = await this.app.vault.adapter.read(installedPath); 1806 | const response = await fetch(repoPath); 1807 | if (!response.ok) { 1808 | throw new Error('Failed to fetch pattern content'); 1809 | } 1810 | const repoContent = await response.text(); 1811 | 1812 | return installedContent !== repoContent; 1813 | } 1814 | 1815 | async updateResults() { 1816 | const searchTerm = this.searchInput.value.toLowerCase(); 1817 | const filteredPatterns = this.patterns.filter(pattern => 1818 | pattern.name.toLowerCase().includes(searchTerm) || 1819 | pattern.description.toLowerCase().includes(searchTerm) || 1820 | pattern.author.toLowerCase().includes(searchTerm) 1821 | ); 1822 | 1823 | this.resultsContainer.empty(); 1824 | for (const pattern of filteredPatterns) { 1825 | try { 1826 | // Check if the pattern_repo URL is valid 1827 | let currentPatternUrl = 'https://raw.githubusercontent.com/'+ pattern.pattern_repo + '/main/patterns/' + pattern.name + '.md'; 1828 | const response = await fetch(currentPatternUrl); 1829 | if (!response.ok) { 1830 | console.warn(`Pattern ${pattern.name} has an invalid URL: ${currentPatternUrl}`); 1831 | continue; // Skip this pattern and move to the next one 1832 | } 1833 | 1834 | const patternEl = this.resultsContainer.createEl('div', { cls: 'community-pattern-item' }); 1835 | 1836 | const patternInfo = patternEl.createEl('div', { cls: 'community-pattern-info' }); 1837 | patternInfo.createEl('div', { text: pattern.name, cls: 'community-pattern-title' }); 1838 | patternInfo.createEl('div', { text: pattern.description, cls: 'community-pattern-description' }); 1839 | patternInfo.createEl('em', {text: `by ${pattern.author}`, cls: 'community-fabric-author'}); 1840 | patternInfo.createEl('small', { text: `For models: ${pattern.for_models}`, cls: 'community-fabric-models' }); 1841 | 1842 | 1843 | const isInstalled = await this.isPatternInstalled(pattern.name); 1844 | const needsUpdate = isInstalled && await this.doesPatternNeedUpdate(pattern.name, pattern.pattern_repo); 1845 | 1846 | const buttonContainer = patternEl.createEl('div', { cls: 'community-pattern-buttons' }); 1847 | 1848 | if (!isInstalled) { 1849 | const downloadBtn = new ButtonComponent(buttonContainer) 1850 | .setButtonText('') 1851 | .onClick(() => this.downloadPattern(pattern.name, pattern.pattern_repo)); 1852 | downloadBtn.buttonEl.className = 'community-pattern-download'; 1853 | setIcon(downloadBtn.buttonEl, 'download'); 1854 | } else { 1855 | if (needsUpdate) { 1856 | const updateBtn = new ButtonComponent(buttonContainer) 1857 | .setButtonText('') 1858 | .onClick(() => this.updatePattern(pattern.name, pattern.pattern_repo)); 1859 | updateBtn.buttonEl.className = 'community-pattern-update'; 1860 | setIcon(updateBtn.buttonEl, 'refresh-cw'); 1861 | } 1862 | const uninstallBtn = new ButtonComponent(buttonContainer) 1863 | .setButtonText('') 1864 | .onClick(() => this.uninstallPattern(pattern.name)); 1865 | uninstallBtn.buttonEl.className = 'community-pattern-uninstall'; 1866 | setIcon(uninstallBtn.buttonEl, 'trash'); 1867 | } 1868 | } catch (error) { 1869 | console.error(`Error processing pattern ${pattern.name}:`, error); 1870 | // Skip this pattern and move to the next one 1871 | } 1872 | } 1873 | } 1874 | 1875 | async downloadPattern(patternName: string, pattern_repo: string) { 1876 | try { 1877 | let patternUrl = 'https://raw.githubusercontent.com/'+ pattern_repo + '/main/patterns/' + patternName + '.md'; 1878 | console.log(patternUrl); 1879 | const response = await fetch(patternUrl); 1880 | if (!response.ok) { 1881 | throw new Error('Failed to download pattern'); 1882 | } 1883 | const content = await response.text(); 1884 | 1885 | const file = `${this.plugin.settings.customPatternsFolder}/${patternName}.md`; 1886 | await this.app.vault.create(file, content); 1887 | 1888 | new Notice(`Pattern ${patternName} downloaded successfully`); 1889 | this.updateResults(); 1890 | } catch (error) { 1891 | console.error('Error downloading pattern:', error); 1892 | new Notice('Failed to download pattern'); 1893 | } 1894 | } 1895 | 1896 | async updatePattern(patternName: string, pattern_repo: string) { 1897 | try { 1898 | let patternUrl = 'https://raw.githubusercontent.com/'+ pattern_repo + '/main/patterns/' + patternName + '.md'; 1899 | const response = await fetch(patternUrl); 1900 | if (!response.ok) { 1901 | throw new Error('Failed to update pattern'); 1902 | } 1903 | const content = await response.text(); 1904 | 1905 | const file = `${this.plugin.settings.customPatternsFolder}/${patternName}.md`; 1906 | await this.app.vault.adapter.write(file, content); 1907 | 1908 | new Notice(`Pattern ${patternName} updated successfully`); 1909 | this.updateResults(); 1910 | } catch (error) { 1911 | console.error('Error updating pattern:', error); 1912 | new Notice('Failed to update pattern'); 1913 | } 1914 | } 1915 | 1916 | async refreshPatterns() { 1917 | await this.fetchPatterns(); 1918 | this.updateResults(); 1919 | new Notice('Patterns list refreshed'); 1920 | } 1921 | 1922 | 1923 | async uninstallPattern(patternName: string) { 1924 | try { 1925 | const file = `${this.plugin.settings.customPatternsFolder}/${patternName}.md`; 1926 | await this.app.vault.adapter.remove(file); 1927 | new Notice(`Pattern ${patternName} uninstalled successfully`); 1928 | this.updateResults(); 1929 | } catch (error) { 1930 | console.error('Error uninstalling pattern:', error); 1931 | new Notice('Failed to uninstall pattern'); 1932 | } 1933 | } 1934 | 1935 | async updateAllPatterns() { 1936 | let updatedCount = 0; 1937 | for (const pattern of this.patterns) { 1938 | const isInstalled = await this.isPatternInstalled(pattern.name); 1939 | const needsUpdate = isInstalled && await this.doesPatternNeedUpdate(pattern.name, pattern.pattern_repo); 1940 | if (needsUpdate) { 1941 | await this.updatePattern(pattern.name, pattern.pattern_repo); 1942 | updatedCount++; 1943 | } 1944 | } 1945 | new Notice(`Updated ${updatedCount} patterns`); 1946 | this.updateResults(); 1947 | } 1948 | 1949 | onClose() { 1950 | const { contentEl } = this; 1951 | contentEl.empty(); 1952 | } 1953 | } -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "unofficial-fabric-integration", 3 | "name": "Unofficial Fabric Integration", 4 | "version": "1.3.3", 5 | "minAppVersion": "1.6.5", 6 | "description": "Integrates Fabric into your vault", 7 | "author": "Chasebank87", 8 | "authorUrl": "https://chaseelder.com", 9 | "fundingUrl": "https://buymeacoffee.com/chasebank87", 10 | "isDesktopOnly": true 11 | } 12 | 13 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "obsidian-fabric-plugin", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "obsidian-fabric-plugin", 9 | "version": "1.0.0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "@types/he": "^1.2.3", 13 | "he": "^1.2.0", 14 | "shell-escape": "^0.2.0" 15 | }, 16 | "devDependencies": { 17 | "@types/fuzzaldrin-plus": "^0.6.5", 18 | "@types/node": "^16.11.6", 19 | "@types/semver": "^7.5.8", 20 | "@types/shell-escape": "^0.2.3", 21 | "@typescript-eslint/eslint-plugin": "5.29.0", 22 | "@typescript-eslint/parser": "5.29.0", 23 | "builtin-modules": "3.3.0", 24 | "esbuild": "0.17.3", 25 | "fuzzaldrin-plus": "^0.6.0", 26 | "obsidian": "latest", 27 | "tslib": "^2.4.0", 28 | "typescript": "4.7.4" 29 | } 30 | }, 31 | "node_modules/@codemirror/state": { 32 | "version": "6.4.1", 33 | "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.1.tgz", 34 | "integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==", 35 | "dev": true, 36 | "peer": true 37 | }, 38 | "node_modules/@codemirror/view": { 39 | "version": "6.28.2", 40 | "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.28.2.tgz", 41 | "integrity": "sha512-A3DmyVfjgPsGIjiJqM/zvODUAPQdQl3ci0ghehYNnbt5x+o76xq+dL5+mMBuysDXnI3kapgOkoeJ0sbtL/3qPw==", 42 | "dev": true, 43 | "peer": true, 44 | "dependencies": { 45 | "@codemirror/state": "^6.4.0", 46 | "style-mod": "^4.1.0", 47 | "w3c-keyname": "^2.2.4" 48 | } 49 | }, 50 | "node_modules/@esbuild/android-arm": { 51 | "version": "0.17.3", 52 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.3.tgz", 53 | "integrity": "sha512-1Mlz934GvbgdDmt26rTLmf03cAgLg5HyOgJN+ZGCeP3Q9ynYTNMn2/LQxIl7Uy+o4K6Rfi2OuLsr12JQQR8gNg==", 54 | "cpu": [ 55 | "arm" 56 | ], 57 | "dev": true, 58 | "optional": true, 59 | "os": [ 60 | "android" 61 | ], 62 | "engines": { 63 | "node": ">=12" 64 | } 65 | }, 66 | "node_modules/@esbuild/android-arm64": { 67 | "version": "0.17.3", 68 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.3.tgz", 69 | "integrity": "sha512-XvJsYo3dO3Pi4kpalkyMvfQsjxPWHYjoX4MDiB/FUM4YMfWcXa5l4VCwFWVYI1+92yxqjuqrhNg0CZg3gSouyQ==", 70 | "cpu": [ 71 | "arm64" 72 | ], 73 | "dev": true, 74 | "optional": true, 75 | "os": [ 76 | "android" 77 | ], 78 | "engines": { 79 | "node": ">=12" 80 | } 81 | }, 82 | "node_modules/@esbuild/android-x64": { 83 | "version": "0.17.3", 84 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.3.tgz", 85 | "integrity": "sha512-nuV2CmLS07Gqh5/GrZLuqkU9Bm6H6vcCspM+zjp9TdQlxJtIe+qqEXQChmfc7nWdyr/yz3h45Utk1tUn8Cz5+A==", 86 | "cpu": [ 87 | "x64" 88 | ], 89 | "dev": true, 90 | "optional": true, 91 | "os": [ 92 | "android" 93 | ], 94 | "engines": { 95 | "node": ">=12" 96 | } 97 | }, 98 | "node_modules/@esbuild/darwin-arm64": { 99 | "version": "0.17.3", 100 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.3.tgz", 101 | "integrity": "sha512-01Hxaaat6m0Xp9AXGM8mjFtqqwDjzlMP0eQq9zll9U85ttVALGCGDuEvra5Feu/NbP5AEP1MaopPwzsTcUq1cw==", 102 | "cpu": [ 103 | "arm64" 104 | ], 105 | "dev": true, 106 | "optional": true, 107 | "os": [ 108 | "darwin" 109 | ], 110 | "engines": { 111 | "node": ">=12" 112 | } 113 | }, 114 | "node_modules/@esbuild/darwin-x64": { 115 | "version": "0.17.3", 116 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.3.tgz", 117 | "integrity": "sha512-Eo2gq0Q/er2muf8Z83X21UFoB7EU6/m3GNKvrhACJkjVThd0uA+8RfKpfNhuMCl1bKRfBzKOk6xaYKQZ4lZqvA==", 118 | "cpu": [ 119 | "x64" 120 | ], 121 | "dev": true, 122 | "optional": true, 123 | "os": [ 124 | "darwin" 125 | ], 126 | "engines": { 127 | "node": ">=12" 128 | } 129 | }, 130 | "node_modules/@esbuild/freebsd-arm64": { 131 | "version": "0.17.3", 132 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.3.tgz", 133 | "integrity": "sha512-CN62ESxaquP61n1ZjQP/jZte8CE09M6kNn3baos2SeUfdVBkWN5n6vGp2iKyb/bm/x4JQzEvJgRHLGd5F5b81w==", 134 | "cpu": [ 135 | "arm64" 136 | ], 137 | "dev": true, 138 | "optional": true, 139 | "os": [ 140 | "freebsd" 141 | ], 142 | "engines": { 143 | "node": ">=12" 144 | } 145 | }, 146 | "node_modules/@esbuild/freebsd-x64": { 147 | "version": "0.17.3", 148 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.3.tgz", 149 | "integrity": "sha512-feq+K8TxIznZE+zhdVurF3WNJ/Sa35dQNYbaqM/wsCbWdzXr5lyq+AaTUSER2cUR+SXPnd/EY75EPRjf4s1SLg==", 150 | "cpu": [ 151 | "x64" 152 | ], 153 | "dev": true, 154 | "optional": true, 155 | "os": [ 156 | "freebsd" 157 | ], 158 | "engines": { 159 | "node": ">=12" 160 | } 161 | }, 162 | "node_modules/@esbuild/linux-arm": { 163 | "version": "0.17.3", 164 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.3.tgz", 165 | "integrity": "sha512-CLP3EgyNuPcg2cshbwkqYy5bbAgK+VhyfMU7oIYyn+x4Y67xb5C5ylxsNUjRmr8BX+MW3YhVNm6Lq6FKtRTWHQ==", 166 | "cpu": [ 167 | "arm" 168 | ], 169 | "dev": true, 170 | "optional": true, 171 | "os": [ 172 | "linux" 173 | ], 174 | "engines": { 175 | "node": ">=12" 176 | } 177 | }, 178 | "node_modules/@esbuild/linux-arm64": { 179 | "version": "0.17.3", 180 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.3.tgz", 181 | "integrity": "sha512-JHeZXD4auLYBnrKn6JYJ0o5nWJI9PhChA/Nt0G4MvLaMrvXuWnY93R3a7PiXeJQphpL1nYsaMcoV2QtuvRnF/g==", 182 | "cpu": [ 183 | "arm64" 184 | ], 185 | "dev": true, 186 | "optional": true, 187 | "os": [ 188 | "linux" 189 | ], 190 | "engines": { 191 | "node": ">=12" 192 | } 193 | }, 194 | "node_modules/@esbuild/linux-ia32": { 195 | "version": "0.17.3", 196 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.3.tgz", 197 | "integrity": "sha512-FyXlD2ZjZqTFh0sOQxFDiWG1uQUEOLbEh9gKN/7pFxck5Vw0qjWSDqbn6C10GAa1rXJpwsntHcmLqydY9ST9ZA==", 198 | "cpu": [ 199 | "ia32" 200 | ], 201 | "dev": true, 202 | "optional": true, 203 | "os": [ 204 | "linux" 205 | ], 206 | "engines": { 207 | "node": ">=12" 208 | } 209 | }, 210 | "node_modules/@esbuild/linux-loong64": { 211 | "version": "0.17.3", 212 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.3.tgz", 213 | "integrity": "sha512-OrDGMvDBI2g7s04J8dh8/I7eSO+/E7nMDT2Z5IruBfUO/RiigF1OF6xoH33Dn4W/OwAWSUf1s2nXamb28ZklTA==", 214 | "cpu": [ 215 | "loong64" 216 | ], 217 | "dev": true, 218 | "optional": true, 219 | "os": [ 220 | "linux" 221 | ], 222 | "engines": { 223 | "node": ">=12" 224 | } 225 | }, 226 | "node_modules/@esbuild/linux-mips64el": { 227 | "version": "0.17.3", 228 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.3.tgz", 229 | "integrity": "sha512-DcnUpXnVCJvmv0TzuLwKBC2nsQHle8EIiAJiJ+PipEVC16wHXaPEKP0EqN8WnBe0TPvMITOUlP2aiL5YMld+CQ==", 230 | "cpu": [ 231 | "mips64el" 232 | ], 233 | "dev": true, 234 | "optional": true, 235 | "os": [ 236 | "linux" 237 | ], 238 | "engines": { 239 | "node": ">=12" 240 | } 241 | }, 242 | "node_modules/@esbuild/linux-ppc64": { 243 | "version": "0.17.3", 244 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.3.tgz", 245 | "integrity": "sha512-BDYf/l1WVhWE+FHAW3FzZPtVlk9QsrwsxGzABmN4g8bTjmhazsId3h127pliDRRu5674k1Y2RWejbpN46N9ZhQ==", 246 | "cpu": [ 247 | "ppc64" 248 | ], 249 | "dev": true, 250 | "optional": true, 251 | "os": [ 252 | "linux" 253 | ], 254 | "engines": { 255 | "node": ">=12" 256 | } 257 | }, 258 | "node_modules/@esbuild/linux-riscv64": { 259 | "version": "0.17.3", 260 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.3.tgz", 261 | "integrity": "sha512-WViAxWYMRIi+prTJTyV1wnqd2mS2cPqJlN85oscVhXdb/ZTFJdrpaqm/uDsZPGKHtbg5TuRX/ymKdOSk41YZow==", 262 | "cpu": [ 263 | "riscv64" 264 | ], 265 | "dev": true, 266 | "optional": true, 267 | "os": [ 268 | "linux" 269 | ], 270 | "engines": { 271 | "node": ">=12" 272 | } 273 | }, 274 | "node_modules/@esbuild/linux-s390x": { 275 | "version": "0.17.3", 276 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.3.tgz", 277 | "integrity": "sha512-Iw8lkNHUC4oGP1O/KhumcVy77u2s6+KUjieUqzEU3XuWJqZ+AY7uVMrrCbAiwWTkpQHkr00BuXH5RpC6Sb/7Ug==", 278 | "cpu": [ 279 | "s390x" 280 | ], 281 | "dev": true, 282 | "optional": true, 283 | "os": [ 284 | "linux" 285 | ], 286 | "engines": { 287 | "node": ">=12" 288 | } 289 | }, 290 | "node_modules/@esbuild/linux-x64": { 291 | "version": "0.17.3", 292 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.3.tgz", 293 | "integrity": "sha512-0AGkWQMzeoeAtXQRNB3s4J1/T2XbigM2/Mn2yU1tQSmQRmHIZdkGbVq2A3aDdNslPyhb9/lH0S5GMTZ4xsjBqg==", 294 | "cpu": [ 295 | "x64" 296 | ], 297 | "dev": true, 298 | "optional": true, 299 | "os": [ 300 | "linux" 301 | ], 302 | "engines": { 303 | "node": ">=12" 304 | } 305 | }, 306 | "node_modules/@esbuild/netbsd-x64": { 307 | "version": "0.17.3", 308 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.3.tgz", 309 | "integrity": "sha512-4+rR/WHOxIVh53UIQIICryjdoKdHsFZFD4zLSonJ9RRw7bhKzVyXbnRPsWSfwybYqw9sB7ots/SYyufL1mBpEg==", 310 | "cpu": [ 311 | "x64" 312 | ], 313 | "dev": true, 314 | "optional": true, 315 | "os": [ 316 | "netbsd" 317 | ], 318 | "engines": { 319 | "node": ">=12" 320 | } 321 | }, 322 | "node_modules/@esbuild/openbsd-x64": { 323 | "version": "0.17.3", 324 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.3.tgz", 325 | "integrity": "sha512-cVpWnkx9IYg99EjGxa5Gc0XmqumtAwK3aoz7O4Dii2vko+qXbkHoujWA68cqXjhh6TsLaQelfDO4MVnyr+ODeA==", 326 | "cpu": [ 327 | "x64" 328 | ], 329 | "dev": true, 330 | "optional": true, 331 | "os": [ 332 | "openbsd" 333 | ], 334 | "engines": { 335 | "node": ">=12" 336 | } 337 | }, 338 | "node_modules/@esbuild/sunos-x64": { 339 | "version": "0.17.3", 340 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.3.tgz", 341 | "integrity": "sha512-RxmhKLbTCDAY2xOfrww6ieIZkZF+KBqG7S2Ako2SljKXRFi+0863PspK74QQ7JpmWwncChY25JTJSbVBYGQk2Q==", 342 | "cpu": [ 343 | "x64" 344 | ], 345 | "dev": true, 346 | "optional": true, 347 | "os": [ 348 | "sunos" 349 | ], 350 | "engines": { 351 | "node": ">=12" 352 | } 353 | }, 354 | "node_modules/@esbuild/win32-arm64": { 355 | "version": "0.17.3", 356 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.3.tgz", 357 | "integrity": "sha512-0r36VeEJ4efwmofxVJRXDjVRP2jTmv877zc+i+Pc7MNsIr38NfsjkQj23AfF7l0WbB+RQ7VUb+LDiqC/KY/M/A==", 358 | "cpu": [ 359 | "arm64" 360 | ], 361 | "dev": true, 362 | "optional": true, 363 | "os": [ 364 | "win32" 365 | ], 366 | "engines": { 367 | "node": ">=12" 368 | } 369 | }, 370 | "node_modules/@esbuild/win32-ia32": { 371 | "version": "0.17.3", 372 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.3.tgz", 373 | "integrity": "sha512-wgO6rc7uGStH22nur4aLFcq7Wh86bE9cOFmfTr/yxN3BXvDEdCSXyKkO+U5JIt53eTOgC47v9k/C1bITWL/Teg==", 374 | "cpu": [ 375 | "ia32" 376 | ], 377 | "dev": true, 378 | "optional": true, 379 | "os": [ 380 | "win32" 381 | ], 382 | "engines": { 383 | "node": ">=12" 384 | } 385 | }, 386 | "node_modules/@esbuild/win32-x64": { 387 | "version": "0.17.3", 388 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.3.tgz", 389 | "integrity": "sha512-FdVl64OIuiKjgXBjwZaJLKp0eaEckifbhn10dXWhysMJkWblg3OEEGKSIyhiD5RSgAya8WzP3DNkngtIg3Nt7g==", 390 | "cpu": [ 391 | "x64" 392 | ], 393 | "dev": true, 394 | "optional": true, 395 | "os": [ 396 | "win32" 397 | ], 398 | "engines": { 399 | "node": ">=12" 400 | } 401 | }, 402 | "node_modules/@eslint-community/eslint-utils": { 403 | "version": "4.4.0", 404 | "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", 405 | "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", 406 | "dev": true, 407 | "peer": true, 408 | "dependencies": { 409 | "eslint-visitor-keys": "^3.3.0" 410 | }, 411 | "engines": { 412 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 413 | }, 414 | "peerDependencies": { 415 | "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" 416 | } 417 | }, 418 | "node_modules/@eslint-community/regexpp": { 419 | "version": "4.10.1", 420 | "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.1.tgz", 421 | "integrity": "sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==", 422 | "dev": true, 423 | "peer": true, 424 | "engines": { 425 | "node": "^12.0.0 || ^14.0.0 || >=16.0.0" 426 | } 427 | }, 428 | "node_modules/@eslint/eslintrc": { 429 | "version": "2.1.4", 430 | "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", 431 | "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", 432 | "dev": true, 433 | "peer": true, 434 | "dependencies": { 435 | "ajv": "^6.12.4", 436 | "debug": "^4.3.2", 437 | "espree": "^9.6.0", 438 | "globals": "^13.19.0", 439 | "ignore": "^5.2.0", 440 | "import-fresh": "^3.2.1", 441 | "js-yaml": "^4.1.0", 442 | "minimatch": "^3.1.2", 443 | "strip-json-comments": "^3.1.1" 444 | }, 445 | "engines": { 446 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 447 | }, 448 | "funding": { 449 | "url": "https://opencollective.com/eslint" 450 | } 451 | }, 452 | "node_modules/@eslint/js": { 453 | "version": "8.57.0", 454 | "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", 455 | "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", 456 | "dev": true, 457 | "peer": true, 458 | "engines": { 459 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 460 | } 461 | }, 462 | "node_modules/@humanwhocodes/config-array": { 463 | "version": "0.11.14", 464 | "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", 465 | "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", 466 | "deprecated": "Use @eslint/config-array instead", 467 | "dev": true, 468 | "peer": true, 469 | "dependencies": { 470 | "@humanwhocodes/object-schema": "^2.0.2", 471 | "debug": "^4.3.1", 472 | "minimatch": "^3.0.5" 473 | }, 474 | "engines": { 475 | "node": ">=10.10.0" 476 | } 477 | }, 478 | "node_modules/@humanwhocodes/module-importer": { 479 | "version": "1.0.1", 480 | "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", 481 | "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", 482 | "dev": true, 483 | "peer": true, 484 | "engines": { 485 | "node": ">=12.22" 486 | }, 487 | "funding": { 488 | "type": "github", 489 | "url": "https://github.com/sponsors/nzakas" 490 | } 491 | }, 492 | "node_modules/@humanwhocodes/object-schema": { 493 | "version": "2.0.3", 494 | "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", 495 | "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", 496 | "deprecated": "Use @eslint/object-schema instead", 497 | "dev": true, 498 | "peer": true 499 | }, 500 | "node_modules/@nodelib/fs.scandir": { 501 | "version": "2.1.5", 502 | "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", 503 | "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", 504 | "dev": true, 505 | "dependencies": { 506 | "@nodelib/fs.stat": "2.0.5", 507 | "run-parallel": "^1.1.9" 508 | }, 509 | "engines": { 510 | "node": ">= 8" 511 | } 512 | }, 513 | "node_modules/@nodelib/fs.stat": { 514 | "version": "2.0.5", 515 | "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", 516 | "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", 517 | "dev": true, 518 | "engines": { 519 | "node": ">= 8" 520 | } 521 | }, 522 | "node_modules/@nodelib/fs.walk": { 523 | "version": "1.2.8", 524 | "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", 525 | "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", 526 | "dev": true, 527 | "dependencies": { 528 | "@nodelib/fs.scandir": "2.1.5", 529 | "fastq": "^1.6.0" 530 | }, 531 | "engines": { 532 | "node": ">= 8" 533 | } 534 | }, 535 | "node_modules/@types/codemirror": { 536 | "version": "5.60.8", 537 | "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.8.tgz", 538 | "integrity": "sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw==", 539 | "dev": true, 540 | "dependencies": { 541 | "@types/tern": "*" 542 | } 543 | }, 544 | "node_modules/@types/estree": { 545 | "version": "1.0.5", 546 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", 547 | "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", 548 | "dev": true 549 | }, 550 | "node_modules/@types/fuzzaldrin-plus": { 551 | "version": "0.6.5", 552 | "resolved": "https://registry.npmjs.org/@types/fuzzaldrin-plus/-/fuzzaldrin-plus-0.6.5.tgz", 553 | "integrity": "sha512-fUFxKXq/Qu8WVgPtSkQMK7jF5odTnBaIthuhKQb6PivpTaTTUlOTMS3WsT+UpWVZcYQH5skjHuFRTNmrUGSDfg==", 554 | "dev": true 555 | }, 556 | "node_modules/@types/he": { 557 | "version": "1.2.3", 558 | "resolved": "https://registry.npmjs.org/@types/he/-/he-1.2.3.tgz", 559 | "integrity": "sha512-q67/qwlxblDzEDvzHhVkwc1gzVWxaNxeyHUBF4xElrvjL11O+Ytze+1fGpBHlr/H9myiBUaUXNnNPmBHxxfAcA==" 560 | }, 561 | "node_modules/@types/json-schema": { 562 | "version": "7.0.15", 563 | "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", 564 | "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", 565 | "dev": true 566 | }, 567 | "node_modules/@types/node": { 568 | "version": "16.18.101", 569 | "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.101.tgz", 570 | "integrity": "sha512-AAsx9Rgz2IzG8KJ6tXd6ndNkVcu+GYB6U/SnFAaokSPNx2N7dcIIfnighYUNumvj6YS2q39Dejz5tT0NCV7CWA==", 571 | "dev": true 572 | }, 573 | "node_modules/@types/semver": { 574 | "version": "7.5.8", 575 | "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", 576 | "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", 577 | "dev": true 578 | }, 579 | "node_modules/@types/shell-escape": { 580 | "version": "0.2.3", 581 | "resolved": "https://registry.npmjs.org/@types/shell-escape/-/shell-escape-0.2.3.tgz", 582 | "integrity": "sha512-xZWkMuQkn1I20gEzhYRa4/t1pwZ8XiIkqGA1Iee1D2IgAUIRLr57nrgJgF2QmHEfkfVzOM59gi/4xp6V+Aq+4A==", 583 | "dev": true 584 | }, 585 | "node_modules/@types/tern": { 586 | "version": "0.23.9", 587 | "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz", 588 | "integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==", 589 | "dev": true, 590 | "dependencies": { 591 | "@types/estree": "*" 592 | } 593 | }, 594 | "node_modules/@typescript-eslint/eslint-plugin": { 595 | "version": "5.29.0", 596 | "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.29.0.tgz", 597 | "integrity": "sha512-kgTsISt9pM53yRFQmLZ4npj99yGl3x3Pl7z4eA66OuTzAGC4bQB5H5fuLwPnqTKU3yyrrg4MIhjF17UYnL4c0w==", 598 | "dev": true, 599 | "dependencies": { 600 | "@typescript-eslint/scope-manager": "5.29.0", 601 | "@typescript-eslint/type-utils": "5.29.0", 602 | "@typescript-eslint/utils": "5.29.0", 603 | "debug": "^4.3.4", 604 | "functional-red-black-tree": "^1.0.1", 605 | "ignore": "^5.2.0", 606 | "regexpp": "^3.2.0", 607 | "semver": "^7.3.7", 608 | "tsutils": "^3.21.0" 609 | }, 610 | "engines": { 611 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 612 | }, 613 | "funding": { 614 | "type": "opencollective", 615 | "url": "https://opencollective.com/typescript-eslint" 616 | }, 617 | "peerDependencies": { 618 | "@typescript-eslint/parser": "^5.0.0", 619 | "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" 620 | }, 621 | "peerDependenciesMeta": { 622 | "typescript": { 623 | "optional": true 624 | } 625 | } 626 | }, 627 | "node_modules/@typescript-eslint/parser": { 628 | "version": "5.29.0", 629 | "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.29.0.tgz", 630 | "integrity": "sha512-ruKWTv+x0OOxbzIw9nW5oWlUopvP/IQDjB5ZqmTglLIoDTctLlAJpAQFpNPJP/ZI7hTT9sARBosEfaKbcFuECw==", 631 | "dev": true, 632 | "dependencies": { 633 | "@typescript-eslint/scope-manager": "5.29.0", 634 | "@typescript-eslint/types": "5.29.0", 635 | "@typescript-eslint/typescript-estree": "5.29.0", 636 | "debug": "^4.3.4" 637 | }, 638 | "engines": { 639 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 640 | }, 641 | "funding": { 642 | "type": "opencollective", 643 | "url": "https://opencollective.com/typescript-eslint" 644 | }, 645 | "peerDependencies": { 646 | "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" 647 | }, 648 | "peerDependenciesMeta": { 649 | "typescript": { 650 | "optional": true 651 | } 652 | } 653 | }, 654 | "node_modules/@typescript-eslint/scope-manager": { 655 | "version": "5.29.0", 656 | "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.29.0.tgz", 657 | "integrity": "sha512-etbXUT0FygFi2ihcxDZjz21LtC+Eps9V2xVx09zFoN44RRHPrkMflidGMI+2dUs821zR1tDS6Oc9IXxIjOUZwA==", 658 | "dev": true, 659 | "dependencies": { 660 | "@typescript-eslint/types": "5.29.0", 661 | "@typescript-eslint/visitor-keys": "5.29.0" 662 | }, 663 | "engines": { 664 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 665 | }, 666 | "funding": { 667 | "type": "opencollective", 668 | "url": "https://opencollective.com/typescript-eslint" 669 | } 670 | }, 671 | "node_modules/@typescript-eslint/type-utils": { 672 | "version": "5.29.0", 673 | "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.29.0.tgz", 674 | "integrity": "sha512-JK6bAaaiJozbox3K220VRfCzLa9n0ib/J+FHIwnaV3Enw/TO267qe0pM1b1QrrEuy6xun374XEAsRlA86JJnyg==", 675 | "dev": true, 676 | "dependencies": { 677 | "@typescript-eslint/utils": "5.29.0", 678 | "debug": "^4.3.4", 679 | "tsutils": "^3.21.0" 680 | }, 681 | "engines": { 682 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 683 | }, 684 | "funding": { 685 | "type": "opencollective", 686 | "url": "https://opencollective.com/typescript-eslint" 687 | }, 688 | "peerDependencies": { 689 | "eslint": "*" 690 | }, 691 | "peerDependenciesMeta": { 692 | "typescript": { 693 | "optional": true 694 | } 695 | } 696 | }, 697 | "node_modules/@typescript-eslint/types": { 698 | "version": "5.29.0", 699 | "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.29.0.tgz", 700 | "integrity": "sha512-X99VbqvAXOMdVyfFmksMy3u8p8yoRGITgU1joBJPzeYa0rhdf5ok9S56/itRoUSh99fiDoMtarSIJXo7H/SnOg==", 701 | "dev": true, 702 | "engines": { 703 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 704 | }, 705 | "funding": { 706 | "type": "opencollective", 707 | "url": "https://opencollective.com/typescript-eslint" 708 | } 709 | }, 710 | "node_modules/@typescript-eslint/typescript-estree": { 711 | "version": "5.29.0", 712 | "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.29.0.tgz", 713 | "integrity": "sha512-mQvSUJ/JjGBdvo+1LwC+GY2XmSYjK1nAaVw2emp/E61wEVYEyibRHCqm1I1vEKbXCpUKuW4G7u9ZCaZhJbLoNQ==", 714 | "dev": true, 715 | "dependencies": { 716 | "@typescript-eslint/types": "5.29.0", 717 | "@typescript-eslint/visitor-keys": "5.29.0", 718 | "debug": "^4.3.4", 719 | "globby": "^11.1.0", 720 | "is-glob": "^4.0.3", 721 | "semver": "^7.3.7", 722 | "tsutils": "^3.21.0" 723 | }, 724 | "engines": { 725 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 726 | }, 727 | "funding": { 728 | "type": "opencollective", 729 | "url": "https://opencollective.com/typescript-eslint" 730 | }, 731 | "peerDependenciesMeta": { 732 | "typescript": { 733 | "optional": true 734 | } 735 | } 736 | }, 737 | "node_modules/@typescript-eslint/utils": { 738 | "version": "5.29.0", 739 | "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.29.0.tgz", 740 | "integrity": "sha512-3Eos6uP1nyLOBayc/VUdKZikV90HahXE5Dx9L5YlSd/7ylQPXhLk1BYb29SDgnBnTp+jmSZUU0QxUiyHgW4p7A==", 741 | "dev": true, 742 | "dependencies": { 743 | "@types/json-schema": "^7.0.9", 744 | "@typescript-eslint/scope-manager": "5.29.0", 745 | "@typescript-eslint/types": "5.29.0", 746 | "@typescript-eslint/typescript-estree": "5.29.0", 747 | "eslint-scope": "^5.1.1", 748 | "eslint-utils": "^3.0.0" 749 | }, 750 | "engines": { 751 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 752 | }, 753 | "funding": { 754 | "type": "opencollective", 755 | "url": "https://opencollective.com/typescript-eslint" 756 | }, 757 | "peerDependencies": { 758 | "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" 759 | } 760 | }, 761 | "node_modules/@typescript-eslint/visitor-keys": { 762 | "version": "5.29.0", 763 | "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.29.0.tgz", 764 | "integrity": "sha512-Hpb/mCWsjILvikMQoZIE3voc9wtQcS0A9FUw3h8bhr9UxBdtI/tw1ZDZUOXHXLOVMedKCH5NxyzATwnU78bWCQ==", 765 | "dev": true, 766 | "dependencies": { 767 | "@typescript-eslint/types": "5.29.0", 768 | "eslint-visitor-keys": "^3.3.0" 769 | }, 770 | "engines": { 771 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 772 | }, 773 | "funding": { 774 | "type": "opencollective", 775 | "url": "https://opencollective.com/typescript-eslint" 776 | } 777 | }, 778 | "node_modules/@ungap/structured-clone": { 779 | "version": "1.2.0", 780 | "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", 781 | "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", 782 | "dev": true, 783 | "peer": true 784 | }, 785 | "node_modules/acorn": { 786 | "version": "8.12.0", 787 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz", 788 | "integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==", 789 | "dev": true, 790 | "peer": true, 791 | "bin": { 792 | "acorn": "bin/acorn" 793 | }, 794 | "engines": { 795 | "node": ">=0.4.0" 796 | } 797 | }, 798 | "node_modules/acorn-jsx": { 799 | "version": "5.3.2", 800 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", 801 | "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", 802 | "dev": true, 803 | "peer": true, 804 | "peerDependencies": { 805 | "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" 806 | } 807 | }, 808 | "node_modules/ajv": { 809 | "version": "6.12.6", 810 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 811 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 812 | "dev": true, 813 | "peer": true, 814 | "dependencies": { 815 | "fast-deep-equal": "^3.1.1", 816 | "fast-json-stable-stringify": "^2.0.0", 817 | "json-schema-traverse": "^0.4.1", 818 | "uri-js": "^4.2.2" 819 | }, 820 | "funding": { 821 | "type": "github", 822 | "url": "https://github.com/sponsors/epoberezkin" 823 | } 824 | }, 825 | "node_modules/ansi-regex": { 826 | "version": "5.0.1", 827 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 828 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 829 | "dev": true, 830 | "peer": true, 831 | "engines": { 832 | "node": ">=8" 833 | } 834 | }, 835 | "node_modules/ansi-styles": { 836 | "version": "4.3.0", 837 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 838 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 839 | "dev": true, 840 | "peer": true, 841 | "dependencies": { 842 | "color-convert": "^2.0.1" 843 | }, 844 | "engines": { 845 | "node": ">=8" 846 | }, 847 | "funding": { 848 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 849 | } 850 | }, 851 | "node_modules/argparse": { 852 | "version": "2.0.1", 853 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 854 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 855 | "dev": true, 856 | "peer": true 857 | }, 858 | "node_modules/array-union": { 859 | "version": "2.1.0", 860 | "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", 861 | "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", 862 | "dev": true, 863 | "engines": { 864 | "node": ">=8" 865 | } 866 | }, 867 | "node_modules/balanced-match": { 868 | "version": "1.0.2", 869 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 870 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 871 | "dev": true, 872 | "peer": true 873 | }, 874 | "node_modules/brace-expansion": { 875 | "version": "1.1.11", 876 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 877 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 878 | "dev": true, 879 | "peer": true, 880 | "dependencies": { 881 | "balanced-match": "^1.0.0", 882 | "concat-map": "0.0.1" 883 | } 884 | }, 885 | "node_modules/braces": { 886 | "version": "3.0.3", 887 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", 888 | "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", 889 | "dev": true, 890 | "dependencies": { 891 | "fill-range": "^7.1.1" 892 | }, 893 | "engines": { 894 | "node": ">=8" 895 | } 896 | }, 897 | "node_modules/builtin-modules": { 898 | "version": "3.3.0", 899 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", 900 | "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", 901 | "dev": true, 902 | "engines": { 903 | "node": ">=6" 904 | }, 905 | "funding": { 906 | "url": "https://github.com/sponsors/sindresorhus" 907 | } 908 | }, 909 | "node_modules/callsites": { 910 | "version": "3.1.0", 911 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 912 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 913 | "dev": true, 914 | "peer": true, 915 | "engines": { 916 | "node": ">=6" 917 | } 918 | }, 919 | "node_modules/chalk": { 920 | "version": "4.1.2", 921 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 922 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 923 | "dev": true, 924 | "peer": true, 925 | "dependencies": { 926 | "ansi-styles": "^4.1.0", 927 | "supports-color": "^7.1.0" 928 | }, 929 | "engines": { 930 | "node": ">=10" 931 | }, 932 | "funding": { 933 | "url": "https://github.com/chalk/chalk?sponsor=1" 934 | } 935 | }, 936 | "node_modules/color-convert": { 937 | "version": "2.0.1", 938 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 939 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 940 | "dev": true, 941 | "peer": true, 942 | "dependencies": { 943 | "color-name": "~1.1.4" 944 | }, 945 | "engines": { 946 | "node": ">=7.0.0" 947 | } 948 | }, 949 | "node_modules/color-name": { 950 | "version": "1.1.4", 951 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 952 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 953 | "dev": true, 954 | "peer": true 955 | }, 956 | "node_modules/concat-map": { 957 | "version": "0.0.1", 958 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 959 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 960 | "dev": true, 961 | "peer": true 962 | }, 963 | "node_modules/cross-spawn": { 964 | "version": "7.0.3", 965 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 966 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 967 | "dev": true, 968 | "peer": true, 969 | "dependencies": { 970 | "path-key": "^3.1.0", 971 | "shebang-command": "^2.0.0", 972 | "which": "^2.0.1" 973 | }, 974 | "engines": { 975 | "node": ">= 8" 976 | } 977 | }, 978 | "node_modules/debug": { 979 | "version": "4.3.5", 980 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", 981 | "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", 982 | "dev": true, 983 | "dependencies": { 984 | "ms": "2.1.2" 985 | }, 986 | "engines": { 987 | "node": ">=6.0" 988 | }, 989 | "peerDependenciesMeta": { 990 | "supports-color": { 991 | "optional": true 992 | } 993 | } 994 | }, 995 | "node_modules/deep-is": { 996 | "version": "0.1.4", 997 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", 998 | "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", 999 | "dev": true, 1000 | "peer": true 1001 | }, 1002 | "node_modules/dir-glob": { 1003 | "version": "3.0.1", 1004 | "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", 1005 | "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", 1006 | "dev": true, 1007 | "dependencies": { 1008 | "path-type": "^4.0.0" 1009 | }, 1010 | "engines": { 1011 | "node": ">=8" 1012 | } 1013 | }, 1014 | "node_modules/doctrine": { 1015 | "version": "3.0.0", 1016 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", 1017 | "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", 1018 | "dev": true, 1019 | "peer": true, 1020 | "dependencies": { 1021 | "esutils": "^2.0.2" 1022 | }, 1023 | "engines": { 1024 | "node": ">=6.0.0" 1025 | } 1026 | }, 1027 | "node_modules/esbuild": { 1028 | "version": "0.17.3", 1029 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.3.tgz", 1030 | "integrity": "sha512-9n3AsBRe6sIyOc6kmoXg2ypCLgf3eZSraWFRpnkto+svt8cZNuKTkb1bhQcitBcvIqjNiK7K0J3KPmwGSfkA8g==", 1031 | "dev": true, 1032 | "hasInstallScript": true, 1033 | "bin": { 1034 | "esbuild": "bin/esbuild" 1035 | }, 1036 | "engines": { 1037 | "node": ">=12" 1038 | }, 1039 | "optionalDependencies": { 1040 | "@esbuild/android-arm": "0.17.3", 1041 | "@esbuild/android-arm64": "0.17.3", 1042 | "@esbuild/android-x64": "0.17.3", 1043 | "@esbuild/darwin-arm64": "0.17.3", 1044 | "@esbuild/darwin-x64": "0.17.3", 1045 | "@esbuild/freebsd-arm64": "0.17.3", 1046 | "@esbuild/freebsd-x64": "0.17.3", 1047 | "@esbuild/linux-arm": "0.17.3", 1048 | "@esbuild/linux-arm64": "0.17.3", 1049 | "@esbuild/linux-ia32": "0.17.3", 1050 | "@esbuild/linux-loong64": "0.17.3", 1051 | "@esbuild/linux-mips64el": "0.17.3", 1052 | "@esbuild/linux-ppc64": "0.17.3", 1053 | "@esbuild/linux-riscv64": "0.17.3", 1054 | "@esbuild/linux-s390x": "0.17.3", 1055 | "@esbuild/linux-x64": "0.17.3", 1056 | "@esbuild/netbsd-x64": "0.17.3", 1057 | "@esbuild/openbsd-x64": "0.17.3", 1058 | "@esbuild/sunos-x64": "0.17.3", 1059 | "@esbuild/win32-arm64": "0.17.3", 1060 | "@esbuild/win32-ia32": "0.17.3", 1061 | "@esbuild/win32-x64": "0.17.3" 1062 | } 1063 | }, 1064 | "node_modules/escape-string-regexp": { 1065 | "version": "4.0.0", 1066 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 1067 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 1068 | "dev": true, 1069 | "peer": true, 1070 | "engines": { 1071 | "node": ">=10" 1072 | }, 1073 | "funding": { 1074 | "url": "https://github.com/sponsors/sindresorhus" 1075 | } 1076 | }, 1077 | "node_modules/eslint": { 1078 | "version": "8.57.0", 1079 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", 1080 | "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", 1081 | "dev": true, 1082 | "peer": true, 1083 | "dependencies": { 1084 | "@eslint-community/eslint-utils": "^4.2.0", 1085 | "@eslint-community/regexpp": "^4.6.1", 1086 | "@eslint/eslintrc": "^2.1.4", 1087 | "@eslint/js": "8.57.0", 1088 | "@humanwhocodes/config-array": "^0.11.14", 1089 | "@humanwhocodes/module-importer": "^1.0.1", 1090 | "@nodelib/fs.walk": "^1.2.8", 1091 | "@ungap/structured-clone": "^1.2.0", 1092 | "ajv": "^6.12.4", 1093 | "chalk": "^4.0.0", 1094 | "cross-spawn": "^7.0.2", 1095 | "debug": "^4.3.2", 1096 | "doctrine": "^3.0.0", 1097 | "escape-string-regexp": "^4.0.0", 1098 | "eslint-scope": "^7.2.2", 1099 | "eslint-visitor-keys": "^3.4.3", 1100 | "espree": "^9.6.1", 1101 | "esquery": "^1.4.2", 1102 | "esutils": "^2.0.2", 1103 | "fast-deep-equal": "^3.1.3", 1104 | "file-entry-cache": "^6.0.1", 1105 | "find-up": "^5.0.0", 1106 | "glob-parent": "^6.0.2", 1107 | "globals": "^13.19.0", 1108 | "graphemer": "^1.4.0", 1109 | "ignore": "^5.2.0", 1110 | "imurmurhash": "^0.1.4", 1111 | "is-glob": "^4.0.0", 1112 | "is-path-inside": "^3.0.3", 1113 | "js-yaml": "^4.1.0", 1114 | "json-stable-stringify-without-jsonify": "^1.0.1", 1115 | "levn": "^0.4.1", 1116 | "lodash.merge": "^4.6.2", 1117 | "minimatch": "^3.1.2", 1118 | "natural-compare": "^1.4.0", 1119 | "optionator": "^0.9.3", 1120 | "strip-ansi": "^6.0.1", 1121 | "text-table": "^0.2.0" 1122 | }, 1123 | "bin": { 1124 | "eslint": "bin/eslint.js" 1125 | }, 1126 | "engines": { 1127 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 1128 | }, 1129 | "funding": { 1130 | "url": "https://opencollective.com/eslint" 1131 | } 1132 | }, 1133 | "node_modules/eslint-scope": { 1134 | "version": "5.1.1", 1135 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", 1136 | "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", 1137 | "dev": true, 1138 | "dependencies": { 1139 | "esrecurse": "^4.3.0", 1140 | "estraverse": "^4.1.1" 1141 | }, 1142 | "engines": { 1143 | "node": ">=8.0.0" 1144 | } 1145 | }, 1146 | "node_modules/eslint-utils": { 1147 | "version": "3.0.0", 1148 | "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", 1149 | "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", 1150 | "dev": true, 1151 | "dependencies": { 1152 | "eslint-visitor-keys": "^2.0.0" 1153 | }, 1154 | "engines": { 1155 | "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" 1156 | }, 1157 | "funding": { 1158 | "url": "https://github.com/sponsors/mysticatea" 1159 | }, 1160 | "peerDependencies": { 1161 | "eslint": ">=5" 1162 | } 1163 | }, 1164 | "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { 1165 | "version": "2.1.0", 1166 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", 1167 | "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", 1168 | "dev": true, 1169 | "engines": { 1170 | "node": ">=10" 1171 | } 1172 | }, 1173 | "node_modules/eslint-visitor-keys": { 1174 | "version": "3.4.3", 1175 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", 1176 | "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", 1177 | "dev": true, 1178 | "engines": { 1179 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 1180 | }, 1181 | "funding": { 1182 | "url": "https://opencollective.com/eslint" 1183 | } 1184 | }, 1185 | "node_modules/eslint/node_modules/eslint-scope": { 1186 | "version": "7.2.2", 1187 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", 1188 | "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", 1189 | "dev": true, 1190 | "peer": true, 1191 | "dependencies": { 1192 | "esrecurse": "^4.3.0", 1193 | "estraverse": "^5.2.0" 1194 | }, 1195 | "engines": { 1196 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 1197 | }, 1198 | "funding": { 1199 | "url": "https://opencollective.com/eslint" 1200 | } 1201 | }, 1202 | "node_modules/eslint/node_modules/estraverse": { 1203 | "version": "5.3.0", 1204 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", 1205 | "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", 1206 | "dev": true, 1207 | "peer": true, 1208 | "engines": { 1209 | "node": ">=4.0" 1210 | } 1211 | }, 1212 | "node_modules/espree": { 1213 | "version": "9.6.1", 1214 | "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", 1215 | "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", 1216 | "dev": true, 1217 | "peer": true, 1218 | "dependencies": { 1219 | "acorn": "^8.9.0", 1220 | "acorn-jsx": "^5.3.2", 1221 | "eslint-visitor-keys": "^3.4.1" 1222 | }, 1223 | "engines": { 1224 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 1225 | }, 1226 | "funding": { 1227 | "url": "https://opencollective.com/eslint" 1228 | } 1229 | }, 1230 | "node_modules/esquery": { 1231 | "version": "1.5.0", 1232 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", 1233 | "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", 1234 | "dev": true, 1235 | "peer": true, 1236 | "dependencies": { 1237 | "estraverse": "^5.1.0" 1238 | }, 1239 | "engines": { 1240 | "node": ">=0.10" 1241 | } 1242 | }, 1243 | "node_modules/esquery/node_modules/estraverse": { 1244 | "version": "5.3.0", 1245 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", 1246 | "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", 1247 | "dev": true, 1248 | "peer": true, 1249 | "engines": { 1250 | "node": ">=4.0" 1251 | } 1252 | }, 1253 | "node_modules/esrecurse": { 1254 | "version": "4.3.0", 1255 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", 1256 | "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 1257 | "dev": true, 1258 | "dependencies": { 1259 | "estraverse": "^5.2.0" 1260 | }, 1261 | "engines": { 1262 | "node": ">=4.0" 1263 | } 1264 | }, 1265 | "node_modules/esrecurse/node_modules/estraverse": { 1266 | "version": "5.3.0", 1267 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", 1268 | "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", 1269 | "dev": true, 1270 | "engines": { 1271 | "node": ">=4.0" 1272 | } 1273 | }, 1274 | "node_modules/estraverse": { 1275 | "version": "4.3.0", 1276 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", 1277 | "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", 1278 | "dev": true, 1279 | "engines": { 1280 | "node": ">=4.0" 1281 | } 1282 | }, 1283 | "node_modules/esutils": { 1284 | "version": "2.0.3", 1285 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 1286 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 1287 | "dev": true, 1288 | "peer": true, 1289 | "engines": { 1290 | "node": ">=0.10.0" 1291 | } 1292 | }, 1293 | "node_modules/fast-deep-equal": { 1294 | "version": "3.1.3", 1295 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 1296 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 1297 | "dev": true, 1298 | "peer": true 1299 | }, 1300 | "node_modules/fast-glob": { 1301 | "version": "3.3.2", 1302 | "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", 1303 | "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", 1304 | "dev": true, 1305 | "dependencies": { 1306 | "@nodelib/fs.stat": "^2.0.2", 1307 | "@nodelib/fs.walk": "^1.2.3", 1308 | "glob-parent": "^5.1.2", 1309 | "merge2": "^1.3.0", 1310 | "micromatch": "^4.0.4" 1311 | }, 1312 | "engines": { 1313 | "node": ">=8.6.0" 1314 | } 1315 | }, 1316 | "node_modules/fast-glob/node_modules/glob-parent": { 1317 | "version": "5.1.2", 1318 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 1319 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 1320 | "dev": true, 1321 | "dependencies": { 1322 | "is-glob": "^4.0.1" 1323 | }, 1324 | "engines": { 1325 | "node": ">= 6" 1326 | } 1327 | }, 1328 | "node_modules/fast-json-stable-stringify": { 1329 | "version": "2.1.0", 1330 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 1331 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 1332 | "dev": true, 1333 | "peer": true 1334 | }, 1335 | "node_modules/fast-levenshtein": { 1336 | "version": "2.0.6", 1337 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 1338 | "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", 1339 | "dev": true, 1340 | "peer": true 1341 | }, 1342 | "node_modules/fastq": { 1343 | "version": "1.17.1", 1344 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", 1345 | "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", 1346 | "dev": true, 1347 | "dependencies": { 1348 | "reusify": "^1.0.4" 1349 | } 1350 | }, 1351 | "node_modules/file-entry-cache": { 1352 | "version": "6.0.1", 1353 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", 1354 | "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", 1355 | "dev": true, 1356 | "peer": true, 1357 | "dependencies": { 1358 | "flat-cache": "^3.0.4" 1359 | }, 1360 | "engines": { 1361 | "node": "^10.12.0 || >=12.0.0" 1362 | } 1363 | }, 1364 | "node_modules/fill-range": { 1365 | "version": "7.1.1", 1366 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", 1367 | "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", 1368 | "dev": true, 1369 | "dependencies": { 1370 | "to-regex-range": "^5.0.1" 1371 | }, 1372 | "engines": { 1373 | "node": ">=8" 1374 | } 1375 | }, 1376 | "node_modules/find-up": { 1377 | "version": "5.0.0", 1378 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 1379 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 1380 | "dev": true, 1381 | "peer": true, 1382 | "dependencies": { 1383 | "locate-path": "^6.0.0", 1384 | "path-exists": "^4.0.0" 1385 | }, 1386 | "engines": { 1387 | "node": ">=10" 1388 | }, 1389 | "funding": { 1390 | "url": "https://github.com/sponsors/sindresorhus" 1391 | } 1392 | }, 1393 | "node_modules/flat-cache": { 1394 | "version": "3.2.0", 1395 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", 1396 | "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", 1397 | "dev": true, 1398 | "peer": true, 1399 | "dependencies": { 1400 | "flatted": "^3.2.9", 1401 | "keyv": "^4.5.3", 1402 | "rimraf": "^3.0.2" 1403 | }, 1404 | "engines": { 1405 | "node": "^10.12.0 || >=12.0.0" 1406 | } 1407 | }, 1408 | "node_modules/flatted": { 1409 | "version": "3.3.1", 1410 | "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", 1411 | "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", 1412 | "dev": true, 1413 | "peer": true 1414 | }, 1415 | "node_modules/fs.realpath": { 1416 | "version": "1.0.0", 1417 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 1418 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", 1419 | "dev": true, 1420 | "peer": true 1421 | }, 1422 | "node_modules/functional-red-black-tree": { 1423 | "version": "1.0.1", 1424 | "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", 1425 | "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", 1426 | "dev": true 1427 | }, 1428 | "node_modules/fuzzaldrin-plus": { 1429 | "version": "0.6.0", 1430 | "resolved": "https://registry.npmjs.org/fuzzaldrin-plus/-/fuzzaldrin-plus-0.6.0.tgz", 1431 | "integrity": "sha512-srIDThJHkdp3aPwJpR/HNzYZCRJwm07b/igxseoHSB7qR8e/gQp4F6lMGknE3TQI1Aq14TiFf/wzrHOp9LY/EA==", 1432 | "dev": true 1433 | }, 1434 | "node_modules/glob": { 1435 | "version": "7.2.3", 1436 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 1437 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 1438 | "deprecated": "Glob versions prior to v9 are no longer supported", 1439 | "dev": true, 1440 | "peer": true, 1441 | "dependencies": { 1442 | "fs.realpath": "^1.0.0", 1443 | "inflight": "^1.0.4", 1444 | "inherits": "2", 1445 | "minimatch": "^3.1.1", 1446 | "once": "^1.3.0", 1447 | "path-is-absolute": "^1.0.0" 1448 | }, 1449 | "engines": { 1450 | "node": "*" 1451 | }, 1452 | "funding": { 1453 | "url": "https://github.com/sponsors/isaacs" 1454 | } 1455 | }, 1456 | "node_modules/glob-parent": { 1457 | "version": "6.0.2", 1458 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", 1459 | "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", 1460 | "dev": true, 1461 | "peer": true, 1462 | "dependencies": { 1463 | "is-glob": "^4.0.3" 1464 | }, 1465 | "engines": { 1466 | "node": ">=10.13.0" 1467 | } 1468 | }, 1469 | "node_modules/globals": { 1470 | "version": "13.24.0", 1471 | "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", 1472 | "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", 1473 | "dev": true, 1474 | "peer": true, 1475 | "dependencies": { 1476 | "type-fest": "^0.20.2" 1477 | }, 1478 | "engines": { 1479 | "node": ">=8" 1480 | }, 1481 | "funding": { 1482 | "url": "https://github.com/sponsors/sindresorhus" 1483 | } 1484 | }, 1485 | "node_modules/globby": { 1486 | "version": "11.1.0", 1487 | "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", 1488 | "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", 1489 | "dev": true, 1490 | "dependencies": { 1491 | "array-union": "^2.1.0", 1492 | "dir-glob": "^3.0.1", 1493 | "fast-glob": "^3.2.9", 1494 | "ignore": "^5.2.0", 1495 | "merge2": "^1.4.1", 1496 | "slash": "^3.0.0" 1497 | }, 1498 | "engines": { 1499 | "node": ">=10" 1500 | }, 1501 | "funding": { 1502 | "url": "https://github.com/sponsors/sindresorhus" 1503 | } 1504 | }, 1505 | "node_modules/graphemer": { 1506 | "version": "1.4.0", 1507 | "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", 1508 | "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", 1509 | "dev": true, 1510 | "peer": true 1511 | }, 1512 | "node_modules/has-flag": { 1513 | "version": "4.0.0", 1514 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 1515 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 1516 | "dev": true, 1517 | "peer": true, 1518 | "engines": { 1519 | "node": ">=8" 1520 | } 1521 | }, 1522 | "node_modules/he": { 1523 | "version": "1.2.0", 1524 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 1525 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 1526 | "bin": { 1527 | "he": "bin/he" 1528 | } 1529 | }, 1530 | "node_modules/ignore": { 1531 | "version": "5.3.1", 1532 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", 1533 | "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", 1534 | "dev": true, 1535 | "engines": { 1536 | "node": ">= 4" 1537 | } 1538 | }, 1539 | "node_modules/import-fresh": { 1540 | "version": "3.3.0", 1541 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", 1542 | "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", 1543 | "dev": true, 1544 | "peer": true, 1545 | "dependencies": { 1546 | "parent-module": "^1.0.0", 1547 | "resolve-from": "^4.0.0" 1548 | }, 1549 | "engines": { 1550 | "node": ">=6" 1551 | }, 1552 | "funding": { 1553 | "url": "https://github.com/sponsors/sindresorhus" 1554 | } 1555 | }, 1556 | "node_modules/imurmurhash": { 1557 | "version": "0.1.4", 1558 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 1559 | "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", 1560 | "dev": true, 1561 | "peer": true, 1562 | "engines": { 1563 | "node": ">=0.8.19" 1564 | } 1565 | }, 1566 | "node_modules/inflight": { 1567 | "version": "1.0.6", 1568 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 1569 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 1570 | "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.", 1571 | "dev": true, 1572 | "peer": true, 1573 | "dependencies": { 1574 | "once": "^1.3.0", 1575 | "wrappy": "1" 1576 | } 1577 | }, 1578 | "node_modules/inherits": { 1579 | "version": "2.0.4", 1580 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1581 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 1582 | "dev": true, 1583 | "peer": true 1584 | }, 1585 | "node_modules/is-extglob": { 1586 | "version": "2.1.1", 1587 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 1588 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 1589 | "dev": true, 1590 | "engines": { 1591 | "node": ">=0.10.0" 1592 | } 1593 | }, 1594 | "node_modules/is-glob": { 1595 | "version": "4.0.3", 1596 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 1597 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 1598 | "dev": true, 1599 | "dependencies": { 1600 | "is-extglob": "^2.1.1" 1601 | }, 1602 | "engines": { 1603 | "node": ">=0.10.0" 1604 | } 1605 | }, 1606 | "node_modules/is-number": { 1607 | "version": "7.0.0", 1608 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 1609 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 1610 | "dev": true, 1611 | "engines": { 1612 | "node": ">=0.12.0" 1613 | } 1614 | }, 1615 | "node_modules/is-path-inside": { 1616 | "version": "3.0.3", 1617 | "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", 1618 | "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", 1619 | "dev": true, 1620 | "peer": true, 1621 | "engines": { 1622 | "node": ">=8" 1623 | } 1624 | }, 1625 | "node_modules/isexe": { 1626 | "version": "2.0.0", 1627 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 1628 | "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 1629 | "dev": true, 1630 | "peer": true 1631 | }, 1632 | "node_modules/js-yaml": { 1633 | "version": "4.1.0", 1634 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 1635 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 1636 | "dev": true, 1637 | "peer": true, 1638 | "dependencies": { 1639 | "argparse": "^2.0.1" 1640 | }, 1641 | "bin": { 1642 | "js-yaml": "bin/js-yaml.js" 1643 | } 1644 | }, 1645 | "node_modules/json-buffer": { 1646 | "version": "3.0.1", 1647 | "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", 1648 | "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", 1649 | "dev": true, 1650 | "peer": true 1651 | }, 1652 | "node_modules/json-schema-traverse": { 1653 | "version": "0.4.1", 1654 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 1655 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 1656 | "dev": true, 1657 | "peer": true 1658 | }, 1659 | "node_modules/json-stable-stringify-without-jsonify": { 1660 | "version": "1.0.1", 1661 | "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 1662 | "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", 1663 | "dev": true, 1664 | "peer": true 1665 | }, 1666 | "node_modules/keyv": { 1667 | "version": "4.5.4", 1668 | "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", 1669 | "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", 1670 | "dev": true, 1671 | "peer": true, 1672 | "dependencies": { 1673 | "json-buffer": "3.0.1" 1674 | } 1675 | }, 1676 | "node_modules/levn": { 1677 | "version": "0.4.1", 1678 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", 1679 | "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", 1680 | "dev": true, 1681 | "peer": true, 1682 | "dependencies": { 1683 | "prelude-ls": "^1.2.1", 1684 | "type-check": "~0.4.0" 1685 | }, 1686 | "engines": { 1687 | "node": ">= 0.8.0" 1688 | } 1689 | }, 1690 | "node_modules/locate-path": { 1691 | "version": "6.0.0", 1692 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 1693 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 1694 | "dev": true, 1695 | "peer": true, 1696 | "dependencies": { 1697 | "p-locate": "^5.0.0" 1698 | }, 1699 | "engines": { 1700 | "node": ">=10" 1701 | }, 1702 | "funding": { 1703 | "url": "https://github.com/sponsors/sindresorhus" 1704 | } 1705 | }, 1706 | "node_modules/lodash.merge": { 1707 | "version": "4.6.2", 1708 | "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", 1709 | "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", 1710 | "dev": true, 1711 | "peer": true 1712 | }, 1713 | "node_modules/merge2": { 1714 | "version": "1.4.1", 1715 | "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", 1716 | "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", 1717 | "dev": true, 1718 | "engines": { 1719 | "node": ">= 8" 1720 | } 1721 | }, 1722 | "node_modules/micromatch": { 1723 | "version": "4.0.7", 1724 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", 1725 | "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", 1726 | "dev": true, 1727 | "dependencies": { 1728 | "braces": "^3.0.3", 1729 | "picomatch": "^2.3.1" 1730 | }, 1731 | "engines": { 1732 | "node": ">=8.6" 1733 | } 1734 | }, 1735 | "node_modules/minimatch": { 1736 | "version": "3.1.2", 1737 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 1738 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 1739 | "dev": true, 1740 | "peer": true, 1741 | "dependencies": { 1742 | "brace-expansion": "^1.1.7" 1743 | }, 1744 | "engines": { 1745 | "node": "*" 1746 | } 1747 | }, 1748 | "node_modules/moment": { 1749 | "version": "2.29.4", 1750 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", 1751 | "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", 1752 | "dev": true, 1753 | "engines": { 1754 | "node": "*" 1755 | } 1756 | }, 1757 | "node_modules/ms": { 1758 | "version": "2.1.2", 1759 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1760 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 1761 | "dev": true 1762 | }, 1763 | "node_modules/natural-compare": { 1764 | "version": "1.4.0", 1765 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 1766 | "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", 1767 | "dev": true, 1768 | "peer": true 1769 | }, 1770 | "node_modules/obsidian": { 1771 | "version": "1.5.7-1", 1772 | "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.5.7-1.tgz", 1773 | "integrity": "sha512-T5ZRuQ1FnfXqEoakTTHVDYvzUEEoT8zSPnQCW31PVgYwG4D4tZCQfKHN2hTz1ifnCe8upvwa6mBTAP2WUA5Vng==", 1774 | "dev": true, 1775 | "dependencies": { 1776 | "@types/codemirror": "5.60.8", 1777 | "moment": "2.29.4" 1778 | }, 1779 | "peerDependencies": { 1780 | "@codemirror/state": "^6.0.0", 1781 | "@codemirror/view": "^6.0.0" 1782 | } 1783 | }, 1784 | "node_modules/once": { 1785 | "version": "1.4.0", 1786 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1787 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 1788 | "dev": true, 1789 | "peer": true, 1790 | "dependencies": { 1791 | "wrappy": "1" 1792 | } 1793 | }, 1794 | "node_modules/optionator": { 1795 | "version": "0.9.4", 1796 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", 1797 | "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", 1798 | "dev": true, 1799 | "peer": true, 1800 | "dependencies": { 1801 | "deep-is": "^0.1.3", 1802 | "fast-levenshtein": "^2.0.6", 1803 | "levn": "^0.4.1", 1804 | "prelude-ls": "^1.2.1", 1805 | "type-check": "^0.4.0", 1806 | "word-wrap": "^1.2.5" 1807 | }, 1808 | "engines": { 1809 | "node": ">= 0.8.0" 1810 | } 1811 | }, 1812 | "node_modules/p-limit": { 1813 | "version": "3.1.0", 1814 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 1815 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 1816 | "dev": true, 1817 | "peer": true, 1818 | "dependencies": { 1819 | "yocto-queue": "^0.1.0" 1820 | }, 1821 | "engines": { 1822 | "node": ">=10" 1823 | }, 1824 | "funding": { 1825 | "url": "https://github.com/sponsors/sindresorhus" 1826 | } 1827 | }, 1828 | "node_modules/p-locate": { 1829 | "version": "5.0.0", 1830 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 1831 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 1832 | "dev": true, 1833 | "peer": true, 1834 | "dependencies": { 1835 | "p-limit": "^3.0.2" 1836 | }, 1837 | "engines": { 1838 | "node": ">=10" 1839 | }, 1840 | "funding": { 1841 | "url": "https://github.com/sponsors/sindresorhus" 1842 | } 1843 | }, 1844 | "node_modules/parent-module": { 1845 | "version": "1.0.1", 1846 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 1847 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 1848 | "dev": true, 1849 | "peer": true, 1850 | "dependencies": { 1851 | "callsites": "^3.0.0" 1852 | }, 1853 | "engines": { 1854 | "node": ">=6" 1855 | } 1856 | }, 1857 | "node_modules/path-exists": { 1858 | "version": "4.0.0", 1859 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 1860 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 1861 | "dev": true, 1862 | "peer": true, 1863 | "engines": { 1864 | "node": ">=8" 1865 | } 1866 | }, 1867 | "node_modules/path-is-absolute": { 1868 | "version": "1.0.1", 1869 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1870 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 1871 | "dev": true, 1872 | "peer": true, 1873 | "engines": { 1874 | "node": ">=0.10.0" 1875 | } 1876 | }, 1877 | "node_modules/path-key": { 1878 | "version": "3.1.1", 1879 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 1880 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 1881 | "dev": true, 1882 | "peer": true, 1883 | "engines": { 1884 | "node": ">=8" 1885 | } 1886 | }, 1887 | "node_modules/path-type": { 1888 | "version": "4.0.0", 1889 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", 1890 | "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", 1891 | "dev": true, 1892 | "engines": { 1893 | "node": ">=8" 1894 | } 1895 | }, 1896 | "node_modules/picomatch": { 1897 | "version": "2.3.1", 1898 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 1899 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 1900 | "dev": true, 1901 | "engines": { 1902 | "node": ">=8.6" 1903 | }, 1904 | "funding": { 1905 | "url": "https://github.com/sponsors/jonschlinkert" 1906 | } 1907 | }, 1908 | "node_modules/prelude-ls": { 1909 | "version": "1.2.1", 1910 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", 1911 | "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", 1912 | "dev": true, 1913 | "peer": true, 1914 | "engines": { 1915 | "node": ">= 0.8.0" 1916 | } 1917 | }, 1918 | "node_modules/punycode": { 1919 | "version": "2.3.1", 1920 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", 1921 | "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", 1922 | "dev": true, 1923 | "peer": true, 1924 | "engines": { 1925 | "node": ">=6" 1926 | } 1927 | }, 1928 | "node_modules/queue-microtask": { 1929 | "version": "1.2.3", 1930 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 1931 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", 1932 | "dev": true, 1933 | "funding": [ 1934 | { 1935 | "type": "github", 1936 | "url": "https://github.com/sponsors/feross" 1937 | }, 1938 | { 1939 | "type": "patreon", 1940 | "url": "https://www.patreon.com/feross" 1941 | }, 1942 | { 1943 | "type": "consulting", 1944 | "url": "https://feross.org/support" 1945 | } 1946 | ] 1947 | }, 1948 | "node_modules/regexpp": { 1949 | "version": "3.2.0", 1950 | "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", 1951 | "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", 1952 | "dev": true, 1953 | "engines": { 1954 | "node": ">=8" 1955 | }, 1956 | "funding": { 1957 | "url": "https://github.com/sponsors/mysticatea" 1958 | } 1959 | }, 1960 | "node_modules/resolve-from": { 1961 | "version": "4.0.0", 1962 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 1963 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 1964 | "dev": true, 1965 | "peer": true, 1966 | "engines": { 1967 | "node": ">=4" 1968 | } 1969 | }, 1970 | "node_modules/reusify": { 1971 | "version": "1.0.4", 1972 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", 1973 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", 1974 | "dev": true, 1975 | "engines": { 1976 | "iojs": ">=1.0.0", 1977 | "node": ">=0.10.0" 1978 | } 1979 | }, 1980 | "node_modules/rimraf": { 1981 | "version": "3.0.2", 1982 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 1983 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 1984 | "deprecated": "Rimraf versions prior to v4 are no longer supported", 1985 | "dev": true, 1986 | "peer": true, 1987 | "dependencies": { 1988 | "glob": "^7.1.3" 1989 | }, 1990 | "bin": { 1991 | "rimraf": "bin.js" 1992 | }, 1993 | "funding": { 1994 | "url": "https://github.com/sponsors/isaacs" 1995 | } 1996 | }, 1997 | "node_modules/run-parallel": { 1998 | "version": "1.2.0", 1999 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", 2000 | "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", 2001 | "dev": true, 2002 | "funding": [ 2003 | { 2004 | "type": "github", 2005 | "url": "https://github.com/sponsors/feross" 2006 | }, 2007 | { 2008 | "type": "patreon", 2009 | "url": "https://www.patreon.com/feross" 2010 | }, 2011 | { 2012 | "type": "consulting", 2013 | "url": "https://feross.org/support" 2014 | } 2015 | ], 2016 | "dependencies": { 2017 | "queue-microtask": "^1.2.2" 2018 | } 2019 | }, 2020 | "node_modules/semver": { 2021 | "version": "7.6.2", 2022 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", 2023 | "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", 2024 | "dev": true, 2025 | "bin": { 2026 | "semver": "bin/semver.js" 2027 | }, 2028 | "engines": { 2029 | "node": ">=10" 2030 | } 2031 | }, 2032 | "node_modules/shebang-command": { 2033 | "version": "2.0.0", 2034 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 2035 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 2036 | "dev": true, 2037 | "peer": true, 2038 | "dependencies": { 2039 | "shebang-regex": "^3.0.0" 2040 | }, 2041 | "engines": { 2042 | "node": ">=8" 2043 | } 2044 | }, 2045 | "node_modules/shebang-regex": { 2046 | "version": "3.0.0", 2047 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 2048 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 2049 | "dev": true, 2050 | "peer": true, 2051 | "engines": { 2052 | "node": ">=8" 2053 | } 2054 | }, 2055 | "node_modules/shell-escape": { 2056 | "version": "0.2.0", 2057 | "resolved": "https://registry.npmjs.org/shell-escape/-/shell-escape-0.2.0.tgz", 2058 | "integrity": "sha512-uRRBT2MfEOyxuECseCZd28jC1AJ8hmqqneWQ4VWUTgCAFvb3wKU1jLqj6egC4Exrr88ogg3dp+zroH4wJuaXzw==" 2059 | }, 2060 | "node_modules/slash": { 2061 | "version": "3.0.0", 2062 | "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", 2063 | "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", 2064 | "dev": true, 2065 | "engines": { 2066 | "node": ">=8" 2067 | } 2068 | }, 2069 | "node_modules/strip-ansi": { 2070 | "version": "6.0.1", 2071 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 2072 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 2073 | "dev": true, 2074 | "peer": true, 2075 | "dependencies": { 2076 | "ansi-regex": "^5.0.1" 2077 | }, 2078 | "engines": { 2079 | "node": ">=8" 2080 | } 2081 | }, 2082 | "node_modules/strip-json-comments": { 2083 | "version": "3.1.1", 2084 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 2085 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 2086 | "dev": true, 2087 | "peer": true, 2088 | "engines": { 2089 | "node": ">=8" 2090 | }, 2091 | "funding": { 2092 | "url": "https://github.com/sponsors/sindresorhus" 2093 | } 2094 | }, 2095 | "node_modules/style-mod": { 2096 | "version": "4.1.2", 2097 | "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", 2098 | "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==", 2099 | "dev": true, 2100 | "peer": true 2101 | }, 2102 | "node_modules/supports-color": { 2103 | "version": "7.2.0", 2104 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 2105 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 2106 | "dev": true, 2107 | "peer": true, 2108 | "dependencies": { 2109 | "has-flag": "^4.0.0" 2110 | }, 2111 | "engines": { 2112 | "node": ">=8" 2113 | } 2114 | }, 2115 | "node_modules/text-table": { 2116 | "version": "0.2.0", 2117 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 2118 | "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", 2119 | "dev": true, 2120 | "peer": true 2121 | }, 2122 | "node_modules/to-regex-range": { 2123 | "version": "5.0.1", 2124 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 2125 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 2126 | "dev": true, 2127 | "dependencies": { 2128 | "is-number": "^7.0.0" 2129 | }, 2130 | "engines": { 2131 | "node": ">=8.0" 2132 | } 2133 | }, 2134 | "node_modules/tslib": { 2135 | "version": "2.4.0", 2136 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", 2137 | "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", 2138 | "dev": true 2139 | }, 2140 | "node_modules/tsutils": { 2141 | "version": "3.21.0", 2142 | "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", 2143 | "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", 2144 | "dev": true, 2145 | "dependencies": { 2146 | "tslib": "^1.8.1" 2147 | }, 2148 | "engines": { 2149 | "node": ">= 6" 2150 | }, 2151 | "peerDependencies": { 2152 | "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" 2153 | } 2154 | }, 2155 | "node_modules/tsutils/node_modules/tslib": { 2156 | "version": "1.14.1", 2157 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 2158 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", 2159 | "dev": true 2160 | }, 2161 | "node_modules/type-check": { 2162 | "version": "0.4.0", 2163 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", 2164 | "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 2165 | "dev": true, 2166 | "peer": true, 2167 | "dependencies": { 2168 | "prelude-ls": "^1.2.1" 2169 | }, 2170 | "engines": { 2171 | "node": ">= 0.8.0" 2172 | } 2173 | }, 2174 | "node_modules/type-fest": { 2175 | "version": "0.20.2", 2176 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", 2177 | "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", 2178 | "dev": true, 2179 | "peer": true, 2180 | "engines": { 2181 | "node": ">=10" 2182 | }, 2183 | "funding": { 2184 | "url": "https://github.com/sponsors/sindresorhus" 2185 | } 2186 | }, 2187 | "node_modules/typescript": { 2188 | "version": "4.7.4", 2189 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", 2190 | "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", 2191 | "dev": true, 2192 | "bin": { 2193 | "tsc": "bin/tsc", 2194 | "tsserver": "bin/tsserver" 2195 | }, 2196 | "engines": { 2197 | "node": ">=4.2.0" 2198 | } 2199 | }, 2200 | "node_modules/uri-js": { 2201 | "version": "4.4.1", 2202 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 2203 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 2204 | "dev": true, 2205 | "peer": true, 2206 | "dependencies": { 2207 | "punycode": "^2.1.0" 2208 | } 2209 | }, 2210 | "node_modules/w3c-keyname": { 2211 | "version": "2.2.8", 2212 | "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", 2213 | "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", 2214 | "dev": true, 2215 | "peer": true 2216 | }, 2217 | "node_modules/which": { 2218 | "version": "2.0.2", 2219 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 2220 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 2221 | "dev": true, 2222 | "peer": true, 2223 | "dependencies": { 2224 | "isexe": "^2.0.0" 2225 | }, 2226 | "bin": { 2227 | "node-which": "bin/node-which" 2228 | }, 2229 | "engines": { 2230 | "node": ">= 8" 2231 | } 2232 | }, 2233 | "node_modules/word-wrap": { 2234 | "version": "1.2.5", 2235 | "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", 2236 | "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", 2237 | "dev": true, 2238 | "peer": true, 2239 | "engines": { 2240 | "node": ">=0.10.0" 2241 | } 2242 | }, 2243 | "node_modules/wrappy": { 2244 | "version": "1.0.2", 2245 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 2246 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 2247 | "dev": true, 2248 | "peer": true 2249 | }, 2250 | "node_modules/yocto-queue": { 2251 | "version": "0.1.0", 2252 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 2253 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 2254 | "dev": true, 2255 | "peer": true, 2256 | "engines": { 2257 | "node": ">=10" 2258 | }, 2259 | "funding": { 2260 | "url": "https://github.com/sponsors/sindresorhus" 2261 | } 2262 | } 2263 | } 2264 | } 2265 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "obsidian-fabric-plugin", 3 | "version": "1.0.0", 4 | "description": "Fabric integration for Obsidian", 5 | "main": "main.js", 6 | "scripts": { 7 | "dev": "node esbuild.config.mjs", 8 | "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production", 9 | "version": "node version-bump.mjs && git add manifest.json versions.json" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "@types/fuzzaldrin-plus": "^0.6.5", 16 | "@types/node": "^16.11.6", 17 | "@types/semver": "^7.5.8", 18 | "@types/shell-escape": "^0.2.3", 19 | "@typescript-eslint/eslint-plugin": "5.29.0", 20 | "@typescript-eslint/parser": "5.29.0", 21 | "builtin-modules": "3.3.0", 22 | "esbuild": "0.17.3", 23 | "fuzzaldrin-plus": "^0.6.0", 24 | "obsidian": "latest", 25 | "tslib": "^2.4.0", 26 | "typescript": "4.7.4" 27 | }, 28 | "dependencies": { 29 | "@types/he": "^1.2.3", 30 | "he": "^1.2.0", 31 | "shell-escape": "^0.2.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | 2 | :root { 3 | --dropdown-top-offset: 5px; 4 | --dropdown-left-offset: 0; 5 | --dropdown-width-offset: 0; 6 | } 7 | 8 | .fabric-view { 9 | padding: 2rem; 10 | background-color: var(--background-primary); 11 | border-left: 1px solid var(--border-color); 12 | } 13 | 14 | .fabric-logo-container { 15 | text-align: center; 16 | margin-bottom: 3rem; 17 | position: relative; 18 | } 19 | 20 | @keyframes pulse { 21 | 0%, 50%, 100% { 22 | transform: scale(0.95); 23 | opacity: 0.7; 24 | } 25 | 25%, 75% { 26 | transform: scale(1); 27 | opacity: 1; 28 | } 29 | } 30 | 31 | .fabric-logo-container.loading .fabric-logo { 32 | animation: pulse 4s infinite ease-in-out; 33 | } 34 | 35 | .fabric-loading-text { 36 | position: absolute; 37 | bottom: -0.625rem; 38 | left: 50%; 39 | transform: translateX(-50%); 40 | font-size: 1rem; 41 | color: var(--text-muted); 42 | opacity: 0; 43 | transition: opacity 0.3s ease; 44 | } 45 | 46 | .fabric-logo-container.loading .fabric-loading-text { 47 | opacity: 1; 48 | } 49 | 50 | @keyframes blink { 51 | 0%, 100% { opacity: 1; } 52 | 50% { opacity: 0; } 53 | } 54 | 55 | .fabric-loading-text::after { 56 | content: '|'; 57 | animation: blink 1s infinite; 58 | margin-left: 0.125rem; 59 | } 60 | 61 | .fabric-logo { 62 | max-width: 100%; 63 | height: auto; 64 | max-height: 28.125rem; 65 | transition: all 0.3s ease; 66 | } 67 | 68 | .fabric-title { 69 | font-size: 1.5rem; 70 | font-weight: bold; 71 | margin-bottom: 1.25rem; 72 | color: var(--text-normal); 73 | } 74 | 75 | .fabric-content { 76 | background-color: var(--background-secondary); 77 | border-radius: 0.625rem; 78 | padding: 1.25rem 1.25rem 3.125rem 1.25rem; 79 | box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.1); 80 | display: flex; 81 | flex-direction: column; 82 | align-items: center; 83 | width: 90%; 84 | max-width: 43.75rem; 85 | margin: 0 auto; 86 | position: relative; 87 | } 88 | 89 | .fabric-refresh-button, 90 | .fabric-sync-button { 91 | position: absolute; 92 | bottom: 0.625rem; 93 | background: none; 94 | border: none; 95 | cursor: pointer; 96 | color: var(--text-muted); 97 | transition: color 0.2s ease; 98 | } 99 | 100 | .fabric-refresh-button { 101 | right: 0.625rem; 102 | } 103 | 104 | .fabric-sync-button { 105 | right: 3.125rem; 106 | } 107 | 108 | .fabric-refresh-button:hover, 109 | .fabric-sync-button:hover { 110 | color: var(--text-accent); 111 | } 112 | 113 | .fabric-buttons { 114 | display: flex; 115 | justify-content: space-between; 116 | margin-bottom: 0.625rem; 117 | width: 100%; 118 | max-width: 28.125rem; 119 | } 120 | 121 | .fabric-button { 122 | flex: 1; 123 | padding: 0.9375rem; 124 | border: none; 125 | border-radius: 0.3125rem; 126 | background-color: var(--interactive-accent); 127 | color: var(--text-on-accent); 128 | cursor: pointer; 129 | transition: background-color 0.3s ease, box-shadow 0.3s ease; 130 | margin: 0 0.625rem; 131 | position: relative; 132 | overflow: hidden; 133 | } 134 | 135 | .fabric-button::before { 136 | content: ""; 137 | position: absolute; 138 | top: 0; 139 | left: 0; 140 | width: 0; 141 | height: 0; 142 | background-color: rgba(255, 255, 255, 0.3); 143 | border-radius: 50%; 144 | transform: translate(-50%, -50%); 145 | transition: width 0.5s, height 0.5s; 146 | } 147 | 148 | .fabric-button:hover::before { 149 | width: 200%; 150 | height: 200%; 151 | } 152 | 153 | .fabric-button:first-child { 154 | margin-left: 0; 155 | } 156 | 157 | .fabric-button:last-child { 158 | margin-right: 0; 159 | } 160 | 161 | .fabric-button:hover { 162 | background-color: var(--interactive-accent-hover); 163 | box-shadow: 0 0 0.625rem rgba(0, 0, 0, 0.1); 164 | } 165 | 166 | .fabric-button.clipboard { 167 | background: rgb(171, 118, 209); 168 | transition: background 0.3s ease; 169 | margin: 0; 170 | } 171 | 172 | .fabric-button.current-note { 173 | background: rgb(60, 62, 149); 174 | } 175 | 176 | .fabric-button.clipboard:hover, 177 | .fabric-button.current-note:hover { 178 | box-shadow: 0 0 0.625rem rgba(107, 63, 160, 0.5), 0 0 1.25rem rgba(76, 127, 210, 0.5); 179 | } 180 | 181 | .fabric-inputs-container { 182 | display: flex; 183 | flex-direction: column; 184 | align-items: center; 185 | gap: 0.625rem; 186 | margin-bottom: 0.625rem; 187 | width: 100%; 188 | } 189 | 190 | .fabric-input, 191 | .fabric-dropdown-input, 192 | .fabric-output-note-input { 193 | width: 100%; 194 | max-width: 28.125rem; 195 | padding: 0.75rem; 196 | border: 1px solid var(--border-color); 197 | border-radius: 0.3125rem; 198 | background-color: var(--background-primary); 199 | color: var(--text-normal); 200 | transition: box-shadow 0.3s ease; 201 | box-shadow: 0 0 0.625rem rgba(0, 0, 0, 0.1); 202 | } 203 | 204 | .fabric-input.active, 205 | .fabric-dropdown-input.active, 206 | .fabric-output-note-input.active { 207 | box-shadow: 0 0 0.625rem rgba(107, 63, 160, 0.5), 0 0 1.25rem rgba(76, 127, 210, 0.5); 208 | } 209 | 210 | .fabric-dropdown { 211 | width: 100%; 212 | max-width: 28.125rem; 213 | max-height: 12.5rem; 214 | overflow-y: auto; 215 | scrollbar-width: thin; 216 | scroll-behavior: auto; 217 | border: 1px solid var(--border-color); 218 | border-radius: 0.3125rem; 219 | background-color: var(--background-primary); 220 | color: var(--text-normal); 221 | margin-top: 11.875rem; 222 | box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.1); 223 | position: absolute; 224 | z-index: 100; 225 | } 226 | 227 | .fabric-dropdown-option { 228 | padding: 0.5rem 0.75rem; 229 | cursor: pointer; 230 | background-color: var(--background-primary); 231 | border-radius: 0.3125rem; 232 | width: 100%; 233 | } 234 | 235 | .fabric-dropdown-option.selected { 236 | background: linear-gradient(to right, #6B3FA0, #4C7FD2); 237 | color: var(--text-on-accent); 238 | } 239 | 240 | .fabric-progress-spinner { 241 | border: 0.25rem solid var(--border-color); 242 | border-top-color: var(--interactive-accent); 243 | border-radius: 50%; 244 | width: 1.875rem; 245 | height: 1.875rem; 246 | animation: spin 2s linear infinite; 247 | display: none; 248 | margin: 0 auto; 249 | } 250 | 251 | .fabric-progress-spinner.active { 252 | display: block; 253 | } 254 | 255 | @keyframes spin { 256 | 0% { transform: rotate(0deg); } 257 | 100% { transform: rotate(360deg); } 258 | } 259 | 260 | .theme-light .fabric-input, 261 | .theme-light .fabric-dropdown-input, 262 | .theme-light .fabric-output-note-input, 263 | .theme-light .fabric-dropdown { 264 | color: var(--text-normal); 265 | } 266 | 267 | .fabric-content { 268 | position: relative; 269 | padding-top: 2.5rem; 270 | } 271 | 272 | .fabric-yt-toggle-container { 273 | position: absolute; 274 | top: 0.625rem; 275 | left: 0.625rem; 276 | display: flex; 277 | align-items: center; 278 | } 279 | 280 | .fabric-yt-toggle { 281 | position: relative; 282 | width: 1.875rem; 283 | height: 1.0625rem; 284 | background-color: #ccc; 285 | border-radius: 1.0625rem; 286 | cursor: pointer; 287 | transition: background-color 0.3s; 288 | } 289 | 290 | .fabric-yt-toggle.active { 291 | background-color: #2196F3; 292 | } 293 | 294 | .fabric-yt-toggle-slider { 295 | position: absolute; 296 | height: 0.8125rem; 297 | width: 0.8125rem; 298 | left: 0.25rem; 299 | bottom: 0.125rem; 300 | background-color: white; 301 | border-radius: 50%; 302 | transition: 0.3s; 303 | } 304 | 305 | .fabric-yt-toggle.active .fabric-yt-toggle-slider { 306 | transform: translateX(0.625rem); 307 | } 308 | 309 | .fabric-yt-label { 310 | margin-left: 0.625rem; 311 | font-size: 0.875rem; 312 | color: var(--text-normal); 313 | } 314 | 315 | .fabric-yt-link-list { 316 | max-height: 18.75rem; 317 | overflow-y: auto; 318 | margin-bottom: 0.625rem; 319 | border: 1px solid var(--background-modifier-border); 320 | border-radius: 0.25rem; 321 | } 322 | 323 | .fabric-yt-link { 324 | padding: 0.5rem 0.75rem; 325 | cursor: pointer; 326 | border-bottom: 1px solid var(--background-modifier-border); 327 | } 328 | 329 | .fabric-yt-link:last-child { 330 | border-bottom: none; 331 | } 332 | 333 | .fabric-yt-link.is-selected { 334 | background: linear-gradient(to right, #6B3FA0, #4C7FD2); 335 | color: var(--text-on-accent); 336 | } 337 | 338 | .fabric-yt-link:hover:not(.is-selected) { 339 | background-color: var(--background-modifier-hover); 340 | } 341 | 342 | .fabric-yt-modal-buttons { 343 | display: flex; 344 | justify-content: flex-end; 345 | gap: 0.625rem; 346 | } 347 | 348 | .fabric-ts-toggle-container { 349 | position: absolute; 350 | top: 2.1rem; 351 | left: 0.625rem; 352 | display: flex; 353 | align-items: center; 354 | } 355 | 356 | .fabric-ts-toggle { 357 | position: relative; 358 | width: 1.875rem; 359 | height: 1.0625rem; 360 | background-color: #ccc; 361 | border-radius: 1.0625rem; 362 | cursor: pointer; 363 | transition: background-color 0.3s; 364 | } 365 | 366 | .fabric-ts-toggle.active { 367 | background-color: #2196F3; 368 | } 369 | 370 | .fabric-ts-toggle-slider { 371 | position: absolute; 372 | height: 0.8125rem; 373 | width: 0.8125rem; 374 | left: 0.25rem; 375 | bottom: 0.125rem; 376 | background-color: white; 377 | border-radius: 50%; 378 | transition: 0.3s; 379 | } 380 | 381 | .fabric-ts-toggle.active .fabric-ts-toggle-slider { 382 | transform: translateX(0.625rem); 383 | } 384 | 385 | .fabric-ts-label { 386 | margin-left: 0.625rem; 387 | font-size: 0.875rem; 388 | color: var(--text-normal); 389 | } 390 | 391 | .fabric-ts-link-list { 392 | max-height: 18.75rem; 393 | overflow-y: auto; 394 | margin-bottom: 0.625rem; 395 | border: 1px solid var(--background-modifier-border); 396 | border-radius: 0.25rem; 397 | } 398 | 399 | .fabric-ts-link { 400 | padding: 0.5rem 0.75rem; 401 | cursor: pointer; 402 | border-bottom: 1px solid var(--background-modifier-border); 403 | } 404 | 405 | .fabric-ts-link:last-child { 406 | border-bottom: none; 407 | } 408 | 409 | .fabric-ts-link.is-selected { 410 | background: linear-gradient(to right, #6B3FA0, #4C7FD2); 411 | color: var(--text-on-accent); 412 | } 413 | 414 | .fabric-ts-link:hover:not(.is-selected) { 415 | background-color: var(--background-modifier-hover); 416 | } 417 | 418 | .fabric-ts-modal-buttons { 419 | display: flex; 420 | justify-content: flex-end; 421 | gap: 0.625rem; 422 | } 423 | 424 | .skip-button, 425 | .run-button { 426 | padding: 0.5rem 1rem; 427 | border: none; 428 | border-radius: 0.25rem; 429 | cursor: pointer; 430 | color: var(--text-on-accent); 431 | position: relative; 432 | overflow: hidden; 433 | } 434 | 435 | .skip-button { 436 | background: rgb(60, 62, 149); 437 | } 438 | 439 | .run-button { 440 | background: linear-gradient(to right, #6B3FA0, #4C7FD2); 441 | } 442 | 443 | .skip-button::before, 444 | .run-button::before { 445 | content: ""; 446 | position: absolute; 447 | top: 0; 448 | left: 0; 449 | width: 0; 450 | height: 0; 451 | background-color: rgba(255, 255, 255, 0.3); 452 | border-radius: 50%; 453 | transform: translate(-50%, -50%); 454 | transition: width 0.5s, height 0.5s; 455 | } 456 | 457 | .skip-button:hover::before, 458 | .run-button:hover::before { 459 | width: 200%; 460 | height: 200%; 461 | } 462 | 463 | .run-button:hover { 464 | box-shadow: 0 0 0.625rem rgba(107, 63, 160, 0.5), 0 0 1.25rem rgba(76, 127, 210, 0.5); 465 | } 466 | 467 | .skip-button:hover { 468 | box-shadow: 0 0 0.625rem rgba(63, 42, 86, 0.5), 0 0 1.25rem rgba(46, 80, 119, 0.5); 469 | } 470 | 471 | @keyframes swipeUp { 472 | 0% { 473 | transform: rotateX(45deg) translateY(0.3125rem); 474 | opacity: 0; 475 | } 476 | 100% { 477 | transform: rotateX(0deg) translateY(0); 478 | opacity: 1; 479 | } 480 | } 481 | 482 | .fabric-default-model { 483 | text-align: center; 484 | margin-top: 1.25rem; 485 | font-size: 0.9em; 486 | color: var(--text-muted); 487 | opacity: 0.8; 488 | } 489 | 490 | .fabric-default-model .model-name { 491 | transition: color 0.5s ease; 492 | display: inline-block; 493 | } 494 | 495 | .fabric-default-model .model-name.updating { 496 | animation: swipeUp 0.5s ease-out; 497 | color: var(--text-accent); 498 | } 499 | 500 | #model-dropdown { 501 | top: 22%; 502 | max-height: 9.375rem; 503 | } 504 | 505 | .fabric-button-container { 506 | display: flex; 507 | justify-content: flex-end; 508 | margin-bottom: 0.625rem; 509 | } 510 | 511 | .fabric-icon-button { 512 | background-color: transparent; 513 | border: none; 514 | cursor: pointer; 515 | padding: 0.25rem; 516 | margin-left: 0.5rem; 517 | border-radius: 0.25rem; 518 | display: flex; 519 | align-items: center; 520 | justify-content: center; 521 | } 522 | 523 | .fabric-icon-button:hover { 524 | background-color: var(--interactive-hover); 525 | color: var(--text-accent); 526 | } 527 | 528 | .fabric-icon-button svg { 529 | width: 1.5625rem; 530 | height: 1rem; 531 | color: var(--text-muted); 532 | } 533 | 534 | .fabric-icon-button:hover svg { 535 | color: var(--text-accent); 536 | } 537 | 538 | .fabric-confirm-deletion { 539 | padding: 1rem; 540 | max-width: 18.75rem; 541 | } 542 | 543 | .fabric-confirm-deletion h3 { 544 | margin-top: 0; 545 | margin-bottom: 0.75rem; 546 | } 547 | 548 | .fabric-confirm-deletion p { 549 | margin-bottom: 1rem; 550 | } 551 | 552 | .fabric-confirm-buttons { 553 | display: flex; 554 | justify-content: flex-end; 555 | gap: 0.5rem; 556 | } 557 | 558 | .fabric-confirm-buttons button { 559 | padding: 0.375rem 0.75rem; 560 | border: none; 561 | border-radius: 0.25rem; 562 | cursor: pointer; 563 | } 564 | 565 | .fabric-confirm-buttons button:first-child { 566 | background-color: var(--interactive-accent); 567 | color: var(--text-on-accent); 568 | } 569 | 570 | .fabric-confirm-buttons button:last-child { 571 | background-color: var(--interactive-normal); 572 | } 573 | 574 | .fabric-button.tavily { 575 | background: linear-gradient(to right, #6B3FA0, #4C7FD2); 576 | } 577 | 578 | .fabric-button.clipboard, 579 | .fabric-button.tavily { 580 | flex: 0.5; 581 | } 582 | 583 | .fabric-tavily-modal { 584 | background-color: var(--background-primary); 585 | border-radius: 10px; 586 | padding: 20px; 587 | max-width: 500px; 588 | width: 90%; 589 | } 590 | 591 | .fabric-tavily-modal .modal-title { 592 | font-size: 1.5rem; 593 | font-weight: bold; 594 | margin-bottom: 20px; 595 | color: var(--text-normal); 596 | text-align: center; 597 | } 598 | 599 | .fabric-tavily-modal .fabric-tavily-input { 600 | width: 100%; 601 | padding: 10px; 602 | border: 1px solid var(--border-color); 603 | border-radius: 5px; 604 | background-color: var(--background-secondary); 605 | color: var(--text-normal); 606 | font-size: 1rem; 607 | margin-bottom: 20px; 608 | transition: box-shadow 0.3s ease; 609 | } 610 | 611 | .fabric-tavily-modal .fabric-tavily-input:focus { 612 | outline: none; 613 | box-shadow: 0 0 0 2px var(--interactive-accent); 614 | } 615 | 616 | .fabric-tavily-modal .fabric-tavily-button-container { 617 | display: flex; 618 | justify-content: flex-end; 619 | } 620 | 621 | .fabric-tavily-modal .fabric-tavily-search-button { 622 | background: linear-gradient(to right, #6B3FA0, #4C7FD2); 623 | color: var(--text-on-accent); 624 | border: none; 625 | border-radius: 5px; 626 | padding: 10px 20px; 627 | font-size: 1rem; 628 | cursor: pointer; 629 | transition: all 0.3s ease; 630 | position: relative; 631 | overflow: hidden; 632 | } 633 | 634 | .fabric-tavily-modal .fabric-tavily-search-button:hover { 635 | box-shadow: 0 0 10px rgba(107, 63, 160, 0.5), 0 0 20px rgba(76, 127, 210, 0.5); 636 | } 637 | 638 | .fabric-tavily-modal .fabric-tavily-search-button::before { 639 | content: ""; 640 | position: absolute; 641 | top: 0; 642 | left: 0; 643 | width: 0; 644 | height: 0; 645 | background-color: rgba(255, 255, 255, 0.3); 646 | border-radius: 50%; 647 | transform: translate(-50%, -50%); 648 | transition: width 0.5s, height 0.5s; 649 | } 650 | 651 | .fabric-tavily-modal .fabric-tavily-search-button:hover::before { 652 | width: 300%; 653 | height: 300%; 654 | } 655 | .community-patterns-title { 656 | font-size: 1.5rem; 657 | font-weight: bold; 658 | margin-bottom: 1.25rem; 659 | color: var(--text-normal); 660 | } 661 | 662 | .community-patterns-search { 663 | width: 100%; 664 | padding: 0.75rem; 665 | border: 1px solid var(--border-color); 666 | border-radius: 0.3125rem; 667 | background-color: var(--background-primary); 668 | color: var(--text-normal); 669 | transition: box-shadow 0.3s ease; 670 | box-shadow: 0 0 0.625rem rgba(0, 0, 0, 0.1); 671 | margin-bottom: 1rem; 672 | margin-top: 1rem; 673 | } 674 | 675 | .community-patterns-results { 676 | display: flex; 677 | flex-direction: column; 678 | gap: 0.25rem; 679 | width: 100%; 680 | max-height: 70vh; 681 | overflow-y: auto; 682 | margin-top: 1rem; 683 | } 684 | 685 | .community-pattern-item { 686 | background-color: var(--background-secondary); 687 | border-radius: 0.25rem; 688 | padding: 0.25rem 0.5rem; 689 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); 690 | display: flex; 691 | justify-content: space-between; 692 | align-items: center; 693 | gap: 0.5rem; 694 | font-size: 0.75rem; 695 | min-height: 2rem; 696 | width: 100%; 697 | } 698 | 699 | .community-pattern-info { 700 | display: flex; 701 | flex-direction: column; 702 | gap: 0.125rem; 703 | flex-grow: 1; 704 | overflow: hidden; 705 | text-align: left; 706 | } 707 | 708 | .community-pattern-title { 709 | font-size: 0.8125rem; 710 | font-weight: bold; 711 | margin: 0; 712 | white-space: nowrap; 713 | overflow: hidden; 714 | text-overflow: ellipsis; 715 | } 716 | 717 | .community-pattern-description { 718 | color: var(--text-muted); 719 | font-size: 0.6875rem; 720 | margin: 0; 721 | white-space: nowrap; 722 | overflow: hidden; 723 | text-overflow: ellipsis; 724 | } 725 | 726 | .community-pattern-buttons { 727 | display: flex; 728 | gap: 0.25rem; 729 | } 730 | 731 | .community-pattern-download, 732 | .community-pattern-update, 733 | .community-pattern-uninstall { 734 | padding: 0.125rem 0.25rem; 735 | font-size: 0.6875rem; 736 | min-width: 3rem; 737 | border: none; 738 | border-radius: 0.1875rem; 739 | cursor: pointer; 740 | transition: background-color 0.3s ease; 741 | } 742 | 743 | .community-pattern-download { 744 | background-color: var(--interactive-accent); 745 | color: var(--text-on-accent); 746 | } 747 | 748 | .community-pattern-update { 749 | background-color: var(--text-accent); 750 | color: var(--text-on-accent); 751 | } 752 | 753 | .community-pattern-uninstall { 754 | background-color: var(--text-error); 755 | color: var(--text-on-accent); 756 | } 757 | 758 | .community-pattern-download:hover, 759 | .community-pattern-update:hover, 760 | .community-pattern-uninstall:hover { 761 | opacity: 0.8; 762 | } 763 | 764 | .fabric-icon-button.community-patterns { 765 | position: absolute; 766 | bottom: 0.625rem; 767 | right: 5.6rem; 768 | background-color: var(--interactive-normal); 769 | border: none; 770 | cursor: pointer; 771 | color: var(--text-muted); 772 | transition: color 0.2s ease; 773 | } 774 | 775 | .fabric-icon-button.community-patterns:hover { 776 | color: var(--text-on-accent); 777 | } 778 | 779 | .community-patterns-button-container { 780 | display: flex; 781 | gap: 0.5rem; /* Space between buttons */ 782 | margin-bottom: 1rem; /* Space below the button container */ 783 | } 784 | 785 | .fabric-button.community-patterns-update-all, 786 | .fabric-button.community-patterns-refresh { 787 | background-color: var(--interactive-normal); 788 | border: none; 789 | cursor: pointer; 790 | color: var(--text-muted); 791 | transition: color 0.2s ease; 792 | } 793 | 794 | .fabric-button.community-patterns-update-all:hover, 795 | .fabric-button.community-patterns-refresh:hover { 796 | color: var(--text-accent); 797 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "inlineSourceMap": true, 5 | "inlineSources": true, 6 | "module": "ESNext", 7 | "target": "ES6", 8 | "allowJs": true, 9 | "noImplicitAny": true, 10 | "moduleResolution": "node", 11 | "importHelpers": true, 12 | "isolatedModules": true, 13 | "strictNullChecks": true, 14 | "lib": [ 15 | "DOM", 16 | "ES5", 17 | "ES6", 18 | "ES7" 19 | ] 20 | }, 21 | "include": [ 22 | "**/*.ts" 23 | ] 24 | } --------------------------------------------------------------------------------