full PC config setup
shifting towards manual symlinks for more complex setup than laptop, allows for GUI settings changes to actually apply, and is much easier than nix-ifying every config
This commit is contained in:
149 files changed
+9539
-478
No files matched your search
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"patternStore.savedPatterns": [
|
||||
{
|
||||
"label": "Import quotes to angles",
|
||||
"find": "\"([^\"]+)\"",
|
||||
"replace": "<$1>",
|
||||
"flags": {
|
||||
"isRegex": true,
|
||||
"isCaseSensitive": false,
|
||||
"matchWholeWord": false,
|
||||
"isMultiline": false
|
||||
},
|
||||
"scope": "global"
|
||||
},
|
||||
{
|
||||
"label": "TODO",
|
||||
"find": "TODO",
|
||||
"flags": {
|
||||
"isRegex": false,
|
||||
"isCaseSensitive": true,
|
||||
"matchWholeWord": true,
|
||||
"isMultiline": false
|
||||
},
|
||||
"scope": "global"
|
||||
},
|
||||
{
|
||||
"label": "Dynamic module import",
|
||||
"find": "${prompt:module}([^\"]*)",
|
||||
"replace": "<${prompt:module}$1>",
|
||||
"flags": {
|
||||
"isRegex": true,
|
||||
"isCaseSensitive": true,
|
||||
"matchWholeWord": false,
|
||||
"isMultiline": false
|
||||
},
|
||||
"scope": "global"
|
||||
},
|
||||
{
|
||||
"label": "Find all TODOs (no replace)",
|
||||
"find": "TODO",
|
||||
"flags": {
|
||||
"isRegex": false,
|
||||
"isCaseSensitive": true,
|
||||
"matchWholeWord": true,
|
||||
"isMultiline": false
|
||||
},
|
||||
"scope": "global"
|
||||
}
|
||||
],
|
||||
"patternStore.workspacePatterns": [
|
||||
{
|
||||
"label": "Workspace specific pattern",
|
||||
"find": "oldClass",
|
||||
"replace": "newClass",
|
||||
"flags": {
|
||||
"isRegex": false,
|
||||
"isCaseSensitive": true,
|
||||
"matchWholeWord": true,
|
||||
"isMultiline": false
|
||||
},
|
||||
"scope": "workspace"
|
||||
}
|
||||
]
|
||||
}
|
||||
Generated
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
import uuid from "./index.js";
|
||||
export import v1 = uuid.v1;
|
||||
export import v1ToV6 = uuid.v1ToV6;
|
||||
export import v3 = uuid.v3;
|
||||
export import v4 = uuid.v4;
|
||||
export import v5 = uuid.v5;
|
||||
export import v6 = uuid.v6;
|
||||
export import v6ToV1 = uuid.v6ToV1;
|
||||
export import v7 = uuid.v7;
|
||||
export import NIL = uuid.NIL;
|
||||
export import MAX = uuid.MAX;
|
||||
export import version = uuid.version;
|
||||
export import validate = uuid.validate;
|
||||
export import stringify = uuid.stringify;
|
||||
export import parse = uuid.parse;
|
||||
export import V1Options = uuid.V1Options;
|
||||
export import V4Options = uuid.V4Options;
|
||||
export import V6Options = uuid.V6Options;
|
||||
export import V7Options = uuid.V7Options;
|
||||
Generated
Vendored
+54
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"name": "@types/uuid",
|
||||
"version": "10.0.0",
|
||||
"description": "TypeScript definitions for uuid",
|
||||
"homepage": "https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/uuid",
|
||||
"license": "MIT",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Oliver Hoffmann",
|
||||
"githubUsername": "iamolivinius",
|
||||
"url": "https://github.com/iamolivinius"
|
||||
},
|
||||
{
|
||||
"name": "Felipe Ochoa",
|
||||
"githubUsername": "felipeochoa",
|
||||
"url": "https://github.com/felipeochoa"
|
||||
},
|
||||
{
|
||||
"name": "Chris Barth",
|
||||
"githubUsername": "cjbarth",
|
||||
"url": "https://github.com/cjbarth"
|
||||
},
|
||||
{
|
||||
"name": "Linus Unnebäck",
|
||||
"githubUsername": "LinusU",
|
||||
"url": "https://github.com/LinusU"
|
||||
},
|
||||
{
|
||||
"name": "Christoph Tavan",
|
||||
"githubUsername": "ctavan",
|
||||
"url": "https://github.com/ctavan"
|
||||
}
|
||||
],
|
||||
"main": "",
|
||||
"types": "index.d.ts",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"types": {
|
||||
"import": "./index.d.mts",
|
||||
"default": "./index.d.ts"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/DefinitelyTyped/DefinitelyTyped.git",
|
||||
"directory": "types/uuid"
|
||||
},
|
||||
"scripts": {},
|
||||
"dependencies": {},
|
||||
"typesPublisherContentHash": "08fbc5ff7d23aaac1e81b5acf98181d2544ce6ffd5578e9879e2a75f0c087d54",
|
||||
"typeScriptVersion": "4.7"
|
||||
}
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env node
|
||||
import '../uuid-bin.js';
|
||||
Generated
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
export { default as MAX } from './max.js';
|
||||
export { default as NIL } from './nil.js';
|
||||
export { default as parse } from './parse.js';
|
||||
export { default as stringify } from './stringify.js';
|
||||
export { default as v1 } from './v1.js';
|
||||
export { default as v1ToV6 } from './v1ToV6.js';
|
||||
export { default as v3 } from './v3.js';
|
||||
export { default as v4 } from './v4.js';
|
||||
export { default as v5 } from './v5.js';
|
||||
export { default as v6 } from './v6.js';
|
||||
export { default as v6ToV1 } from './v6ToV1.js';
|
||||
export { default as v7 } from './v7.js';
|
||||
export { default as validate } from './validate.js';
|
||||
export { default as version } from './version.js';
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
export default 'ffffffff-ffff-ffff-ffff-ffffffffffff';
|
||||
Generated
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
import { createHash } from 'node:crypto';
|
||||
function md5(bytes) {
|
||||
if (Array.isArray(bytes)) {
|
||||
bytes = Buffer.from(bytes);
|
||||
}
|
||||
else if (typeof bytes === 'string') {
|
||||
bytes = Buffer.from(bytes, 'utf8');
|
||||
}
|
||||
return createHash('md5').update(bytes).digest();
|
||||
}
|
||||
export default md5;
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
import { randomUUID } from 'node:crypto';
|
||||
export default { randomUUID };
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
export default '00000000-0000-0000-0000-000000000000';
|
||||
Generated
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
import validate from './validate.js';
|
||||
function parse(uuid) {
|
||||
if (!validate(uuid)) {
|
||||
throw TypeError('Invalid UUID');
|
||||
}
|
||||
let v;
|
||||
return Uint8Array.of((v = parseInt(uuid.slice(0, 8), 16)) >>> 24, (v >>> 16) & 0xff, (v >>> 8) & 0xff, v & 0xff, (v = parseInt(uuid.slice(9, 13), 16)) >>> 8, v & 0xff, (v = parseInt(uuid.slice(14, 18), 16)) >>> 8, v & 0xff, (v = parseInt(uuid.slice(19, 23), 16)) >>> 8, v & 0xff, ((v = parseInt(uuid.slice(24, 36), 16)) / 0x10000000000) & 0xff, (v / 0x100000000) & 0xff, (v >>> 24) & 0xff, (v >>> 16) & 0xff, (v >>> 8) & 0xff, v & 0xff);
|
||||
}
|
||||
export default parse;
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
export default /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/i;
|
||||
Generated
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
import { randomFillSync } from 'node:crypto';
|
||||
const rnds8Pool = new Uint8Array(256);
|
||||
let poolPtr = rnds8Pool.length;
|
||||
export default function rng() {
|
||||
if (poolPtr > rnds8Pool.length - 16) {
|
||||
randomFillSync(rnds8Pool);
|
||||
poolPtr = 0;
|
||||
}
|
||||
return rnds8Pool.slice(poolPtr, (poolPtr += 16));
|
||||
}
|
||||
Generated
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
import { createHash } from 'node:crypto';
|
||||
function sha1(bytes) {
|
||||
if (Array.isArray(bytes)) {
|
||||
bytes = Buffer.from(bytes);
|
||||
}
|
||||
else if (typeof bytes === 'string') {
|
||||
bytes = Buffer.from(bytes, 'utf8');
|
||||
}
|
||||
return createHash('sha1').update(bytes).digest();
|
||||
}
|
||||
export default sha1;
|
||||
Generated
Vendored
+35
@@ -0,0 +1,35 @@
|
||||
import validate from './validate.js';
|
||||
const byteToHex = [];
|
||||
for (let i = 0; i < 256; ++i) {
|
||||
byteToHex.push((i + 0x100).toString(16).slice(1));
|
||||
}
|
||||
export function unsafeStringify(arr, offset = 0) {
|
||||
return (byteToHex[arr[offset + 0]] +
|
||||
byteToHex[arr[offset + 1]] +
|
||||
byteToHex[arr[offset + 2]] +
|
||||
byteToHex[arr[offset + 3]] +
|
||||
'-' +
|
||||
byteToHex[arr[offset + 4]] +
|
||||
byteToHex[arr[offset + 5]] +
|
||||
'-' +
|
||||
byteToHex[arr[offset + 6]] +
|
||||
byteToHex[arr[offset + 7]] +
|
||||
'-' +
|
||||
byteToHex[arr[offset + 8]] +
|
||||
byteToHex[arr[offset + 9]] +
|
||||
'-' +
|
||||
byteToHex[arr[offset + 10]] +
|
||||
byteToHex[arr[offset + 11]] +
|
||||
byteToHex[arr[offset + 12]] +
|
||||
byteToHex[arr[offset + 13]] +
|
||||
byteToHex[arr[offset + 14]] +
|
||||
byteToHex[arr[offset + 15]]).toLowerCase();
|
||||
}
|
||||
function stringify(arr, offset = 0) {
|
||||
const uuid = unsafeStringify(arr, offset);
|
||||
if (!validate(uuid)) {
|
||||
throw TypeError('Stringified UUID is invalid');
|
||||
}
|
||||
return uuid;
|
||||
}
|
||||
export default stringify;
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
export {};
|
||||
Generated
Vendored
+70
@@ -0,0 +1,70 @@
|
||||
import * as assert from 'node:assert/strict';
|
||||
import v1 from './v1.js';
|
||||
import v3 from './v3.js';
|
||||
import v4 from './v4.js';
|
||||
import v5 from './v5.js';
|
||||
import v6 from './v6.js';
|
||||
import v7 from './v7.js';
|
||||
function usage() {
|
||||
console.log('Usage:');
|
||||
console.log(' uuid');
|
||||
console.log(' uuid v1');
|
||||
console.log(' uuid v3 <name> <namespace uuid>');
|
||||
console.log(' uuid v4');
|
||||
console.log(' uuid v5 <name> <namespace uuid>');
|
||||
console.log(' uuid v6');
|
||||
console.log(' uuid v7');
|
||||
console.log(' uuid --help');
|
||||
console.log('\nNote: <namespace uuid> may be "URL" or "DNS" to use the corresponding UUIDs defined by RFC9562');
|
||||
}
|
||||
const args = process.argv.slice(2);
|
||||
if (args.indexOf('--help') >= 0) {
|
||||
usage();
|
||||
process.exit(0);
|
||||
}
|
||||
const version = args.shift() || 'v4';
|
||||
switch (version) {
|
||||
case 'v1':
|
||||
console.log(v1());
|
||||
break;
|
||||
case 'v3': {
|
||||
const name = args.shift();
|
||||
let namespace = args.shift();
|
||||
assert.ok(name != null, 'v3 name not specified');
|
||||
assert.ok(namespace != null, 'v3 namespace not specified');
|
||||
if (namespace === 'URL') {
|
||||
namespace = v3.URL;
|
||||
}
|
||||
if (namespace === 'DNS') {
|
||||
namespace = v3.DNS;
|
||||
}
|
||||
console.log(v3(name, namespace));
|
||||
break;
|
||||
}
|
||||
case 'v4':
|
||||
console.log(v4());
|
||||
break;
|
||||
case 'v5': {
|
||||
const name = args.shift();
|
||||
let namespace = args.shift();
|
||||
assert.ok(name != null, 'v5 name not specified');
|
||||
assert.ok(namespace != null, 'v5 namespace not specified');
|
||||
if (namespace === 'URL') {
|
||||
namespace = v5.URL;
|
||||
}
|
||||
if (namespace === 'DNS') {
|
||||
namespace = v5.DNS;
|
||||
}
|
||||
console.log(v5(name, namespace));
|
||||
break;
|
||||
}
|
||||
case 'v6':
|
||||
console.log(v6());
|
||||
break;
|
||||
case 'v7':
|
||||
console.log(v7());
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
process.exit(1);
|
||||
}
|
||||
Generated
Vendored
+83
@@ -0,0 +1,83 @@
|
||||
import rng from './rng.js';
|
||||
import { unsafeStringify } from './stringify.js';
|
||||
const _state = {};
|
||||
function v1(options, buf, offset) {
|
||||
let bytes;
|
||||
const isV6 = options?._v6 ?? false;
|
||||
if (options) {
|
||||
const optionsKeys = Object.keys(options);
|
||||
if (optionsKeys.length === 1 && optionsKeys[0] === '_v6') {
|
||||
options = undefined;
|
||||
}
|
||||
}
|
||||
if (options) {
|
||||
bytes = v1Bytes(options.random ?? options.rng?.() ?? rng(), options.msecs, options.nsecs, options.clockseq, options.node, buf, offset);
|
||||
}
|
||||
else {
|
||||
const now = Date.now();
|
||||
const rnds = rng();
|
||||
updateV1State(_state, now, rnds);
|
||||
bytes = v1Bytes(rnds, _state.msecs, _state.nsecs, isV6 ? undefined : _state.clockseq, isV6 ? undefined : _state.node, buf, offset);
|
||||
}
|
||||
return buf ?? unsafeStringify(bytes);
|
||||
}
|
||||
export function updateV1State(state, now, rnds) {
|
||||
state.msecs ??= -Infinity;
|
||||
state.nsecs ??= 0;
|
||||
if (now === state.msecs) {
|
||||
state.nsecs++;
|
||||
if (state.nsecs >= 10000) {
|
||||
state.node = undefined;
|
||||
state.nsecs = 0;
|
||||
}
|
||||
}
|
||||
else if (now > state.msecs) {
|
||||
state.nsecs = 0;
|
||||
}
|
||||
else if (now < state.msecs) {
|
||||
state.node = undefined;
|
||||
}
|
||||
if (!state.node) {
|
||||
state.node = rnds.slice(10, 16);
|
||||
state.node[0] |= 0x01;
|
||||
state.clockseq = ((rnds[8] << 8) | rnds[9]) & 0x3fff;
|
||||
}
|
||||
state.msecs = now;
|
||||
return state;
|
||||
}
|
||||
function v1Bytes(rnds, msecs, nsecs, clockseq, node, buf, offset = 0) {
|
||||
if (rnds.length < 16) {
|
||||
throw new Error('Random bytes length must be >= 16');
|
||||
}
|
||||
if (!buf) {
|
||||
buf = new Uint8Array(16);
|
||||
offset = 0;
|
||||
}
|
||||
else {
|
||||
if (offset < 0 || offset + 16 > buf.length) {
|
||||
throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);
|
||||
}
|
||||
}
|
||||
msecs ??= Date.now();
|
||||
nsecs ??= 0;
|
||||
clockseq ??= ((rnds[8] << 8) | rnds[9]) & 0x3fff;
|
||||
node ??= rnds.slice(10, 16);
|
||||
msecs += 12219292800000;
|
||||
const tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;
|
||||
buf[offset++] = (tl >>> 24) & 0xff;
|
||||
buf[offset++] = (tl >>> 16) & 0xff;
|
||||
buf[offset++] = (tl >>> 8) & 0xff;
|
||||
buf[offset++] = tl & 0xff;
|
||||
const tmh = ((msecs / 0x100000000) * 10000) & 0xfffffff;
|
||||
buf[offset++] = (tmh >>> 8) & 0xff;
|
||||
buf[offset++] = tmh & 0xff;
|
||||
buf[offset++] = ((tmh >>> 24) & 0xf) | 0x10;
|
||||
buf[offset++] = (tmh >>> 16) & 0xff;
|
||||
buf[offset++] = (clockseq >>> 8) | 0x80;
|
||||
buf[offset++] = clockseq & 0xff;
|
||||
for (let n = 0; n < 6; ++n) {
|
||||
buf[offset++] = node[n];
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
export default v1;
|
||||
Generated
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
import parse from './parse.js';
|
||||
import { unsafeStringify } from './stringify.js';
|
||||
export default function v1ToV6(uuid) {
|
||||
const v1Bytes = typeof uuid === 'string' ? parse(uuid) : uuid;
|
||||
const v6Bytes = _v1ToV6(v1Bytes);
|
||||
return typeof uuid === 'string' ? unsafeStringify(v6Bytes) : v6Bytes;
|
||||
}
|
||||
function _v1ToV6(v1Bytes) {
|
||||
return Uint8Array.of(((v1Bytes[6] & 0x0f) << 4) | ((v1Bytes[7] >> 4) & 0x0f), ((v1Bytes[7] & 0x0f) << 4) | ((v1Bytes[4] & 0xf0) >> 4), ((v1Bytes[4] & 0x0f) << 4) | ((v1Bytes[5] & 0xf0) >> 4), ((v1Bytes[5] & 0x0f) << 4) | ((v1Bytes[0] & 0xf0) >> 4), ((v1Bytes[0] & 0x0f) << 4) | ((v1Bytes[1] & 0xf0) >> 4), ((v1Bytes[1] & 0x0f) << 4) | ((v1Bytes[2] & 0xf0) >> 4), 0x60 | (v1Bytes[2] & 0x0f), v1Bytes[3], v1Bytes[8], v1Bytes[9], v1Bytes[10], v1Bytes[11], v1Bytes[12], v1Bytes[13], v1Bytes[14], v1Bytes[15]);
|
||||
}
|
||||
Generated
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
import md5 from './md5.js';
|
||||
import v35, { DNS, URL } from './v35.js';
|
||||
export { DNS, URL } from './v35.js';
|
||||
function v3(value, namespace, buf, offset) {
|
||||
return v35(0x30, md5, value, namespace, buf, offset);
|
||||
}
|
||||
v3.DNS = DNS;
|
||||
v3.URL = URL;
|
||||
export default v3;
|
||||
Generated
Vendored
+36
@@ -0,0 +1,36 @@
|
||||
import parse from './parse.js';
|
||||
import { unsafeStringify } from './stringify.js';
|
||||
export function stringToBytes(str) {
|
||||
str = unescape(encodeURIComponent(str));
|
||||
const bytes = new Uint8Array(str.length);
|
||||
for (let i = 0; i < str.length; ++i) {
|
||||
bytes[i] = str.charCodeAt(i);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
export const DNS = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';
|
||||
export const URL = '6ba7b811-9dad-11d1-80b4-00c04fd430c8';
|
||||
export default function v35(version, hash, value, namespace, buf, offset) {
|
||||
const valueBytes = typeof value === 'string' ? stringToBytes(value) : value;
|
||||
const namespaceBytes = typeof namespace === 'string' ? parse(namespace) : namespace;
|
||||
if (typeof namespace === 'string') {
|
||||
namespace = parse(namespace);
|
||||
}
|
||||
if (namespace?.length !== 16) {
|
||||
throw TypeError('Namespace must be array-like (16 iterable integer values, 0-255)');
|
||||
}
|
||||
let bytes = new Uint8Array(16 + valueBytes.length);
|
||||
bytes.set(namespaceBytes);
|
||||
bytes.set(valueBytes, namespaceBytes.length);
|
||||
bytes = hash(bytes);
|
||||
bytes[6] = (bytes[6] & 0x0f) | version;
|
||||
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
||||
if (buf) {
|
||||
offset = offset || 0;
|
||||
for (let i = 0; i < 16; ++i) {
|
||||
buf[offset + i] = bytes[i];
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
return unsafeStringify(bytes);
|
||||
}
|
||||
Generated
Vendored
+30
@@ -0,0 +1,30 @@
|
||||
import native from './native.js';
|
||||
import rng from './rng.js';
|
||||
import { unsafeStringify } from './stringify.js';
|
||||
function _v4(options, buf, offset) {
|
||||
options = options || {};
|
||||
const rnds = options.random ?? options.rng?.() ?? rng();
|
||||
if (rnds.length < 16) {
|
||||
throw new Error('Random bytes length must be >= 16');
|
||||
}
|
||||
rnds[6] = (rnds[6] & 0x0f) | 0x40;
|
||||
rnds[8] = (rnds[8] & 0x3f) | 0x80;
|
||||
if (buf) {
|
||||
offset = offset || 0;
|
||||
if (offset < 0 || offset + 16 > buf.length) {
|
||||
throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);
|
||||
}
|
||||
for (let i = 0; i < 16; ++i) {
|
||||
buf[offset + i] = rnds[i];
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
return unsafeStringify(rnds);
|
||||
}
|
||||
function v4(options, buf, offset) {
|
||||
if (native.randomUUID && !buf && !options) {
|
||||
return native.randomUUID();
|
||||
}
|
||||
return _v4(options, buf, offset);
|
||||
}
|
||||
export default v4;
|
||||
Generated
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
import sha1 from './sha1.js';
|
||||
import v35, { DNS, URL } from './v35.js';
|
||||
export { DNS, URL } from './v35.js';
|
||||
function v5(value, namespace, buf, offset) {
|
||||
return v35(0x50, sha1, value, namespace, buf, offset);
|
||||
}
|
||||
v5.DNS = DNS;
|
||||
v5.URL = URL;
|
||||
export default v5;
|
||||
Generated
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
import { unsafeStringify } from './stringify.js';
|
||||
import v1 from './v1.js';
|
||||
import v1ToV6 from './v1ToV6.js';
|
||||
function v6(options, buf, offset) {
|
||||
options ??= {};
|
||||
offset ??= 0;
|
||||
let bytes = v1({ ...options, _v6: true }, new Uint8Array(16));
|
||||
bytes = v1ToV6(bytes);
|
||||
if (buf) {
|
||||
for (let i = 0; i < 16; i++) {
|
||||
buf[offset + i] = bytes[i];
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
return unsafeStringify(bytes);
|
||||
}
|
||||
export default v6;
|
||||
Generated
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
import parse from './parse.js';
|
||||
import { unsafeStringify } from './stringify.js';
|
||||
export default function v6ToV1(uuid) {
|
||||
const v6Bytes = typeof uuid === 'string' ? parse(uuid) : uuid;
|
||||
const v1Bytes = _v6ToV1(v6Bytes);
|
||||
return typeof uuid === 'string' ? unsafeStringify(v1Bytes) : v1Bytes;
|
||||
}
|
||||
function _v6ToV1(v6Bytes) {
|
||||
return Uint8Array.of(((v6Bytes[3] & 0x0f) << 4) | ((v6Bytes[4] >> 4) & 0x0f), ((v6Bytes[4] & 0x0f) << 4) | ((v6Bytes[5] & 0xf0) >> 4), ((v6Bytes[5] & 0x0f) << 4) | (v6Bytes[6] & 0x0f), v6Bytes[7], ((v6Bytes[1] & 0x0f) << 4) | ((v6Bytes[2] & 0xf0) >> 4), ((v6Bytes[2] & 0x0f) << 4) | ((v6Bytes[3] & 0xf0) >> 4), 0x10 | ((v6Bytes[0] & 0xf0) >> 4), ((v6Bytes[0] & 0x0f) << 4) | ((v6Bytes[1] & 0xf0) >> 4), v6Bytes[8], v6Bytes[9], v6Bytes[10], v6Bytes[11], v6Bytes[12], v6Bytes[13], v6Bytes[14], v6Bytes[15]);
|
||||
}
|
||||
Generated
Vendored
+65
@@ -0,0 +1,65 @@
|
||||
import rng from './rng.js';
|
||||
import { unsafeStringify } from './stringify.js';
|
||||
const _state = {};
|
||||
function v7(options, buf, offset) {
|
||||
let bytes;
|
||||
if (options) {
|
||||
bytes = v7Bytes(options.random ?? options.rng?.() ?? rng(), options.msecs, options.seq, buf, offset);
|
||||
}
|
||||
else {
|
||||
const now = Date.now();
|
||||
const rnds = rng();
|
||||
updateV7State(_state, now, rnds);
|
||||
bytes = v7Bytes(rnds, _state.msecs, _state.seq, buf, offset);
|
||||
}
|
||||
return buf ?? unsafeStringify(bytes);
|
||||
}
|
||||
export function updateV7State(state, now, rnds) {
|
||||
state.msecs ??= -Infinity;
|
||||
state.seq ??= 0;
|
||||
if (now > state.msecs) {
|
||||
state.seq = (rnds[6] << 23) | (rnds[7] << 16) | (rnds[8] << 8) | rnds[9];
|
||||
state.msecs = now;
|
||||
}
|
||||
else {
|
||||
state.seq = (state.seq + 1) | 0;
|
||||
if (state.seq === 0) {
|
||||
state.msecs++;
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
||||
function v7Bytes(rnds, msecs, seq, buf, offset = 0) {
|
||||
if (rnds.length < 16) {
|
||||
throw new Error('Random bytes length must be >= 16');
|
||||
}
|
||||
if (!buf) {
|
||||
buf = new Uint8Array(16);
|
||||
offset = 0;
|
||||
}
|
||||
else {
|
||||
if (offset < 0 || offset + 16 > buf.length) {
|
||||
throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);
|
||||
}
|
||||
}
|
||||
msecs ??= Date.now();
|
||||
seq ??= ((rnds[6] * 0x7f) << 24) | (rnds[7] << 16) | (rnds[8] << 8) | rnds[9];
|
||||
buf[offset++] = (msecs / 0x10000000000) & 0xff;
|
||||
buf[offset++] = (msecs / 0x100000000) & 0xff;
|
||||
buf[offset++] = (msecs / 0x1000000) & 0xff;
|
||||
buf[offset++] = (msecs / 0x10000) & 0xff;
|
||||
buf[offset++] = (msecs / 0x100) & 0xff;
|
||||
buf[offset++] = msecs & 0xff;
|
||||
buf[offset++] = 0x70 | ((seq >>> 28) & 0x0f);
|
||||
buf[offset++] = (seq >>> 20) & 0xff;
|
||||
buf[offset++] = 0x80 | ((seq >>> 14) & 0x3f);
|
||||
buf[offset++] = (seq >>> 6) & 0xff;
|
||||
buf[offset++] = ((seq << 2) & 0xff) | (rnds[10] & 0x03);
|
||||
buf[offset++] = rnds[11];
|
||||
buf[offset++] = rnds[12];
|
||||
buf[offset++] = rnds[13];
|
||||
buf[offset++] = rnds[14];
|
||||
buf[offset++] = rnds[15];
|
||||
return buf;
|
||||
}
|
||||
export default v7;
|
||||
Generated
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
import REGEX from './regex.js';
|
||||
function validate(uuid) {
|
||||
return typeof uuid === 'string' && REGEX.test(uuid);
|
||||
}
|
||||
export default validate;
|
||||
assets/dotfiles/vscode-extensions/jdunkel.patternstore-0.1.12/node_modules/uuid/dist-node/version.js
Generated
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
import validate from './validate.js';
|
||||
function version(uuid) {
|
||||
if (!validate(uuid)) {
|
||||
throw TypeError('Invalid UUID');
|
||||
}
|
||||
return parseInt(uuid.slice(14, 15), 16);
|
||||
}
|
||||
export default version;
|
||||
Generated
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
export { default as MAX } from './max.js';
|
||||
export { default as NIL } from './nil.js';
|
||||
export { default as parse } from './parse.js';
|
||||
export { default as stringify } from './stringify.js';
|
||||
export { default as v1 } from './v1.js';
|
||||
export { default as v1ToV6 } from './v1ToV6.js';
|
||||
export { default as v3 } from './v3.js';
|
||||
export { default as v4 } from './v4.js';
|
||||
export { default as v5 } from './v5.js';
|
||||
export { default as v6 } from './v6.js';
|
||||
export { default as v6ToV1 } from './v6ToV1.js';
|
||||
export { default as v7 } from './v7.js';
|
||||
export { default as validate } from './validate.js';
|
||||
export { default as version } from './version.js';
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
export default 'ffffffff-ffff-ffff-ffff-ffffffffffff';
|
||||
Generated
Vendored
+135
@@ -0,0 +1,135 @@
|
||||
function md5(bytes) {
|
||||
const words = uint8ToUint32(bytes);
|
||||
const md5Bytes = wordsToMd5(words, bytes.length * 8);
|
||||
return uint32ToUint8(md5Bytes);
|
||||
}
|
||||
function uint32ToUint8(input) {
|
||||
const bytes = new Uint8Array(input.length * 4);
|
||||
for (let i = 0; i < input.length * 4; i++) {
|
||||
bytes[i] = (input[i >> 2] >>> ((i % 4) * 8)) & 0xff;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
function getOutputLength(inputLength8) {
|
||||
return (((inputLength8 + 64) >>> 9) << 4) + 14 + 1;
|
||||
}
|
||||
function wordsToMd5(x, len) {
|
||||
const xpad = new Uint32Array(getOutputLength(len)).fill(0);
|
||||
xpad.set(x);
|
||||
xpad[len >> 5] |= 0x80 << len % 32;
|
||||
xpad[xpad.length - 1] = len;
|
||||
x = xpad;
|
||||
let a = 1732584193;
|
||||
let b = -271733879;
|
||||
let c = -1732584194;
|
||||
let d = 271733878;
|
||||
for (let i = 0; i < x.length; i += 16) {
|
||||
const olda = a;
|
||||
const oldb = b;
|
||||
const oldc = c;
|
||||
const oldd = d;
|
||||
a = md5ff(a, b, c, d, x[i], 7, -680876936);
|
||||
d = md5ff(d, a, b, c, x[i + 1], 12, -389564586);
|
||||
c = md5ff(c, d, a, b, x[i + 2], 17, 606105819);
|
||||
b = md5ff(b, c, d, a, x[i + 3], 22, -1044525330);
|
||||
a = md5ff(a, b, c, d, x[i + 4], 7, -176418897);
|
||||
d = md5ff(d, a, b, c, x[i + 5], 12, 1200080426);
|
||||
c = md5ff(c, d, a, b, x[i + 6], 17, -1473231341);
|
||||
b = md5ff(b, c, d, a, x[i + 7], 22, -45705983);
|
||||
a = md5ff(a, b, c, d, x[i + 8], 7, 1770035416);
|
||||
d = md5ff(d, a, b, c, x[i + 9], 12, -1958414417);
|
||||
c = md5ff(c, d, a, b, x[i + 10], 17, -42063);
|
||||
b = md5ff(b, c, d, a, x[i + 11], 22, -1990404162);
|
||||
a = md5ff(a, b, c, d, x[i + 12], 7, 1804603682);
|
||||
d = md5ff(d, a, b, c, x[i + 13], 12, -40341101);
|
||||
c = md5ff(c, d, a, b, x[i + 14], 17, -1502002290);
|
||||
b = md5ff(b, c, d, a, x[i + 15], 22, 1236535329);
|
||||
a = md5gg(a, b, c, d, x[i + 1], 5, -165796510);
|
||||
d = md5gg(d, a, b, c, x[i + 6], 9, -1069501632);
|
||||
c = md5gg(c, d, a, b, x[i + 11], 14, 643717713);
|
||||
b = md5gg(b, c, d, a, x[i], 20, -373897302);
|
||||
a = md5gg(a, b, c, d, x[i + 5], 5, -701558691);
|
||||
d = md5gg(d, a, b, c, x[i + 10], 9, 38016083);
|
||||
c = md5gg(c, d, a, b, x[i + 15], 14, -660478335);
|
||||
b = md5gg(b, c, d, a, x[i + 4], 20, -405537848);
|
||||
a = md5gg(a, b, c, d, x[i + 9], 5, 568446438);
|
||||
d = md5gg(d, a, b, c, x[i + 14], 9, -1019803690);
|
||||
c = md5gg(c, d, a, b, x[i + 3], 14, -187363961);
|
||||
b = md5gg(b, c, d, a, x[i + 8], 20, 1163531501);
|
||||
a = md5gg(a, b, c, d, x[i + 13], 5, -1444681467);
|
||||
d = md5gg(d, a, b, c, x[i + 2], 9, -51403784);
|
||||
c = md5gg(c, d, a, b, x[i + 7], 14, 1735328473);
|
||||
b = md5gg(b, c, d, a, x[i + 12], 20, -1926607734);
|
||||
a = md5hh(a, b, c, d, x[i + 5], 4, -378558);
|
||||
d = md5hh(d, a, b, c, x[i + 8], 11, -2022574463);
|
||||
c = md5hh(c, d, a, b, x[i + 11], 16, 1839030562);
|
||||
b = md5hh(b, c, d, a, x[i + 14], 23, -35309556);
|
||||
a = md5hh(a, b, c, d, x[i + 1], 4, -1530992060);
|
||||
d = md5hh(d, a, b, c, x[i + 4], 11, 1272893353);
|
||||
c = md5hh(c, d, a, b, x[i + 7], 16, -155497632);
|
||||
b = md5hh(b, c, d, a, x[i + 10], 23, -1094730640);
|
||||
a = md5hh(a, b, c, d, x[i + 13], 4, 681279174);
|
||||
d = md5hh(d, a, b, c, x[i], 11, -358537222);
|
||||
c = md5hh(c, d, a, b, x[i + 3], 16, -722521979);
|
||||
b = md5hh(b, c, d, a, x[i + 6], 23, 76029189);
|
||||
a = md5hh(a, b, c, d, x[i + 9], 4, -640364487);
|
||||
d = md5hh(d, a, b, c, x[i + 12], 11, -421815835);
|
||||
c = md5hh(c, d, a, b, x[i + 15], 16, 530742520);
|
||||
b = md5hh(b, c, d, a, x[i + 2], 23, -995338651);
|
||||
a = md5ii(a, b, c, d, x[i], 6, -198630844);
|
||||
d = md5ii(d, a, b, c, x[i + 7], 10, 1126891415);
|
||||
c = md5ii(c, d, a, b, x[i + 14], 15, -1416354905);
|
||||
b = md5ii(b, c, d, a, x[i + 5], 21, -57434055);
|
||||
a = md5ii(a, b, c, d, x[i + 12], 6, 1700485571);
|
||||
d = md5ii(d, a, b, c, x[i + 3], 10, -1894986606);
|
||||
c = md5ii(c, d, a, b, x[i + 10], 15, -1051523);
|
||||
b = md5ii(b, c, d, a, x[i + 1], 21, -2054922799);
|
||||
a = md5ii(a, b, c, d, x[i + 8], 6, 1873313359);
|
||||
d = md5ii(d, a, b, c, x[i + 15], 10, -30611744);
|
||||
c = md5ii(c, d, a, b, x[i + 6], 15, -1560198380);
|
||||
b = md5ii(b, c, d, a, x[i + 13], 21, 1309151649);
|
||||
a = md5ii(a, b, c, d, x[i + 4], 6, -145523070);
|
||||
d = md5ii(d, a, b, c, x[i + 11], 10, -1120210379);
|
||||
c = md5ii(c, d, a, b, x[i + 2], 15, 718787259);
|
||||
b = md5ii(b, c, d, a, x[i + 9], 21, -343485551);
|
||||
a = safeAdd(a, olda);
|
||||
b = safeAdd(b, oldb);
|
||||
c = safeAdd(c, oldc);
|
||||
d = safeAdd(d, oldd);
|
||||
}
|
||||
return Uint32Array.of(a, b, c, d);
|
||||
}
|
||||
function uint8ToUint32(input) {
|
||||
if (input.length === 0) {
|
||||
return new Uint32Array();
|
||||
}
|
||||
const output = new Uint32Array(getOutputLength(input.length * 8)).fill(0);
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
output[i >> 2] |= (input[i] & 0xff) << ((i % 4) * 8);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
function safeAdd(x, y) {
|
||||
const lsw = (x & 0xffff) + (y & 0xffff);
|
||||
const msw = (x >> 16) + (y >> 16) + (lsw >> 16);
|
||||
return (msw << 16) | (lsw & 0xffff);
|
||||
}
|
||||
function bitRotateLeft(num, cnt) {
|
||||
return (num << cnt) | (num >>> (32 - cnt));
|
||||
}
|
||||
function md5cmn(q, a, b, x, s, t) {
|
||||
return safeAdd(bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), b);
|
||||
}
|
||||
function md5ff(a, b, c, d, x, s, t) {
|
||||
return md5cmn((b & c) | (~b & d), a, b, x, s, t);
|
||||
}
|
||||
function md5gg(a, b, c, d, x, s, t) {
|
||||
return md5cmn((b & d) | (c & ~d), a, b, x, s, t);
|
||||
}
|
||||
function md5hh(a, b, c, d, x, s, t) {
|
||||
return md5cmn(b ^ c ^ d, a, b, x, s, t);
|
||||
}
|
||||
function md5ii(a, b, c, d, x, s, t) {
|
||||
return md5cmn(c ^ (b | ~d), a, b, x, s, t);
|
||||
}
|
||||
export default md5;
|
||||
Generated
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
const randomUUID = typeof crypto !== 'undefined' && crypto.randomUUID && crypto.randomUUID.bind(crypto);
|
||||
export default { randomUUID };
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
export default '00000000-0000-0000-0000-000000000000';
|
||||
Generated
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
import validate from './validate.js';
|
||||
function parse(uuid) {
|
||||
if (!validate(uuid)) {
|
||||
throw TypeError('Invalid UUID');
|
||||
}
|
||||
let v;
|
||||
return Uint8Array.of((v = parseInt(uuid.slice(0, 8), 16)) >>> 24, (v >>> 16) & 0xff, (v >>> 8) & 0xff, v & 0xff, (v = parseInt(uuid.slice(9, 13), 16)) >>> 8, v & 0xff, (v = parseInt(uuid.slice(14, 18), 16)) >>> 8, v & 0xff, (v = parseInt(uuid.slice(19, 23), 16)) >>> 8, v & 0xff, ((v = parseInt(uuid.slice(24, 36), 16)) / 0x10000000000) & 0xff, (v / 0x100000000) & 0xff, (v >>> 24) & 0xff, (v >>> 16) & 0xff, (v >>> 8) & 0xff, v & 0xff);
|
||||
}
|
||||
export default parse;
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
export default /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/i;
|
||||
Generated
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
let getRandomValues;
|
||||
const rnds8 = new Uint8Array(16);
|
||||
export default function rng() {
|
||||
if (!getRandomValues) {
|
||||
if (typeof crypto === 'undefined' || !crypto.getRandomValues) {
|
||||
throw new Error('crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported');
|
||||
}
|
||||
getRandomValues = crypto.getRandomValues.bind(crypto);
|
||||
}
|
||||
return getRandomValues(rnds8);
|
||||
}
|
||||
Generated
Vendored
+70
@@ -0,0 +1,70 @@
|
||||
function f(s, x, y, z) {
|
||||
switch (s) {
|
||||
case 0:
|
||||
return (x & y) ^ (~x & z);
|
||||
case 1:
|
||||
return x ^ y ^ z;
|
||||
case 2:
|
||||
return (x & y) ^ (x & z) ^ (y & z);
|
||||
case 3:
|
||||
return x ^ y ^ z;
|
||||
}
|
||||
}
|
||||
function ROTL(x, n) {
|
||||
return (x << n) | (x >>> (32 - n));
|
||||
}
|
||||
function sha1(bytes) {
|
||||
const K = [0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6];
|
||||
const H = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0];
|
||||
const newBytes = new Uint8Array(bytes.length + 1);
|
||||
newBytes.set(bytes);
|
||||
newBytes[bytes.length] = 0x80;
|
||||
bytes = newBytes;
|
||||
const l = bytes.length / 4 + 2;
|
||||
const N = Math.ceil(l / 16);
|
||||
const M = new Array(N);
|
||||
for (let i = 0; i < N; ++i) {
|
||||
const arr = new Uint32Array(16);
|
||||
for (let j = 0; j < 16; ++j) {
|
||||
arr[j] =
|
||||
(bytes[i * 64 + j * 4] << 24) |
|
||||
(bytes[i * 64 + j * 4 + 1] << 16) |
|
||||
(bytes[i * 64 + j * 4 + 2] << 8) |
|
||||
bytes[i * 64 + j * 4 + 3];
|
||||
}
|
||||
M[i] = arr;
|
||||
}
|
||||
M[N - 1][14] = ((bytes.length - 1) * 8) / Math.pow(2, 32);
|
||||
M[N - 1][14] = Math.floor(M[N - 1][14]);
|
||||
M[N - 1][15] = ((bytes.length - 1) * 8) & 0xffffffff;
|
||||
for (let i = 0; i < N; ++i) {
|
||||
const W = new Uint32Array(80);
|
||||
for (let t = 0; t < 16; ++t) {
|
||||
W[t] = M[i][t];
|
||||
}
|
||||
for (let t = 16; t < 80; ++t) {
|
||||
W[t] = ROTL(W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1);
|
||||
}
|
||||
let a = H[0];
|
||||
let b = H[1];
|
||||
let c = H[2];
|
||||
let d = H[3];
|
||||
let e = H[4];
|
||||
for (let t = 0; t < 80; ++t) {
|
||||
const s = Math.floor(t / 20);
|
||||
const T = (ROTL(a, 5) + f(s, b, c, d) + e + K[s] + W[t]) >>> 0;
|
||||
e = d;
|
||||
d = c;
|
||||
c = ROTL(b, 30) >>> 0;
|
||||
b = a;
|
||||
a = T;
|
||||
}
|
||||
H[0] = (H[0] + a) >>> 0;
|
||||
H[1] = (H[1] + b) >>> 0;
|
||||
H[2] = (H[2] + c) >>> 0;
|
||||
H[3] = (H[3] + d) >>> 0;
|
||||
H[4] = (H[4] + e) >>> 0;
|
||||
}
|
||||
return Uint8Array.of(H[0] >> 24, H[0] >> 16, H[0] >> 8, H[0], H[1] >> 24, H[1] >> 16, H[1] >> 8, H[1], H[2] >> 24, H[2] >> 16, H[2] >> 8, H[2], H[3] >> 24, H[3] >> 16, H[3] >> 8, H[3], H[4] >> 24, H[4] >> 16, H[4] >> 8, H[4]);
|
||||
}
|
||||
export default sha1;
|
||||
Generated
Vendored
+35
@@ -0,0 +1,35 @@
|
||||
import validate from './validate.js';
|
||||
const byteToHex = [];
|
||||
for (let i = 0; i < 256; ++i) {
|
||||
byteToHex.push((i + 0x100).toString(16).slice(1));
|
||||
}
|
||||
export function unsafeStringify(arr, offset = 0) {
|
||||
return (byteToHex[arr[offset + 0]] +
|
||||
byteToHex[arr[offset + 1]] +
|
||||
byteToHex[arr[offset + 2]] +
|
||||
byteToHex[arr[offset + 3]] +
|
||||
'-' +
|
||||
byteToHex[arr[offset + 4]] +
|
||||
byteToHex[arr[offset + 5]] +
|
||||
'-' +
|
||||
byteToHex[arr[offset + 6]] +
|
||||
byteToHex[arr[offset + 7]] +
|
||||
'-' +
|
||||
byteToHex[arr[offset + 8]] +
|
||||
byteToHex[arr[offset + 9]] +
|
||||
'-' +
|
||||
byteToHex[arr[offset + 10]] +
|
||||
byteToHex[arr[offset + 11]] +
|
||||
byteToHex[arr[offset + 12]] +
|
||||
byteToHex[arr[offset + 13]] +
|
||||
byteToHex[arr[offset + 14]] +
|
||||
byteToHex[arr[offset + 15]]).toLowerCase();
|
||||
}
|
||||
function stringify(arr, offset = 0) {
|
||||
const uuid = unsafeStringify(arr, offset);
|
||||
if (!validate(uuid)) {
|
||||
throw TypeError('Stringified UUID is invalid');
|
||||
}
|
||||
return uuid;
|
||||
}
|
||||
export default stringify;
|
||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
||||
export {};
|
||||
Generated
Vendored
+70
@@ -0,0 +1,70 @@
|
||||
import * as assert from 'node:assert/strict';
|
||||
import v1 from './v1.js';
|
||||
import v3 from './v3.js';
|
||||
import v4 from './v4.js';
|
||||
import v5 from './v5.js';
|
||||
import v6 from './v6.js';
|
||||
import v7 from './v7.js';
|
||||
function usage() {
|
||||
console.log('Usage:');
|
||||
console.log(' uuid');
|
||||
console.log(' uuid v1');
|
||||
console.log(' uuid v3 <name> <namespace uuid>');
|
||||
console.log(' uuid v4');
|
||||
console.log(' uuid v5 <name> <namespace uuid>');
|
||||
console.log(' uuid v6');
|
||||
console.log(' uuid v7');
|
||||
console.log(' uuid --help');
|
||||
console.log('\nNote: <namespace uuid> may be "URL" or "DNS" to use the corresponding UUIDs defined by RFC9562');
|
||||
}
|
||||
const args = process.argv.slice(2);
|
||||
if (args.indexOf('--help') >= 0) {
|
||||
usage();
|
||||
process.exit(0);
|
||||
}
|
||||
const version = args.shift() || 'v4';
|
||||
switch (version) {
|
||||
case 'v1':
|
||||
console.log(v1());
|
||||
break;
|
||||
case 'v3': {
|
||||
const name = args.shift();
|
||||
let namespace = args.shift();
|
||||
assert.ok(name != null, 'v3 name not specified');
|
||||
assert.ok(namespace != null, 'v3 namespace not specified');
|
||||
if (namespace === 'URL') {
|
||||
namespace = v3.URL;
|
||||
}
|
||||
if (namespace === 'DNS') {
|
||||
namespace = v3.DNS;
|
||||
}
|
||||
console.log(v3(name, namespace));
|
||||
break;
|
||||
}
|
||||
case 'v4':
|
||||
console.log(v4());
|
||||
break;
|
||||
case 'v5': {
|
||||
const name = args.shift();
|
||||
let namespace = args.shift();
|
||||
assert.ok(name != null, 'v5 name not specified');
|
||||
assert.ok(namespace != null, 'v5 namespace not specified');
|
||||
if (namespace === 'URL') {
|
||||
namespace = v5.URL;
|
||||
}
|
||||
if (namespace === 'DNS') {
|
||||
namespace = v5.DNS;
|
||||
}
|
||||
console.log(v5(name, namespace));
|
||||
break;
|
||||
}
|
||||
case 'v6':
|
||||
console.log(v6());
|
||||
break;
|
||||
case 'v7':
|
||||
console.log(v7());
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
process.exit(1);
|
||||
}
|
||||
Generated
Vendored
+83
@@ -0,0 +1,83 @@
|
||||
import rng from './rng.js';
|
||||
import { unsafeStringify } from './stringify.js';
|
||||
const _state = {};
|
||||
function v1(options, buf, offset) {
|
||||
let bytes;
|
||||
const isV6 = options?._v6 ?? false;
|
||||
if (options) {
|
||||
const optionsKeys = Object.keys(options);
|
||||
if (optionsKeys.length === 1 && optionsKeys[0] === '_v6') {
|
||||
options = undefined;
|
||||
}
|
||||
}
|
||||
if (options) {
|
||||
bytes = v1Bytes(options.random ?? options.rng?.() ?? rng(), options.msecs, options.nsecs, options.clockseq, options.node, buf, offset);
|
||||
}
|
||||
else {
|
||||
const now = Date.now();
|
||||
const rnds = rng();
|
||||
updateV1State(_state, now, rnds);
|
||||
bytes = v1Bytes(rnds, _state.msecs, _state.nsecs, isV6 ? undefined : _state.clockseq, isV6 ? undefined : _state.node, buf, offset);
|
||||
}
|
||||
return buf ?? unsafeStringify(bytes);
|
||||
}
|
||||
export function updateV1State(state, now, rnds) {
|
||||
state.msecs ??= -Infinity;
|
||||
state.nsecs ??= 0;
|
||||
if (now === state.msecs) {
|
||||
state.nsecs++;
|
||||
if (state.nsecs >= 10000) {
|
||||
state.node = undefined;
|
||||
state.nsecs = 0;
|
||||
}
|
||||
}
|
||||
else if (now > state.msecs) {
|
||||
state.nsecs = 0;
|
||||
}
|
||||
else if (now < state.msecs) {
|
||||
state.node = undefined;
|
||||
}
|
||||
if (!state.node) {
|
||||
state.node = rnds.slice(10, 16);
|
||||
state.node[0] |= 0x01;
|
||||
state.clockseq = ((rnds[8] << 8) | rnds[9]) & 0x3fff;
|
||||
}
|
||||
state.msecs = now;
|
||||
return state;
|
||||
}
|
||||
function v1Bytes(rnds, msecs, nsecs, clockseq, node, buf, offset = 0) {
|
||||
if (rnds.length < 16) {
|
||||
throw new Error('Random bytes length must be >= 16');
|
||||
}
|
||||
if (!buf) {
|
||||
buf = new Uint8Array(16);
|
||||
offset = 0;
|
||||
}
|
||||
else {
|
||||
if (offset < 0 || offset + 16 > buf.length) {
|
||||
throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);
|
||||
}
|
||||
}
|
||||
msecs ??= Date.now();
|
||||
nsecs ??= 0;
|
||||
clockseq ??= ((rnds[8] << 8) | rnds[9]) & 0x3fff;
|
||||
node ??= rnds.slice(10, 16);
|
||||
msecs += 12219292800000;
|
||||
const tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;
|
||||
buf[offset++] = (tl >>> 24) & 0xff;
|
||||
buf[offset++] = (tl >>> 16) & 0xff;
|
||||
buf[offset++] = (tl >>> 8) & 0xff;
|
||||
buf[offset++] = tl & 0xff;
|
||||
const tmh = ((msecs / 0x100000000) * 10000) & 0xfffffff;
|
||||
buf[offset++] = (tmh >>> 8) & 0xff;
|
||||
buf[offset++] = tmh & 0xff;
|
||||
buf[offset++] = ((tmh >>> 24) & 0xf) | 0x10;
|
||||
buf[offset++] = (tmh >>> 16) & 0xff;
|
||||
buf[offset++] = (clockseq >>> 8) | 0x80;
|
||||
buf[offset++] = clockseq & 0xff;
|
||||
for (let n = 0; n < 6; ++n) {
|
||||
buf[offset++] = node[n];
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
export default v1;
|
||||
Generated
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
import parse from './parse.js';
|
||||
import { unsafeStringify } from './stringify.js';
|
||||
export default function v1ToV6(uuid) {
|
||||
const v1Bytes = typeof uuid === 'string' ? parse(uuid) : uuid;
|
||||
const v6Bytes = _v1ToV6(v1Bytes);
|
||||
return typeof uuid === 'string' ? unsafeStringify(v6Bytes) : v6Bytes;
|
||||
}
|
||||
function _v1ToV6(v1Bytes) {
|
||||
return Uint8Array.of(((v1Bytes[6] & 0x0f) << 4) | ((v1Bytes[7] >> 4) & 0x0f), ((v1Bytes[7] & 0x0f) << 4) | ((v1Bytes[4] & 0xf0) >> 4), ((v1Bytes[4] & 0x0f) << 4) | ((v1Bytes[5] & 0xf0) >> 4), ((v1Bytes[5] & 0x0f) << 4) | ((v1Bytes[0] & 0xf0) >> 4), ((v1Bytes[0] & 0x0f) << 4) | ((v1Bytes[1] & 0xf0) >> 4), ((v1Bytes[1] & 0x0f) << 4) | ((v1Bytes[2] & 0xf0) >> 4), 0x60 | (v1Bytes[2] & 0x0f), v1Bytes[3], v1Bytes[8], v1Bytes[9], v1Bytes[10], v1Bytes[11], v1Bytes[12], v1Bytes[13], v1Bytes[14], v1Bytes[15]);
|
||||
}
|
||||
Generated
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
import md5 from './md5.js';
|
||||
import v35, { DNS, URL } from './v35.js';
|
||||
export { DNS, URL } from './v35.js';
|
||||
function v3(value, namespace, buf, offset) {
|
||||
return v35(0x30, md5, value, namespace, buf, offset);
|
||||
}
|
||||
v3.DNS = DNS;
|
||||
v3.URL = URL;
|
||||
export default v3;
|
||||
Generated
Vendored
+36
@@ -0,0 +1,36 @@
|
||||
import parse from './parse.js';
|
||||
import { unsafeStringify } from './stringify.js';
|
||||
export function stringToBytes(str) {
|
||||
str = unescape(encodeURIComponent(str));
|
||||
const bytes = new Uint8Array(str.length);
|
||||
for (let i = 0; i < str.length; ++i) {
|
||||
bytes[i] = str.charCodeAt(i);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
export const DNS = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';
|
||||
export const URL = '6ba7b811-9dad-11d1-80b4-00c04fd430c8';
|
||||
export default function v35(version, hash, value, namespace, buf, offset) {
|
||||
const valueBytes = typeof value === 'string' ? stringToBytes(value) : value;
|
||||
const namespaceBytes = typeof namespace === 'string' ? parse(namespace) : namespace;
|
||||
if (typeof namespace === 'string') {
|
||||
namespace = parse(namespace);
|
||||
}
|
||||
if (namespace?.length !== 16) {
|
||||
throw TypeError('Namespace must be array-like (16 iterable integer values, 0-255)');
|
||||
}
|
||||
let bytes = new Uint8Array(16 + valueBytes.length);
|
||||
bytes.set(namespaceBytes);
|
||||
bytes.set(valueBytes, namespaceBytes.length);
|
||||
bytes = hash(bytes);
|
||||
bytes[6] = (bytes[6] & 0x0f) | version;
|
||||
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
||||
if (buf) {
|
||||
offset = offset || 0;
|
||||
for (let i = 0; i < 16; ++i) {
|
||||
buf[offset + i] = bytes[i];
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
return unsafeStringify(bytes);
|
||||
}
|
||||
Generated
Vendored
+30
@@ -0,0 +1,30 @@
|
||||
import native from './native.js';
|
||||
import rng from './rng.js';
|
||||
import { unsafeStringify } from './stringify.js';
|
||||
function _v4(options, buf, offset) {
|
||||
options = options || {};
|
||||
const rnds = options.random ?? options.rng?.() ?? rng();
|
||||
if (rnds.length < 16) {
|
||||
throw new Error('Random bytes length must be >= 16');
|
||||
}
|
||||
rnds[6] = (rnds[6] & 0x0f) | 0x40;
|
||||
rnds[8] = (rnds[8] & 0x3f) | 0x80;
|
||||
if (buf) {
|
||||
offset = offset || 0;
|
||||
if (offset < 0 || offset + 16 > buf.length) {
|
||||
throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);
|
||||
}
|
||||
for (let i = 0; i < 16; ++i) {
|
||||
buf[offset + i] = rnds[i];
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
return unsafeStringify(rnds);
|
||||
}
|
||||
function v4(options, buf, offset) {
|
||||
if (native.randomUUID && !buf && !options) {
|
||||
return native.randomUUID();
|
||||
}
|
||||
return _v4(options, buf, offset);
|
||||
}
|
||||
export default v4;
|
||||
Generated
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
import sha1 from './sha1.js';
|
||||
import v35, { DNS, URL } from './v35.js';
|
||||
export { DNS, URL } from './v35.js';
|
||||
function v5(value, namespace, buf, offset) {
|
||||
return v35(0x50, sha1, value, namespace, buf, offset);
|
||||
}
|
||||
v5.DNS = DNS;
|
||||
v5.URL = URL;
|
||||
export default v5;
|
||||
Generated
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
import { unsafeStringify } from './stringify.js';
|
||||
import v1 from './v1.js';
|
||||
import v1ToV6 from './v1ToV6.js';
|
||||
function v6(options, buf, offset) {
|
||||
options ??= {};
|
||||
offset ??= 0;
|
||||
let bytes = v1({ ...options, _v6: true }, new Uint8Array(16));
|
||||
bytes = v1ToV6(bytes);
|
||||
if (buf) {
|
||||
for (let i = 0; i < 16; i++) {
|
||||
buf[offset + i] = bytes[i];
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
return unsafeStringify(bytes);
|
||||
}
|
||||
export default v6;
|
||||
Generated
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
import parse from './parse.js';
|
||||
import { unsafeStringify } from './stringify.js';
|
||||
export default function v6ToV1(uuid) {
|
||||
const v6Bytes = typeof uuid === 'string' ? parse(uuid) : uuid;
|
||||
const v1Bytes = _v6ToV1(v6Bytes);
|
||||
return typeof uuid === 'string' ? unsafeStringify(v1Bytes) : v1Bytes;
|
||||
}
|
||||
function _v6ToV1(v6Bytes) {
|
||||
return Uint8Array.of(((v6Bytes[3] & 0x0f) << 4) | ((v6Bytes[4] >> 4) & 0x0f), ((v6Bytes[4] & 0x0f) << 4) | ((v6Bytes[5] & 0xf0) >> 4), ((v6Bytes[5] & 0x0f) << 4) | (v6Bytes[6] & 0x0f), v6Bytes[7], ((v6Bytes[1] & 0x0f) << 4) | ((v6Bytes[2] & 0xf0) >> 4), ((v6Bytes[2] & 0x0f) << 4) | ((v6Bytes[3] & 0xf0) >> 4), 0x10 | ((v6Bytes[0] & 0xf0) >> 4), ((v6Bytes[0] & 0x0f) << 4) | ((v6Bytes[1] & 0xf0) >> 4), v6Bytes[8], v6Bytes[9], v6Bytes[10], v6Bytes[11], v6Bytes[12], v6Bytes[13], v6Bytes[14], v6Bytes[15]);
|
||||
}
|
||||
Generated
Vendored
+65
@@ -0,0 +1,65 @@
|
||||
import rng from './rng.js';
|
||||
import { unsafeStringify } from './stringify.js';
|
||||
const _state = {};
|
||||
function v7(options, buf, offset) {
|
||||
let bytes;
|
||||
if (options) {
|
||||
bytes = v7Bytes(options.random ?? options.rng?.() ?? rng(), options.msecs, options.seq, buf, offset);
|
||||
}
|
||||
else {
|
||||
const now = Date.now();
|
||||
const rnds = rng();
|
||||
updateV7State(_state, now, rnds);
|
||||
bytes = v7Bytes(rnds, _state.msecs, _state.seq, buf, offset);
|
||||
}
|
||||
return buf ?? unsafeStringify(bytes);
|
||||
}
|
||||
export function updateV7State(state, now, rnds) {
|
||||
state.msecs ??= -Infinity;
|
||||
state.seq ??= 0;
|
||||
if (now > state.msecs) {
|
||||
state.seq = (rnds[6] << 23) | (rnds[7] << 16) | (rnds[8] << 8) | rnds[9];
|
||||
state.msecs = now;
|
||||
}
|
||||
else {
|
||||
state.seq = (state.seq + 1) | 0;
|
||||
if (state.seq === 0) {
|
||||
state.msecs++;
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
||||
function v7Bytes(rnds, msecs, seq, buf, offset = 0) {
|
||||
if (rnds.length < 16) {
|
||||
throw new Error('Random bytes length must be >= 16');
|
||||
}
|
||||
if (!buf) {
|
||||
buf = new Uint8Array(16);
|
||||
offset = 0;
|
||||
}
|
||||
else {
|
||||
if (offset < 0 || offset + 16 > buf.length) {
|
||||
throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);
|
||||
}
|
||||
}
|
||||
msecs ??= Date.now();
|
||||
seq ??= ((rnds[6] * 0x7f) << 24) | (rnds[7] << 16) | (rnds[8] << 8) | rnds[9];
|
||||
buf[offset++] = (msecs / 0x10000000000) & 0xff;
|
||||
buf[offset++] = (msecs / 0x100000000) & 0xff;
|
||||
buf[offset++] = (msecs / 0x1000000) & 0xff;
|
||||
buf[offset++] = (msecs / 0x10000) & 0xff;
|
||||
buf[offset++] = (msecs / 0x100) & 0xff;
|
||||
buf[offset++] = msecs & 0xff;
|
||||
buf[offset++] = 0x70 | ((seq >>> 28) & 0x0f);
|
||||
buf[offset++] = (seq >>> 20) & 0xff;
|
||||
buf[offset++] = 0x80 | ((seq >>> 14) & 0x3f);
|
||||
buf[offset++] = (seq >>> 6) & 0xff;
|
||||
buf[offset++] = ((seq << 2) & 0xff) | (rnds[10] & 0x03);
|
||||
buf[offset++] = rnds[11];
|
||||
buf[offset++] = rnds[12];
|
||||
buf[offset++] = rnds[13];
|
||||
buf[offset++] = rnds[14];
|
||||
buf[offset++] = rnds[15];
|
||||
return buf;
|
||||
}
|
||||
export default v7;
|
||||
Generated
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
import REGEX from './regex.js';
|
||||
function validate(uuid) {
|
||||
return typeof uuid === 'string' && REGEX.test(uuid);
|
||||
}
|
||||
export default validate;
|
||||
Generated
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
import validate from './validate.js';
|
||||
function version(uuid) {
|
||||
if (!validate(uuid)) {
|
||||
throw TypeError('Invalid UUID');
|
||||
}
|
||||
return parseInt(uuid.slice(14, 15), 16);
|
||||
}
|
||||
export default version;
|
||||
Generated
Vendored
+119
@@ -0,0 +1,119 @@
|
||||
{
|
||||
"name": "uuid",
|
||||
"version": "13.0.0",
|
||||
"description": "RFC9562 UUIDs",
|
||||
"type": "module",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa",
|
||||
"https://github.com/sponsors/ctavan"
|
||||
],
|
||||
"commitlint": {
|
||||
"extends": [
|
||||
"@commitlint/config-conventional"
|
||||
]
|
||||
},
|
||||
"keywords": [
|
||||
"uuid",
|
||||
"guid",
|
||||
"rfc4122",
|
||||
"rfc9562"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"uuid": "./dist-node/bin/uuid"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"node": "./dist-node/index.js",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"dist-node",
|
||||
"!**/test"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "7.27.1",
|
||||
"@commitlint/cli": "19.8.0",
|
||||
"@commitlint/config-conventional": "19.8.0",
|
||||
"@eslint/js": "9.26.0",
|
||||
"bundlewatch": "0.4.1",
|
||||
"commander": "13.1.0",
|
||||
"eslint": "9.26.0",
|
||||
"eslint-config-prettier": "10.1.2",
|
||||
"eslint-plugin-prettier": "5.4.0",
|
||||
"globals": "16.0.0",
|
||||
"husky": "9.1.7",
|
||||
"jest": "29.7.0",
|
||||
"lint-staged": "15.5.2",
|
||||
"neostandard": "0.12.1",
|
||||
"npm-run-all": "4.1.5",
|
||||
"prettier": "3.5.3",
|
||||
"release-please": "17.0.0",
|
||||
"runmd": "1.4.1",
|
||||
"standard-version": "9.5.0",
|
||||
"typescript": "5.2.2",
|
||||
"typescript-eslint": "8.32.0"
|
||||
},
|
||||
"optionalDevDependencies": {
|
||||
"@wdio/browserstack-service": "9.2.1",
|
||||
"@wdio/cli": "9.2.1",
|
||||
"@wdio/jasmine-framework": "9.2.1",
|
||||
"@wdio/local-runner": "9.2.1",
|
||||
"@wdio/spec-reporter": "9.1.3",
|
||||
"@wdio/static-server-service": "9.1.3"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "./scripts/build.sh",
|
||||
"build:watch": "tsc --watch -p tsconfig.json",
|
||||
"bundlewatch": "npm run pretest:browser && bundlewatch --config bundlewatch.config.json",
|
||||
"docs:diff": "npm run docs && git diff --quiet -I \"[0-9a-f-]{36}\" README.md",
|
||||
"docs": "npm run build && npx runmd --output=README.md README_js.md",
|
||||
"eslint:check": "eslint src/ test/ examples/ *.[jt]s",
|
||||
"eslint:fix": "eslint --fix src/ test/ examples/ *.[jt]s",
|
||||
"examples:browser:rollup:build": "cd examples/browser-rollup && npm run build",
|
||||
"examples:browser:webpack:build": "cd examples/browser-webpack && npm run build",
|
||||
"examples:node:esmodules:test": "cd examples/node-esmodules && npm test",
|
||||
"examples:node:jest:test": "cd examples/node-jest && npm test",
|
||||
"examples:node:typescript:test": "cd examples/typescript && npm test",
|
||||
"lint": "npm run eslint:check && npm run prettier:check",
|
||||
"md": "runmd --watch --output=README.md README_js.md",
|
||||
"prepack": "npm run build -- --no-pack",
|
||||
"prepare": "husky",
|
||||
"prepublishOnly": "npm run build",
|
||||
"pretest:benchmark": "npm run build",
|
||||
"pretest:browser": "./scripts/iodd && npm run build && npm-run-all --parallel examples:browser:**",
|
||||
"pretest:node": "npm run build",
|
||||
"pretest": "npm run build",
|
||||
"prettier:check": "prettier --check .",
|
||||
"prettier:fix": "prettier --write .",
|
||||
"release": "standard-version --no-verify",
|
||||
"test:benchmark": "cd examples/benchmark && npm test",
|
||||
"test:browser": "wdio run ./wdio.conf.js",
|
||||
"test:node": "npm-run-all --parallel examples:node:**",
|
||||
"test:watch": "node --test --enable-source-maps --watch dist-node/test/*.js",
|
||||
"test": "node --test --enable-source-maps dist-node/test/*.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/uuidjs/uuid.git"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*": [
|
||||
"prettier --no-error-on-unmatched-pattern --write"
|
||||
],
|
||||
"*.{js,jsx}": [
|
||||
"eslint --no-error-on-unmatched-pattern --fix"
|
||||
]
|
||||
},
|
||||
"standard-version": {
|
||||
"scripts": {
|
||||
"postchangelog": "prettier --write CHANGELOG.md"
|
||||
}
|
||||
},
|
||||
"packageManager": "npm@11.3.0"
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.CHOICE_WORKSPACE_DESC = exports.CHOICE_WORKSPACE_LABEL = exports.CHOICE_GLOBAL_DESC = exports.CHOICE_GLOBAL_LABEL = exports.PROMPT_CONFIRM_OVERWRITE = exports.PROMPT_CONFIRM_DELETE = exports.PROMPT_SELECT_PATTERN = exports.PROMPT_SELECT_SCOPE = exports.PROMPT_PATTERN_NAME_PLACEHOLDER = exports.PROMPT_PATTERN_NAME = exports.INFO_PATTERN_DELETED = exports.INFO_PATTERN_SAVED = exports.INFO_PATTERN_LOADED = exports.INFO_NO_PATTERNS = exports.ERR_MANAGING_PATTERNS = exports.ERR_LOADING_PATTERN = exports.ERR_SAVING_PATTERN = exports.ERR_PATTERN_NAME_EMPTY = exports.ERR_NO_SEARCH_STATE = exports.UI_MANAGE_TITLE = exports.UI_SECTION_USER = exports.UI_SECTION_WORKSPACE = exports.UI_NO_PATTERNS_FOUND = exports.UI_NO_PATTERNS = exports.MSG_TYPE_SAVE = exports.MSG_TYPE_LOAD = exports.MSG_TYPE_DELETE = exports.MSG_TYPE_PATTERNS = exports.MSG_TYPE_READY = exports.COMMAND_MANAGE = exports.SCOPE_WORKSPACE = exports.SCOPE_GLOBAL = exports.CONFIG_KEY_WORKSPACE_PATTERNS = exports.CONFIG_KEY_USER_PATTERNS = exports.CONFIG_NAMESPACE = void 0;
|
||||
exports.CONFIG_NAMESPACE = 'patternStore';
|
||||
exports.CONFIG_KEY_USER_PATTERNS = 'savedPatterns';
|
||||
exports.CONFIG_KEY_WORKSPACE_PATTERNS = 'workspacePatterns';
|
||||
exports.SCOPE_GLOBAL = 'global';
|
||||
exports.SCOPE_WORKSPACE = 'workspace';
|
||||
exports.COMMAND_MANAGE = 'patternStore.manage';
|
||||
exports.MSG_TYPE_READY = 'ready';
|
||||
exports.MSG_TYPE_PATTERNS = 'patterns';
|
||||
exports.MSG_TYPE_DELETE = 'delete';
|
||||
exports.MSG_TYPE_LOAD = 'load';
|
||||
exports.MSG_TYPE_SAVE = 'save';
|
||||
exports.UI_NO_PATTERNS = 'No patterns yet';
|
||||
exports.UI_NO_PATTERNS_FOUND = 'No patterns found';
|
||||
exports.UI_SECTION_WORKSPACE = 'Workspace';
|
||||
exports.UI_SECTION_USER = 'User';
|
||||
exports.UI_MANAGE_TITLE = 'Manage Patterns';
|
||||
exports.ERR_NO_SEARCH_STATE = 'Could not read current search state';
|
||||
exports.ERR_PATTERN_NAME_EMPTY = 'Pattern name cannot be empty';
|
||||
exports.ERR_SAVING_PATTERN = 'Error saving pattern';
|
||||
exports.ERR_LOADING_PATTERN = 'Error loading pattern';
|
||||
exports.ERR_MANAGING_PATTERNS = 'Error managing patterns';
|
||||
exports.INFO_NO_PATTERNS = 'No saved patterns found. Use "PatternStore: Save Pattern" to create one.';
|
||||
exports.INFO_PATTERN_LOADED = 'Loaded pattern';
|
||||
exports.INFO_PATTERN_SAVED = 'Pattern saved';
|
||||
exports.INFO_PATTERN_DELETED = 'Pattern deleted';
|
||||
exports.PROMPT_PATTERN_NAME = 'Enter a name for this pattern';
|
||||
exports.PROMPT_PATTERN_NAME_PLACEHOLDER = 'e.g., "Replace quotes with angles"';
|
||||
exports.PROMPT_SELECT_SCOPE = 'Choose storage scope';
|
||||
exports.PROMPT_SELECT_PATTERN = 'Select a pattern to load';
|
||||
exports.PROMPT_CONFIRM_DELETE = 'Delete pattern';
|
||||
exports.PROMPT_CONFIRM_OVERWRITE = 'already exists. Overwrite?';
|
||||
exports.CHOICE_GLOBAL_LABEL = 'Global';
|
||||
exports.CHOICE_GLOBAL_DESC = 'Available in all workspaces';
|
||||
exports.CHOICE_WORKSPACE_LABEL = 'Workspace';
|
||||
exports.CHOICE_WORKSPACE_DESC = 'Only for this workspace';
|
||||
//# sourceMappingURL=constants.js.map
|
||||
@@ -0,0 +1,127 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.activate = activate;
|
||||
exports.deactivate = deactivate;
|
||||
const vscode = __importStar(require("vscode"));
|
||||
const storage = __importStar(require("./storage"));
|
||||
const searchCtx = __importStar(require("./searchCtx"));
|
||||
const WebviewManager_1 = require("./webview/WebviewManager");
|
||||
/**
|
||||
* Called when the extension is activated
|
||||
*/
|
||||
function activate(context) {
|
||||
console.log('PatternStore extension is now active!');
|
||||
// Register command: Manage Patterns
|
||||
const manageCommand = vscode.commands.registerCommand('patternStore.manage', async () => {
|
||||
await managePatternsCommand(context.extensionUri);
|
||||
});
|
||||
context.subscriptions.push(manageCommand);
|
||||
}
|
||||
/**
|
||||
* Called when the extension is deactivated
|
||||
*/
|
||||
function deactivate() {
|
||||
console.log('PatternStore extension is now deactivated');
|
||||
}
|
||||
/**
|
||||
* Command: Save the current search state as a pattern
|
||||
*/
|
||||
async function savePatternCommand() {
|
||||
try {
|
||||
vscode.window.showInformationMessage('PatternStore: Save Pattern command executed!');
|
||||
// Get current search state
|
||||
const searchState = await searchCtx.getCurrentSearchState();
|
||||
if (!searchState) {
|
||||
vscode.window.showErrorMessage('Could not read current search state');
|
||||
return;
|
||||
}
|
||||
// Prompt for pattern name
|
||||
const label = await vscode.window.showInputBox({
|
||||
prompt: 'Enter a name for this pattern',
|
||||
placeHolder: 'e.g., "Replace quotes with angles"',
|
||||
validateInput: (value) => {
|
||||
if (!value || value.trim().length === 0) {
|
||||
return 'Pattern name cannot be empty';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
if (!label) {
|
||||
return; // User cancelled
|
||||
}
|
||||
// Ask for scope
|
||||
const scopeChoice = await vscode.window.showQuickPick([
|
||||
{ label: 'Global', description: 'Available in all workspaces', value: 'global' },
|
||||
{ label: 'Workspace', description: 'Only for this workspace', value: 'workspace' }
|
||||
], { placeHolder: 'Choose storage scope' });
|
||||
if (!scopeChoice) {
|
||||
return; // User cancelled
|
||||
}
|
||||
// Create pattern object
|
||||
const pattern = {
|
||||
id: 'new', // Temporary ID for new patterns
|
||||
label: label.trim(),
|
||||
find: searchState.find || '',
|
||||
replace: searchState.replace || '',
|
||||
flags: searchState.flags || {
|
||||
isRegex: false,
|
||||
isCaseSensitive: false,
|
||||
matchWholeWord: false,
|
||||
isMultiline: false
|
||||
},
|
||||
scope: scopeChoice.value
|
||||
};
|
||||
// Check if pattern exists (will check when we implement storage)
|
||||
const exists = storage.patternExists(pattern.label, pattern.scope);
|
||||
if (exists) {
|
||||
const overwrite = await vscode.window.showWarningMessage(`A pattern named "${pattern.label}" already exists. Overwrite?`, 'Yes', 'No');
|
||||
if (overwrite !== 'Yes') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Save pattern
|
||||
await storage.savePattern(pattern);
|
||||
}
|
||||
catch (error) {
|
||||
vscode.window.showErrorMessage(`Error saving pattern: ${error}`);
|
||||
}
|
||||
}
|
||||
async function managePatternsCommand(extensionUri) {
|
||||
console.log('Manage Patterns command pressed - opening webview');
|
||||
// For now, just open global patterns. We'll add separate commands later
|
||||
WebviewManager_1.WebviewManager.show(extensionUri, 'global');
|
||||
}
|
||||
//# sourceMappingURL=extension.js.map
|
||||
@@ -0,0 +1,129 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getCurrentSearchState = getCurrentSearchState;
|
||||
exports.loadPatternIntoSearch = loadPatternIntoSearch;
|
||||
const vscode = __importStar(require("vscode"));
|
||||
/**
|
||||
* Get the current search state from the Search panel
|
||||
* Returns null if unable to get the state
|
||||
*
|
||||
* NOTE: VS Code doesn't expose search panel state via API.
|
||||
* This function tries to use editor selection as fallback.
|
||||
*/
|
||||
async function getCurrentSearchState() {
|
||||
// Try to get selected text from active editor
|
||||
const editor = vscode.window.activeTextEditor;
|
||||
const selection = editor?.document.getText(editor.selection);
|
||||
if (selection && selection.length > 0) {
|
||||
// Use selection as find text
|
||||
return {
|
||||
find: selection,
|
||||
replace: '',
|
||||
flags: {
|
||||
isRegex: false,
|
||||
isCaseSensitive: false,
|
||||
matchWholeWord: false,
|
||||
isMultiline: false
|
||||
}
|
||||
};
|
||||
}
|
||||
// No selection available - return null
|
||||
// User will need to enter manually
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Resolve placeholders like ${prompt:varname} in multiple strings
|
||||
* Prompts once per unique placeholder name and reuses the value
|
||||
*/
|
||||
async function resolvePlaceholdersMultiple(texts) {
|
||||
const RE = /\$\{prompt:([^}]+)\}/g;
|
||||
// Collect all unique placeholder names across all texts
|
||||
const allNames = new Set();
|
||||
for (const text of texts) {
|
||||
Array.from(text.matchAll(RE), m => m[1]).forEach(name => allNames.add(name));
|
||||
}
|
||||
const vals = {};
|
||||
// Prompt once for each unique placeholder
|
||||
for (const name of allNames) {
|
||||
const value = await vscode.window.showInputBox({
|
||||
prompt: `Value for ${name}`,
|
||||
placeHolder: name
|
||||
});
|
||||
if (value === undefined) {
|
||||
return undefined; // User cancelled
|
||||
}
|
||||
vals[name] = value;
|
||||
}
|
||||
// Replace placeholders in all texts using the collected values
|
||||
return texts.map(text => text.replace(RE, (_, name) => vals[name] ?? ""));
|
||||
}
|
||||
/**
|
||||
* Resolve placeholders like ${prompt:varname} in a string (single text version)
|
||||
*/
|
||||
async function resolvePlaceholders(text) {
|
||||
const result = await resolvePlaceholdersMultiple([text]);
|
||||
return result ? result[0] : undefined;
|
||||
}
|
||||
async function loadPatternIntoSearch(pattern) {
|
||||
try {
|
||||
const hasReplace = pattern.replace && pattern.replace.trim().length > 0;
|
||||
const textsToResolve = hasReplace ? [pattern.find, pattern.replace] : [pattern.find];
|
||||
const resolved = await resolvePlaceholdersMultiple(textsToResolve);
|
||||
if (!resolved) {
|
||||
return; // User cancelled
|
||||
}
|
||||
const find = resolved[0];
|
||||
const replace = hasReplace ? resolved[1] : '';
|
||||
const commandArgs = {
|
||||
query: find,
|
||||
replace: replace,
|
||||
triggerSearch: false, // Don't auto-trigger search
|
||||
isRegex: pattern.flags.isRegex,
|
||||
isCaseSensitive: pattern.flags.isCaseSensitive,
|
||||
matchWholeWord: pattern.flags.matchWholeWord,
|
||||
preserveCase: false,
|
||||
filesToInclude: pattern.filesToInclude || '',
|
||||
filesToExclude: pattern.filesToExclude || ''
|
||||
};
|
||||
await vscode.commands.executeCommand("workbench.action.findInFiles", commandArgs);
|
||||
vscode.window.showInformationMessage(`✅ Loaded pattern: "${pattern.label}"`);
|
||||
}
|
||||
catch (error) {
|
||||
vscode.window.showErrorMessage(`Failed to load pattern: ${error}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=searchCtx.js.map
|
||||
@@ -0,0 +1,158 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getAllPatterns = getAllPatterns;
|
||||
exports.savePattern = savePattern;
|
||||
exports.deletePattern = deletePattern;
|
||||
exports.renamePattern = renamePattern;
|
||||
exports.patternExists = patternExists;
|
||||
const vscode = __importStar(require("vscode"));
|
||||
const C = __importStar(require("./constants"));
|
||||
/**
|
||||
* Get patterns from settings and add runtime IDs
|
||||
*/
|
||||
function getPatternsByScope(scope) {
|
||||
const config = vscode.workspace.getConfiguration(C.CONFIG_NAMESPACE);
|
||||
const configKey = scope === 'global' ? C.CONFIG_KEY_USER_PATTERNS : C.CONFIG_KEY_WORKSPACE_PATTERNS;
|
||||
let storedPatterns;
|
||||
if (scope === 'global') {
|
||||
storedPatterns = config.inspect(C.CONFIG_KEY_USER_PATTERNS)?.globalValue || [];
|
||||
}
|
||||
else {
|
||||
storedPatterns = config.inspect(C.CONFIG_KEY_WORKSPACE_PATTERNS)?.workspaceValue || [];
|
||||
}
|
||||
// Add runtime IDs and ensure scope is set
|
||||
return storedPatterns.map((p, index) => ({
|
||||
...p,
|
||||
id: `${scope}-${index}`, // Index-based runtime ID
|
||||
scope: scope
|
||||
}));
|
||||
}
|
||||
function getAllPatterns() {
|
||||
const workspace = getPatternsByScope('workspace');
|
||||
const global = getPatternsByScope('global');
|
||||
// Return all patterns - duplicates across scopes are allowed
|
||||
return [...workspace, ...global];
|
||||
}
|
||||
async function savePattern(pattern) {
|
||||
const config = vscode.workspace.getConfiguration(C.CONFIG_NAMESPACE);
|
||||
const configKey = pattern.scope === 'global' ? C.CONFIG_KEY_USER_PATTERNS : C.CONFIG_KEY_WORKSPACE_PATTERNS;
|
||||
const target = pattern.scope === 'global'
|
||||
? vscode.ConfigurationTarget.Global
|
||||
: vscode.ConfigurationTarget.Workspace;
|
||||
// Read fresh patterns with runtime IDs
|
||||
const patternsWithIds = getPatternsByScope(pattern.scope);
|
||||
// Find existing pattern by ID (runtime ID tells us the index it came from)
|
||||
const existingIndex = patternsWithIds.findIndex(p => p.id === pattern.id);
|
||||
const isUpdate = existingIndex !== -1;
|
||||
// Strip runtime ID before saving
|
||||
const { id, ...storedPattern } = pattern;
|
||||
// Create array of stored patterns (without IDs)
|
||||
let storedPatterns = patternsWithIds.map(p => {
|
||||
const { id, ...stored } = p;
|
||||
return stored;
|
||||
});
|
||||
if (isUpdate) {
|
||||
// Update existing pattern IN PLACE (maintains order)
|
||||
storedPatterns[existingIndex] = storedPattern;
|
||||
}
|
||||
else {
|
||||
// New pattern - add at beginning
|
||||
storedPatterns.unshift(storedPattern);
|
||||
}
|
||||
// Save to settings (without IDs)
|
||||
await config.update(configKey, storedPatterns, target);
|
||||
// Only show message if it's a new pattern (not an update)
|
||||
if (!isUpdate) {
|
||||
vscode.window.showInformationMessage(`✅ Pattern "${pattern.label}" saved to ${pattern.scope} settings`);
|
||||
}
|
||||
}
|
||||
async function deletePattern(id, scope) {
|
||||
const config = vscode.workspace.getConfiguration(C.CONFIG_NAMESPACE);
|
||||
const configKey = scope === 'global' ? C.CONFIG_KEY_USER_PATTERNS : C.CONFIG_KEY_WORKSPACE_PATTERNS;
|
||||
const target = scope === 'global'
|
||||
? vscode.ConfigurationTarget.Global
|
||||
: vscode.ConfigurationTarget.Workspace;
|
||||
// Read fresh patterns with runtime IDs
|
||||
const patternsWithIds = getPatternsByScope(scope);
|
||||
// Find pattern by runtime ID
|
||||
const patternToDelete = patternsWithIds.find(p => p.id === id);
|
||||
if (!patternToDelete) {
|
||||
vscode.window.showWarningMessage(`Pattern not found in ${scope} settings`);
|
||||
return;
|
||||
}
|
||||
// Filter out the pattern and strip IDs
|
||||
const storedPatterns = patternsWithIds
|
||||
.filter(p => p.id !== id)
|
||||
.map(p => {
|
||||
const { id, ...stored } = p;
|
||||
return stored;
|
||||
});
|
||||
// Save updated list (without IDs)
|
||||
await config.update(configKey, storedPatterns, target);
|
||||
vscode.window.showInformationMessage(`🗑️ Pattern "${patternToDelete.label}" deleted from ${scope} settings`);
|
||||
}
|
||||
async function renamePattern(id, newLabel, scope) {
|
||||
const config = vscode.workspace.getConfiguration(C.CONFIG_NAMESPACE);
|
||||
const configKey = scope === 'global' ? C.CONFIG_KEY_USER_PATTERNS : C.CONFIG_KEY_WORKSPACE_PATTERNS;
|
||||
const target = scope === 'global'
|
||||
? vscode.ConfigurationTarget.Global
|
||||
: vscode.ConfigurationTarget.Workspace;
|
||||
// Read fresh patterns with runtime IDs
|
||||
const patternsWithIds = getPatternsByScope(scope);
|
||||
// Find pattern by runtime ID
|
||||
const pattern = patternsWithIds.find(p => p.id === id);
|
||||
if (!pattern) {
|
||||
vscode.window.showWarningMessage(`Pattern not found in ${scope} settings`);
|
||||
return;
|
||||
}
|
||||
const oldLabel = pattern.label;
|
||||
// Update label and strip IDs for saving
|
||||
const storedPatterns = patternsWithIds.map(p => {
|
||||
const { id: _id, ...stored } = p;
|
||||
if (p.id === id) {
|
||||
return { ...stored, label: newLabel };
|
||||
}
|
||||
return stored;
|
||||
});
|
||||
// Save updated list (without IDs)
|
||||
await config.update(configKey, storedPatterns, target);
|
||||
vscode.window.showInformationMessage(`✏️ Pattern renamed from "${oldLabel}" to "${newLabel}" in ${scope} settings`);
|
||||
}
|
||||
function patternExists(label, scope) {
|
||||
const patterns = getPatternsByScope(scope);
|
||||
return patterns.some(p => p.label === label);
|
||||
}
|
||||
//# sourceMappingURL=storage.js.map
|
||||
@@ -0,0 +1,3 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=types.js.map
|
||||
+266
@@ -0,0 +1,266 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.WebviewManager = void 0;
|
||||
const vscode = __importStar(require("vscode"));
|
||||
const path = __importStar(require("path"));
|
||||
const fs = __importStar(require("fs"));
|
||||
const storage = __importStar(require("../storage"));
|
||||
const searchCtx = __importStar(require("../searchCtx"));
|
||||
/**
|
||||
* Manages the webview panel for pattern management
|
||||
*/
|
||||
class WebviewManager {
|
||||
/**
|
||||
* Create or show the webview panel
|
||||
*/
|
||||
static show(extensionUri, scope) {
|
||||
const column = vscode.window.activeTextEditor
|
||||
? vscode.window.activeTextEditor.viewColumn
|
||||
: undefined;
|
||||
// If we already have a panel, show it
|
||||
if (WebviewManager.currentPanel) {
|
||||
WebviewManager.currentPanel.panel.reveal(column);
|
||||
return;
|
||||
}
|
||||
const panel = vscode.window.createWebviewPanel('patternStoreManage', 'PatternStore', column || vscode.ViewColumn.One, {
|
||||
enableScripts: true,
|
||||
retainContextWhenHidden: true,
|
||||
});
|
||||
WebviewManager.currentPanel = new WebviewManager(panel, extensionUri, scope);
|
||||
}
|
||||
constructor(panel, extensionUri, scope) {
|
||||
this.disposables = [];
|
||||
this.isInternalSave = false;
|
||||
this.panel = panel;
|
||||
this.extensionUri = extensionUri;
|
||||
this.scope = scope;
|
||||
// Set initial HTML content
|
||||
this.panel.webview.html = this.getHtmlContent();
|
||||
// Listen for when the panel is disposed
|
||||
this.panel.onDidDispose(() => this.dispose(), null, this.disposables);
|
||||
// Handle messages from the webview
|
||||
this.panel.webview.onDidReceiveMessage(message => {
|
||||
this.handleMessage(message);
|
||||
}, null, this.disposables);
|
||||
// Watch for external config changes
|
||||
vscode.workspace.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration('patternStore')) {
|
||||
this.handleExternalConfigChange();
|
||||
}
|
||||
}, null, this.disposables);
|
||||
this.sendPatterns();
|
||||
}
|
||||
sendPatterns(selectId, selectScope, clearSelection) {
|
||||
const allPatterns = storage.getAllPatterns();
|
||||
const workspacePatterns = allPatterns.filter(p => p.scope === 'workspace');
|
||||
const userPatterns = allPatterns.filter(p => p.scope === 'global');
|
||||
const message = {
|
||||
type: 'patterns',
|
||||
workspace: workspacePatterns,
|
||||
user: userPatterns
|
||||
};
|
||||
if (selectId && selectScope) {
|
||||
message.selectPattern = {
|
||||
id: selectId,
|
||||
scope: selectScope
|
||||
};
|
||||
}
|
||||
if (clearSelection) {
|
||||
message.clearSelection = true;
|
||||
}
|
||||
this.panel.webview.postMessage(message);
|
||||
}
|
||||
/**
|
||||
* Handle external configuration changes (e.g., manual edits to settings.json)
|
||||
*/
|
||||
handleExternalConfigChange() {
|
||||
// Don't show notification if this was an internal save
|
||||
if (this.isInternalSave) {
|
||||
this.isInternalSave = false;
|
||||
return;
|
||||
}
|
||||
// Show warning to user for actual external changes
|
||||
vscode.window.showWarningMessage('Pattern settings changed externally. Webview reloaded.');
|
||||
// Force full reload - this clears any dirty state and rebuilds runtime IDs
|
||||
// Pass clearSelection flag to force the webview to clear the details view
|
||||
this.sendPatterns(undefined, undefined, true);
|
||||
}
|
||||
async handleMessage(message) {
|
||||
console.log('Received message from webview:', message);
|
||||
switch (message.type) {
|
||||
case 'ready':
|
||||
// Webview is ready, send patterns
|
||||
this.sendPatterns();
|
||||
break;
|
||||
case 'delete':
|
||||
await this.handleDelete(message.id, message.scope);
|
||||
break;
|
||||
case 'load':
|
||||
await this.handleLoad(message.id, message.scope, message.pattern);
|
||||
break;
|
||||
case 'create':
|
||||
await this.handleCreate(message.scope);
|
||||
break;
|
||||
case 'save':
|
||||
await this.handleSave(message.pattern);
|
||||
break;
|
||||
}
|
||||
}
|
||||
async handleCreate(scope) {
|
||||
// Generate unique label for new pattern
|
||||
const existingPatterns = storage.getAllPatterns().filter(p => p.scope === scope);
|
||||
const baseName = 'New Pattern';
|
||||
let label = baseName;
|
||||
let counter = 1;
|
||||
while (existingPatterns.some(p => p.label === label)) {
|
||||
counter++;
|
||||
label = `${baseName} ${counter}`;
|
||||
}
|
||||
const newPattern = {
|
||||
id: 'new', // Temporary ID for new patterns
|
||||
label: label,
|
||||
find: '',
|
||||
replace: '',
|
||||
flags: {
|
||||
isRegex: false,
|
||||
isCaseSensitive: false,
|
||||
matchWholeWord: false,
|
||||
isMultiline: false
|
||||
},
|
||||
scope: scope
|
||||
};
|
||||
// Mark as internal save to suppress external change notification
|
||||
this.isInternalSave = true;
|
||||
await storage.savePattern(newPattern);
|
||||
// New pattern will be at index 0 (savePattern uses unshift)
|
||||
this.sendPatterns(`${scope}-0`, scope);
|
||||
}
|
||||
async handleSave(patternData) {
|
||||
const { id, label, find, replace, flags, filesToInclude, filesToExclude, scope } = patternData;
|
||||
const pattern = {
|
||||
id: id, // Preserve existing ID
|
||||
label: label,
|
||||
find: find,
|
||||
replace: replace,
|
||||
flags: flags,
|
||||
filesToInclude: filesToInclude,
|
||||
filesToExclude: filesToExclude,
|
||||
scope: scope
|
||||
};
|
||||
// Mark as internal save to suppress external change notification
|
||||
this.isInternalSave = true;
|
||||
await storage.savePattern(pattern);
|
||||
this.sendPatterns(id, scope);
|
||||
}
|
||||
async handleDelete(id, scope) {
|
||||
// Find pattern to get label for confirmation
|
||||
const allPatterns = storage.getAllPatterns();
|
||||
const pattern = allPatterns.find(p => p.id === id && p.scope === scope);
|
||||
if (!pattern) {
|
||||
vscode.window.showErrorMessage(`Pattern not found`);
|
||||
return;
|
||||
}
|
||||
// Confirm deletion
|
||||
const confirm = await vscode.window.showWarningMessage(`Delete pattern "${pattern.label}"?`, { modal: true }, 'Delete');
|
||||
if (confirm === 'Delete') {
|
||||
// Mark as internal save to suppress external change notification
|
||||
this.isInternalSave = true;
|
||||
await storage.deletePattern(id, scope);
|
||||
// Refresh pattern list
|
||||
this.sendPatterns();
|
||||
}
|
||||
}
|
||||
async handleLoad(id, scope, providedPattern) {
|
||||
let pattern = providedPattern;
|
||||
// If no pattern was provided, look it up
|
||||
if (!pattern) {
|
||||
const allPatterns = storage.getAllPatterns();
|
||||
pattern = allPatterns.find(p => p.id === id && p.scope === scope);
|
||||
if (!pattern) {
|
||||
vscode.window.showErrorMessage(`Pattern "${id}" not found`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
await searchCtx.loadPatternIntoSearch(pattern);
|
||||
}
|
||||
getHtmlContent() {
|
||||
// Generate nonce for CSP
|
||||
const nonce = this.getNonce();
|
||||
// Get URIs for webview resources
|
||||
const cspSource = this.panel.webview.cspSource;
|
||||
// Load HTML, CSS, and JS from files
|
||||
const htmlPath = path.join(this.extensionUri.fsPath, 'src', 'webview', 'html', 'patternManager.html');
|
||||
const cssPath = path.join(this.extensionUri.fsPath, 'src', 'webview', 'css', 'patternManager.css');
|
||||
const jsPath = path.join(this.extensionUri.fsPath, 'src', 'webview', 'js', 'patternManager.js');
|
||||
let html = fs.readFileSync(htmlPath, 'utf8');
|
||||
const css = fs.readFileSync(cssPath, 'utf8');
|
||||
const js = fs.readFileSync(jsPath, 'utf8');
|
||||
// Build style tag with nonce
|
||||
const styleTag = `<style nonce="${nonce}">\n${css}\n </style>`;
|
||||
// Replace placeholders
|
||||
html = html.replace(/{{nonce}}/g, nonce);
|
||||
html = html.replace(/{{cspSource}}/g, cspSource);
|
||||
html = html.replace(/{{styleTag}}/g, styleTag);
|
||||
html = html.replace(/{{script}}/g, js);
|
||||
return html;
|
||||
}
|
||||
/**
|
||||
* Generate a nonce for CSP
|
||||
*/
|
||||
getNonce() {
|
||||
let text = '';
|
||||
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
for (let i = 0; i < 32; i++) {
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
}
|
||||
return text;
|
||||
}
|
||||
/**
|
||||
* Clean up resources
|
||||
*/
|
||||
dispose() {
|
||||
WebviewManager.currentPanel = undefined;
|
||||
this.panel.dispose();
|
||||
while (this.disposables.length) {
|
||||
const disposable = this.disposables.pop();
|
||||
if (disposable) {
|
||||
disposable.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.WebviewManager = WebviewManager;
|
||||
//# sourceMappingURL=WebviewManager.js.map
|
||||
@@ -0,0 +1,148 @@
|
||||
{
|
||||
"name": "patternstore",
|
||||
"displayName": "PatternStore",
|
||||
"description": "Save and recall reusable regex find/replace pairs in VS Code",
|
||||
"version": "0.1.12",
|
||||
"publisher": "jdunkel",
|
||||
"engines": {
|
||||
"vscode": "^1.85.0"
|
||||
},
|
||||
"icon": "assets/pattern-store-icon.png",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/JoachimDunkel/PatternStore"
|
||||
},
|
||||
"keywords": [
|
||||
"search",
|
||||
"replace",
|
||||
"regex",
|
||||
"find",
|
||||
"pattern",
|
||||
"snippet"
|
||||
],
|
||||
"categories": [
|
||||
"Other"
|
||||
],
|
||||
"galleryBanner": {
|
||||
"color": "#1e1e1e",
|
||||
"theme": "dark"
|
||||
},
|
||||
"main": "./out/extension.js",
|
||||
"contributes": {
|
||||
"commands": [
|
||||
{
|
||||
"command": "patternStore.manage",
|
||||
"title": "PatternStore: Manage Patterns",
|
||||
"icon": "$(book)"
|
||||
}
|
||||
],
|
||||
"configuration": {
|
||||
"title": "PatternStore",
|
||||
"properties": {
|
||||
"patternStore.savedPatterns": {
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"description": "Global patterns available in all workspaces",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"find": {
|
||||
"type": "string"
|
||||
},
|
||||
"replace": {
|
||||
"type": "string"
|
||||
},
|
||||
"flags": {
|
||||
"type": "object"
|
||||
},
|
||||
"filesToInclude": {
|
||||
"type": "string",
|
||||
"description": "Files to include (glob pattern, e.g. '*.ts,*.js' or 'src/**')"
|
||||
},
|
||||
"filesToExclude": {
|
||||
"type": "string",
|
||||
"description": "Files to exclude (glob pattern, e.g. 'node_modules/**,dist/**')"
|
||||
},
|
||||
"scope": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"patternStore.workspacePatterns": {
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"description": "Workspace-specific patterns",
|
||||
"scope": "resource",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"find": {
|
||||
"type": "string"
|
||||
},
|
||||
"replace": {
|
||||
"type": "string"
|
||||
},
|
||||
"flags": {
|
||||
"type": "object"
|
||||
},
|
||||
"filesToInclude": {
|
||||
"type": "string",
|
||||
"description": "Files to include (glob pattern, e.g. '*.ts,*.js' or 'src/**')"
|
||||
},
|
||||
"filesToExclude": {
|
||||
"type": "string",
|
||||
"description": "Files to exclude (glob pattern, e.g. 'node_modules/**,dist/**')"
|
||||
},
|
||||
"scope": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"keybindings": [
|
||||
{
|
||||
"command": "patternStore.manage",
|
||||
"key": "ctrl+alt+r",
|
||||
"mac": "cmd+alt+r"
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
"view/title": [
|
||||
{
|
||||
"command": "patternStore.manage",
|
||||
"when": "view == workbench.view.search",
|
||||
"group": "navigation"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"vscode:prepublish": "npm run compile",
|
||||
"compile": "tsc -p ./",
|
||||
"watch": "tsc -watch -p ./",
|
||||
"pretest": "npm run compile && npm run lint",
|
||||
"lint": "eslint src --ext ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.x",
|
||||
"@types/vscode": "^1.85.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.x",
|
||||
"@typescript-eslint/parser": "^6.x",
|
||||
"eslint": "^8.x",
|
||||
"typescript": "^5.3.0"
|
||||
},
|
||||
"__metadata": {
|
||||
"installedTimestamp": 1783822352061,
|
||||
"targetPlatform": "undefined",
|
||||
"size": 1218391
|
||||
}
|
||||
}
|
||||
+561
@@ -0,0 +1,561 @@
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: var(--vscode-font-family);
|
||||
color: var(--vscode-foreground);
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
gap: 0;
|
||||
background: var(--vscode-panel-border);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Left panel - Pattern list */
|
||||
.pattern-list-panel {
|
||||
flex: 0 0 300px;
|
||||
min-width: 200px;
|
||||
max-width: 600px;
|
||||
background: var(--vscode-sideBar-background);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Resize Handle */
|
||||
.resize-handle {
|
||||
flex: 0 0 4px;
|
||||
background: var(--vscode-panel-border);
|
||||
cursor: ew-resize;
|
||||
position: relative;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.resize-handle:hover {
|
||||
background: var(--vscode-focusBorder);
|
||||
}
|
||||
|
||||
.resize-handle.resizing {
|
||||
background: var(--vscode-focusBorder);
|
||||
}
|
||||
|
||||
.search-box {
|
||||
padding: 8px;
|
||||
background: var(--vscode-sideBar-background);
|
||||
border-bottom: 1px solid var(--vscode-panel-border);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.search-wrapper {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
pointer-events: none;
|
||||
color: var(--vscode-input-placeholderForeground);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 100%;
|
||||
padding: 6px 30px 6px 30px;
|
||||
background: var(--vscode-input-background);
|
||||
color: var(--vscode-input-foreground);
|
||||
border: 1px solid var(--vscode-input-border);
|
||||
border-radius: 2px;
|
||||
font-size: 13px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.search-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--vscode-focusBorder);
|
||||
}
|
||||
|
||||
.search-clear {
|
||||
position: absolute;
|
||||
right: 6px;
|
||||
padding: 4px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
color: var(--vscode-input-placeholderForeground);
|
||||
border-radius: 3px;
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.search-clear:hover {
|
||||
background: var(--vscode-toolbar-hoverBackground);
|
||||
color: var(--vscode-foreground);
|
||||
}
|
||||
|
||||
.search-clear.visible {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.search-clear .codicon {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.pattern-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
padding: 8px 12px;
|
||||
font-weight: 600;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
color: var(--vscode-foreground);
|
||||
opacity: 0.6;
|
||||
letter-spacing: 0.5px;
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.section-header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
cursor: pointer;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.add-pattern-btn {
|
||||
padding: 4px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
color: var(--vscode-foreground);
|
||||
opacity: 0.6;
|
||||
border-radius: 3px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.add-pattern-btn:hover {
|
||||
background: var(--vscode-toolbar-hoverBackground);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.add-pattern-btn .codicon {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.section-header:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.section-header:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.section-header .codicon {
|
||||
font-size: 14px;
|
||||
transition: transform 0.1s ease;
|
||||
}
|
||||
|
||||
.section-header.collapsed .codicon {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.section-content {
|
||||
max-height: 10000px;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.15s ease;
|
||||
}
|
||||
|
||||
.section-content.collapsed {
|
||||
max-height: 0;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 48px 24px;
|
||||
color: var(--vscode-descriptionForeground);
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.empty-state-section {
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
color: var(--vscode-descriptionForeground);
|
||||
font-size: 13px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.pattern-item {
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
border-radius: 3px;
|
||||
margin-bottom: 2px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.pattern-item:hover {
|
||||
background: var(--vscode-list-hoverBackground);
|
||||
}
|
||||
|
||||
.pattern-item:hover .pattern-actions {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.pattern-item.selected {
|
||||
background: var(--vscode-list-activeSelectionBackground);
|
||||
color: var(--vscode-list-activeSelectionForeground);
|
||||
}
|
||||
|
||||
.pattern-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.pattern-name {
|
||||
font-weight: 500;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.pattern-preview {
|
||||
font-size: 12px;
|
||||
opacity: 0.8;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.pattern-item-invalid {
|
||||
border-left: 2px solid var(--vscode-errorForeground);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.pattern-item-invalid .pattern-name .codicon-warning {
|
||||
color: var(--vscode-errorForeground);
|
||||
margin-left: 4px;
|
||||
font-size: 13px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.pattern-item-invalid .pattern-preview {
|
||||
color: var(--vscode-errorForeground);
|
||||
}
|
||||
|
||||
.pattern-item-invalid .pattern-preview em {
|
||||
font-style: italic;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* Action buttons */
|
||||
.pattern-actions {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s ease;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
padding: 4px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
color: var(--vscode-foreground);
|
||||
opacity: 0.7;
|
||||
border-radius: 3px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.action-btn:hover {
|
||||
background: var(--vscode-toolbar-hoverBackground);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.action-btn .codicon {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.action-btn.delete:hover {
|
||||
color: var(--vscode-errorForeground);
|
||||
}
|
||||
|
||||
.action-btn.load:hover {
|
||||
color: var(--vscode-textLink-foreground);
|
||||
}
|
||||
|
||||
/* Right panel - Edit form */
|
||||
.edit-panel {
|
||||
flex: 1;
|
||||
background: var(--vscode-editor-background);
|
||||
overflow-y: auto;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.edit-empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
opacity: 0.5;
|
||||
text-align: center;
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
.edit-empty-state .codicon {
|
||||
font-size: 64px;
|
||||
margin-bottom: 16px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.edit-empty-state p {
|
||||
margin: 4px 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.edit-empty-state .hint {
|
||||
font-size: 12px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.edit-form {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.edit-form.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.edit-form-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.edit-form h2 {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
text-transform: uppercase;
|
||||
opacity: 0.8;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.form-input-inline {
|
||||
flex: 1;
|
||||
padding: 4px 6px;
|
||||
background: var(--vscode-input-background);
|
||||
color: var(--vscode-input-foreground);
|
||||
border: 1px solid var(--vscode-input-border);
|
||||
border-radius: 2px;
|
||||
font-size: 13px;
|
||||
font-family: var(--vscode-editor-font-family);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-input-inline:focus {
|
||||
outline: none;
|
||||
border-color: var(--vscode-focusBorder);
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
margin-bottom: 3px;
|
||||
font-weight: 500;
|
||||
font-size: 11px;
|
||||
opacity: 0.8;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.form-label-with-options {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.form-label-with-options .form-label {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.form-input,
|
||||
.form-textarea {
|
||||
width: 100%;
|
||||
padding: 4px 6px;
|
||||
background: var(--vscode-input-background);
|
||||
color: var(--vscode-input-foreground);
|
||||
border: 1px solid var(--vscode-input-border);
|
||||
border-radius: 2px;
|
||||
font-size: 13px;
|
||||
font-family: var(--vscode-editor-font-family);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-input:focus,
|
||||
.form-textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--vscode-focusBorder);
|
||||
}
|
||||
|
||||
.form-input.error,
|
||||
.form-textarea.error {
|
||||
border-color: var(--vscode-errorForeground);
|
||||
}
|
||||
|
||||
.form-input.dirty,
|
||||
.form-input-inline.dirty,
|
||||
.form-textarea.dirty {
|
||||
box-shadow: 0 0 0 1px rgba(106, 153, 85, 0.6);
|
||||
}
|
||||
|
||||
.form-textarea {
|
||||
min-height: 60px;
|
||||
resize: vertical;
|
||||
font-family: var(--vscode-editor-font-family);
|
||||
}
|
||||
|
||||
.validation-error {
|
||||
display: none;
|
||||
color: var(--vscode-errorForeground);
|
||||
font-size: 11px;
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.validation-error.visible {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.checkbox-group-icons {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.toggle-btn {
|
||||
padding: 4px 8px;
|
||||
background: var(--vscode-button-secondaryBackground);
|
||||
color: var(--vscode-button-secondaryForeground);
|
||||
border: 1px solid transparent;
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.15s ease;
|
||||
min-width: 28px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.toggle-btn:hover {
|
||||
background: var(--vscode-button-secondaryHoverBackground);
|
||||
}
|
||||
|
||||
.toggle-btn.active {
|
||||
background: var(--vscode-button-background);
|
||||
color: var(--vscode-button-foreground);
|
||||
border-color: var(--vscode-focusBorder);
|
||||
}
|
||||
|
||||
.toggle-btn.dirty {
|
||||
border-color: #6a9955;
|
||||
}
|
||||
|
||||
.toggle-btn.active.dirty {
|
||||
background: var(--vscode-button-background);
|
||||
color: var(--vscode-button-foreground);
|
||||
border-color: #6a9955;
|
||||
}
|
||||
|
||||
.toggle-btn .codicon {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
margin-top: 16px;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid var(--vscode-panel-border);
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 5px 12px;
|
||||
background: var(--vscode-button-background);
|
||||
color: var(--vscode-button-foreground);
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: var(--vscode-button-hoverBackground);
|
||||
}
|
||||
|
||||
button.secondary {
|
||||
background: var(--vscode-button-secondaryBackground);
|
||||
color: var(--vscode-button-secondaryForeground);
|
||||
}
|
||||
|
||||
button.secondary:hover {
|
||||
background: var(--vscode-button-secondaryHoverBackground);
|
||||
}
|
||||
|
||||
.dirty-indicator {
|
||||
margin-left: 6px;
|
||||
color: var(--vscode-editorWarning-foreground);
|
||||
font-size: 8px;
|
||||
animation: pulse 2s ease-in-out infinite;
|
||||
width: 8px;
|
||||
display: inline-block;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulseBorder {
|
||||
0%, 100% {
|
||||
border-color: #4ec9b0;
|
||||
}
|
||||
50% {
|
||||
border-color: #dcdcaa;
|
||||
}
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="Content-Security-Policy"
|
||||
content="default-src 'none'; style-src {{cspSource}} https://unpkg.com 'nonce-{{nonce}}'; font-src https://unpkg.com; script-src 'nonce-{{nonce}}';">
|
||||
<title>Manage Patterns</title>
|
||||
<link href="https://unpkg.com/@vscode/codicons/dist/codicon.css" rel="stylesheet" />
|
||||
{{styleTag}}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<!-- Left: Pattern List -->
|
||||
<div class="pattern-list-panel">
|
||||
<div class="search-box">
|
||||
<div class="search-wrapper">
|
||||
<i class="codicon codicon-search search-icon"></i>
|
||||
<input type="text" class="search-input" placeholder="Search patterns..." id="searchInput" />
|
||||
<button class="search-clear" id="searchClear" title="Clear search">
|
||||
<i class="codicon codicon-close"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pattern-list" id="patternList">
|
||||
<!-- Patterns will be inserted here -->
|
||||
<div class="empty-state">No patterns yet</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Resize Handle -->
|
||||
<div class="resize-handle" id="resizeHandle"></div>
|
||||
|
||||
<div class="edit-panel" id="editPanel">
|
||||
<div class="edit-empty-state" id="editEmptyState">
|
||||
<i class="codicon codicon-list-selection"></i>
|
||||
<p>Select a pattern to edit</p>
|
||||
<p class="hint">or create a new one with the + button</p>
|
||||
</div>
|
||||
|
||||
<div class="edit-form" id="editForm">
|
||||
<div class="edit-form-header">
|
||||
<h2>Pattern</h2>
|
||||
<input type="text" class="form-input-inline" id="labelInput" placeholder="Pattern name" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-label-with-options">
|
||||
<label class="form-label" for="findInput">Find</label>
|
||||
<div class="checkbox-group-icons">
|
||||
|
||||
<button type="button" class="toggle-btn" id="isCaseSensitiveBtn" title="Match Case">
|
||||
<i class="codicon codicon-case-sensitive"></i>
|
||||
</button>
|
||||
<button type="button" class="toggle-btn" id="matchWholeWordBtn" title="Match Whole Word">
|
||||
<i class="codicon codicon-whole-word"></i>
|
||||
</button>
|
||||
<button type="button" class="toggle-btn" id="isRegexBtn" title="Use Regular Expression">
|
||||
<i class="codicon codicon-regex"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<textarea class="form-textarea" id="findInput" placeholder="Search pattern or regex"></textarea>
|
||||
<div class="validation-error" id="findValidationError"></div>
|
||||
<!-- Hidden checkboxes to maintain state -->
|
||||
<input type="checkbox" id="isCaseSensitive" hidden />
|
||||
<input type="checkbox" id="matchWholeWord" hidden />
|
||||
<input type="checkbox" id="isRegex" hidden />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="replaceInput">Replace (optional)</label>
|
||||
<textarea class="form-textarea" id="replaceInput" placeholder="Replacement text"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="filesToInclude">Files to Include</label>
|
||||
<input type="text" class="form-input" id="filesToInclude" placeholder="*.ts,*.js or src/**" />
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="filesToExclude">Files to Exclude</label>
|
||||
<input type="text" class="form-input" id="filesToExclude" placeholder="node_modules/**,dist/**" />
|
||||
</div>
|
||||
|
||||
<div class="button-group">
|
||||
<button id="saveBtn">
|
||||
Save
|
||||
<span id="dirtyIndicator" class="dirty-indicator">
|
||||
<i class="codicon codicon-circle-filled"></i>
|
||||
</span>
|
||||
</button>
|
||||
<button id="loadBtn" class="secondary">
|
||||
<i class="codicon codicon-arrow-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script nonce="{{nonce}}">{{script}}</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
+857
@@ -0,0 +1,857 @@
|
||||
const vscode = acquireVsCodeApi();
|
||||
|
||||
// Store patterns data
|
||||
let workspacePatterns = [];
|
||||
let userPatterns = [];
|
||||
let workspacePatternsMap = new Map(); // id -> pattern
|
||||
let userPatternsMap = new Map(); // id -> pattern
|
||||
let searchQuery = '';
|
||||
|
||||
// Dirty state tracking
|
||||
let savedPatternState = null;
|
||||
|
||||
// Cache DOM elements for performance
|
||||
const domCache = {
|
||||
labelInput: null,
|
||||
findInput: null,
|
||||
replaceInput: null,
|
||||
filesToInclude: null,
|
||||
filesToExclude: null,
|
||||
isRegex: null,
|
||||
isCaseSensitive: null,
|
||||
matchWholeWord: null,
|
||||
isRegexBtn: null,
|
||||
isCaseSensitiveBtn: null,
|
||||
matchWholeWordBtn: null,
|
||||
dirtyIndicator: null,
|
||||
patternList: null,
|
||||
editForm: null,
|
||||
editEmptyState: null,
|
||||
findValidationError: null
|
||||
};
|
||||
|
||||
function initDomCache() {
|
||||
domCache.labelInput = document.getElementById('labelInput');
|
||||
domCache.findInput = document.getElementById('findInput');
|
||||
domCache.replaceInput = document.getElementById('replaceInput');
|
||||
domCache.filesToInclude = document.getElementById('filesToInclude');
|
||||
domCache.filesToExclude = document.getElementById('filesToExclude');
|
||||
domCache.isRegex = document.getElementById('isRegex');
|
||||
domCache.isCaseSensitive = document.getElementById('isCaseSensitive');
|
||||
domCache.matchWholeWord = document.getElementById('matchWholeWord');
|
||||
domCache.isRegexBtn = document.getElementById('isRegexBtn');
|
||||
domCache.isCaseSensitiveBtn = document.getElementById('isCaseSensitiveBtn');
|
||||
domCache.matchWholeWordBtn = document.getElementById('matchWholeWordBtn');
|
||||
domCache.dirtyIndicator = document.getElementById('dirtyIndicator');
|
||||
domCache.patternList = document.getElementById('patternList');
|
||||
domCache.editForm = document.getElementById('editForm');
|
||||
domCache.editEmptyState = document.getElementById('editEmptyState');
|
||||
domCache.findValidationError = document.getElementById('findValidationError');
|
||||
}
|
||||
|
||||
function checkIfDirty() {
|
||||
const state = vscode.getState() || {};
|
||||
const currentPattern = state.currentPattern;
|
||||
|
||||
if (!currentPattern || !savedPatternState) {
|
||||
updateDirtyIndicator(false);
|
||||
clearAllDirtyClasses();
|
||||
return;
|
||||
}
|
||||
|
||||
// Compare current form values with saved state
|
||||
const isDirty =
|
||||
domCache.labelInput.value.trim() !== savedPatternState.label ||
|
||||
domCache.findInput.value !== savedPatternState.find ||
|
||||
domCache.replaceInput.value !== savedPatternState.replace ||
|
||||
domCache.isRegex.checked !== savedPatternState.flags.isRegex ||
|
||||
domCache.isCaseSensitive.checked !== savedPatternState.flags.isCaseSensitive ||
|
||||
domCache.matchWholeWord.checked !== savedPatternState.flags.matchWholeWord ||
|
||||
(domCache.filesToInclude.value.trim() || '') !== (savedPatternState.filesToInclude || '') ||
|
||||
(domCache.filesToExclude.value.trim() || '') !== (savedPatternState.filesToExclude || '');
|
||||
|
||||
updateDirtyIndicator(isDirty);
|
||||
updateFieldDirtyStates();
|
||||
}
|
||||
|
||||
function updateDirtyIndicator(isDirty) {
|
||||
if (domCache.dirtyIndicator) {
|
||||
domCache.dirtyIndicator.style.visibility = isDirty ? 'visible' : 'hidden';
|
||||
}
|
||||
}
|
||||
|
||||
function updateFieldDirtyStates() {
|
||||
if (!savedPatternState) {
|
||||
clearAllDirtyClasses();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check text inputs using cached DOM references
|
||||
if (domCache.labelInput) {
|
||||
toggleDirtyClass(domCache.labelInput, domCache.labelInput.value.trim() !== savedPatternState.label);
|
||||
}
|
||||
if (domCache.findInput) {
|
||||
toggleDirtyClass(domCache.findInput, domCache.findInput.value !== savedPatternState.find);
|
||||
}
|
||||
if (domCache.replaceInput) {
|
||||
toggleDirtyClass(domCache.replaceInput, domCache.replaceInput.value !== savedPatternState.replace);
|
||||
}
|
||||
if (domCache.filesToInclude) {
|
||||
toggleDirtyClass(domCache.filesToInclude, (domCache.filesToInclude.value.trim() || '') !== (savedPatternState.filesToInclude || ''));
|
||||
}
|
||||
if (domCache.filesToExclude) {
|
||||
toggleDirtyClass(domCache.filesToExclude, (domCache.filesToExclude.value.trim() || '') !== (savedPatternState.filesToExclude || ''));
|
||||
}
|
||||
|
||||
// Check toggle buttons using cached DOM references
|
||||
if (domCache.isRegexBtn && domCache.isRegex) {
|
||||
toggleDirtyClass(domCache.isRegexBtn, domCache.isRegex.checked !== savedPatternState.flags.isRegex);
|
||||
}
|
||||
if (domCache.isCaseSensitiveBtn && domCache.isCaseSensitive) {
|
||||
toggleDirtyClass(domCache.isCaseSensitiveBtn, domCache.isCaseSensitive.checked !== savedPatternState.flags.isCaseSensitive);
|
||||
}
|
||||
if (domCache.matchWholeWordBtn && domCache.matchWholeWord) {
|
||||
toggleDirtyClass(domCache.matchWholeWordBtn, domCache.matchWholeWord.checked !== savedPatternState.flags.matchWholeWord);
|
||||
}
|
||||
}
|
||||
|
||||
function toggleDirtyClass(element, isDirty) {
|
||||
if (isDirty) {
|
||||
element.classList.add('dirty');
|
||||
} else {
|
||||
element.classList.remove('dirty');
|
||||
}
|
||||
}
|
||||
|
||||
function clearAllDirtyClasses() {
|
||||
const elements = [
|
||||
domCache.labelInput, domCache.findInput, domCache.replaceInput,
|
||||
domCache.filesToInclude, domCache.filesToExclude,
|
||||
domCache.isRegexBtn, domCache.isCaseSensitiveBtn, domCache.matchWholeWordBtn
|
||||
];
|
||||
|
||||
elements.forEach(element => {
|
||||
if (element) {
|
||||
element.classList.remove('dirty');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function saveDirtyState(pattern) {
|
||||
savedPatternState = {
|
||||
label: pattern.label,
|
||||
find: pattern.find || '',
|
||||
replace: pattern.replace || '',
|
||||
flags: { ...pattern.flags },
|
||||
filesToInclude: pattern.filesToInclude || '',
|
||||
filesToExclude: pattern.filesToExclude || ''
|
||||
};
|
||||
updateDirtyIndicator(false);
|
||||
clearAllDirtyClasses();
|
||||
}
|
||||
|
||||
function findPatternById(id, scope) {
|
||||
const map = scope === 'workspace' ? workspacePatternsMap : userPatternsMap;
|
||||
return map.get(id);
|
||||
}
|
||||
|
||||
function rebuildPatternMaps() {
|
||||
workspacePatternsMap.clear();
|
||||
userPatternsMap.clear();
|
||||
|
||||
workspacePatterns.forEach(p => workspacePatternsMap.set(p.id, p));
|
||||
userPatterns.forEach(p => userPatternsMap.set(p.id, p));
|
||||
}
|
||||
|
||||
// Get collapse state from localStorage
|
||||
const getCollapseState = (section) => {
|
||||
const state = vscode.getState() || {};
|
||||
return state[section + '_collapsed'] === true;
|
||||
};
|
||||
|
||||
// Get collapse state with a default value if not previously set
|
||||
const getCollapseStateWithDefault = (section, defaultValue) => {
|
||||
const state = vscode.getState() || {};
|
||||
const key = section + '_collapsed';
|
||||
// If user has explicitly set a preference, use it; otherwise use default
|
||||
return state[key] !== undefined ? state[key] : defaultValue;
|
||||
};
|
||||
|
||||
// Save collapse state to localStorage
|
||||
const setCollapseState = (section, collapsed) => {
|
||||
const state = vscode.getState() || {};
|
||||
state[section + '_collapsed'] = collapsed;
|
||||
vscode.setState(state);
|
||||
};
|
||||
|
||||
window.addEventListener('message', event => {
|
||||
const message = event.data;
|
||||
|
||||
if (message.type === 'patterns') {
|
||||
|
||||
disablePatternList(false);
|
||||
document.body.style.cursor = '';
|
||||
|
||||
workspacePatterns = message.workspace;
|
||||
userPatterns = message.user;
|
||||
rebuildPatternMaps(); // Rebuild Maps for O(1) lookups
|
||||
renderPatternList();
|
||||
setupSearchInput();
|
||||
|
||||
// Force clear selection if requested (e.g., on external config changes)
|
||||
if (message.clearSelection) {
|
||||
clearDetailsView();
|
||||
} else if (message.selectPattern) {
|
||||
selectPattern(message.selectPattern.id, message.selectPattern.scope);
|
||||
} else {
|
||||
const state = vscode.getState() || {};
|
||||
if (state.currentPattern) {
|
||||
const allPatterns = state.currentPattern.scope === 'workspace' ? workspacePatterns : userPatterns;
|
||||
const stillExists = allPatterns.some(p => p.id === state.currentPattern.id);
|
||||
|
||||
if (!stillExists) {
|
||||
clearDetailsView();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Focus search input when webview opens
|
||||
const searchInput = document.getElementById('searchInput');
|
||||
if (searchInput) {
|
||||
searchInput.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function setupSearchInput() {
|
||||
const searchInput = document.getElementById('searchInput');
|
||||
const searchClear = document.getElementById('searchClear');
|
||||
|
||||
if (!searchInput || !searchClear) return;
|
||||
|
||||
// if (searchInput.dataset.initialized === 'true') {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// searchInput.dataset.initialized = 'true';
|
||||
|
||||
searchInput.addEventListener('input', (e) => {
|
||||
searchQuery = e.target.value.toLowerCase();
|
||||
|
||||
if (searchQuery) {
|
||||
searchClear.classList.add('visible');
|
||||
} else {
|
||||
searchClear.classList.remove('visible');
|
||||
}
|
||||
|
||||
renderPatternList();
|
||||
});
|
||||
|
||||
searchClear.addEventListener('click', () => {
|
||||
searchInput.value = '';
|
||||
searchQuery = '';
|
||||
searchClear.classList.remove('visible');
|
||||
renderPatternList();
|
||||
searchInput.focus();
|
||||
});
|
||||
|
||||
searchInput.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape' && searchQuery) {
|
||||
searchInput.value = '';
|
||||
searchQuery = '';
|
||||
searchClear.classList.remove('visible');
|
||||
renderPatternList();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function filterPatterns(patterns) {
|
||||
if (!searchQuery) return patterns;
|
||||
|
||||
return patterns.filter(pattern => {
|
||||
const nameMatch = pattern.label.toLowerCase().includes(searchQuery);
|
||||
const findMatch = pattern.find.toLowerCase().includes(searchQuery);
|
||||
const replaceMatch = pattern.replace && pattern.replace.toLowerCase().includes(searchQuery);
|
||||
return nameMatch || findMatch || replaceMatch;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function renderPatternList() {
|
||||
const listContainer = document.getElementById('patternList');
|
||||
|
||||
const filteredWorkspace = filterPatterns(workspacePatterns);
|
||||
const filteredUser = filterPatterns(userPatterns);
|
||||
let html = '';
|
||||
|
||||
if (searchQuery && filteredWorkspace.length === 0 && filteredUser.length === 0) {
|
||||
listContainer.innerHTML = '<div class="empty-state">No patterns found</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Collapse empty sections by default, but respect user's saved preference
|
||||
const workspaceCollapsed = searchQuery ? false : getCollapseStateWithDefault('workspace', filteredWorkspace.length === 0);
|
||||
html += renderSection('workspace', 'Workspace', filteredWorkspace, workspaceCollapsed);
|
||||
|
||||
const userCollapsed = searchQuery ? false : getCollapseStateWithDefault('user', filteredUser.length === 0);
|
||||
html += renderSection('user', 'User', filteredUser, userCollapsed);
|
||||
|
||||
listContainer.innerHTML = html;
|
||||
|
||||
document.querySelectorAll('.section-header').forEach(header => {
|
||||
header.addEventListener('click', toggleSection);
|
||||
});
|
||||
|
||||
// Event delegation handles all pattern list interactions, no need to re-attach
|
||||
}
|
||||
|
||||
|
||||
function renderSection(id, title, patterns, collapsed) {
|
||||
const chevron = collapsed ? 'codicon-chevron-right' : 'codicon-chevron-down';
|
||||
const contentClass = collapsed ? 'section-content collapsed' : 'section-content';
|
||||
|
||||
let html = `
|
||||
<div class="section-header ${collapsed ? 'collapsed' : ''}" data-section="${id}">
|
||||
<div class="section-header-left">
|
||||
<i class="codicon ${chevron}"></i>
|
||||
<span>${title} (${patterns.length})</span>
|
||||
</div>
|
||||
<button class="add-pattern-btn" data-scope="${id}" title="Add new pattern">
|
||||
<i class="codicon codicon-add"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="${contentClass}" data-section-content="${id}">
|
||||
`;
|
||||
|
||||
if (patterns.length === 0) {
|
||||
html += '<div class="empty-state-section">No patterns in this scope</div>';
|
||||
} else {
|
||||
patterns.forEach(pattern => {
|
||||
html += renderPatternItem(pattern);
|
||||
});
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
function toggleSection(event) {
|
||||
if (event.target.closest('.add-pattern-btn')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const header = event.currentTarget;
|
||||
const sectionId = header.dataset.section;
|
||||
const content = document.querySelector('[data-section-content="' + sectionId + '"]');
|
||||
const chevron = header.querySelector('.codicon');
|
||||
|
||||
const isCollapsed = header.classList.contains('collapsed');
|
||||
|
||||
if (isCollapsed) {
|
||||
header.classList.remove('collapsed');
|
||||
content.classList.remove('collapsed');
|
||||
chevron.classList.remove('codicon-chevron-right');
|
||||
chevron.classList.add('codicon-chevron-down');
|
||||
setCollapseState(sectionId, false);
|
||||
} else {
|
||||
header.classList.add('collapsed');
|
||||
content.classList.add('collapsed');
|
||||
chevron.classList.remove('codicon-chevron-down');
|
||||
chevron.classList.add('codicon-chevron-right');
|
||||
setCollapseState(sectionId, true);
|
||||
}
|
||||
}
|
||||
|
||||
function renderPatternItem(pattern) {
|
||||
const preview = pattern.find.substring(0, 40);
|
||||
const truncated = pattern.find.length > 40 ? '...' : '';
|
||||
const isInvalid = !pattern.find || pattern.find.trim() === '';
|
||||
const invalidClass = isInvalid ? 'pattern-item-invalid' : '';
|
||||
|
||||
return `
|
||||
<div class="pattern-item ${invalidClass}" data-id="${pattern.id}" data-label="${pattern.label}" data-scope="${pattern.scope}"> <div class="pattern-info">
|
||||
<div class="pattern-name">
|
||||
${pattern.label}
|
||||
${isInvalid ? '<i class="codicon codicon-warning" title="Find field is empty"></i>' : ''}
|
||||
</div>
|
||||
<div class="pattern-preview">${isInvalid ? '<em>Empty find pattern</em>' : preview + truncated}</div>
|
||||
</div>
|
||||
<div class="pattern-actions">
|
||||
${renderActionButton('trash', 'delete', 'Delete pattern')}
|
||||
${renderActionButton('arrow-right', 'load', 'Load to search')}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderActionButton(icon, action, title) {
|
||||
return `
|
||||
<button class="action-btn ${action}"
|
||||
data-action="${action}"
|
||||
title="${title}"
|
||||
onclick="event.stopPropagation()">
|
||||
<i class="codicon codicon-${icon}"></i>
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
|
||||
// Event delegation for pattern list - set up once, works for all items
|
||||
function setupEventDelegation() {
|
||||
if (!domCache.patternList) return;
|
||||
|
||||
// Delegate all pattern list events to the parent container
|
||||
domCache.patternList.addEventListener('click', (event) => {
|
||||
// Handle pattern item click
|
||||
const patternItem = event.target.closest('.pattern-item');
|
||||
if (patternItem && !event.target.closest('.action-btn') && !event.target.closest('.add-pattern-btn')) {
|
||||
handlePatternClick(patternItem);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle action button clicks (load, delete)
|
||||
const actionBtn = event.target.closest('.action-btn');
|
||||
if (actionBtn) {
|
||||
event.stopPropagation();
|
||||
handleActionClick(actionBtn);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle add pattern button clicks
|
||||
const addBtn = event.target.closest('.add-pattern-btn');
|
||||
if (addBtn) {
|
||||
event.stopPropagation();
|
||||
handleAddPattern(addBtn);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handlePatternClick(item) {
|
||||
const id = item.dataset.id;
|
||||
const scope = item.dataset.scope;
|
||||
|
||||
document.querySelectorAll('.pattern-item').forEach(i => {
|
||||
i.classList.remove('selected');
|
||||
});
|
||||
|
||||
item.classList.add('selected');
|
||||
|
||||
const pattern = findPatternById(id, scope);
|
||||
|
||||
if (pattern) {
|
||||
populateDetailsView(pattern);
|
||||
}
|
||||
}
|
||||
|
||||
function populateDetailsView(pattern) {
|
||||
domCache.editEmptyState.style.display = 'none';
|
||||
domCache.editForm.classList.add('active');
|
||||
|
||||
domCache.labelInput.value = pattern.label;
|
||||
domCache.findInput.value = pattern.find || '';
|
||||
domCache.replaceInput.value = pattern.replace || '';
|
||||
|
||||
domCache.isRegex.checked = pattern.flags.isRegex;
|
||||
domCache.isCaseSensitive.checked = pattern.flags.isCaseSensitive;
|
||||
domCache.matchWholeWord.checked = pattern.flags.matchWholeWord;
|
||||
|
||||
updateToggleButtonState(domCache.isRegexBtn, pattern.flags.isRegex);
|
||||
updateToggleButtonState(domCache.isCaseSensitiveBtn, pattern.flags.isCaseSensitive);
|
||||
updateToggleButtonState(domCache.matchWholeWordBtn, pattern.flags.matchWholeWord);
|
||||
|
||||
domCache.findInput.classList.remove('error');
|
||||
domCache.findValidationError.classList.remove('visible');
|
||||
validateRegexPattern();
|
||||
|
||||
domCache.filesToInclude.value = pattern.filesToInclude || '';
|
||||
domCache.filesToExclude.value = pattern.filesToExclude || '';
|
||||
|
||||
const state = vscode.getState() || {};
|
||||
state.currentPattern = {
|
||||
id: pattern.id,
|
||||
label: pattern.label,
|
||||
scope: pattern.scope
|
||||
};
|
||||
vscode.setState(state);
|
||||
|
||||
// Save the clean state for dirty tracking
|
||||
saveDirtyState(pattern);
|
||||
}
|
||||
|
||||
function selectPattern(id, scope) {
|
||||
const item = document.querySelector(
|
||||
`.pattern-item[data-id="${id}"][data-scope="${scope}"]`
|
||||
);
|
||||
|
||||
if (item) {
|
||||
item.click();
|
||||
}
|
||||
}
|
||||
|
||||
function handleSavePattern() {
|
||||
const state = vscode.getState() || {};
|
||||
const currentPattern = state.currentPattern;
|
||||
|
||||
if (!currentPattern) {
|
||||
console.error('No pattern selected to save');
|
||||
return;
|
||||
}
|
||||
|
||||
const label = domCache.labelInput.value.trim();
|
||||
const find = domCache.findInput.value;
|
||||
const replace = domCache.replaceInput.value;
|
||||
|
||||
if (!label) {
|
||||
alert('Pattern name cannot be empty');
|
||||
return;
|
||||
}
|
||||
|
||||
disablePatternList(true);
|
||||
document.body.style.cursor = 'wait';
|
||||
|
||||
const pattern = {
|
||||
id: currentPattern.id,
|
||||
label: label,
|
||||
find: find,
|
||||
replace: replace,
|
||||
flags: {
|
||||
isRegex: domCache.isRegex.checked,
|
||||
isCaseSensitive: domCache.isCaseSensitive.checked,
|
||||
matchWholeWord: domCache.matchWholeWord.checked,
|
||||
isMultiline: false
|
||||
},
|
||||
filesToInclude: domCache.filesToInclude.value.trim() || undefined,
|
||||
filesToExclude: domCache.filesToExclude.value.trim() || undefined,
|
||||
scope: currentPattern.scope
|
||||
};
|
||||
|
||||
vscode.postMessage({
|
||||
type: 'save',
|
||||
pattern: pattern
|
||||
});
|
||||
}
|
||||
|
||||
function disablePatternList(disabled) {
|
||||
const listPanel = document.querySelector('.pattern-list-panel');
|
||||
if (disabled) {
|
||||
listPanel.style.pointerEvents = 'none';
|
||||
listPanel.style.opacity = '0.6';
|
||||
} else {
|
||||
listPanel.style.pointerEvents = '';
|
||||
listPanel.style.opacity = '';
|
||||
}
|
||||
}
|
||||
|
||||
function handleDeletePattern() {
|
||||
const state = vscode.getState() || {};
|
||||
const currentPattern = state.currentPattern;
|
||||
|
||||
if (!currentPattern) {
|
||||
console.error('No pattern selected to delete');
|
||||
return;
|
||||
}
|
||||
|
||||
vscode.postMessage({
|
||||
type: 'delete',
|
||||
id: currentPattern.id,
|
||||
scope: currentPattern.scope
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function handleLoadPatternFromDetails() {
|
||||
const state = vscode.getState() || {};
|
||||
const currentPattern = state.currentPattern;
|
||||
|
||||
if (!currentPattern) {
|
||||
console.error('No pattern selected to load');
|
||||
return;
|
||||
}
|
||||
|
||||
// Build pattern from current form values (may be dirty)
|
||||
const pattern = {
|
||||
id: currentPattern.id,
|
||||
label: domCache.labelInput.value.trim(),
|
||||
find: domCache.findInput.value,
|
||||
replace: domCache.replaceInput.value,
|
||||
flags: {
|
||||
isRegex: domCache.isRegex.checked,
|
||||
isCaseSensitive: domCache.isCaseSensitive.checked,
|
||||
matchWholeWord: domCache.matchWholeWord.checked,
|
||||
isMultiline: false
|
||||
},
|
||||
filesToInclude: domCache.filesToInclude.value.trim() || undefined,
|
||||
filesToExclude: domCache.filesToExclude.value.trim() || undefined,
|
||||
scope: currentPattern.scope
|
||||
};
|
||||
|
||||
vscode.postMessage({
|
||||
type: 'load',
|
||||
id: currentPattern.id,
|
||||
scope: currentPattern.scope,
|
||||
pattern: pattern
|
||||
});
|
||||
}
|
||||
|
||||
function clearDetailsView() {
|
||||
domCache.editForm.classList.remove('active');
|
||||
domCache.editEmptyState.style.display = 'flex';
|
||||
|
||||
domCache.labelInput.value = '';
|
||||
domCache.findInput.value = '';
|
||||
domCache.replaceInput.value = '';
|
||||
domCache.isRegex.checked = false;
|
||||
domCache.isCaseSensitive.checked = false;
|
||||
domCache.matchWholeWord.checked = false;
|
||||
domCache.filesToInclude.value = '';
|
||||
domCache.filesToExclude.value = '';
|
||||
|
||||
const state = vscode.getState() || {};
|
||||
delete state.currentPattern;
|
||||
vscode.setState(state);
|
||||
|
||||
document.querySelectorAll('.pattern-item').forEach(i => {
|
||||
i.classList.remove('selected');
|
||||
});
|
||||
|
||||
// Clear dirty state
|
||||
savedPatternState = null;
|
||||
updateDirtyIndicator(false);
|
||||
clearAllDirtyClasses();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup toggle buttons for flags
|
||||
*/
|
||||
function setupToggleButtons() {
|
||||
const buttons = [
|
||||
{ btnId: 'isRegexBtn', checkboxId: 'isRegex' },
|
||||
{ btnId: 'isCaseSensitiveBtn', checkboxId: 'isCaseSensitive' },
|
||||
{ btnId: 'matchWholeWordBtn', checkboxId: 'matchWholeWord' }
|
||||
];
|
||||
|
||||
buttons.forEach(({ btnId, checkboxId }) => {
|
||||
const btn = document.getElementById(btnId);
|
||||
const checkbox = document.getElementById(checkboxId);
|
||||
|
||||
if (!btn || !checkbox) return;
|
||||
|
||||
btn.addEventListener('click', () => {
|
||||
checkbox.checked = !checkbox.checked;
|
||||
updateToggleButtonState(btn, checkbox.checked);
|
||||
|
||||
// Validate regex when regex flag changes
|
||||
if (checkboxId === 'isRegex') {
|
||||
validateRegexPattern();
|
||||
}
|
||||
|
||||
// Check if field is dirty
|
||||
checkIfDirty();
|
||||
});
|
||||
|
||||
// Initialize button state
|
||||
updateToggleButtonState(btn, checkbox.checked);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update visual state of toggle button
|
||||
*/
|
||||
function updateToggleButtonState(button, isActive) {
|
||||
if (isActive) {
|
||||
button.classList.add('active');
|
||||
} else {
|
||||
button.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate regex pattern
|
||||
*/
|
||||
function validateRegexPattern() {
|
||||
const isRegex = domCache.isRegex.checked;
|
||||
|
||||
if (!isRegex) {
|
||||
// Not using regex, clear any errors
|
||||
domCache.findInput.classList.remove('error');
|
||||
domCache.findValidationError.classList.remove('visible');
|
||||
domCache.findValidationError.textContent = '';
|
||||
return true;
|
||||
}
|
||||
|
||||
const pattern = domCache.findInput.value;
|
||||
|
||||
if (!pattern) {
|
||||
// Empty pattern is okay
|
||||
domCache.findInput.classList.remove('error');
|
||||
domCache.findValidationError.classList.remove('visible');
|
||||
domCache.findValidationError.textContent = '';
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
new RegExp(pattern);
|
||||
// Valid regex
|
||||
domCache.findInput.classList.remove('error');
|
||||
domCache.findValidationError.classList.remove('visible');
|
||||
domCache.findValidationError.textContent = '';
|
||||
return true;
|
||||
} catch (e) {
|
||||
// Invalid regex
|
||||
domCache.findInput.classList.add('error');
|
||||
domCache.findValidationError.classList.add('visible');
|
||||
domCache.findValidationError.textContent = `Invalid regular expression: ${e.message}`;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleAddPattern(button) {
|
||||
const scope = button.dataset.scope;
|
||||
|
||||
// Visual feedback: disable button temporarily
|
||||
button.disabled = true;
|
||||
button.style.opacity = '0.5';
|
||||
button.style.cursor = 'wait';
|
||||
|
||||
vscode.postMessage({
|
||||
type: 'create',
|
||||
scope: scope === 'workspace' ? 'workspace' : 'global'
|
||||
});
|
||||
|
||||
// Re-enable after 2 seconds (fallback in case of error)
|
||||
setTimeout(() => {
|
||||
button.disabled = false;
|
||||
button.style.opacity = '';
|
||||
button.style.cursor = '';
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
function handleActionClick(button) {
|
||||
const action = button.dataset.action;
|
||||
const item = button.closest('.pattern-item');
|
||||
const id = item.dataset.id;
|
||||
const scope = item.dataset.scope;
|
||||
|
||||
if (action === 'delete') {
|
||||
handleDelete(id, scope);
|
||||
} else if (action === 'load') {
|
||||
handleLoad(id, scope);
|
||||
}
|
||||
}
|
||||
|
||||
function handleDelete(id, scope) {
|
||||
vscode.postMessage({
|
||||
type: 'delete',
|
||||
id: id,
|
||||
scope: scope
|
||||
});
|
||||
}
|
||||
|
||||
function handleLoad(id, scope) {
|
||||
if (!id || !scope) {
|
||||
console.error('Pattern not found');
|
||||
return;
|
||||
}
|
||||
|
||||
vscode.postMessage({
|
||||
type: 'load',
|
||||
id: id,
|
||||
scope: scope
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById('saveBtn').addEventListener('click', handleSavePattern);
|
||||
document.getElementById('loadBtn').addEventListener('click', handleLoadPatternFromDetails);
|
||||
|
||||
setupToggleButtons();
|
||||
|
||||
// Add event listeners to all form inputs for dirty tracking
|
||||
const formInputs = [
|
||||
'labelInput', 'findInput', 'replaceInput',
|
||||
'isRegex', 'isCaseSensitive', 'matchWholeWord',
|
||||
'filesToInclude', 'filesToExclude'
|
||||
];
|
||||
|
||||
formInputs.forEach(id => {
|
||||
const element = document.getElementById(id);
|
||||
if (element) {
|
||||
element.addEventListener('input', checkIfDirty);
|
||||
element.addEventListener('change', checkIfDirty);
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize DOM cache for performance
|
||||
initDomCache();
|
||||
|
||||
// Set up event delegation for pattern list (once, not per render)
|
||||
setupEventDelegation();
|
||||
|
||||
// Resizable panels functionality
|
||||
function setupResizablePanels() {
|
||||
const resizeHandle = document.getElementById('resizeHandle');
|
||||
const patternListPanel = document.querySelector('.pattern-list-panel');
|
||||
|
||||
if (!resizeHandle || !patternListPanel) return;
|
||||
|
||||
let isResizing = false;
|
||||
let startX = 0;
|
||||
let startWidth = 0;
|
||||
|
||||
// Load saved width from state
|
||||
const state = vscode.getState() || {};
|
||||
if (state.panelWidth) {
|
||||
patternListPanel.style.flexBasis = `${state.panelWidth}px`;
|
||||
}
|
||||
|
||||
resizeHandle.addEventListener('mousedown', (e) => {
|
||||
isResizing = true;
|
||||
startX = e.clientX;
|
||||
startWidth = patternListPanel.offsetWidth;
|
||||
resizeHandle.classList.add('resizing');
|
||||
document.body.style.cursor = 'ew-resize';
|
||||
document.body.style.userSelect = 'none';
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
document.addEventListener('mousemove', (e) => {
|
||||
if (!isResizing) return;
|
||||
|
||||
const deltaX = e.clientX - startX;
|
||||
const newWidth = startWidth + deltaX;
|
||||
|
||||
// Respect min/max constraints
|
||||
const minWidth = 200;
|
||||
const maxWidth = 600;
|
||||
const clampedWidth = Math.max(minWidth, Math.min(maxWidth, newWidth));
|
||||
|
||||
patternListPanel.style.flexBasis = `${clampedWidth}px`;
|
||||
});
|
||||
|
||||
document.addEventListener('mouseup', () => {
|
||||
if (isResizing) {
|
||||
isResizing = false;
|
||||
resizeHandle.classList.remove('resizing');
|
||||
document.body.style.cursor = '';
|
||||
document.body.style.userSelect = '';
|
||||
|
||||
// Save width to state
|
||||
const currentWidth = patternListPanel.offsetWidth;
|
||||
const state = vscode.getState() || {};
|
||||
state.panelWidth = currentWidth;
|
||||
vscode.setState(state);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Set up resizable panels
|
||||
setupResizablePanels();
|
||||
|
||||
// Add validation listener to find input
|
||||
if (domCache.findInput) {
|
||||
domCache.findInput.addEventListener('input', () => {
|
||||
validateRegexPattern();
|
||||
});
|
||||
}
|
||||
|
||||
vscode.postMessage({ type: 'ready' });
|
||||
Reference in new issue
Block a user