diff --git a/apps/scishop/.gitignore b/apps/scishop/.gitignore new file mode 100644 index 0000000..0227833 --- /dev/null +++ b/apps/scishop/.gitignore @@ -0,0 +1,26 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +src/data/tmp*.json \ No newline at end of file diff --git a/apps/scishop/README.md b/apps/scishop/README.md new file mode 100644 index 0000000..7f1ddfc --- /dev/null +++ b/apps/scishop/README.md @@ -0,0 +1,3 @@ +# scishop + +Create, edit and export `Sci0` & `Sci01` PIC resources. diff --git a/apps/scishop/index.html b/apps/scishop/index.html new file mode 100644 index 0000000..3f20ae9 --- /dev/null +++ b/apps/scishop/index.html @@ -0,0 +1,12 @@ + + + + + + scishop + + +
+ + + diff --git a/apps/scishop/package.json b/apps/scishop/package.json new file mode 100644 index 0000000..aeac3e0 --- /dev/null +++ b/apps/scishop/package.json @@ -0,0 +1,27 @@ +{ + "name": "scishop", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc && vite build", + "preview": "vite preview" + }, + "dependencies": { + "@4bitlabs/sci0": "^4.0.1", + "@4bitlabs/color": "^2.1.5", + "@4bitlabs/color-space": "^1.2.3", + "@4bitlabs/image": "^3.3.11", + "@4bitlabs/vec2": "^1.2.0", + "fast-deep-equal": "^3.1.3", + "vue": "^3.4.21", + "transformation-matrix": "^2.16.1" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.0.4", + "typescript": "^5.2.2", + "vite": "^5.2.0", + "vue-tsc": "^2.0.6" + } +} diff --git a/apps/scishop/src/App.vue b/apps/scishop/src/App.vue new file mode 100644 index 0000000..e098925 --- /dev/null +++ b/apps/scishop/src/App.vue @@ -0,0 +1,18 @@ + + + diff --git a/apps/scishop/src/assets/cursor-pen-minus.svg b/apps/scishop/src/assets/cursor-pen-minus.svg new file mode 100644 index 0000000..b858f1d --- /dev/null +++ b/apps/scishop/src/assets/cursor-pen-minus.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/apps/scishop/src/assets/cursor-pen-plus.svg b/apps/scishop/src/assets/cursor-pen-plus.svg new file mode 100644 index 0000000..ef82338 --- /dev/null +++ b/apps/scishop/src/assets/cursor-pen-plus.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/apps/scishop/src/assets/cursor-pen-star.svg b/apps/scishop/src/assets/cursor-pen-star.svg new file mode 100644 index 0000000..8d34b71 --- /dev/null +++ b/apps/scishop/src/assets/cursor-pen-star.svg @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/apps/scishop/src/assets/cursor-pen.svg b/apps/scishop/src/assets/cursor-pen.svg new file mode 100644 index 0000000..568bad7 --- /dev/null +++ b/apps/scishop/src/assets/cursor-pen.svg @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/apps/scishop/src/components/Header.vue b/apps/scishop/src/components/Header.vue new file mode 100644 index 0000000..a74ef64 --- /dev/null +++ b/apps/scishop/src/components/Header.vue @@ -0,0 +1,59 @@ + + + + + diff --git a/apps/scishop/src/components/LayerNavigator.vue b/apps/scishop/src/components/LayerNavigator.vue new file mode 100644 index 0000000..63887f3 --- /dev/null +++ b/apps/scishop/src/components/LayerNavigator.vue @@ -0,0 +1,173 @@ + + + + + diff --git a/apps/scishop/src/components/PalettePicker/ModeSelector.vue b/apps/scishop/src/components/PalettePicker/ModeSelector.vue new file mode 100644 index 0000000..619df9e --- /dev/null +++ b/apps/scishop/src/components/PalettePicker/ModeSelector.vue @@ -0,0 +1,75 @@ + + + + diff --git a/apps/scishop/src/components/PalettePicker/PalettePicker.vue b/apps/scishop/src/components/PalettePicker/PalettePicker.vue new file mode 100644 index 0000000..bcbba2c --- /dev/null +++ b/apps/scishop/src/components/PalettePicker/PalettePicker.vue @@ -0,0 +1,164 @@ + + + + + diff --git a/apps/scishop/src/components/PalettePicker/index.ts b/apps/scishop/src/components/PalettePicker/index.ts new file mode 100644 index 0000000..469ca1e --- /dev/null +++ b/apps/scishop/src/components/PalettePicker/index.ts @@ -0,0 +1,2 @@ +import PalettePicker from './PalettePicker.vue'; +export default PalettePicker; diff --git a/apps/scishop/src/components/Sidebar.vue b/apps/scishop/src/components/Sidebar.vue new file mode 100644 index 0000000..8203c01 --- /dev/null +++ b/apps/scishop/src/components/Sidebar.vue @@ -0,0 +1,30 @@ + + + + + diff --git a/apps/scishop/src/components/Stage.vue b/apps/scishop/src/components/Stage.vue new file mode 100644 index 0000000..6a5e35f --- /dev/null +++ b/apps/scishop/src/components/Stage.vue @@ -0,0 +1,137 @@ + + + + + diff --git a/apps/scishop/src/components/StatusBar.vue b/apps/scishop/src/components/StatusBar.vue new file mode 100644 index 0000000..6850fe5 --- /dev/null +++ b/apps/scishop/src/components/StatusBar.vue @@ -0,0 +1,39 @@ + + + diff --git a/apps/scishop/src/components/Toolbar.vue b/apps/scishop/src/components/Toolbar.vue new file mode 100644 index 0000000..eabf1b1 --- /dev/null +++ b/apps/scishop/src/components/Toolbar.vue @@ -0,0 +1,134 @@ + + + + + diff --git a/apps/scishop/src/components/command-items/BrushCommandItem.vue b/apps/scishop/src/components/command-items/BrushCommandItem.vue new file mode 100644 index 0000000..767692c --- /dev/null +++ b/apps/scishop/src/components/command-items/BrushCommandItem.vue @@ -0,0 +1,24 @@ + + + diff --git a/apps/scishop/src/components/command-items/FillCommandItem.vue b/apps/scishop/src/components/command-items/FillCommandItem.vue new file mode 100644 index 0000000..1bf5d51 --- /dev/null +++ b/apps/scishop/src/components/command-items/FillCommandItem.vue @@ -0,0 +1,19 @@ + + + diff --git a/apps/scishop/src/components/command-items/GenericCommandItem.vue b/apps/scishop/src/components/command-items/GenericCommandItem.vue new file mode 100644 index 0000000..d7ddc4f --- /dev/null +++ b/apps/scishop/src/components/command-items/GenericCommandItem.vue @@ -0,0 +1,8 @@ + + + diff --git a/apps/scishop/src/components/command-items/PolyLineCommandItem.vue b/apps/scishop/src/components/command-items/PolyLineCommandItem.vue new file mode 100644 index 0000000..fb8818c --- /dev/null +++ b/apps/scishop/src/components/command-items/PolyLineCommandItem.vue @@ -0,0 +1,19 @@ + + + diff --git a/apps/scishop/src/components/command-items/SetPaletteCommand.vue b/apps/scishop/src/components/command-items/SetPaletteCommand.vue new file mode 100644 index 0000000..666f4de --- /dev/null +++ b/apps/scishop/src/components/command-items/SetPaletteCommand.vue @@ -0,0 +1,13 @@ + + + diff --git a/apps/scishop/src/components/command-items/Swatches.vue b/apps/scishop/src/components/command-items/Swatches.vue new file mode 100644 index 0000000..f415fef --- /dev/null +++ b/apps/scishop/src/components/command-items/Swatches.vue @@ -0,0 +1,91 @@ + + + + + diff --git a/apps/scishop/src/composables/useCanvasRenderer.ts b/apps/scishop/src/composables/useCanvasRenderer.ts new file mode 100644 index 0000000..a73c423 --- /dev/null +++ b/apps/scishop/src/composables/useCanvasRenderer.ts @@ -0,0 +1,75 @@ +import { computed, Ref, shallowRef, triggerRef, unref, watch } from 'vue'; +import { RenderResult } from '@4bitlabs/sci0/dist/screen/render-result.ts'; + +import { DrawCommand, renderPic } from '@4bitlabs/sci0'; +import { createDitherFilter, renderPixelData } from '@4bitlabs/image'; +import { generateSciDitherPairs, Mixers } from '@4bitlabs/color'; +import { nearestNeighbor } from '@4bitlabs/resize-filters'; +import { isEqual, vec2, Vec2 } from '@4bitlabs/vec2'; +import { get2dContext } from '../helpers/getContext'; +import { setCanvasDimensions } from '../helpers/setCanvasDimensions.ts'; +import { clamp } from '../helpers/clamp.ts'; +import { mustInject } from '../data/mustInject.ts'; +import { paletteKey, stageOptionsKey, viewKey } from '../data/keys.ts'; + +export function useRenderedPixels(picDataRef: Ref) { + const { canvasSize } = mustInject(stageOptionsKey); + return computed(() => { + const picData = unref(picDataRef); + const [width, height] = unref(canvasSize); + return renderPic(picData, { width, height }); + }); +} + +export function useCanvasRenderer( + renderedRef: Ref, +): Ref { + const { canvasSize } = mustInject(stageOptionsKey); + const { finalColors } = mustInject(paletteKey); + const { viewZoom: zoomRef } = mustInject(viewKey); + + const canvasRef = shallowRef(new OffscreenCanvas(1, 1)); + + const oversampleRef = computed((prev = [1, 1]) => { + const zoom = unref(zoomRef); + if (zoom > 12) return [1, 1]; + const samples = Math.round(clamp(zoom, 1, 5)); + const next = vec2(samples, samples); + return isEqual(prev, next) ? prev : next; + }); + + const isSoftRef = computed(() => unref(zoomRef) >= 1); + const ditherRef = computed(() => + createDitherFilter( + generateSciDitherPairs( + unref(finalColors), + Mixers.mixBy(0.25), + unref(isSoftRef) ? Mixers.softMixer() : ([a, b]) => [a, b], + ), + ), + ); + + watch( + [renderedRef, canvasSize, oversampleRef, ditherRef], + ([pic, [width, height], oversample, dither]) => { + const canvas = unref(canvasRef); + setCanvasDimensions( + canvas, + width * oversample[0], + height * oversample[1], + ); + + const imgData = renderPixelData(pic.visible, { + dither, + post: [nearestNeighbor(oversample)], + }) as ImageData; + + const ctx = get2dContext(canvas); + ctx.putImageData(imgData, 0, 0); + triggerRef(canvasRef); + }, + { immediate: true }, + ); + + return canvasRef; +} diff --git a/apps/scishop/src/composables/useCursorEffect.ts b/apps/scishop/src/composables/useCursorEffect.ts new file mode 100644 index 0000000..434e9dc --- /dev/null +++ b/apps/scishop/src/composables/useCursorEffect.ts @@ -0,0 +1,51 @@ +import { Ref, ShallowRef, DeepReadonly, unref, computed } from 'vue'; +import { watch } from 'vue'; +import { applyToPoints, Matrix } from 'transformation-matrix'; + +import { setCanvasDimensions } from '../helpers/setCanvasDimensions.ts'; +import { get2dContext } from '../helpers/getContext.ts'; +import { isInsideBounds, pixel } from '../helpers/polygons.ts'; +import { pixelBorder } from '../render/pixel-border.ts'; +import { cursorDot } from '../render/cursor-dot.ts'; +import { mustInject } from '../data/mustInject.ts'; +import { stageOptionsKey, toolKey } from '../data/keys.ts'; +import { CursorPosition } from './useCursorWatcher.ts'; + +export function usePrecisionCursorEffect( + matrixRef: DeepReadonly>, + elRef: ShallowRef, + stageResRef: DeepReadonly>, + cursorPos: CursorPosition, +) { + const toolRef = mustInject(toolKey); + const { canvasSize } = mustInject(stageOptionsKey); + + const isOverCanvas = computed(() => { + const [cWidth, cHeight] = unref(canvasSize); + return isInsideBounds([cWidth, cHeight], unref(cursorPos.pixel)); + }); + + watch( + [elRef, stageResRef, matrixRef, cursorPos.screen, cursorPos.pixel], + ([el, [sWidth, sHeight], matrix, screenPosition, canvasPixel]) => { + if (!el) return; + + setCanvasDimensions(el, sWidth, sHeight); + const ctx = get2dContext(el); + ctx.clearRect(0, 0, sWidth, sHeight); + + const tool = unref(toolRef); + const simpleCursor = !(tool === 'line' || tool === 'fill'); + + if (simpleCursor) return; + + // Draw precision cursor + if (isOverCanvas) { + ctx.save(); + pixelBorder(ctx, applyToPoints(matrix, pixel(canvasPixel, -0.0125))); + cursorDot(ctx, screenPosition); + ctx.restore(); + } + }, + ); +} diff --git a/apps/scishop/src/composables/useCursorWatcher.ts b/apps/scishop/src/composables/useCursorWatcher.ts new file mode 100644 index 0000000..a0d2fd9 --- /dev/null +++ b/apps/scishop/src/composables/useCursorWatcher.ts @@ -0,0 +1,63 @@ +import { computed, ComputedRef, Ref, ShallowRef } from 'vue'; +import { onMounted, onUnmounted, unref } from 'vue'; +import { applyToPoint, inverse, Matrix } from 'transformation-matrix'; + +import { isEqual, round, vec2 } from '@4bitlabs/vec2'; +import { useRafRef } from './useRafRef.ts'; +import { isInsideBounds } from '../helpers/polygons.ts'; +import { mustInject } from '../data/mustInject.ts'; +import { stageOptionsKey } from '../data/keys.ts'; + +export interface CursorPosition { + screen: Readonly>; + canvas: Readonly>; + pixel: Readonly>; + isOver: Readonly>; +} + +export function useCursorWatcher( + targetRef: ShallowRef, + matrixRef: Ref, +): CursorPosition { + const { canvasSize } = mustInject(stageOptionsKey); + + const iMatrixRef = computed(() => inverse(unref(matrixRef))); + + const screenPositionRef = useRafRef<[number, number]>([0, 0]); + const canvasPositionRef = computed<[number, number]>((prev = vec2()) => { + const actual = applyToPoint(unref(iMatrixRef), unref(screenPositionRef)); + const next = round(actual, vec2(), (i) => Math.floor(i * 8) / 8); + return isEqual(prev, next) ? prev : next; + }); + + const canvasPixelRef = computed<[number, number]>((prev = vec2()) => { + const next = round(unref(canvasPositionRef), vec2(), Math.floor); + return isEqual(prev, next) ? prev : next; + }); + + const isOverCanvasRef = computed(() => { + const canvasPoint = unref(canvasPixelRef); + const [cWidth, cHeight] = unref(canvasSize); + return isInsideBounds([cWidth, cHeight], canvasPoint); + }); + + onMounted(() => { + const target = unref(targetRef); + if (!target) return; + target.addEventListener('pointermove', (e) => { + screenPositionRef.value = [e.offsetX, e.offsetY]; + }); + }); + + onUnmounted(() => { + const target = unref(targetRef); + if (!target) return; + }); + + return { + screen: screenPositionRef, + canvas: canvasPositionRef, + pixel: canvasPixelRef, + isOver: isOverCanvasRef, + }; +} diff --git a/apps/scishop/src/composables/useDebouncedRef.ts b/apps/scishop/src/composables/useDebouncedRef.ts new file mode 100644 index 0000000..cadd737 --- /dev/null +++ b/apps/scishop/src/composables/useDebouncedRef.ts @@ -0,0 +1,18 @@ +import { customRef } from 'vue'; + +export function useDebouncedRef(value: T, delay = 100) { + let timeout: ReturnType | null = null; + return customRef((track, trigger) => ({ + get() { + track(); + return value; + }, + set(newValue) { + if (timeout) clearTimeout(timeout); + timeout = setTimeout(() => { + value = newValue; + trigger(); + }, delay); + }, + })); +} diff --git a/apps/scishop/src/composables/useFindTool.ts b/apps/scishop/src/composables/useFindTool.ts new file mode 100644 index 0000000..c08120b --- /dev/null +++ b/apps/scishop/src/composables/useFindTool.ts @@ -0,0 +1,50 @@ +import { computed, onMounted, onUnmounted, Ref, unref } from 'vue'; + +import { RenderResult } from '@4bitlabs/sci0'; +import { mustInject } from '../data/mustInject.ts'; +import { pointersKey, stageOptionsKey, toolKey } from '../data/keys.ts'; +import { CursorPosition } from './useCursorWatcher.ts'; + +const MAX = ~0 >>> 0; + +export function useFindTool( + target: Ref, + pixels: Ref, + pos: CursorPosition, +) { + const { selectedIdx, topIdx } = mustInject(pointersKey); + const toolRef = mustInject(toolKey); + const { canvasSize } = mustInject(stageOptionsKey); + + const commandUnderPosition = computed(() => { + const isOver = unref(pos.isOver); + if (!isOver) return -1; + + const [x, y] = unref(pos.pixel); + const [sWidth] = unref(canvasSize); + const { tBuffer } = unref(pixels); + + const cmd = tBuffer[y * sWidth + x]; + return cmd !== MAX ? cmd : -1; + }); + + const handleClick = (e: MouseEvent) => { + if (unref(toolRef) !== 'find') return; + const val = unref(commandUnderPosition); + if (val === -1) return; + selectedIdx.value = val; + if (e.shiftKey) topIdx.value = val + 1; + }; + + onMounted(() => { + const el = unref(target); + if (!el) return; + el.addEventListener('click', handleClick); + }); + + onUnmounted(() => { + const el = unref(target); + if (!el) return; + el.removeEventListener('click', handleClick); + }); +} diff --git a/apps/scishop/src/composables/useInputMachine.ts b/apps/scishop/src/composables/useInputMachine.ts new file mode 100644 index 0000000..a46b799 --- /dev/null +++ b/apps/scishop/src/composables/useInputMachine.ts @@ -0,0 +1,626 @@ +import type { ShallowRef } from 'vue'; +import { + computed, + onMounted, + shallowRef, + unref, + watch, + watchEffect, + onUnmounted, +} from 'vue'; +import { + applyToPoint, + applyToPoints, + compose, + inverse, + Matrix, + rotateDEG, + scale, + translate, +} from 'transformation-matrix'; +import deepEqual from 'fast-deep-equal'; + +import { round, vec2, Vec2, sub, add } from '@4bitlabs/vec2'; +import { FillCommand, PolylineCommand } from '@4bitlabs/sci0'; +import { get2dContext } from '../helpers/getContext.ts'; +import { isInsidePolygon, pathPoly, rect } from '../helpers/polygons.ts'; +import { fillSkeleton } from '../render/fill-skeleton.ts'; +import { plineSkeleton } from '../render/pline-skeleton.ts'; +import { + anyPointCloseTo, + extractVertices, + FindResult, + moveFillVertex, + moveLineVertex, + mustGetVertexFrom, + nearestPointWithRange, + PointAlongPathResult, + pointAlongPaths, +} from '../helpers/command-helpers.ts'; +import { insert, remove } from '../helpers/array-helpers.ts'; +import { BasicEditorCommand } from '../models/EditorCommand.ts'; +import cursorPenSvg from '../assets/cursor-pen.svg'; +import cursorPenStarSvg from '../assets/cursor-pen-star.svg'; +import cursorPenPlusSvg from '../assets/cursor-pen-plus.svg'; +import cursorPenMinusSvg from '../assets/cursor-pen-minus.svg'; +import { setCanvasDimensions } from '../helpers/setCanvasDimensions.ts'; +import { pointSkeleton } from '../render/point-skeleton.ts'; +import { useUpdateSelectionFn } from '../data/useUpdateSelectionFn.ts'; +import { useCurrentCommandActions } from '../data/useCurrentCommandActions.ts'; +import { + currentKey, + drawStateKey, + layersKey, + pointersKey, + toolKey, + viewKey, +} from '../data/keys.ts'; +import { mustInject } from '../data/mustInject.ts'; +import { CursorPosition } from './useCursorWatcher.ts'; + +const clampZoom = (current: number, next: number, min: number, max: number) => { + if (current * next < min) return min / current; + if (current * next > max) return max / current; + return next; +}; + +type SelectionEntry = [layerIdx: number, cmdIdx: number, vertexIdx: number]; + +type EmptyDragState = ['none']; +type ViewDragState = ['view', iMatrix: Matrix, iPosition: Vec2]; +type PointDragEntry = [...SelectionEntry, initial: Vec2]; +type PointDragState = ['point', iPosition: Vec2, ...PointDragEntry[]]; +type SelectionDragState = ['sel-rect', start: Vec2]; + +type DragStates = + | EmptyDragState + | ViewDragState + | PointDragState + | SelectionDragState; + +export function useInputMachine( + matrixRef: ShallowRef, + canvasRef: ShallowRef, + selCanvasRef: ShallowRef, + stageResRef: ShallowRef<[number, number]>, + cursorPosition: CursorPosition, +) { + const layersRef = mustInject(layersKey); + const toolRef = mustInject(toolKey); + const currentRef = mustInject(currentKey); + const { matrix: viewMatrixRef, viewZoom: zoomRef } = mustInject(viewKey); + const { raw: rawDrawState } = mustInject(drawStateKey); + const { topIdx: topIdxRef, selectedIdx: selectedIdxRef } = + mustInject(pointersKey); + + const updateSelection = useUpdateSelectionFn({ + layers: layersRef, + topIdx: topIdxRef, + selectedIdx: selectedIdxRef, + }); + + const cmdStore = useCurrentCommandActions({ + layers: layersRef, + current: currentRef, + topIdx: topIdxRef, + selectedIdx: selectedIdxRef, + }); + + const selectedLayerRef = computed(() => { + const selIdx = unref(selectedIdxRef); + return selIdx !== null ? unref(layersRef)[selIdx] : null; + }); + + const activeLayerRef = computed(() => { + const current = unref(currentRef); + if (current) return current; + return unref(selectedLayerRef); + }); + + const pointerRadiusRef = computed(() => + Math.max(0.404, 7.5 / unref(zoomRef)), + ); + + const iMatrixRef = computed(() => inverse(unref(matrixRef))); + const dragStateRef = shallowRef(['none']); + const selectionStateRef = shallowRef([]); + + const { + screen: lastCursorPositionRef, + canvas: canvasPositionRef, + pixel: canvasPixelRef, + } = cursorPosition; + + const nearestExistingPointRef = computed((prev = null) => { + const cmds = unref(selectedLayerRef)?.commands; + if (!cmds || cmds.length < 1) return null; + + const cPos = unref(canvasPositionRef); + const radius = unref(pointerRadiusRef); + const next = nearestPointWithRange(cmds, cPos, radius); + + return deepEqual(prev, next) ? prev : next; + }); + + const nearestAddPointRef = computed( + (prev = null) => { + const cmds = unref(selectedLayerRef)?.commands; + if (!cmds || cmds.length < 1) return null; + + const cPos = unref(canvasPositionRef); + const radius = unref(pointerRadiusRef); + const next = pointAlongPaths(cmds, cPos, radius); + + return deepEqual(prev, next) ? prev : next; + }, + ); + + // Cursor updater + watchEffect(() => { + const el = unref(canvasRef); + if (!el) return; + + const [dragMode] = unref(dragStateRef); + const selectedTool = unref(toolRef); + + let currentCursor = 'auto'; + if (dragMode === 'view') { + currentCursor = 'grabbing'; + } else if (selectedTool === 'find') { + currentCursor = 'crosshair'; + } else if (selectedTool === 'pan') { + currentCursor = 'grab'; + } else if (selectedTool === 'select') { + currentCursor = 'crosshair'; + } else if (selectedTool == 'line') { + const isOverCanvas = unref(cursorPosition.isOver); + if (isOverCanvas) { + if (unref(currentRef)) { + currentCursor = `url(${cursorPenSvg}) 0 0, none`; + } else if (unref(nearestExistingPointRef) !== null) { + currentCursor = `url(${cursorPenMinusSvg}) 0 0, none`; + } else if (unref(nearestAddPointRef) !== null) { + currentCursor = `url(${cursorPenPlusSvg}) 0 0, none`; + } else { + currentCursor = `url(${cursorPenStarSvg}) 0 0, none`; + } + } + } + el.style.cursor = currentCursor; + }); + + // Apply current pan state + watch([dragStateRef, lastCursorPositionRef], ([dragState, [cX, cY]]) => { + const [mode] = dragState; + if (mode !== 'view') return; + + const [, matrix, [ix, iy]] = dragState; + const dx = ix - cX; + const dy = iy - cY; + viewMatrixRef.value = compose(translate(-dx, -dy), matrix); + }); + + // Apply point drag state + watch([canvasPositionRef, dragStateRef], ([currentPosition, dragState]) => { + if (!dragState) return; + const [mode] = dragState; + if (mode !== 'point') return; + + const [, initialPosition, ...pairs] = dragState; + const delta = sub(currentPosition, initialPosition); + + layersRef.value = pairs.reduce((prevLayers, [lIdx, cIdx, vIdx, iVec]) => { + const nextVec = round(add(iVec, delta)); + const layer = prevLayers[lIdx]; + + switch (layer?.type) { + case 'PLINE': { + const cmd = layer.commands[cIdx]; + const next: BasicEditorCommand = { + ...layer, + commands: [moveLineVertex(cmd, vIdx, nextVec)], + }; + return insert(prevLayers, lIdx, next, true); + } + case 'FILL': { + const cmd = layer.commands[cIdx]; + const next: BasicEditorCommand = { + ...layer, + commands: [moveFillVertex(cmd, nextVec)], + }; + return insert(prevLayers, lIdx, next, true); + } + } + return prevLayers; + }, unref(layersRef)); + }); + + // Update Selection UI + watch( + [selCanvasRef, stageResRef, dragStateRef, lastCursorPositionRef], + ([el, [sWidth, sHeight], dragState, p1]) => { + if (!el) return; + setCanvasDimensions(el, sWidth, sHeight); + + const [dragMode] = dragState; + if (dragMode !== 'sel-rect') return; + + const ctx = get2dContext(el); + ctx.clearRect(0, 0, sWidth, sHeight); + + const [, p0] = dragState; + ctx.save(); + const selRect = rect(p0, p1); + pathPoly(ctx, selRect); + ctx.fillStyle = 'rgba(42 82 190 / 25%)'; + ctx.fill(); + ctx.strokeStyle = 'rgba(42 82 190 / 100%)'; + ctx.stroke(); + ctx.restore(); + }, + ); + + watch([selCanvasRef, dragStateRef], ([el, [dragMode]]) => { + if (!el) return; + if (dragMode !== 'none') return; + setCanvasDimensions(el, 1, 1); + const ctx = get2dContext(el); + ctx.clearRect(0, 0, 1, 1); + }); + + // Update the UI canvas + watch( + [canvasRef, stageResRef, matrixRef, activeLayerRef, selectionStateRef], + ([el, [sWidth, sHeight], matrix, layer, selection]) => { + if (!el) return; + setCanvasDimensions(el, sWidth, sHeight); + const ctx = get2dContext(el); + ctx.clearRect(0, 0, sWidth, sHeight); + + if (layer) { + ctx.save(); + layer.commands.forEach((cmd) => { + const [type] = cmd; + ctx.strokeStyle = 'white'; + ctx.fillStyle = '#ddd'; + if (type === 'PLINE') plineSkeleton(ctx, matrix, cmd); + if (type === 'FILL') fillSkeleton(ctx, matrix, cmd); + }); + ctx.restore(); + } + + ctx.save(); + ctx.fillStyle = 'white'; + selection.forEach(([lIdx, cIdx, vIdx]) => { + const layers = unref(layersRef); + const cmd = layers[lIdx]?.commands?.[cIdx]; + if (!cmd) return; + const [type] = cmd; + if (type === 'PLINE' || type === 'FILL') { + const vertex = extractVertices(cmd)[vIdx]; + if (!vertex) return; + pointSkeleton(ctx, matrix, vertex); + } + }); + ctx.restore(); + }, + ); + + // update current + watch([canvasPixelRef], ([pos]) => { + const current = unref(currentRef); + if (current === null) return; + if (current.type === 'PLINE') { + const [type, options, ...coords] = current.commands[0]; + cmdStore.begin({ + ...current, + commands: [[type, options, ...coords.slice(0, -1), pos]], + }); + } + }); + + watch([dragStateRef, lastCursorPositionRef], ([dragState, p1]) => { + const [mode] = dragState; + + if (mode !== 'sel-rect') return; + + // TODO rethink this + const layerIdx = unref(selectedIdxRef); + if (layerIdx === null) return; + const selectedLayer = unref(layersRef)[layerIdx]; + if (!selectedLayer) return; + if (selectedLayer.type !== 'PLINE' && selectedLayer.type !== 'FILL') return; + + const [, p0] = dragState; + const iMatrix = unref(iMatrixRef); + const bounds = applyToPoints(iMatrix, rect(p0, p1)); + const selPoints: SelectionEntry[] = []; + selectedLayer.commands.forEach((cmd, cmdIdx) => { + const verts = extractVertices(cmd); + verts.forEach((v, vIdx) => { + if (!isInsidePolygon(bounds, v)) return; + selPoints.push([layerIdx, cmdIdx, vIdx]); + }); + }); + selectionStateRef.value = selPoints; + }); + + const mouseHandlers = { + contextMenu: (e: MouseEvent) => { + const selectedTool = unref(toolRef); + if (selectedTool === 'line') { + e.preventDefault(); + const current = unref(currentRef); + if (current === null || current.type !== 'PLINE') return; + + const [type, options, ...coords] = current.commands[0]; + if (coords.length < 3) { + cmdStore.abort(); + } else { + cmdStore.commit({ + ...current, + commands: [[type, options, ...coords.slice(0, -1)]], + }); + } + return; + } + + if (selectedTool === 'select') { + e.preventDefault(); + } + }, + + wheel: (e: WheelEvent) => { + const stage = unref(canvasRef); + if (!stage) return; + const [sWidth, sHeight] = unref(stageResRef); + const [dx, dy] = [e.offsetX - sWidth / 2, e.offsetY - sHeight / 2]; + const scaleFactor = clampZoom( + unref(zoomRef), + e.deltaY / 1000 + 1, + 0.5, + 100, + ); + viewMatrixRef.value = compose( + translate(dx, dy), + scale(scaleFactor), + rotateDEG(-e.deltaX / 40), + translate(-dx, -dy), + unref(viewMatrixRef), + ); + }, + }; + + const pointerHandlers = { + downPan(e: PointerEvent) { + const selectedTool = unref(toolRef); + const startPan = + (selectedTool === 'pan' && e.button === 0) || e.button === 1; + + if (!startPan) return; + + dragStateRef.value = [ + 'view', + unref(viewMatrixRef), + [e.offsetX, e.offsetY], + ]; + + e.stopImmediatePropagation(); + e.preventDefault(); + }, + + downSelectionMoveStart(e: PointerEvent) { + const selectedTool = unref(toolRef); + if (!(selectedTool === 'select' && e.button === 0)) return; + + const layers = unref(layersRef); + const cPos = unref(canvasPositionRef); + + const selState = unref(selectionStateRef); + const selectedVertices = selState.flatMap(([lIdx, cIdx, vIdx]) => { + const cmd = layers[lIdx]?.commands[cIdx]; + if (!cmd) return []; + if (cmd[0] !== 'PLINE' && cmd[0] !== 'FILL') return []; + const vertex = extractVertices(cmd)[vIdx]; + if (!vertex) return []; + const [x, y] = vertex; + return [vec2(x + 0.5, y + 0.5)]; + }); + + const found = anyPointCloseTo( + selectedVertices, + cPos, + unref(pointerRadiusRef), + ); + + if (!found) return; + + const dragEntries = selState.map(([lIdx, cIdx, pIdx]) => [ + lIdx, + cIdx, + pIdx, + mustGetVertexFrom(layers[lIdx].commands, cIdx, pIdx), + ]); + + dragStateRef.value = ['point', cPos, ...dragEntries]; + + e.stopImmediatePropagation(); + e.preventDefault(); + }, + + downPointSelect(e: PointerEvent) { + const selectedTool = unref(toolRef); + if (!(selectedTool === 'select' && e.button === 0)) return; + + const layers = unref(layersRef); + const cPos = unref(canvasPositionRef); + + const iIdx = unref(selectedIdxRef); + if (iIdx === null) return; + + const layer = layers[iIdx]; + if (!layer) return; + + const found = nearestPointWithRange( + layer.commands, + cPos, + unref(pointerRadiusRef), + ); + + if (found) { + const [cIdx, pIdx] = found; + const p = mustGetVertexFrom(layer.commands, cIdx, pIdx); + dragStateRef.value = ['point', cPos, [iIdx, cIdx, pIdx, p]]; + + e.stopImmediatePropagation(); + e.preventDefault(); + return; + } + }, + + downPointStartRect(e: PointerEvent) { + const selectedTool = unref(toolRef); + if (!(selectedTool === 'select' && e.button === 0)) return; + selectionStateRef.value = []; + dragStateRef.value = ['sel-rect', vec2(e.offsetX, e.offsetY)]; + }, + + downFill(e: MouseEvent) { + const selectedTool = unref(toolRef); + if (!(selectedTool === 'fill' && e.button === 0)) { + return; + } + + const pos = round( + applyToPoint(unref(iMatrixRef), [e.offsetX, e.offsetY]), + vec2(), + Math.floor, + ); + + const [drawMode, ...drawCodes] = unref(rawDrawState); + cmdStore.commit({ + id: Math.random().toString(36).substring(2), + type: 'FILL', + commands: [['FILL', [drawMode, drawCodes], pos]], + }); + + e.preventDefault(); + }, + + downLine(e: MouseEvent) { + const selectedTool = unref(toolRef); + if (!(selectedTool === 'line' && e.button === 0)) { + return; + } + + const pos = round( + applyToPoint(unref(iMatrixRef), [e.offsetX, e.offsetY]), + vec2(), + Math.floor, + ); + + const current = unref(currentRef); + + // Append to current line + if (current?.type === 'PLINE') { + const [type, options, ...coords] = current.commands[0]; + cmdStore.begin({ + ...current, + commands: [ + [type, options, ...coords.slice(0, -1), [...pos], [...pos]], + ], + }); + e.preventDefault(); + return; + } + + // Remove an existing point on the selected line + const nearestExistingPoint = unref(nearestExistingPointRef); + if (nearestExistingPoint) { + const [cmdIdx, pointIdx] = nearestExistingPoint; + updateSelection((prev) => { + if (prev.type !== 'PLINE') return prev; + + const [type, options, ...prevVerts] = prev.commands[cmdIdx]; + const nextVerts = remove(prevVerts, pointIdx); + + if (nextVerts.length <= 1) return null; + return { + ...prev, + commands: [[type, options, ...nextVerts]], + }; + }); + e.preventDefault(); + return; + } + + // Insert a new point on an existing line + const nearestAddPoint = unref(nearestAddPointRef); + if (nearestAddPoint) { + const [cmdIdx, , idx, vert] = nearestAddPoint; + updateSelection((prev) => { + if (prev.type !== 'PLINE') return prev; + const [type, options, ...prevVerts] = prev.commands[cmdIdx]; + const nextVerts = insert( + prevVerts, + idx, + round(vert, vert, Math.floor), + ); + return { + ...prev, + commands: [[type, options, ...nextVerts]], + }; + }); + e.preventDefault(); + return; + } + + // Start a new line + if (current === null) { + const [drawMode, ...drawCodes] = unref(rawDrawState); + cmdStore.begin({ + id: Math.random().toString(36).substring(2), + type: 'PLINE', + commands: [['PLINE', [drawMode, drawCodes], pos, pos]], + }); + e.preventDefault(); + return; + } + }, + + up() { + dragStateRef.value = ['none']; + }, + }; + + onMounted(() => { + const el = unref(canvasRef); + if (!el) return; + el.addEventListener('contextmenu', mouseHandlers.contextMenu); + el.addEventListener('wheel', mouseHandlers.wheel); + el.addEventListener('pointerdown', pointerHandlers.downPan); + el.addEventListener('pointerdown', pointerHandlers.downSelectionMoveStart); + el.addEventListener('pointerdown', pointerHandlers.downPointSelect); + el.addEventListener('pointerdown', pointerHandlers.downPointStartRect); + el.addEventListener('pointerdown', pointerHandlers.downFill); + el.addEventListener('pointerdown', pointerHandlers.downLine); + el.addEventListener('pointerup', pointerHandlers.up); + }); + + onUnmounted(() => { + const el = unref(canvasRef); + if (!el) return; + el.removeEventListener('pointerup', pointerHandlers.up); + el.removeEventListener('pointerdown', pointerHandlers.downLine); + el.removeEventListener('pointerdown', pointerHandlers.downFill); + el.removeEventListener('pointerdown', pointerHandlers.downPointStartRect); + el.removeEventListener('pointerdown', pointerHandlers.downPointSelect); + el.removeEventListener( + 'pointerdown', + pointerHandlers.downSelectionMoveStart, + ); + el.removeEventListener('pointerdown', pointerHandlers.downPan); + el.removeEventListener('wheel', mouseHandlers.wheel); + el.removeEventListener('contextmenu', mouseHandlers.contextMenu); + }); +} diff --git a/apps/scishop/src/composables/useRafRef.ts b/apps/scishop/src/composables/useRafRef.ts new file mode 100644 index 0000000..ec645dd --- /dev/null +++ b/apps/scishop/src/composables/useRafRef.ts @@ -0,0 +1,21 @@ +import { customRef } from 'vue'; + +export function useRafRef(initialValue: T) { + let value = initialValue; + let rafHandle: number | null = null; + + return customRef((track, trigger) => ({ + get() { + track(); + return value; + }, + set(newValue) { + if (rafHandle) cancelAnimationFrame(rafHandle); + rafHandle = requestAnimationFrame(() => { + value = newValue; + trigger(); + rafHandle = null; + }); + }, + })); +} diff --git a/apps/scishop/src/composables/useResizeWatcher.ts b/apps/scishop/src/composables/useResizeWatcher.ts new file mode 100644 index 0000000..1afcdaa --- /dev/null +++ b/apps/scishop/src/composables/useResizeWatcher.ts @@ -0,0 +1,37 @@ +import { watch, onUnmounted, triggerRef, ShallowRef } from 'vue'; + +import { useRafRef } from './useRafRef.ts'; + +export function useResizeWatcher( + refEl: ShallowRef, +) { + const resolution = useRafRef<[width: number, height: number]>([-1, -1]); + + const visiblityHandler = () => { + if (!document.hidden) { + triggerRef(resolution); + } + }; + + document.addEventListener('visibilitychange', visiblityHandler); + + const watcher = new ResizeObserver((els) => { + const match = els.find((it) => it.target === refEl.value); + resolution.value = [ + match?.contentRect.width ?? 0, + match?.contentRect.height ?? 0, + ]; + }); + + watch(refEl, (next, prev) => { + if (prev) watcher.unobserve(prev); + if (next) watcher.observe(next); + }); + + onUnmounted(() => { + watcher.disconnect(); + document.removeEventListener('visibilitychange', visiblityHandler); + }); + + return resolution; +} diff --git a/apps/scishop/src/data/initial-pic-data.ts b/apps/scishop/src/data/initial-pic-data.ts new file mode 100644 index 0000000..c8ffc13 --- /dev/null +++ b/apps/scishop/src/data/initial-pic-data.ts @@ -0,0 +1,14 @@ +import { DrawCommand } from '@4bitlabs/sci0'; +import { EditorCommand } from '../models/EditorCommand.ts'; +import d from './tmp3.json'; + +const nextId = () => Math.random().toString(36).substring(2); + +const wrapRawCommand = (cmd: DrawCommand): EditorCommand => + ({ id: nextId(), type: cmd[0], commands: [cmd] }) as EditorCommand; + +const refData: DrawCommand[] = [...(d as unknown as DrawCommand[])]; + +const data: EditorCommand[] = refData.map(wrapRawCommand); + +export default data; diff --git a/apps/scishop/src/data/keys.ts b/apps/scishop/src/data/keys.ts new file mode 100644 index 0000000..24ee3b0 --- /dev/null +++ b/apps/scishop/src/data/keys.ts @@ -0,0 +1,20 @@ +import { InjectionKey, Ref, ShallowRef } from 'vue'; + +import type { EditorCommand } from '../models/EditorCommand.ts'; +import type { DrawStateStore } from './stores/draw-state-store.ts'; +import type { Tool } from '../models/tool.ts'; +import { LayerPointerStore } from './stores/layer-pointer-store.ts'; +import { ViewStore } from './stores/view-store.ts'; +import { PaletteStore } from './stores/palette-store.ts'; +import { StageOptionStore } from './stores/stage-option-store.ts'; + +const keyOf = (name: string = '') => Symbol(name) as InjectionKey; + +export const layersKey = keyOf>('layers'); +export const pointersKey = keyOf('pointers'); +export const stageOptionsKey = keyOf('stageOptions'); +export const currentKey = keyOf>('current'); +export const viewKey = keyOf('viewKey'); +export const toolKey = keyOf>('currentTool'); +export const drawStateKey = keyOf('drawState'); +export const paletteKey = keyOf('palette'); diff --git a/apps/scishop/src/data/mustInject.ts b/apps/scishop/src/data/mustInject.ts new file mode 100644 index 0000000..c70b943 --- /dev/null +++ b/apps/scishop/src/data/mustInject.ts @@ -0,0 +1,12 @@ +import { InjectionKey } from '@vue/runtime-core'; +import { inject } from 'vue'; + +export const mustInject = ( + key: InjectionKey, + message = 'injection error', +) => { + const value = inject(key); + if (value === undefined) + throw new Error(`${message}: ${String(key)} is not set`); + return value; +}; diff --git a/apps/scishop/src/data/stores/draw-state-store.ts b/apps/scishop/src/data/stores/draw-state-store.ts new file mode 100644 index 0000000..7094ee0 --- /dev/null +++ b/apps/scishop/src/data/stores/draw-state-store.ts @@ -0,0 +1,13 @@ +import { Ref, WritableComputedRef } from 'vue'; + +import { DrawCodes, DrawMode } from '@4bitlabs/sci0'; + +export interface DrawStateStore { + readonly raw: Ref<[DrawMode, ...DrawCodes]>; + readonly visualEnabled: WritableComputedRef; + readonly priorityEnabled: WritableComputedRef; + readonly controlEnabled: WritableComputedRef; + readonly visualCode: WritableComputedRef; + readonly priorityCode: WritableComputedRef; + readonly controlCode: WritableComputedRef; +} diff --git a/apps/scishop/src/data/stores/layer-pointer-store.ts b/apps/scishop/src/data/stores/layer-pointer-store.ts new file mode 100644 index 0000000..275b4f2 --- /dev/null +++ b/apps/scishop/src/data/stores/layer-pointer-store.ts @@ -0,0 +1,6 @@ +import type { Ref } from 'vue'; + +export interface LayerPointerStore { + topIdx: Ref; + selectedIdx: Ref; +} diff --git a/apps/scishop/src/data/stores/palette-store.ts b/apps/scishop/src/data/stores/palette-store.ts new file mode 100644 index 0000000..5ff6840 --- /dev/null +++ b/apps/scishop/src/data/stores/palette-store.ts @@ -0,0 +1,14 @@ +import type { ComputedRef, Ref } from 'vue'; + +import type { PaletteSet } from '../../helpers/palette-helpers.ts'; + +export interface PaletteStore { + readonly contrast: Ref; + readonly baseColors: Ref; + readonly variant: Ref<0 | 1 | 2 | 3>; + readonly finalColors: ComputedRef; + readonly topPaletteSet: ComputedRef; + readonly currentPalette: ComputedRef; + + resolvePaletteAtIdx(n: number): PaletteSet; +} diff --git a/apps/scishop/src/data/stores/stage-option-store.ts b/apps/scishop/src/data/stores/stage-option-store.ts new file mode 100644 index 0000000..be00129 --- /dev/null +++ b/apps/scishop/src/data/stores/stage-option-store.ts @@ -0,0 +1,10 @@ +import type { ComputedRef, Ref } from 'vue'; +import type { Matrix } from 'transformation-matrix'; + +import type { Vec2 } from '@4bitlabs/vec2'; + +export interface StageOptionStore { + canvasSize: Ref; + aspectRatio: Ref; + aspectRatioScaleComponent: ComputedRef; +} diff --git a/apps/scishop/src/data/stores/view-store.ts b/apps/scishop/src/data/stores/view-store.ts new file mode 100644 index 0000000..9af6e3d --- /dev/null +++ b/apps/scishop/src/data/stores/view-store.ts @@ -0,0 +1,8 @@ +import type { ComputedRef, ShallowRef } from 'vue'; +import type { Matrix } from 'transformation-matrix'; + +export interface ViewStore { + readonly matrix: ShallowRef; + readonly viewZoom: ComputedRef; + readonly viewRotation: ComputedRef; +} \ No newline at end of file diff --git a/apps/scishop/src/data/useAppStoreProvider.ts b/apps/scishop/src/data/useAppStoreProvider.ts new file mode 100644 index 0000000..2e47421 --- /dev/null +++ b/apps/scishop/src/data/useAppStoreProvider.ts @@ -0,0 +1,61 @@ +import { computed, provide, ref, shallowRef, unref } from 'vue'; +import { decomposeTSR, identity, scale } from 'transformation-matrix'; + +import { vec2 } from '@4bitlabs/vec2'; +import * as Keys from './keys.ts'; +import data from './initial-pic-data.ts'; +import { Tool } from '../models/tool.ts'; +import { useDrawStateProvider } from './useDrawStateProvider.ts'; +import { usePaletteProvider } from './usePaletteProvider.ts'; +import { EditorCommand } from '../models/EditorCommand.ts'; + +export function useAppStoreProvider() { + // Layers + const layersRef = shallowRef(data); + const topIdxRef = ref(data.length); + const selectedIdxRef = ref(data.length); + + provide(Keys.layersKey, layersRef); + provide(Keys.pointersKey, { + topIdx: topIdxRef, + selectedIdx: selectedIdxRef, + }); + + // Current + const currentRef = shallowRef(null); + provide(Keys.currentKey, currentRef); + + // Stage + const stageSize = ref(vec2(320, 190)); + const aspectRatio = ref(vec2(5, 6)); + const aspectRatioScaleComponent = computed(() => { + const [width, height] = unref(aspectRatio); + return scale(1, 1 / (width / height)); + }); + + provide(Keys.stageOptionsKey, { + canvasSize: stageSize, + aspectRatio, + aspectRatioScaleComponent, + }); + + // Toolbar + const toolRef = ref('pan'); + + provide(Keys.toolKey, toolRef); + + // View + const viewMatrixRef = shallowRef(identity()); + const viewTransformsRef = computed(() => decomposeTSR(unref(viewMatrixRef))); + const viewZoom = computed(() => { + const { sx, sy } = unref(viewTransformsRef).scale; + return (sx + sy) / 2; + }); + const viewRotation = computed(() => unref(viewTransformsRef).rotation.angle); + provide(Keys.viewKey, { matrix: viewMatrixRef, viewZoom, viewRotation }); + + // DrawState + useDrawStateProvider(); + // Palette + usePaletteProvider({ layersRef, topIdxRef }); +} diff --git a/apps/scishop/src/data/useCurrentCommandActions.ts b/apps/scishop/src/data/useCurrentCommandActions.ts new file mode 100644 index 0000000..eff119f --- /dev/null +++ b/apps/scishop/src/data/useCurrentCommandActions.ts @@ -0,0 +1,34 @@ +import { ShallowRef, Ref, unref } from 'vue'; + +import { EditorCommand } from '../models/EditorCommand.ts'; +import { insert } from '../helpers/array-helpers.ts'; + +export function useCurrentCommandActions(deps: { + layers: ShallowRef; + current: ShallowRef; + selectedIdx: Ref; + topIdx: Ref; +}) { + const { + layers: layersRef, + current: currentRef, + selectedIdx: selectedIdxRef, + topIdx: topIdxRef, + } = deps; + + return { + begin(cmd: EditorCommand) { + currentRef.value = cmd; + }, + commit(cmd: EditorCommand) { + currentRef.value = null; + const insertPosition = unref(topIdxRef); + layersRef.value = insert(layersRef.value, insertPosition, cmd); + topIdxRef.value += 1; + selectedIdxRef.value = insertPosition; + }, + abort() { + currentRef.value = null; + }, + }; +} diff --git a/apps/scishop/src/data/useDrawStateProvider.ts b/apps/scishop/src/data/useDrawStateProvider.ts new file mode 100644 index 0000000..bc49e87 --- /dev/null +++ b/apps/scishop/src/data/useDrawStateProvider.ts @@ -0,0 +1,59 @@ +import { computed, provide, Ref, ref, unref } from 'vue'; + +import { DrawCodes, DrawMode } from '@4bitlabs/sci0'; +import * as Keys from './keys.ts'; + +const changeDrawMode = (mode: DrawMode, flag: DrawMode, enabled: boolean) => + enabled ? mode | flag : mode & ~flag; + +const ModePosition: Record< + DrawMode.Visual | DrawMode.Priority | DrawMode.Control, + 1 | 2 | 3 +> = { + [DrawMode.Visual]: 1, + [DrawMode.Priority]: 2, + [DrawMode.Control]: 3, +}; + +const codeComputed = ( + source: Ref<[DrawMode, ...DrawCodes]>, + mode: DrawMode.Visual | DrawMode.Priority | DrawMode.Control, +) => + computed({ + get: () => unref(source)[ModePosition[mode]], + set(next) { + source.value[0] |= DrawMode.Visual; + source.value[ModePosition[mode]] = next; + }, + }); + +const enabledComputed = ( + source: Ref<[DrawMode, ...DrawCodes]>, + mode: DrawMode.Visual | DrawMode.Priority | DrawMode.Control, +) => + computed({ + get: () => (unref(source)[0] & mode) !== 0, + set(next) { + const prev = unref(source)[0]; + source.value[0] = changeDrawMode(prev, mode, next); + }, + }); + +export function useDrawStateProvider() { + const drawState = ref<[DrawMode, ...DrawCodes]>([ + DrawMode.Visual | DrawMode.Priority, + 0, + 0, + 0, + ]); + + provide(Keys.drawStateKey, { + raw: drawState, + visualEnabled: enabledComputed(drawState, DrawMode.Visual), + priorityEnabled: enabledComputed(drawState, DrawMode.Priority), + controlEnabled: enabledComputed(drawState, DrawMode.Control), + visualCode: codeComputed(drawState, DrawMode.Visual), + priorityCode: codeComputed(drawState, DrawMode.Priority), + controlCode: codeComputed(drawState, DrawMode.Control), + }); +} diff --git a/apps/scishop/src/data/usePaletteProvider.ts b/apps/scishop/src/data/usePaletteProvider.ts new file mode 100644 index 0000000..3c69c90 --- /dev/null +++ b/apps/scishop/src/data/usePaletteProvider.ts @@ -0,0 +1,75 @@ +import { type ShallowRef, type Ref, computed, ref, unref, provide } from 'vue'; +import deepEqual from 'fast-deep-equal'; + +import { IBM5153Contrast, Palettes } from '@4bitlabs/color'; +import { DrawCommand } from '@4bitlabs/sci0'; +import { + DEFAULT_PALETTE_SET, + IndexedPaletteSet, + PaletteSet, + reduceMutations, +} from '../helpers/palette-helpers.ts'; +import { EditorCommand } from '../models/EditorCommand.ts'; +import { paletteKey } from './keys.ts'; + +export function usePaletteProvider(deps: { + layersRef: ShallowRef; + topIdxRef: Ref; +}) { + const { layersRef, topIdxRef } = deps; + + const baseRef = ref(Palettes.TRUE_CGA_PALETTE); + const contrastRef = ref(0.4); + const variantRef = ref<0 | 1 | 2 | 3>(2); + + const finalRef = computed(() => { + const base = unref(baseRef); + const contrast = unref(contrastRef); + return contrast !== false ? IBM5153Contrast(base, contrast) : base; + }); + + const paletteMutationsRef = computed<[number, DrawCommand][]>((prev = []) => { + const layers = unref(layersRef); + const next = Array.from(layers.entries()) + .filter( + ([, it]) => it.type === 'SET_PALETTE' || it.type === 'UPDATE_PALETTE', + ) + .flatMap<[number, DrawCommand]>(([idx, it]) => + it.commands.map((cmd) => [idx, cmd]), + ); + return deepEqual(prev, next) ? prev : next; + }); + + const paletteSetChangesRef = computed((prev = []) => { + const mutations = unref(paletteMutationsRef); + const next = reduceMutations(mutations); + return deepEqual(prev, next) ? prev : next; + }); + + function resolvePaletteAtIdx(n: number): PaletteSet { + const changes = unref(paletteSetChangesRef); + const found = changes.findLast(([idx]) => idx < n); + if (!found) return DEFAULT_PALETTE_SET; + const [, actual] = found; + return actual; + } + + const topPaletteSetRef = computed(() => + resolvePaletteAtIdx(unref(topIdxRef)), + ); + + const currentPaletteRef = computed( + () => unref(topPaletteSetRef)[unref(variantRef)], + ); + + provide(paletteKey, { + contrast: contrastRef, + baseColors: baseRef, + variant: variantRef, + // computed + finalColors: finalRef, + topPaletteSet: topPaletteSetRef, + currentPalette: currentPaletteRef, + resolvePaletteAtIdx, + }); +} diff --git a/apps/scishop/src/data/useUpdateSelectionFn.ts b/apps/scishop/src/data/useUpdateSelectionFn.ts new file mode 100644 index 0000000..0f78966 --- /dev/null +++ b/apps/scishop/src/data/useUpdateSelectionFn.ts @@ -0,0 +1,32 @@ +import { Ref, type ShallowRef, unref } from 'vue'; + +import { EditorCommand } from '../models/EditorCommand.ts'; +import { insert, remove } from '../helpers/array-helpers.ts'; + +export function useUpdateSelectionFn(deps: { + layers: ShallowRef; + topIdx: Ref; + selectedIdx: Ref; +}) { + const { layers, topIdx, selectedIdx } = deps; + return function updateSelection( + updateFn: (it: EditorCommand) => EditorCommand | null, + ): boolean { + const idx = unref(selectedIdx); + if (idx === null) return false; + + const cmd = layers.value[idx]; + if (!cmd) return false; + + const next = updateFn(cmd); + if (next === null) { + layers.value = remove(layers.value, idx); + selectedIdx.value = null; + if (idx < unref(topIdx)) topIdx.value -= 1; + } else { + layers.value = insert(layers.value, idx, next, true); + } + + return true; + }; +} diff --git a/apps/scishop/src/helpers/array-helpers.ts b/apps/scishop/src/helpers/array-helpers.ts new file mode 100644 index 0000000..86c9899 --- /dev/null +++ b/apps/scishop/src/helpers/array-helpers.ts @@ -0,0 +1,26 @@ +export const insert = ( + source: T[], + index: number, + item: T, + replaceAtIndex = false, +): T[] => [ + ...source.slice(0, index), + item, + ...source.slice(replaceAtIndex ? index + 1 : index), +]; + +export const insertN = ( + source: T[], + index: number, + items: T[], + replace = 0, +): T[] => [ + ...source.slice(0, index), + ...items, + ...source.slice(index + Math.max(0, replace)), +]; + +export const remove = (source: T[], index: number, count: number = 1) => [ + ...source.slice(0, index), + ...source.slice(index + count), +]; diff --git a/apps/scishop/src/helpers/clamp.ts b/apps/scishop/src/helpers/clamp.ts new file mode 100644 index 0000000..f9428de --- /dev/null +++ b/apps/scishop/src/helpers/clamp.ts @@ -0,0 +1,2 @@ +export const clamp = (x: number, lower: number = 0, upper: number = 1) => + Math.max(Math.min(x, upper), lower); diff --git a/apps/scishop/src/helpers/command-helpers.ts b/apps/scishop/src/helpers/command-helpers.ts new file mode 100644 index 0000000..5f54625 --- /dev/null +++ b/apps/scishop/src/helpers/command-helpers.ts @@ -0,0 +1,158 @@ +import { + DrawCommandStruct, + DrawCommand, + FillCommand, + PolylineCommand, +} from '@4bitlabs/sci0'; +import { + distanceBetween, + project, + squaredDistanceBetween, + Vec2, +} from '@4bitlabs/vec2'; +import { insert } from './array-helpers.ts'; +import { getSegments } from './polygons.ts'; + +export const extractVertices = ( + cmd: DrawCommandStruct, +): Vec2[] => { + const [type] = cmd; + switch (type) { + case 'PLINE': { + const [, , ...vertices] = cmd; + return vertices; + } + case 'BRUSH': + case 'FILL': { + const [, , vertex] = cmd; + return [vertex]; + } + default: + return []; + } +}; + +export type ClosestPointState = [dSqrd: number, idx: number]; +export const findClosestPointTo = ( + vertices: Readonly[], + position: Readonly, + initial: ClosestPointState = [Infinity, NaN], +): ClosestPointState => + vertices.reduce((state, point, pIdx) => { + const [prevD2] = state; + const thisD2 = squaredDistanceBetween(position, point); + return thisD2 < prevD2 ? [thisD2, pIdx] : state; + }, initial); + +export const anyPointCloseTo = ( + vertices: Readonly[], + position: Readonly, + proximity: number, +): boolean => { + const [result] = findClosestPointTo(vertices, position); + return Math.sqrt(result) <= proximity; +}; + +export type FindState = [dSqrd: number, commandIdx: number, pointIdx: number]; +export const findClosestPointIn = ( + commands: DrawCommand[], + position: Readonly, +): FindState => + commands.reduce( + (state, cmd, cmdIdx) => { + const [type] = cmd; + if (type !== 'PLINE' && type !== 'FILL') return state; + const [prevD2] = state; + const [nextD2, pIdx] = findClosestPointTo( + extractVertices(cmd).map(([x, y]) => [x + 0.5, y + 0.5]), + position, + ); + return nextD2 < prevD2 ? [nextD2, cmdIdx, pIdx] : state; + }, + [Infinity, NaN, NaN], + ); + +export type FindResult = [commandIdx: number, pointIdx: number]; +export function nearestPointWithRange( + commands: DrawCommand[], + position: Readonly, + range: number, +): FindResult | null { + const result = findClosestPointIn(commands, position); + + const [distSqrd, ...rest] = result; + if (!Number.isFinite(distSqrd)) return null; + + const dist = Math.sqrt(distSqrd); + if (dist > range) return null; + + return rest; +} + +export const moveLineVertex = ( + [type, options, ...verts]: PolylineCommand, + idx: number, + pos: [number, number], +): PolylineCommand => [type, options, ...insert(verts, idx, pos, true)]; + +export const moveFillVertex = ( + [type, options]: FillCommand, + pos: [number, number], +): FillCommand => [type, options, pos]; + +export type PointAlongPathResult = [ + cmdIdx: number, + i0: number, + i1: number, + point: Vec2, +]; +export function pointAlongPaths( + commands: DrawCommand[], + position: Readonly, + range: number, +): PointAlongPathResult | null { + for (let cmdIdx = 0; cmdIdx < commands.length; cmdIdx++) { + const cmd = commands[cmdIdx]; + const [type] = cmd; + if (type !== 'PLINE') continue; + + const points = extractVertices(cmd).map(([x, y]) => [ + x + 0.5, + y + 0.5, + ]); + for (const segment of getSegments(points)) { + const [v0, v1, i0, i1] = segment; + const p = project(v0, v1, position); + const dist = distanceBetween(p, position); + if (dist < range) { + return [cmdIdx, i0, i1, p]; + } + } + } + + return null; +} + +export function getVertexFrom( + commands: DrawCommand[], + cmdIdx: number, + vertexIdx: number, +) { + const cmd = commands[cmdIdx]; + if (!cmd) return null; + const [type] = cmd; + if (type !== 'PLINE' && type !== 'FILL') return null; + const points = extractVertices(cmd); + return points[vertexIdx]; +} + +export function mustGetVertexFrom( + commands: DrawCommand[], + cmdIdx: number, + vertexIdx: number, +) { + const result = getVertexFrom(commands, cmdIdx, vertexIdx); + if (!result) + throw new Error(`Command does not contain [${cmdIdx}/${vertexIdx}]`); + return result; +} diff --git a/apps/scishop/src/helpers/exhaustive.ts b/apps/scishop/src/helpers/exhaustive.ts new file mode 100644 index 0000000..ae3ead0 --- /dev/null +++ b/apps/scishop/src/helpers/exhaustive.ts @@ -0,0 +1,3 @@ +export function exhaustive(msg: string, _: never): never { + throw new Error(`${msg}: ${JSON.stringify(_)}`); +} diff --git a/apps/scishop/src/helpers/getContext.ts b/apps/scishop/src/helpers/getContext.ts new file mode 100644 index 0000000..44bc419 --- /dev/null +++ b/apps/scishop/src/helpers/getContext.ts @@ -0,0 +1,24 @@ +type Canvas = HTMLCanvasElement | OffscreenCanvas; +type Context = CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D; +const cache = new WeakMap(); + +export function get2dContext( + el: OffscreenCanvas, +): OffscreenCanvasRenderingContext2D; +export function get2dContext(el: HTMLCanvasElement): CanvasRenderingContext2D; +export function get2dContext(el: Canvas): Context { + // TODO This might be no better than just calling getContext() again and letting *it* return the context + const cachedCtx = cache.get(el); + if (cachedCtx) return cachedCtx; + + const ctx = el.getContext('2d'); + if (!ctx) throw new Error('cannot establish new canvas context'); + if ( + !(ctx instanceof CanvasRenderingContext2D) && + !(ctx instanceof OffscreenCanvasRenderingContext2D) + ) + throw new Error('context is not the correct type'); + + cache.set(el, ctx); + return ctx; +} diff --git a/apps/scishop/src/helpers/palette-helpers.ts b/apps/scishop/src/helpers/palette-helpers.ts new file mode 100644 index 0000000..52690f7 --- /dev/null +++ b/apps/scishop/src/helpers/palette-helpers.ts @@ -0,0 +1,62 @@ +import { DrawCommand } from '@4bitlabs/sci0'; + +// prettier-ignore +export const DEFAULT_PALETTE: number[] = [ + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, + 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0x88, 0x88, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x88, 0x88, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, + 0xfe, 0xff, 0x08, 0x91, 0x2a, 0x3b, 0x4c, 0x5d, 0x6e, 0x88, +] as const; + +export type PaletteSet = [number[], number[], number[], number[]]; + +export const DEFAULT_PALETTE_SET: PaletteSet = [ + DEFAULT_PALETTE, + DEFAULT_PALETTE, + DEFAULT_PALETTE, + DEFAULT_PALETTE, +]; + +export type IndexedPaletteSet = [number, PaletteSet]; + +export function reduceMutations( + mutations: + | Array<[number, DrawCommand]> + | IterableIterator<[number, DrawCommand]>, +): IndexedPaletteSet[] { + let prevSet: PaletteSet = [ + DEFAULT_PALETTE, + DEFAULT_PALETTE, + DEFAULT_PALETTE, + DEFAULT_PALETTE, + ]; + + const sets: [number, PaletteSet][] = []; + for (const [idx, cmd] of mutations) { + const [type] = cmd; + switch (type) { + case 'SET_PALETTE': { + const [, [palIdx], ...colors] = cmd; + const nextSet: PaletteSet = [...prevSet]; + nextSet[palIdx] = [...colors]; + sets.push([idx, nextSet]); + prevSet = nextSet; + break; + } + case 'UPDATE_PALETTE': { + const [, , ...entries] = cmd; + const next = entries.reduce((prevSet_, [palIdx, clrIdx, color]) => { + const nextSet: PaletteSet = [...prevSet_]; + const nextPal = [...nextSet[palIdx]]; + nextPal[clrIdx] = color; + nextSet[palIdx] = nextPal; + return nextSet; + }, prevSet); + sets.push([idx, next]); + prevSet = next; + break; + } + } + } + return sets; +} diff --git a/apps/scishop/src/helpers/polygons.ts b/apps/scishop/src/helpers/polygons.ts new file mode 100644 index 0000000..45400bf --- /dev/null +++ b/apps/scishop/src/helpers/polygons.ts @@ -0,0 +1,74 @@ +import { type Vec2 } from '@4bitlabs/vec2'; + +export const isInsideBounds = ( + [width, height]: Readonly, + [x, y]: Readonly, +): boolean => x >= 0 && y >= 0 && x < width && y < height; + +export const pixel = ([x, y]: Readonly, offset = 0.0): Vec2[] => [ + [Math.floor(x) + offset, Math.floor(y) + offset], + [Math.floor(x) + 1 - offset, Math.floor(y) + offset], + [Math.floor(x) + 1 - offset, Math.floor(y) + 1 - offset], + [Math.floor(x) + offset, Math.floor(y) + 1 - offset], +]; + +export const areaOfPolygon = (points: Readonly[]) => { + const { length } = points; + let area = 0; + + for (let i = 0; i < length; i++) { + const [x1, y1] = points[i]; + const [x2, y2] = points[(i + 1) % length]; + area += x1 * y2 - x2 * y1; + } + + return Math.abs(area) / 2; +}; + +export const pathPoly = ( + ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D, + points: Readonly[], +) => { + ctx.beginPath(); + points.forEach(([x, y], i) => ctx[i === 0 ? 'moveTo' : 'lineTo'](x, y)); + ctx.closePath(); +}; + +export const rect = ([x0, y0]: Vec2, [x1, y1]: Vec2): Vec2[] => [ + [x0, y0], + [x1, y0], + [x1, y1], + [x0, y1], +]; + +export function* getSegments( + points: T[], + loop = false, +): Generator<[T, T, number, number]> { + const length = points.length; + for (let i = 0; i < points.length - (loop ? 0 : 1); i++) { + const next = (i + 1) % length; + yield [points[i], points[next], i, next]; + } +} + +export const isInsidePolygon = ( + verts: Vec2[], + [x, y]: Vec2, + loop = true, +): boolean => { + let inside = false; + + for (const [[x0, y0], [x1, y1]] of getSegments(verts, loop)) { + if (y <= Math.min(y0, y1)) continue; + if (y > Math.max(y0, y1)) continue; + if (x > Math.max(x0, x1)) continue; + + const x_intersection = ((y - y0) * (x1 - x0)) / (y1 - y0) + x0; + if (!(x0 === x1 || x <= x_intersection)) continue; + + inside = !inside; + } + + return inside; +}; diff --git a/apps/scishop/src/helpers/setCanvasDimensions.ts b/apps/scishop/src/helpers/setCanvasDimensions.ts new file mode 100644 index 0000000..473f042 --- /dev/null +++ b/apps/scishop/src/helpers/setCanvasDimensions.ts @@ -0,0 +1,11 @@ +export function setCanvasDimensions( + target: { height: number; width: number }, + width: number, + height: number, +) { + const isChanged = target.width !== width || target.height !== height; + if (isChanged) { + target.width = width; + target.height = height; + } +} diff --git a/apps/scishop/src/helpers/smoothstep.ts b/apps/scishop/src/helpers/smoothstep.ts new file mode 100644 index 0000000..b65f87b --- /dev/null +++ b/apps/scishop/src/helpers/smoothstep.ts @@ -0,0 +1,19 @@ +import { clamp } from './clamp.ts'; + +export const s0 = (left: number, right: number, val: number) => + clamp((val - left) / (right - left)); + +export const s1 = (left: number, right: number, val: number) => { + const x = clamp((val - left) / (right - left)); + return x ** 2 * (-2.0 * x + 3.0); +}; + +export const s2 = (left: number, right: number, val: number) => { + const x = clamp((val - left) / (right - left)); + return x ** 3 * (6 * x ** 2 - 15 * x + 10); +}; + +export const s3 = (left: number, right: number, val: number) => { + const x = clamp((val - left) / (right - left)); + return -20 * x ** 7 + 70 * x ** 6 + -84 * x ** 5 + 35 * x ** 4; +}; diff --git a/apps/scishop/src/main.ts b/apps/scishop/src/main.ts new file mode 100644 index 0000000..90d917c --- /dev/null +++ b/apps/scishop/src/main.ts @@ -0,0 +1,7 @@ +import { createApp } from 'vue'; + +import './reset.css'; +import './style.css'; +import App from './App.vue'; + +createApp(App).mount('#app'); diff --git a/apps/scishop/src/models/EditorCommand.ts b/apps/scishop/src/models/EditorCommand.ts new file mode 100644 index 0000000..9a461c0 --- /dev/null +++ b/apps/scishop/src/models/EditorCommand.ts @@ -0,0 +1,34 @@ +import { + BrushCommand, + DrawCommand, + EmbeddedCelCommand, + FillCommand, + PolylineCommand, + SetPaletteCommand, + UpdatePaletteCommand, +} from '@4bitlabs/sci0'; + +export type BasicEditorCommand = { + readonly id: symbol | number | string; + readonly type: T[0]; + readonly name?: string; + readonly commands: [T]; +}; + +// export type CompositeEditorCommand = { +// readonly id: symbol | number | string; +// readonly type: T; +// readonly name?: string; +// readonly commands: (PolylineCommand | FillCommand | BrushCommand)[]; +// }; +// +// export type GroupEditorCommand = CompositeEditorCommand<'group'>; + +export type EditorCommand = + // | GroupEditorCommand + | BasicEditorCommand + | BasicEditorCommand + | BasicEditorCommand + | BasicEditorCommand + | BasicEditorCommand + | BasicEditorCommand; diff --git a/apps/scishop/src/models/tool.ts b/apps/scishop/src/models/tool.ts new file mode 100644 index 0000000..795d8e4 --- /dev/null +++ b/apps/scishop/src/models/tool.ts @@ -0,0 +1,10 @@ +export type Tool = + | 'select' + | 'pan' + | 'line' + | 'bezier' + | 'rect' + | 'oval' + | 'brush' + | 'fill' + | 'find'; diff --git a/apps/scishop/src/render/cursor-dot.ts b/apps/scishop/src/render/cursor-dot.ts new file mode 100644 index 0000000..507c26d --- /dev/null +++ b/apps/scishop/src/render/cursor-dot.ts @@ -0,0 +1,24 @@ +import { Vec2 } from '@4bitlabs/vec2'; + +export function cursorDot( + ctx: CanvasRenderingContext2D, + [x, y]: Readonly, + size = 2, +) { + ctx.save(); + + ctx.translate(x, y); + + ctx.beginPath(); + ctx.arc(0, 0, size * 1.75, 0, Math.PI * 2); + ctx.arc(0, 0, size, 0, Math.PI * 2); + ctx.fillStyle = 'rgba(0 0 0 / 20%)'; + ctx.fill('evenodd'); + + ctx.beginPath(); + ctx.arc(0, 0, size, 0, Math.PI * 2); + ctx.fillStyle = 'rgba(255 255 255 / 40%)'; + ctx.fill(); + + ctx.restore(); +} diff --git a/apps/scishop/src/render/fill-skeleton.ts b/apps/scishop/src/render/fill-skeleton.ts new file mode 100644 index 0000000..1a3d07e --- /dev/null +++ b/apps/scishop/src/render/fill-skeleton.ts @@ -0,0 +1,31 @@ +import { applyToPoints, Matrix } from 'transformation-matrix'; + +import { FillCommand } from '@4bitlabs/sci0'; + +export function fillSkeleton( + ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D, + matrix: Matrix, + cmd: FillCommand, + highlight: boolean = false, +) { + const [, , ...points] = cmd; + const all = applyToPoints( + matrix, + points.map(([x, y]) => [x + 0.5, y + 0.5]), + ); + + all.forEach(([x, y]) => { + ctx.save(); + ctx.translate(x, y); + ctx.beginPath(); + ctx.moveTo(3, 0); + ctx.lineTo(0, -4); + ctx.lineTo(-3, 0); + ctx.lineTo(0, 4); + ctx.lineTo(3, 0); + ctx.lineWidth = 1.5; + if (highlight) ctx.fill(); + ctx.stroke(); + ctx.restore(); + }); +} diff --git a/apps/scishop/src/render/pixel-border.ts b/apps/scishop/src/render/pixel-border.ts new file mode 100644 index 0000000..b7f3b9b --- /dev/null +++ b/apps/scishop/src/render/pixel-border.ts @@ -0,0 +1,24 @@ +import { type Vec2 } from '@4bitlabs/vec2'; +import { areaOfPolygon, pathPoly } from '../helpers/polygons.ts'; +import * as SmoothStep from '../helpers/smoothstep.ts'; + +export function pixelBorder( + ctx: CanvasRenderingContext2D, + points: Readonly[], +) { + ctx.save(); + pathPoly(ctx, points); + + ctx.lineWidth = 1; + ctx.setLineDash([1, 1]); + ctx.strokeStyle = 'white'; + + const area = areaOfPolygon(points); + const alpha = SmoothStep.s1(1, 6 * 6, area); + ctx.strokeStyle = `oklab(${(alpha * 100).toFixed(0)}% 0 0)`; + ctx.fillStyle = `oklab(${(alpha * 100 * 0.45).toFixed(0)}% 0 0)`; + + ctx.fill(); + ctx.stroke(); + ctx.restore(); +} diff --git a/apps/scishop/src/render/pline-skeleton.ts b/apps/scishop/src/render/pline-skeleton.ts new file mode 100644 index 0000000..0b3caf5 --- /dev/null +++ b/apps/scishop/src/render/pline-skeleton.ts @@ -0,0 +1,37 @@ +import { applyToPoints, Matrix } from 'transformation-matrix'; + +import { PolylineCommand } from '@4bitlabs/sci0'; + +export function plineSkeleton( + ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D, + matrix: Matrix, + cmd: PolylineCommand, + highlights: number[] = [], +) { + ctx.save(); + + const [, , ...cPoints] = cmd; + const points = applyToPoints( + matrix, + cPoints.map(([x, y]) => [x + 0.5, y + 0.5]), + ); + + ctx.lineWidth = 1; + ctx.beginPath(); + points.forEach(([x, y], idx) => ctx[idx ? 'lineTo' : 'moveTo'](x, y)); + ctx.stroke(); + + ctx.lineWidth = 1.5; + points.forEach(([x, y], idx) => { + ctx.save(); + ctx.translate(x, y); + ctx.beginPath(); + ctx.clearRect(-4, -4, 8, 8); + ctx.roundRect(-2.5, -2.5, 5, 5, 0); + ctx.stroke(); + if (highlights.includes(idx)) ctx.fill(); + ctx.restore(); + }); + + ctx.restore(); +} diff --git a/apps/scishop/src/render/point-skeleton.ts b/apps/scishop/src/render/point-skeleton.ts new file mode 100644 index 0000000..c85509e --- /dev/null +++ b/apps/scishop/src/render/point-skeleton.ts @@ -0,0 +1,23 @@ +import { applyToPoint, Matrix } from 'transformation-matrix'; + +import { Vec2 } from '@4bitlabs/vec2'; + +export function pointSkeleton( + ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D, + matrix: Matrix, + p: Vec2, + size = 8, + fill = true, +) { + const [x, y] = applyToPoint(matrix, [p[0] + 0.5, p[1] + 0.5]); + + ctx.save(); + ctx.lineWidth = 1.5; + ctx.translate(x, y); + ctx.beginPath(); + ctx.clearRect(-((size * 2) / 2), -((size * 2) / 2), size * 2, size * 2); + ctx.roundRect(-(size / 2), -(size / 2), size, size, 1); + ctx.stroke(); + if (fill) ctx.fill(); + ctx.restore(); +} diff --git a/apps/scishop/src/reset.css b/apps/scishop/src/reset.css new file mode 100644 index 0000000..668f8b4 --- /dev/null +++ b/apps/scishop/src/reset.css @@ -0,0 +1,61 @@ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} +body { + line-height: 1; +} +ol, ul, menu { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} + +button { + font: inherit; + text-rendering: inherit; + color: inherit; + letter-spacing: inherit; + word-spacing: inherit; + text-transform: inherit; + padding-block: 0; + padding-inline: 0; + margin: 0; + border: 0; + background: none; + text-indent: inherit; + text-shadow: inherit; + text-align: inherit; + cursor: pointer; +} \ No newline at end of file diff --git a/apps/scishop/src/style.css b/apps/scishop/src/style.css new file mode 100644 index 0000000..11f4291 --- /dev/null +++ b/apps/scishop/src/style.css @@ -0,0 +1,79 @@ +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + --clr-ink: #181614; + --clr-ink-A10: color-mix(in srgb, var(--clr-ink), transparent 90%); + --clr-ink-A20: color-mix(in srgb, var(--clr-ink), transparent 80%); + --clr-ink-A30: color-mix(in srgb, var(--clr-ink), transparent 70%); + --clr-ink-A40: color-mix(in srgb, var(--clr-ink), transparent 60%); + --clr-ink-A50: color-mix(in srgb, var(--clr-ink), transparent 50%); + --clr-ink-A60: color-mix(in srgb, var(--clr-ink), transparent 40%); + --clr-ink-A70: color-mix(in srgb, var(--clr-ink), transparent 30%); + --clr-ink-A80: color-mix(in srgb, var(--clr-ink), transparent 20%); + --clr-ink-A90: color-mix(in srgb, var(--clr-ink), transparent 10%); + + --clr-primary: #2a52be; + --clr-primary-100: color-mix(in oklab, var(--clr-primary), var(--clr-ink) 80%); + --clr-primary-200: color-mix(in oklab, var(--clr-primary), var(--clr-ink) 60%); + --clr-primary-300: color-mix(in oklab, var(--clr-primary), var(--clr-ink) 40%); + --clr-primary-400: color-mix(in oklab, var(--clr-primary), var(--clr-ink) 20%); + --clr-primary-500: var(--clr-primary); + --clr-primary-600: color-mix(in oklab, var(--clr-primary), white 20%); + --clr-primary-700: color-mix(in oklab, var(--clr-primary), white 40%); + --clr-primary-800: color-mix(in oklab, var(--clr-primary), white 60%); + --clr-primary-900: color-mix(in oklab, var(--clr-primary), white 80%); + --clr-primary-A10: color-mix(in srgb, var(--clr-primary), transparent 90%); + --clr-primary-A20: color-mix(in srgb, var(--clr-primary), transparent 80%); + --clr-primary-A30: color-mix(in srgb, var(--clr-primary), transparent 70%); + --clr-primary-A40: color-mix(in srgb, var(--clr-primary), transparent 60%); + --clr-primary-A50: color-mix(in srgb, var(--clr-primary), transparent 50%); + --clr-primary-A60: color-mix(in srgb, var(--clr-primary), transparent 40%); + --clr-primary-A70: color-mix(in srgb, var(--clr-primary), transparent 30%); + --clr-primary-A80: color-mix(in srgb, var(--clr-primary), transparent 20%); + --clr-primary-A90: color-mix(in srgb, var(--clr-primary), transparent 10%); + + + --clr-surface--default: #f5f5f5; + --clr-surface--glass: #2f2f2f; + --clr-surface: var(--clr-surface--default); + --clr-surface-800: color-mix(in srgb, var(--clr-surface--default), var(--clr-surface--glass) 80%); + --clr-surface-500: color-mix(in srgb, var(--clr-surface--default), var(--clr-surface--glass) 50%); + --clr-surface-200: color-mix(in srgb, var(--clr-surface--default), var(--clr-surface--glass) 20%); + + --radius: 0.25rlh; + --shadow: 0 0.5rlh 0.5rlh rgba(0 0 0 / 20%); + + --transparent-img: repeating-linear-gradient( + -45deg, + rgba(0 0 0 / 5%) 0px, + rgba(0 0 0 / 5%) 4px, + rgba(0 0 0 / 0%) 4px, + rgba(0 0 0 / 0%) 8px + ); +} + +#app { + position: absolute; + top: 0; + left: 0; + right: 0; + + display: grid; + grid-template-rows: 3rem 1rem calc(100vh - 6.5rem) 1rem 1.5rem; + grid-template-columns: 1rem 3rem 1fr 19rem; + overflow: hidden; + + grid-template-areas: + "header header header header" + ". . . sidebar" + ". toolbar . sidebar" + ". . . sidebar" + "status status status status"; +} \ No newline at end of file diff --git a/apps/scishop/src/vite-env.d.ts b/apps/scishop/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/apps/scishop/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/apps/scishop/tsconfig.json b/apps/scishop/tsconfig.json new file mode 100644 index 0000000..3d9a264 --- /dev/null +++ b/apps/scishop/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2023", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "preserve", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/apps/scishop/tsconfig.node.json b/apps/scishop/tsconfig.node.json new file mode 100644 index 0000000..97ede7e --- /dev/null +++ b/apps/scishop/tsconfig.node.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "strict": true + }, + "include": ["vite.config.ts"] +} diff --git a/apps/scishop/vite.config.ts b/apps/scishop/vite.config.ts new file mode 100644 index 0000000..6a8d3b2 --- /dev/null +++ b/apps/scishop/vite.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vite'; +import vue from '@vitejs/plugin-vue'; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [vue()], + resolve: { + preserveSymlinks: true, + }, +}); diff --git a/package-lock.json b/package-lock.json index f0e156a..5da203a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,8 @@ "libs/sci0", "libs/vec2", "libs/crt-lite", - "apps/scibud" + "apps/scibud", + "apps/scishop" ], "devDependencies": { "@eslint/js": "^9.6.0", @@ -67,6 +68,25 @@ "@types/pngjs": "^6.0.5" } }, + "apps/scishop": { + "version": "0.0.0", + "dependencies": { + "@4bitlabs/color": "^2.1.5", + "@4bitlabs/color-space": "^1.2.3", + "@4bitlabs/image": "^3.3.11", + "@4bitlabs/sci0": "^4.0.1", + "@4bitlabs/vec2": "^1.2.0", + "fast-deep-equal": "^3.1.3", + "transformation-matrix": "^2.16.1", + "vue": "^3.4.21" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.0.4", + "typescript": "^5.2.2", + "vite": "^5.2.0", + "vue-tsc": "^2.0.6" + } + }, "libs/blur-filters": { "name": "@4bitlabs/blur-filters", "version": "1.0.17", @@ -675,10 +695,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.0.tgz", - "integrity": "sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==", - "dev": true, + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", + "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", "bin": { "parser": "bin/babel-parser.js" }, @@ -923,8 +942,376 @@ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.2.0.tgz", "integrity": "sha512-bV21/9LQmcQeCPEg3BDFtvwL6cwiTMksYNWQQ4KOxCZikEGalWtenoZ0wCiukJINlGCIi2KXx01g4FoH/LxpzQ==", "optional": true, - "dependencies": { - "tslib": "^2.4.0" + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" } }, "node_modules/@eslint-community/eslint-utils": { @@ -1941,8 +2328,7 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.20", @@ -3227,6 +3613,214 @@ "node": ">=14" } }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", + "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", + "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", + "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", + "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", + "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", + "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", + "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", + "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", + "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", + "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", + "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", + "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", + "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", + "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", + "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", + "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@sigstore/bundle": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-2.3.2.tgz", @@ -3427,6 +4021,12 @@ "integrity": "sha512-YPEVzmy3kJupUee1ueLuvGspy6U2JHcxt6rYvRsSCEgVC54+KdBFjQ6NG/0koZk69e1bfXwSusgChwdFhvEXMw==", "dev": true }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, "node_modules/@types/expand-tilde": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@types/expand-tilde/-/expand-tilde-2.0.2.tgz", @@ -3503,9 +4103,12 @@ "dev": true }, "node_modules/@types/node": { - "version": "17.0.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.8.tgz", - "integrity": "sha512-YofkM6fGv4gDJq78g4j0mMuGMkZVxZDgtU0JRdx6FgiJDG+0fY0GKVolOV8WqVmEhLCXkQRjwDdKyPxJp/uucg==" + "version": "20.14.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz", + "integrity": "sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==", + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/@types/normalize-package-data": { "version": "2.4.4", @@ -3770,6 +4373,184 @@ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "dev": true }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.5.tgz", + "integrity": "sha512-LOjm7XeIimLBZyzinBQ6OSm3UBCNVCpLkxGC0oWmm2YPzVZoxMsdvNVimLTBzpAnR9hl/yn1SHGuRfe6/Td9rQ==", + "dev": true, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@volar/language-core": { + "version": "2.4.0-alpha.15", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.0-alpha.15.tgz", + "integrity": "sha512-mt8z4Fm2WxfQYoQHPcKVjLQV6PgPqyKLbkCVY2cr5RSaamqCHjhKEpsFX66aL4D/7oYguuaUw9Bx03Vt0TpIIA==", + "dev": true, + "dependencies": { + "@volar/source-map": "2.4.0-alpha.15" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.0-alpha.15", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.0-alpha.15.tgz", + "integrity": "sha512-8Htngw5TmBY4L3ClDqBGyfLhsB8EmoEXUH1xydyEtEoK0O6NX5ur4Jw8jgvscTlwzizyl/wsN1vn0cQXVbbXYg==", + "dev": true + }, + "node_modules/@volar/typescript": { + "version": "2.4.0-alpha.15", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.0-alpha.15.tgz", + "integrity": "sha512-U3StRBbDuxV6Woa4hvGS4kz3XcOzrWUKgFdEFN+ba1x3eaYg7+ytau8ul05xgA+UNGLXXsKur7fTUhDFyISk0w==", + "dev": true, + "dependencies": { + "@volar/language-core": "2.4.0-alpha.15", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.31.tgz", + "integrity": "sha512-skOiodXWTV3DxfDhB4rOf3OGalpITLlgCeOwb+Y9GJpfQ8ErigdBUHomBzvG78JoVE8MJoQsb+qhZiHfKeNeEg==", + "dependencies": { + "@babel/parser": "^7.24.7", + "@vue/shared": "3.4.31", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.31.tgz", + "integrity": "sha512-wK424WMXsG1IGMyDGyLqB+TbmEBFM78hIsOJ9QwUVLGrcSk0ak6zYty7Pj8ftm7nEtdU/DGQxAXp0/lM/2cEpQ==", + "dependencies": { + "@vue/compiler-core": "3.4.31", + "@vue/shared": "3.4.31" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.31.tgz", + "integrity": "sha512-einJxqEw8IIJxzmnxmJBuK2usI+lJonl53foq+9etB2HAzlPjAS/wa7r0uUpXw5ByX3/0uswVSrjNb17vJm1kQ==", + "dependencies": { + "@babel/parser": "^7.24.7", + "@vue/compiler-core": "3.4.31", + "@vue/compiler-dom": "3.4.31", + "@vue/compiler-ssr": "3.4.31", + "@vue/shared": "3.4.31", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.10", + "postcss": "^8.4.38", + "source-map-js": "^1.2.0" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.31.tgz", + "integrity": "sha512-RtefmITAje3fJ8FSg1gwgDhdKhZVntIVbwupdyZDSifZTRMiWxWehAOTCc8/KZDnBOcYQ4/9VWxsTbd3wT0hAA==", + "dependencies": { + "@vue/compiler-dom": "3.4.31", + "@vue/shared": "3.4.31" + } + }, + "node_modules/@vue/language-core": { + "version": "2.0.26", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.0.26.tgz", + "integrity": "sha512-/lt6SfQ3O1yDAhPsnLv9iSUgXd1dMHqUm/t3RctfqjuwQf1LnftZ414X3UBn6aXT4MiwXWtbNJ4Z0NZWwDWgJQ==", + "dev": true, + "dependencies": { + "@volar/language-core": "~2.4.0-alpha.15", + "@vue/compiler-dom": "^3.4.0", + "@vue/shared": "^3.4.0", + "computeds": "^0.0.1", + "minimatch": "^9.0.3", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1", + "vue-template-compiler": "^2.7.14" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/language-core/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@vue/language-core/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.31.tgz", + "integrity": "sha512-VGkTani8SOoVkZNds1PfJ/T1SlAIOf8E58PGAhIOUDYPC4GAmFA2u/E14TDAFcf3vVDKunc4QqCe/SHr8xC65Q==", + "dependencies": { + "@vue/shared": "3.4.31" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.31.tgz", + "integrity": "sha512-LDkztxeUPazxG/p8c5JDDKPfkCDBkkiNLVNf7XZIUnJ+66GVGkP+TIh34+8LtPisZ+HMWl2zqhIw0xN5MwU1cw==", + "dependencies": { + "@vue/reactivity": "3.4.31", + "@vue/shared": "3.4.31" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.31.tgz", + "integrity": "sha512-2Auws3mB7+lHhTFCg8E9ZWopA6Q6L455EcU7bzcQ4x6Dn4cCPuqj6S2oBZgN2a8vJRS/LSYYxwFFq2Hlx3Fsaw==", + "dependencies": { + "@vue/reactivity": "3.4.31", + "@vue/runtime-core": "3.4.31", + "@vue/shared": "3.4.31", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.31.tgz", + "integrity": "sha512-D5BLbdvrlR9PE3by9GaUp1gQXlCNadIZytMIb8H2h3FMWJd4oUfkUTEH2wAr3qxoRz25uxbTcbqd3WKlm9EHQA==", + "dependencies": { + "@vue/compiler-ssr": "3.4.31", + "@vue/shared": "3.4.31" + }, + "peerDependencies": { + "vue": "3.4.31" + } + }, + "node_modules/@vue/shared": { + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.31.tgz", + "integrity": "sha512-Yp3wtJk//8cO4NItOPpi3QkLExAr/aLBGZMmTtW9WpdwBCJpRM6zj9WgWktXAl8IDIozwNMByT45JP3tO3ACWA==" + }, "node_modules/@yarnpkg/lockfile": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", @@ -4917,6 +5698,12 @@ "dot-prop": "^5.1.0" } }, + "node_modules/computeds": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/computeds/-/computeds-0.0.1.tgz", + "integrity": "sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==", + "dev": true + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -5241,6 +6028,11 @@ "node": ">=4" } }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, "node_modules/dargs": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", @@ -5259,6 +6051,12 @@ "node": "*" } }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -5600,6 +6398,17 @@ "node": ">=8.6" } }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/env-paths": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", @@ -5758,6 +6567,44 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -6204,6 +7051,11 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -6313,8 +7165,7 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-glob": { "version": "3.3.2", @@ -7155,6 +8006,15 @@ "node": ">= 0.4" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, "node_modules/homedir-polyfill": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", @@ -9188,6 +10048,14 @@ "yallist": "^3.0.2" } }, + "node_modules/magic-string": { + "version": "0.30.10", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", + "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + } + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -9733,6 +10601,12 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true + }, "node_modules/multimatch": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-5.0.0.tgz", @@ -9767,6 +10641,23 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -10727,6 +11618,12 @@ "parse-path": "^7.0.0" } }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -10795,10 +11692,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -10854,6 +11750,33 @@ "node": ">= 0.4" } }, + "node_modules/postcss": { + "version": "8.4.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", + "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.1", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/postcss-selector-parser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz", @@ -11503,6 +12426,41 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rollup": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", + "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.18.0", + "@rollup/rollup-android-arm64": "4.18.0", + "@rollup/rollup-darwin-arm64": "4.18.0", + "@rollup/rollup-darwin-x64": "4.18.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", + "@rollup/rollup-linux-arm-musleabihf": "4.18.0", + "@rollup/rollup-linux-arm64-gnu": "4.18.0", + "@rollup/rollup-linux-arm64-musl": "4.18.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", + "@rollup/rollup-linux-riscv64-gnu": "4.18.0", + "@rollup/rollup-linux-s390x-gnu": "4.18.0", + "@rollup/rollup-linux-x64-gnu": "4.18.0", + "@rollup/rollup-linux-x64-musl": "4.18.0", + "@rollup/rollup-win32-arm64-msvc": "4.18.0", + "@rollup/rollup-win32-ia32-msvc": "4.18.0", + "@rollup/rollup-win32-x64-msvc": "4.18.0", + "fsevents": "~2.3.2" + } + }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -11611,6 +12569,10 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "node_modules/scishop": { + "resolved": "apps/scishop", + "link": true + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -11869,6 +12831,14 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-support": { "version": "0.5.13", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", @@ -12362,6 +13332,14 @@ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "dev": true }, + "node_modules/transformation-matrix": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/transformation-matrix/-/transformation-matrix-2.16.1.tgz", + "integrity": "sha512-tdtC3wxVEuzU7X/ydL131Q3JU5cPMEn37oqVLITjRDSDsnSHVFzW2JiCLfZLIQEgWzZHdSy3J6bZzvKEN24jGA==", + "funding": { + "url": "https://github.com/sponsors/chrvadala" + } + }, "node_modules/treeverse": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/treeverse/-/treeverse-3.0.0.tgz", @@ -12610,7 +13588,7 @@ "version": "5.5.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -12673,6 +13651,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, "node_modules/unique-filename": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", @@ -12813,6 +13796,126 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/vite": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.3.tgz", + "integrity": "sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==", + "dev": true, + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.39", + "rollup": "^4.13.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vscode-uri": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", + "dev": true + }, + "node_modules/vue": { + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.31.tgz", + "integrity": "sha512-njqRrOy7W3YLAlVqSKpBebtZpDVg21FPoaq1I7f/+qqBThK9ChAIjkRWgeP6Eat+8C+iia4P3OYqpATP21BCoQ==", + "dependencies": { + "@vue/compiler-dom": "3.4.31", + "@vue/compiler-sfc": "3.4.31", + "@vue/runtime-dom": "3.4.31", + "@vue/server-renderer": "3.4.31", + "@vue/shared": "3.4.31" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-template-compiler": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz", + "integrity": "sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==", + "dev": true, + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/vue-tsc": { + "version": "2.0.26", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.0.26.tgz", + "integrity": "sha512-tOhuwy2bIXbMhz82ef37qeiaQHMXKQkD6mOF6CCPl3/uYtST3l6fdNyfMxipudrQTxTfXVPlgJdMENBFfC1CfQ==", + "dev": true, + "dependencies": { + "@volar/typescript": "~2.4.0-alpha.15", + "@vue/language-core": "2.0.26", + "semver": "^7.5.4" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + }, + "node_modules/vue-tsc/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/walk-up-path": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-3.0.1.tgz", @@ -13516,10 +14619,9 @@ } }, "@babel/parser": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.0.tgz", - "integrity": "sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==", - "dev": true + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", + "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==" }, "@babel/plugin-syntax-async-generators": { "version": "7.8.4", @@ -13634,73 +14736,234 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", + "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/template": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", + "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" + } + }, + "@babel/traverse": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.0.tgz", + "integrity": "sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0", + "debug": "^4.3.1", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", + "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + } + }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "@emnapi/runtime": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.2.0.tgz", + "integrity": "sha512-bV21/9LQmcQeCPEg3BDFtvwL6cwiTMksYNWQQ4KOxCZikEGalWtenoZ0wCiukJINlGCIi2KXx01g4FoH/LxpzQ==", + "optional": true, + "requires": { + "tslib": "^2.4.0" + } + }, + "@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "dev": true, + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "dev": true, + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "dev": true, + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "dev": true, + "optional": true }, - "@babel/plugin-syntax-typescript": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", - "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", + "@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } + "optional": true }, - "@babel/template": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", - "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", + "@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", "dev": true, - "requires": { - "@babel/code-frame": "^7.23.5", - "@babel/parser": "^7.24.0", - "@babel/types": "^7.24.0" - } + "optional": true }, - "@babel/traverse": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.0.tgz", - "integrity": "sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==", + "@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", "dev": true, - "requires": { - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.24.0", - "@babel/types": "^7.24.0", - "debug": "^4.3.1", - "globals": "^11.1.0" - } + "optional": true }, - "@babel/types": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", - "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", + "@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - } + "optional": true }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true + "@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "dev": true, + "optional": true }, - "@emnapi/runtime": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.2.0.tgz", - "integrity": "sha512-bV21/9LQmcQeCPEg3BDFtvwL6cwiTMksYNWQQ4KOxCZikEGalWtenoZ0wCiukJINlGCIi2KXx01g4FoH/LxpzQ==", - "optional": true, - "requires": { - "tslib": "^2.4.0" - } + "@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "dev": true, + "optional": true }, "@eslint-community/eslint-utils": { "version": "4.4.0", @@ -14278,8 +15541,7 @@ "@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "@jridgewell/trace-mapping": { "version": "0.3.20", @@ -15205,6 +16467,118 @@ "dev": true, "optional": true }, + "@rollup/rollup-android-arm-eabi": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", + "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-android-arm64": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", + "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-darwin-arm64": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", + "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", + "dev": true, + "optional": true + }, + "@rollup/rollup-darwin-x64": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", + "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", + "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm-musleabihf": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", + "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", + "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-arm64-musl": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", + "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", + "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-riscv64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", + "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-s390x-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", + "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-x64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", + "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", + "dev": true, + "optional": true + }, + "@rollup/rollup-linux-x64-musl": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", + "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-arm64-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", + "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-ia32-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", + "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", + "dev": true, + "optional": true + }, + "@rollup/rollup-win32-x64-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", + "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", + "dev": true, + "optional": true + }, "@sigstore/bundle": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-2.3.2.tgz", @@ -15377,6 +16751,12 @@ "integrity": "sha512-YPEVzmy3kJupUee1ueLuvGspy6U2JHcxt6rYvRsSCEgVC54+KdBFjQ6NG/0koZk69e1bfXwSusgChwdFhvEXMw==", "dev": true }, + "@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, "@types/expand-tilde": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@types/expand-tilde/-/expand-tilde-2.0.2.tgz", @@ -15453,9 +16833,12 @@ "dev": true }, "@types/node": { - "version": "17.0.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.8.tgz", - "integrity": "sha512-YofkM6fGv4gDJq78g4j0mMuGMkZVxZDgtU0JRdx6FgiJDG+0fY0GKVolOV8WqVmEhLCXkQRjwDdKyPxJp/uucg==" + "version": "20.14.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.9.tgz", + "integrity": "sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==", + "requires": { + "undici-types": "~5.26.4" + } }, "@types/normalize-package-data": { "version": "2.4.4", @@ -15621,6 +17004,163 @@ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "dev": true }, + "@vitejs/plugin-vue": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.5.tgz", + "integrity": "sha512-LOjm7XeIimLBZyzinBQ6OSm3UBCNVCpLkxGC0oWmm2YPzVZoxMsdvNVimLTBzpAnR9hl/yn1SHGuRfe6/Td9rQ==", + "dev": true, + "requires": {} + }, + "@volar/language-core": { + "version": "2.4.0-alpha.15", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.0-alpha.15.tgz", + "integrity": "sha512-mt8z4Fm2WxfQYoQHPcKVjLQV6PgPqyKLbkCVY2cr5RSaamqCHjhKEpsFX66aL4D/7oYguuaUw9Bx03Vt0TpIIA==", + "dev": true, + "requires": { + "@volar/source-map": "2.4.0-alpha.15" + } + }, + "@volar/source-map": { + "version": "2.4.0-alpha.15", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.0-alpha.15.tgz", + "integrity": "sha512-8Htngw5TmBY4L3ClDqBGyfLhsB8EmoEXUH1xydyEtEoK0O6NX5ur4Jw8jgvscTlwzizyl/wsN1vn0cQXVbbXYg==", + "dev": true + }, + "@volar/typescript": { + "version": "2.4.0-alpha.15", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.0-alpha.15.tgz", + "integrity": "sha512-U3StRBbDuxV6Woa4hvGS4kz3XcOzrWUKgFdEFN+ba1x3eaYg7+ytau8ul05xgA+UNGLXXsKur7fTUhDFyISk0w==", + "dev": true, + "requires": { + "@volar/language-core": "2.4.0-alpha.15", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "@vue/compiler-core": { + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.31.tgz", + "integrity": "sha512-skOiodXWTV3DxfDhB4rOf3OGalpITLlgCeOwb+Y9GJpfQ8ErigdBUHomBzvG78JoVE8MJoQsb+qhZiHfKeNeEg==", + "requires": { + "@babel/parser": "^7.24.7", + "@vue/shared": "3.4.31", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.0" + } + }, + "@vue/compiler-dom": { + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.31.tgz", + "integrity": "sha512-wK424WMXsG1IGMyDGyLqB+TbmEBFM78hIsOJ9QwUVLGrcSk0ak6zYty7Pj8ftm7nEtdU/DGQxAXp0/lM/2cEpQ==", + "requires": { + "@vue/compiler-core": "3.4.31", + "@vue/shared": "3.4.31" + } + }, + "@vue/compiler-sfc": { + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.31.tgz", + "integrity": "sha512-einJxqEw8IIJxzmnxmJBuK2usI+lJonl53foq+9etB2HAzlPjAS/wa7r0uUpXw5ByX3/0uswVSrjNb17vJm1kQ==", + "requires": { + "@babel/parser": "^7.24.7", + "@vue/compiler-core": "3.4.31", + "@vue/compiler-dom": "3.4.31", + "@vue/compiler-ssr": "3.4.31", + "@vue/shared": "3.4.31", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.10", + "postcss": "^8.4.38", + "source-map-js": "^1.2.0" + } + }, + "@vue/compiler-ssr": { + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.31.tgz", + "integrity": "sha512-RtefmITAje3fJ8FSg1gwgDhdKhZVntIVbwupdyZDSifZTRMiWxWehAOTCc8/KZDnBOcYQ4/9VWxsTbd3wT0hAA==", + "requires": { + "@vue/compiler-dom": "3.4.31", + "@vue/shared": "3.4.31" + } + }, + "@vue/language-core": { + "version": "2.0.26", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.0.26.tgz", + "integrity": "sha512-/lt6SfQ3O1yDAhPsnLv9iSUgXd1dMHqUm/t3RctfqjuwQf1LnftZ414X3UBn6aXT4MiwXWtbNJ4Z0NZWwDWgJQ==", + "dev": true, + "requires": { + "@volar/language-core": "~2.4.0-alpha.15", + "@vue/compiler-dom": "^3.4.0", + "@vue/shared": "^3.4.0", + "computeds": "^0.0.1", + "minimatch": "^9.0.3", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1", + "vue-template-compiler": "^2.7.14" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "@vue/reactivity": { + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.31.tgz", + "integrity": "sha512-VGkTani8SOoVkZNds1PfJ/T1SlAIOf8E58PGAhIOUDYPC4GAmFA2u/E14TDAFcf3vVDKunc4QqCe/SHr8xC65Q==", + "requires": { + "@vue/shared": "3.4.31" + } + }, + "@vue/runtime-core": { + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.31.tgz", + "integrity": "sha512-LDkztxeUPazxG/p8c5JDDKPfkCDBkkiNLVNf7XZIUnJ+66GVGkP+TIh34+8LtPisZ+HMWl2zqhIw0xN5MwU1cw==", + "requires": { + "@vue/reactivity": "3.4.31", + "@vue/shared": "3.4.31" + } + }, + "@vue/runtime-dom": { + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.31.tgz", + "integrity": "sha512-2Auws3mB7+lHhTFCg8E9ZWopA6Q6L455EcU7bzcQ4x6Dn4cCPuqj6S2oBZgN2a8vJRS/LSYYxwFFq2Hlx3Fsaw==", + "requires": { + "@vue/reactivity": "3.4.31", + "@vue/runtime-core": "3.4.31", + "@vue/shared": "3.4.31", + "csstype": "^3.1.3" + } + }, + "@vue/server-renderer": { + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.31.tgz", + "integrity": "sha512-D5BLbdvrlR9PE3by9GaUp1gQXlCNadIZytMIb8H2h3FMWJd4oUfkUTEH2wAr3qxoRz25uxbTcbqd3WKlm9EHQA==", + "requires": { + "@vue/compiler-ssr": "3.4.31", + "@vue/shared": "3.4.31" + } + }, + "@vue/shared": { + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.31.tgz", + "integrity": "sha512-Yp3wtJk//8cO4NItOPpi3QkLExAr/aLBGZMmTtW9WpdwBCJpRM6zj9WgWktXAl8IDIozwNMByT45JP3tO3ACWA==" + }, "@yarnpkg/lockfile": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", @@ -16443,6 +17983,12 @@ "dot-prop": "^5.1.0" } }, + "computeds": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/computeds/-/computeds-0.0.1.tgz", + "integrity": "sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -16684,6 +18230,11 @@ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true }, + "csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, "dargs": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", @@ -16696,6 +18247,12 @@ "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", "dev": true }, + "de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -16939,6 +18496,11 @@ "ansi-colors": "^4.1.1" } }, + "entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" + }, "env-paths": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", @@ -17067,6 +18629,37 @@ "is-symbol": "^1.0.2" } }, + "esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "requires": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -17395,6 +18988,11 @@ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true }, + "estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -17482,8 +19080,7 @@ "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-glob": { "version": "3.3.2", @@ -18105,6 +19702,12 @@ "function-bind": "^1.1.2" } }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, "homedir-polyfill": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", @@ -19630,6 +21233,14 @@ "yallist": "^3.0.2" } }, + "magic-string": { + "version": "0.30.10", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", + "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "requires": { + "@jridgewell/sourcemap-codec": "^1.4.15" + } + }, "make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -20051,6 +21662,12 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true + }, "multimatch": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-5.0.0.tgz", @@ -20078,6 +21695,11 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==" + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -20767,6 +22389,12 @@ "parse-path": "^7.0.0" } }, + "path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true + }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -20816,10 +22444,9 @@ "dev": true }, "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" }, "picomatch": { "version": "2.3.1", @@ -20854,6 +22481,16 @@ "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", "dev": true }, + "postcss": { + "version": "8.4.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", + "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", + "requires": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.1", + "source-map-js": "^1.2.0" + } + }, "postcss-selector-parser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz", @@ -21319,6 +22956,32 @@ } } }, + "rollup": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", + "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", + "dev": true, + "requires": { + "@rollup/rollup-android-arm-eabi": "4.18.0", + "@rollup/rollup-android-arm64": "4.18.0", + "@rollup/rollup-darwin-arm64": "4.18.0", + "@rollup/rollup-darwin-x64": "4.18.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", + "@rollup/rollup-linux-arm-musleabihf": "4.18.0", + "@rollup/rollup-linux-arm64-gnu": "4.18.0", + "@rollup/rollup-linux-arm64-musl": "4.18.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", + "@rollup/rollup-linux-riscv64-gnu": "4.18.0", + "@rollup/rollup-linux-s390x-gnu": "4.18.0", + "@rollup/rollup-linux-x64-gnu": "4.18.0", + "@rollup/rollup-linux-x64-musl": "4.18.0", + "@rollup/rollup-win32-arm64-msvc": "4.18.0", + "@rollup/rollup-win32-ia32-msvc": "4.18.0", + "@rollup/rollup-win32-x64-msvc": "4.18.0", + "@types/estree": "1.0.5", + "fsevents": "~2.3.2" + } + }, "run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -21386,6 +23049,23 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "scishop": { + "version": "file:apps/scishop", + "requires": { + "@4bitlabs/color": "^2.1.5", + "@4bitlabs/color-space": "^1.2.3", + "@4bitlabs/image": "^3.3.11", + "@4bitlabs/sci0": "^4.0.1", + "@4bitlabs/vec2": "^1.2.0", + "@vitejs/plugin-vue": "^5.0.4", + "fast-deep-equal": "^3.1.3", + "transformation-matrix": "^2.16.1", + "typescript": "^5.2.2", + "vite": "^5.2.0", + "vue": "^3.4.21", + "vue-tsc": "^2.0.6" + } + }, "semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -21585,6 +23265,11 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, + "source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==" + }, "source-map-support": { "version": "0.5.13", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", @@ -21983,6 +23668,11 @@ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "dev": true }, + "transformation-matrix": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/transformation-matrix/-/transformation-matrix-2.16.1.tgz", + "integrity": "sha512-tdtC3wxVEuzU7X/ydL131Q3JU5cPMEn37oqVLITjRDSDsnSHVFzW2JiCLfZLIQEgWzZHdSy3J6bZzvKEN24jGA==" + }, "treeverse": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/treeverse/-/treeverse-3.0.0.tgz", @@ -22145,7 +23835,7 @@ "version": "5.5.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", - "dev": true + "devOptional": true }, "typescript-eslint": { "version": "7.15.0", @@ -22177,6 +23867,11 @@ "which-boxed-primitive": "^1.0.2" } }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, "unique-filename": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", @@ -22271,6 +23966,65 @@ "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", "dev": true }, + "vite": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.3.tgz", + "integrity": "sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==", + "dev": true, + "requires": { + "esbuild": "^0.21.3", + "fsevents": "~2.3.3", + "postcss": "^8.4.39", + "rollup": "^4.13.0" + } + }, + "vscode-uri": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", + "dev": true + }, + "vue": { + "version": "3.4.31", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.31.tgz", + "integrity": "sha512-njqRrOy7W3YLAlVqSKpBebtZpDVg21FPoaq1I7f/+qqBThK9ChAIjkRWgeP6Eat+8C+iia4P3OYqpATP21BCoQ==", + "requires": { + "@vue/compiler-dom": "3.4.31", + "@vue/compiler-sfc": "3.4.31", + "@vue/runtime-dom": "3.4.31", + "@vue/server-renderer": "3.4.31", + "@vue/shared": "3.4.31" + } + }, + "vue-template-compiler": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz", + "integrity": "sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==", + "dev": true, + "requires": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "vue-tsc": { + "version": "2.0.26", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.0.26.tgz", + "integrity": "sha512-tOhuwy2bIXbMhz82ef37qeiaQHMXKQkD6mOF6CCPl3/uYtST3l6fdNyfMxipudrQTxTfXVPlgJdMENBFfC1CfQ==", + "dev": true, + "requires": { + "@volar/typescript": "~2.4.0-alpha.15", + "@vue/language-core": "2.0.26", + "semver": "^7.5.4" + }, + "dependencies": { + "semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true + } + } + }, "walk-up-path": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-3.0.1.tgz", diff --git a/package.json b/package.json index 745aa75..2f63d9d 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,8 @@ "libs/sci0", "libs/vec2", "libs/crt-lite", - "apps/scibud" + "apps/scibud", + "apps/scishop" ], "devDependencies": { "@eslint/js": "^9.6.0",