diff --git a/package.json b/package.json index 9a7c9f9a..10c98ada 100644 --- a/package.json +++ b/package.json @@ -13,5 +13,10 @@ "devDependencies": { "wsrun": "^5.2.4" }, - "packageManager": "yarn@3.3.0" + "packageManager": "yarn@3.3.0", + "dependencies": { + "@radix-ui/react-popover": "latest", + "@radix-ui/react-scroll-area": "^1.0.3", + "react-draggable-tree": "^0.7.0" + } } diff --git a/pkgs/core/package.json b/pkgs/core/package.json index 171416f5..a3799bf7 100644 --- a/pkgs/core/package.json +++ b/pkgs/core/package.json @@ -3,7 +3,7 @@ "version": "0.0.0", "main": "dist/index.umd.js", "module": "dist/index.mjs", - "types": "./dist/index.d.ts", + "types": "dist/index.d.ts", "license": "MIT", "exports": { ".": { @@ -25,8 +25,9 @@ "@rollup/plugin-typescript": "^9.0.2", "@types/gl-matrix": "^3.2.0", "@types/three": "^0.146.0", - "typescript": "^4.9.3", + "typescript": "^5.0.3", "vite": "^4.0.3", + "vite-plugin-dts": "^2.1.0", "vite-tsconfig-paths": "^4.0.3", "vitest": "^0.26.2" }, diff --git a/pkgs/core/src/Commands/ICommand.ts b/pkgs/core/src/Commands/ICommand.ts new file mode 100644 index 00000000..e69de29b diff --git a/pkgs/core/src/Document/Document.ts b/pkgs/core/src/Document/Document.ts index 17f4779f..8e1fae3e 100644 --- a/pkgs/core/src/Document/Document.ts +++ b/pkgs/core/src/Document/Document.ts @@ -8,6 +8,10 @@ export namespace PaplicoDocument { export type Meta = { schemaVersion: '2' title: string + mainArtboard: { + width: number + height: number + } } export type SerializedSchema = { @@ -34,12 +38,20 @@ export class PaplicoDocument { public meta: PaplicoDocument.Meta = { schemaVersion: '2', title: '', + mainArtboard: { + width: 100, + height: 100, + }, } public layerEntities: LayerEntity[] = [] public layerTree: LayerNode[] = [] public blobs: PaplicoBlob[] = [] + public constructor({ width, height }: { width: number; height: number }) { + this.meta.mainArtboard = { width, height } + } + public resolveLayerEntity(layerId: string) { return this.layerEntities.find((layer) => layer.uid === layerId) } diff --git a/pkgs/core/src/Document/LayerEntity/InkSetting.ts b/pkgs/core/src/Document/LayerEntity/InkSetting.ts new file mode 100644 index 00000000..6d864e01 --- /dev/null +++ b/pkgs/core/src/Document/LayerEntity/InkSetting.ts @@ -0,0 +1,7 @@ +export type InkSetting | null = {}> = { + inkId: string + inkVersion: string + + /** Ink specific settings */ + specific: T +} diff --git a/pkgs/core/src/Document/factory.ts b/pkgs/core/src/Document/factory.ts index 9ec364b8..83afe1b4 100644 --- a/pkgs/core/src/Document/factory.ts +++ b/pkgs/core/src/Document/factory.ts @@ -1,6 +1,7 @@ import prand from 'pure-rand' import { ulid } from '@/utils/ulid' +import { PaplicoDocument } from './Document' import { FilterLayer, RasterLayer, VectorLayer } from './LayerEntity' import { VectorObject } from './LayerEntity/VectorObject' import { VectorPath } from './LayerEntity/VectorPath' @@ -9,6 +10,16 @@ type Requires = Omit & { [P in K]-?: T[P] } +export const createDocument = ({ + width, + height, +}: { + width: number + height: number +}) => { + return new PaplicoDocument({ width, height }) +} + export const createRasterLayerEntity = ({ name = '', width, diff --git a/pkgs/core/src/Engine/Brush.ts b/pkgs/core/src/Engine/Brush.ts index dc9dcfe2..8570bbe2 100644 --- a/pkgs/core/src/Engine/Brush.ts +++ b/pkgs/core/src/Engine/Brush.ts @@ -6,10 +6,13 @@ import { WebGLRenderer, Camera } from 'three' // import { VectorObject } from '../DOM/VectorObject' import { VectorStrokeSetting, VectorPath } from '@/Document' import { RenderCycleLogger } from './RenderCycleLogger' +import { InkGenerator } from './Ink' export type BrushContext> = { abort: AbortSignal abortIfNeeded: () => never | void + + /** Render result destination canvas */ context: CanvasRenderingContext2D threeRenderer: WebGLRenderer threeCamera: Camera @@ -21,7 +24,7 @@ export type BrushContext> = { scale: { x: number; y: number } translate: { x: number; y: number } } - // ink: IInk + ink: InkGenerator brushSetting: VectorStrokeSetting /** Expected destination canvas size */ destSize: { width: number; height: number } diff --git a/pkgs/core/src/Engine/Brushes/CircleBrush.ts b/pkgs/core/src/Engine/Brushes/CircleBrush.ts index dbd93ec1..49093b14 100644 --- a/pkgs/core/src/Engine/Brushes/CircleBrush.ts +++ b/pkgs/core/src/Engine/Brushes/CircleBrush.ts @@ -19,7 +19,7 @@ export class CircleBrush implements IBrush { public getInitialSpecificConfig(): CircleBrush.SpecificSetting { return { - lineCap: 'square', + lineCap: 'round', } } @@ -72,8 +72,8 @@ export class CircleBrush implements IBrush { // console.log(scattered) - path.mapPoints( - scattered, + mapPoints( + path.points, (point, prev) => { ctx.bezierCurveTo( prev!.out?.x ?? prev!.x, diff --git a/pkgs/core/src/Engine/Brushes/index.ts b/pkgs/core/src/Engine/Brushes/index.ts new file mode 100644 index 00000000..1d4c023f --- /dev/null +++ b/pkgs/core/src/Engine/Brushes/index.ts @@ -0,0 +1 @@ +export { CircleBrush } from './CircleBrush' diff --git a/pkgs/core/src/Engine/Ink.ts b/pkgs/core/src/Engine/Ink.ts new file mode 100644 index 00000000..2292e771 --- /dev/null +++ b/pkgs/core/src/Engine/Ink.ts @@ -0,0 +1,35 @@ +import { ColorRGBA } from '@/Document' +import { VectorPathPoint } from '@/Document/LayerEntity/VectorPath' +import { Camera, WebGLRenderer } from 'three' + +export interface InkClass { + readonly id: string + readonly version: string + + new (): IInk +} + +export interface IInk { + class: InkClass + + initialize(): Promise | void + getInkGenerator(ctx: any): InkGenerator +} + +export interface InkGenerator { + getColor(inkContext: { + pointIndex: number + points: VectorPathPoint[] + pointAtLength: number + totalLength: number + baseColor: ColorRGBA + }): ColorRGBA + + applyTexture( + canvas: CanvasRenderingContext2D, + context: { + threeRenderer: WebGLRenderer + threeCamera: Camera + } + ): void +} diff --git a/pkgs/core/src/Engine/InkRegistry.ts b/pkgs/core/src/Engine/InkRegistry.ts new file mode 100644 index 00000000..a07989d2 --- /dev/null +++ b/pkgs/core/src/Engine/InkRegistry.ts @@ -0,0 +1,39 @@ +import { Emitter } from '@/utils/Emitter' +import { IInk, InkClass } from './Ink' + +type Events = { + entriesChanged: void +} + +export class InkRegistry extends Emitter { + protected brushes: Map = new Map() + protected instances: WeakMap = new WeakMap() + + public async register(Brush: InkClass) { + try { + this.brushes.set(Brush.id, Brush) + + const brush = new Brush() + await brush.initialize() + + this.instances.set(Brush, brush) + } catch (e) { + this.brushes.delete(Brush.id) + this.instances.delete(Brush) + throw e + } + + this.emit('entriesChanged') + } + + public get brushEntries(): InkClass[] { + return [...this.brushes.values()] + } + + public getInstance(id: string): IInk | null { + const Class = this.brushes.get(id) + if (!Class) return null + + return this.instances.get(Class) ?? null + } +} diff --git a/pkgs/core/src/Engine/Inks/PlainInk.ts b/pkgs/core/src/Engine/Inks/PlainInk.ts new file mode 100644 index 00000000..daa6ff3f --- /dev/null +++ b/pkgs/core/src/Engine/Inks/PlainInk.ts @@ -0,0 +1,23 @@ +import { IInk, InkGenerator } from '../Ink' + +export class PlainInk implements IInk { + public static id = '@paplico/core/ink/plain' + public static version = '0.0.1' + + public get class() { + return PlainInk + } + + public initialize() {} + + getInkGenerator(ctx: any): InkGenerator { + return { + applyTexture(canvas, context) { + return + }, + getColor({ pointIndex, points, baseColor }) { + return baseColor + }, + } + } +} diff --git a/pkgs/core/src/Engine/Inks/RainbowInk.ts b/pkgs/core/src/Engine/Inks/RainbowInk.ts new file mode 100644 index 00000000..223639a1 --- /dev/null +++ b/pkgs/core/src/Engine/Inks/RainbowInk.ts @@ -0,0 +1,33 @@ +import { createSeededRandom } from '@/Math' +import { IInk, InkGenerator } from '../Ink' + +export class RainbowInk implements IInk { + public static id = '@paplico/core/ink/rainbow' + public static version = '0.0.1' + + public get class() { + return RainbowInk + } + + public initialize() {} + + getInkGenerator(ctx: any): InkGenerator { + return { + applyTexture(canvas, context) { + return + }, + getColor({ pointIndex, points, baseColor }) { + const random = createSeededRandom(pointIndex) + + return baseColor + + return { + r: random.nextFloat(), + g: random.nextFloat(), + b: random.nextFloat(), + a: baseColor.a, + } + }, + } + } +} diff --git a/pkgs/core/src/Engine/MixerPipeline.ts b/pkgs/core/src/Engine/MixerPipeline.ts index 9a297276..e1a28c68 100644 --- a/pkgs/core/src/Engine/MixerPipeline.ts +++ b/pkgs/core/src/Engine/MixerPipeline.ts @@ -66,34 +66,42 @@ export class MixerPipeline { continue } - logger.log('Render layer', layer.uid, layer.name, layer.layerType) + logger.log( + 'Render layer', + layer.source.uid, + layer.source.name, + layer.source.layerType + ) let image: ImageBitmap | HTMLCanvasElement | null | void - if (override?.[layer.uid]) { - image = override[layer.uid] - } else if (layer.layerType === 'raster') { - image = (await doc.getOrCreateLayerBitmapCache(layer.uid))! - } else if (layer.layerType === 'vector') { + if (override?.[layer.source.uid]) { + image = override[layer.source.uid] + } else if (layer.source.layerType === 'raster') { + image = (await doc.getOrCreateLayerBitmapCache(layer.source.uid))! + } else if (layer.source.layerType === 'vector') { const requestSize = { width: viewport.width, height: viewport.height } - if (doc.hasLayerBitmapCache(layer.uid, requestSize)) { - logger.info('Use cached bitmap for vector layer', layer.uid) + if (doc.hasLayerBitmapCache(layer.source.uid, requestSize)) { + logger.info('Use cached bitmap for vector layer', layer.source.uid) image = (await doc.getOrCreateLayerBitmapCache( - layer.uid, + layer.source.uid, requestSize ))! } else { - logger.info('Cache is invalidated, re-render vector layer', layer.uid) + logger.info( + 'Cache is invalidated, re-render vector layer', + layer.source.uid + ) - await render.renderVectorLayer(tmp, layer, { + await render.renderVectorLayer(tmp, layer.source, { viewport, abort, logger, }) image = await doc.updateOrCreateLayerBitmapCache( - layer.uid, + layer.source.uid, tmpctx.getImageData(0, 0, viewport.width, viewport.height) ) } @@ -102,11 +110,13 @@ export class MixerPipeline { continue } - const mode = layerCompositeModeToCanvasCompositeMode(layer.compositeMode) + const mode = layerCompositeModeToCanvasCompositeMode( + layer.source.compositeMode + ) await saveAndRestoreCanvas(dest, async () => { logger.log( - `Will draw layer ${layer.uid} as ${mode} to destination.`, + `Will draw layer ${layer.source.uid} as ${mode} to destination.`, image ) dest.globalCompositeOperation = mode diff --git a/pkgs/core/src/Engine/Renderer.ts b/pkgs/core/src/Engine/Renderer.ts index df9025bb..0d180f55 100644 --- a/pkgs/core/src/Engine/Renderer.ts +++ b/pkgs/core/src/Engine/Renderer.ts @@ -1,5 +1,6 @@ import { VectorObject, VectorPath } from '@/Document' import { VectorLayer } from '@/Document/LayerEntity' +import { InkSetting } from '@/Document/LayerEntity/InkSetting' import { VectorStrokeSetting } from '@/Document/LayerEntity/VectorStrokeSetting' import { AbortError } from '@/Errors' import { UIStroke } from '@/UI/UIStroke' @@ -9,6 +10,7 @@ import { deepClone } from '@/utils/object' import { OrthographicCamera, WebGLRenderer } from 'three' import { BrushRegistry } from './BrushRegistry' import { createCanvas } from './CanvasFactory' +import { InkRegistry } from './InkRegistry' import { RenderCycleLogger } from './RenderCycleLogger' import { Viewport } from './types' import { @@ -22,10 +24,15 @@ import { export class Renderer { protected atomicThreeRenderer: AtomicResource protected brushRegistry: BrushRegistry + protected inkRegistry: InkRegistry protected camera: OrthographicCamera - constructor(options: { brushRegistry: BrushRegistry }) { + constructor(options: { + brushRegistry: BrushRegistry + inkRegistry: InkRegistry + }) { this.brushRegistry = options.brushRegistry + this.inkRegistry = options.inkRegistry const renderer = new WebGLRenderer({ alpha: true, @@ -160,7 +167,7 @@ export class Renderer { } } - protected async renderPath() {} + // protected async renderPath() {} public async renderVectorObject(dest: HTMLCanvasElement, path: VectorPath) { const dstctx = dest.getContext('2d')! @@ -184,10 +191,12 @@ export class Renderer { path: VectorPath, strokeSetting: VectorStrokeSetting, { + inkSetting, transform, abort, logger, }: { + inkSetting: InkSetting transform: { position: { x: number; y: number } scale: { x: number; y: number } @@ -198,8 +207,10 @@ export class Renderer { } ) { const brush = this.brushRegistry.getInstance(strokeSetting.brushId) + const ink = this.inkRegistry.getInstance(inkSetting.inkId) if (!brush) throw new Error(`Unregistered brush ${strokeSetting.brushId}`) + if (!ink) throw new Error(`Unregistered ink ${inkSetting.inkId}`) const renderer = await this.atomicThreeRenderer.enjure() const dstctx = dest.getContext('2d')! @@ -226,6 +237,7 @@ export class Renderer { threeCamera: this.camera, hintInput: null, brushSetting: deepClone(strokeSetting), + ink: ink.getInkGenerator({}), path: [path], destSize: { width: dest.width, height: dest.height }, transform: { diff --git a/pkgs/core/src/Engine/RuntimeDocument.ts b/pkgs/core/src/Engine/RuntimeDocument.ts index c9753efe..10b24219 100644 --- a/pkgs/core/src/Engine/RuntimeDocument.ts +++ b/pkgs/core/src/Engine/RuntimeDocument.ts @@ -33,7 +33,7 @@ export class RuntimeDocument { this.document.layerEntities.forEach((l) => { this.layerEntities.set(l.uid, { lastUpdated: 0, - ...l, + source: l, }) }) } diff --git a/pkgs/core/src/Engine/RuntimeDocument/RuntimeLayerEntity.ts b/pkgs/core/src/Engine/RuntimeDocument/RuntimeLayerEntity.ts index 4236df79..41c53a4d 100644 --- a/pkgs/core/src/Engine/RuntimeDocument/RuntimeLayerEntity.ts +++ b/pkgs/core/src/Engine/RuntimeDocument/RuntimeLayerEntity.ts @@ -1,5 +1,6 @@ import { LayerEntity } from '@/Document' -export type RuntimeLayerEntity = LayerEntity & { +export type RuntimeLayerEntity = { lastUpdated: number + source: LayerEntity } diff --git a/pkgs/core/src/Engine/index.ts b/pkgs/core/src/Engine/index.ts index f5f6e419..be08310e 100644 --- a/pkgs/core/src/Engine/index.ts +++ b/pkgs/core/src/Engine/index.ts @@ -1,6 +1,7 @@ export * as CanvasFactory from './CanvasFactory' export { MixerPipeline as Pipeline } from './MixerPipeline' export { RuntimeDocument } from './RuntimeDocument' +export * as StandardBrushes from './Brushes/index' export { createCustomBrush, type IBrush, diff --git a/pkgs/core/src/Errors/InvalidOptionError.ts b/pkgs/core/src/Errors/InvalidOptionError.ts new file mode 100644 index 00000000..a18fdfb3 --- /dev/null +++ b/pkgs/core/src/Errors/InvalidOptionError.ts @@ -0,0 +1,3 @@ +import { PaplicoIgnoreableError } from './PaplicoIgnoreableError' + +export class InvalidOptionError extends Error {} diff --git a/pkgs/core/src/Errors/index.ts b/pkgs/core/src/Errors/index.ts index 82e91435..db2d51b4 100644 --- a/pkgs/core/src/Errors/index.ts +++ b/pkgs/core/src/Errors/index.ts @@ -1,2 +1,3 @@ export { AbortError } from './AbortError' export { PaplicoIgnoreableError } from './PaplicoIgnoreableError' +export { InvalidOptionError } from './InvalidOptionError' diff --git a/pkgs/core/src/Extras/Brushes/ScatterBrush-worker.ts b/pkgs/core/src/Extras/Brushes/ScatterBrush-worker.ts index 58e59951..15d48bc8 100644 --- a/pkgs/core/src/Extras/Brushes/ScatterBrush-worker.ts +++ b/pkgs/core/src/Extras/Brushes/ScatterBrush-worker.ts @@ -1,9 +1,10 @@ import { pointsToSVGCommandArray } from '@/Engine/VectorUtils' import { type BrushLayoutData } from '@/index' import { getRadianFromTangent } from '@/StrokeHelper' -import { interpolateMapObject, Matrix4 } from '@/Math' +import { interpolateMapObject, lerp, Matrix4 } from '@/Math' import { indexedPointAtLength } from '@/fastsvg/CachedPointAtLength' import { type VectorPath } from '@/Document/LayerEntity/VectorPath' +import { degToRad } from '@/utils/math' export type Payload = | { type: 'warming' } @@ -13,6 +14,7 @@ export type Payload = type: 'getPoints' path: VectorPath destSize: { width: number; height: number } + brushSize: number scatterRange: number scatterScale: number } @@ -21,12 +23,15 @@ export type GetPointWorkerResponse = { id: string type: 'getPoints' matrices: Array + lengths: number[] + totalLength: number bbox: { left: number; top: number; right: number; bottom: number } | null } export type WorkerResponse = { type: 'warming' } | GetPointWorkerResponse const abortedTasks = new Set() +const RAD90DEG = degToRad(90) self.onmessage = async ({ data }: MessageEvent) => { switch (data.type) { @@ -41,9 +46,10 @@ self.onmessage = async ({ data }: MessageEvent) => { } case 'getPoints': { - const { id, path, destSize, scatterRange, scatterScale } = data + const { id, path, destSize, brushSize, scatterRange, scatterScale } = data const { points } = path + const lengths: number[] = [] const matrices: (number[] | Float32Array)[] = [] const bbox: BrushLayoutData['bbox'] = { left: 0, @@ -61,6 +67,7 @@ self.onmessage = async ({ data }: MessageEvent) => { const pal = indexedPointAtLength( pointsToSVGCommandArray(path.points, path.closed) ) + // const seqPal = pal.getSequencialReader() const totalLen = pal.totalLength @@ -70,41 +77,39 @@ self.onmessage = async ({ data }: MessageEvent) => { } const step = 1000 / totalLen - const counts = Math.round(totalLen / step) - - const interX = interpolateMapObject(points, (idx, arr) => arr[idx].x) - const interY = interpolateMapObject(points, (idx, arr) => arr[idx].y) - let i = 0 for (let len = 0; len <= totalLen; len += step) { if (abortedTasks.has(id)) { abortedTasks.delete(id) return } - i++ - i % 1000 === 0 && (await new Promise((r) => setTimeout(r, 0))) - const t = len / totalLen - const [x, y] = [interX(t), interY(t)] + // const [x, y] = [interX(t), interY(t)] + const [x, y] = pal.at(t * totalLen) + const next = pal.at(t + 0.01, { seek: false }) - const rad = getRadianFromTangent( - { x, y }, - { x: interX(t + 0.01), y: interY(t + 0.01) } - ) + const rad = + getRadianFromTangent({ x, y }, { x: next[0], y: next[1] }) + RAD90DEG const ypos = y / destSize.height const matt4 = new Matrix4() .translate( - x - destSize.width / 2, - (1 - ypos) * destSize.height - destSize.height / 2, + x + + lerp(-scatterRange, scatterRange, Math.cos(rad)) - + destSize.width / 2, + (1 - (ypos + lerp(-scatterRange, scatterRange, Math.sin(rad)))) * + destSize.height - + destSize.height / 2, 0 ) + .scale([brushSize, brushSize, 1]) .rotateZ(rad) // _mat4.fromArray(matt4.toArray()) matrices.push(matt4.toArray()) + lengths.push(len) bbox.left = Math.min(bbox.left, x) bbox.top = Math.min(bbox.top, y) @@ -121,6 +126,8 @@ self.onmessage = async ({ data }: MessageEvent) => { type: 'getPoints', id, bbox, + lengths, + totalLength: totalLen, matrices, } satisfies GetPointWorkerResponse) } diff --git a/pkgs/core/src/Extras/Brushes/ScatterBrush.ts b/pkgs/core/src/Extras/Brushes/ScatterBrush.ts index 0f8c7628..6244d961 100644 --- a/pkgs/core/src/Extras/Brushes/ScatterBrush.ts +++ b/pkgs/core/src/Extras/Brushes/ScatterBrush.ts @@ -1,4 +1,4 @@ -import { BrushContext, IBrush, BrushLayoutData } from '@/index' +import { BrushContext, IBrush, BrushLayoutData, StrokeHelper } from '@/index' import * as Textures from './ScatterTexture/index' import { mergeToNew } from '@/utils/object' import { @@ -22,13 +22,14 @@ import { type WorkerResponse, type Payload, } from './ScatterBrush-worker' +import { ColorRGBA } from '@/Document' const _mat4 = new ThreeMatrix4() const generateId = () => (Date.now() + Math.random()).toString(36) export declare namespace ScatterBrush { - export type SpecificSetting = { + export type SpecificSetting = Partial<{ texture: keyof typeof Textures divisions: number scatterRange: number @@ -44,7 +45,7 @@ export declare namespace ScatterBrush { pressureInfluence: number /** 0..1 */ noiseInfluence: number - } + }> } export class ScatterBrush implements IBrush { @@ -112,6 +113,17 @@ export class ScatterBrush implements IBrush { blendEquation: AddEquation, }) + // this.materials[name] = new ShaderMaterial({ + // uniforms: { + // texture: { value: texture }, + // // color: { value: new Color(0xffffff) }, + // }, + // vertexShader: VS, + // fragmentShader: FS, + // vertexColors: true, + // depthTest: true, + // }) + // this.textures[name] = await createImageBitmap(data) }) ) @@ -123,13 +135,15 @@ export class ScatterBrush implements IBrush { context: ctx, path: inputPath, transform, - // ink, + ink, brushSetting: { size, color, opacity, specific }, threeRenderer, threeCamera, destSize, }: BrushContext): Promise { const sp = mergeToNew(this.getInitialSpecificConfig(), specific) + const baseColor: ColorRGBA = { ...color, a: opacity } + const _color = new Color() const bbox: BrushLayoutData['bbox'] = { left: 0, @@ -171,17 +185,18 @@ export class ScatterBrush implements IBrush { type: 'getPoints', path, // closed, + brushSize: size, destSize, scatterRange: sp.scatterRange, scatterScale: 1, - } as const satisfies Payload) + } satisfies Payload) }) if (res.matrices == null) return { bbox } const scene = new Scene() const material = this.materials[sp.texture] - material.color.set(new Color(color.r, color.g, color.b)) + // material.color.set(new Color(color.r, color.g, color.b)) material.needsUpdate = true const group = new Group() @@ -194,6 +209,19 @@ export class ScatterBrush implements IBrush { for (let i = 0, l = res.matrices.length; i < l; i++) { _mat4.fromArray(res.matrices[i]) mesh.setMatrixAt(i, _mat4) + mesh.setColorAt( + i, + StrokeHelper.rgbToThreeRGB( + ink.getColor({ + pointIndex: i, + points: path.points, + pointAtLength: res.lengths[i], + totalLength: res.totalLength, + baseColor, + }), + _color + ) + ) } if (res.bbox) { diff --git a/pkgs/core/src/History.ts b/pkgs/core/src/History.ts new file mode 100644 index 00000000..e69de29b diff --git a/pkgs/core/src/Math.ts b/pkgs/core/src/Math.ts index 397207ed..0966d72f 100644 --- a/pkgs/core/src/Math.ts +++ b/pkgs/core/src/Math.ts @@ -1,4 +1,7 @@ import { type mat4 } from 'gl-matrix' +import fastRandom from 'fast-random' + +export const createSeededRandom = fastRandom type Interpolator = (a: number, b: number, ratio: number) => number @@ -24,6 +27,8 @@ const clampIndex = (array: any[], index: number) => { index } +export type Vector3Tuple = [x: number, y: number, z: number] + // from: https://github.com/toji/gl-matrix/blob/master/src/mat4.js export class Matrix4 { // prettier-ignore @@ -34,7 +39,7 @@ export class Matrix4 { 0, 0, 0, 1 ] - public translate(...vector: [x: number, y: number, z: number]): this { + public translate(...vector: Vector3Tuple): this { const out = this.matrix, a = this.matrix.slice(), v = vector @@ -87,6 +92,34 @@ export class Matrix4 { return this } + /** + * Scales the mat4 by the dimensions in the given vec3 not using vectorization + **/ + public scale(v: Vector3Tuple) { + const out = this.matrix + const a = this.matrix.slice() + const [x, y, z] = v + + out[0] = a[0] * x + out[1] = a[1] * x + out[2] = a[2] * x + out[3] = a[3] * x + out[4] = a[4] * y + out[5] = a[5] * y + out[6] = a[6] * y + out[7] = a[7] * y + out[8] = a[8] * z + out[9] = a[9] * z + out[10] = a[10] * z + out[11] = a[11] * z + out[12] = a[12] + out[13] = a[13] + out[14] = a[14] + out[15] = a[15] + + return this + } + public rotateZ(rad: number): this { const out = this.matrix, a = this.matrix.slice() diff --git a/pkgs/core/src/Paplico.ts b/pkgs/core/src/Paplico.ts index fcfb0fe7..62f0a21d 100644 --- a/pkgs/core/src/Paplico.ts +++ b/pkgs/core/src/Paplico.ts @@ -17,16 +17,16 @@ import { setCanvasSize } from '@/utils/canvas' import { RuntimeDocument } from './Engine/RuntimeDocument' import { VectorStrokeSetting } from './Document/LayerEntity/VectorStrokeSetting' import { VectorFillSetting } from './Document/LayerEntity/VectorFillSetting' +import { InkSetting as _InkSetting } from './Document/LayerEntity/InkSetting' import { deepClone } from './utils/object' -import { - VectorAppearance, - VectorAppearanceFill, -} from './Document/LayerEntity/VectorAppearance' +import { VectorAppearance } from './Document/LayerEntity/VectorAppearance' import { CircleBrush } from './Engine/Brushes/CircleBrush' import { Emitter } from './utils/Emitter' import { BrushClass } from './Engine/Brush' -import { logImage } from './utils/DebugHelper' import { RenderCycleLogger } from './Engine/RenderCycleLogger' +import { PlainInk } from './Engine/Inks/PlainInk' +import { InkRegistry } from './Engine/InkRegistry' +import { RainbowInk } from './Engine/Inks/RainbowInk' export namespace Paplico { export type StrokeSetting = any> = @@ -34,6 +34,8 @@ export namespace Paplico { export type FillSetting = VectorFillSetting + export type InkSetting = _InkSetting + export type State = { activeLayer: { layerType: LayerEntity['layerType'] @@ -42,6 +44,7 @@ export namespace Paplico { } | null currentStroke: StrokeSetting | null currentFill: FillSetting | null + currentInk: InkSetting strokeComposition: 'normal' | 'erase' brushEntries: BrushClass[] } @@ -82,6 +85,7 @@ const singletonCall = Promise>(fn: T) => { export class Paplico extends Emitter { public brushes: BrushRegistry + public inks: InkRegistry public pipeline: MixerPipeline public renderer: Renderer @@ -102,6 +106,11 @@ export class Paplico extends Emitter { activeLayer: null, currentStroke: null, currentFill: null, + currentInk: { + inkId: RainbowInk.id, + inkVersion: RainbowInk.version, + specific: {}, + }, strokeComposition: 'normal', brushEntries: [], } @@ -114,8 +123,15 @@ export class Paplico extends Emitter { this.brushes = new BrushRegistry() this.brushes.register(CircleBrush) + this.inks = new InkRegistry() + this.inks.register(PlainInk) + this.inks.register(RainbowInk) + this.pipeline = new MixerPipeline({ brushRegistry: this.brushes, canvas }) - this.renderer = new Renderer({ brushRegistry: this.brushes }) + this.renderer = new Renderer({ + brushRegistry: this.brushes, + inkRegistry: this.inks, + }) this.uiCanvas = new UICanvas(canvas).activate() this.dstCanvas = canvas @@ -124,8 +140,8 @@ export class Paplico extends Emitter { this.tmp = createCanvas() this.tmpctx = this.tmp.getContext('2d', { willReadFrequently: true })! - this.onUIStrokeChange = singletonCall(this.onUIStrokeChange.bind(this)) - this.onUIStrokeComplete = singletonCall(this.onUIStrokeComplete.bind(this)) + this.onUIStrokeChange = this.onUIStrokeChange.bind(this) + this.onUIStrokeComplete = this.onUIStrokeComplete.bind(this) this.initilize() } @@ -291,6 +307,7 @@ export class Paplico extends Emitter { renderLogger.log('render stroke') // Write stroke to current layer await this.renderer.renderStroke(this.tmp, path, this.strokeSetting, { + inkSetting: this.#state.currentInk, // abort: this.lastRenderAborter.signal, transform: { position: { x: 0, y: 0 }, @@ -345,7 +362,7 @@ export class Paplico extends Emitter { const path = stroke.toSimplefiedPath() RenderCycleLogger.current.timeEnd('toSimplefiedPath') RenderCycleLogger.current.info( - `Path simplified: ${path.points.length} -> ${stroke.points.length}` + `Path simplified: ${stroke.points.length} -> ${path.points.length}` ) if (this.activeLayerEntity.layerType === 'vector') { @@ -391,6 +408,7 @@ export class Paplico extends Emitter { // Write stroke to current layer await this.renderer.renderStroke(this.tmp, path, this.strokeSetting, { + inkSetting: this.#state.currentInk, // abort: this.lastRenderAborter.signal, transform: { position: { x: 0, y: 0 }, diff --git a/pkgs/core/src/StrokeHelper.ts b/pkgs/core/src/StrokeHelper.ts index 00be46e4..e85225cb 100644 --- a/pkgs/core/src/StrokeHelper.ts +++ b/pkgs/core/src/StrokeHelper.ts @@ -10,17 +10,29 @@ import { pointsToSVGCommandArray, pointsToSVGPath } from './Engine/VectorUtils' import { ColorRGB, createVectorPath } from './Document' import { interpolateMap, interpolateMapObject, lerp } from './Math' import { FuncStats } from './utils/perfstats' -import { degToRad, radToDeg } from './utils/math' +import { degToRad } from './utils/math' +import { type Color } from 'three' export { setCanvasSize } from '@/utils/canvas' export { mapPoints } from './Engine/VectorUtils' -// type ScatteredPoint = VectorPathPoint & {} +export { + interpolateMap, + interpolateMapObject, + indexedPointAtLength, + type IndexedPointAtLength, + pointsToSVGCommandArray, +} export const rgbToHexColor = (color: ColorRGB) => { return rgb(color.r * 255, color.g * 255, color.b * 255) } +export const rgbToThreeRGB = (color: ColorRGB, target: Color) => { + target.setRGB(color.r, color.g, color.b) + return target +} + const getTangentAt = (pal: SequencialPointAtLength, t: number) => { const stat = FuncStats.start(scatterPlot) diff --git a/pkgs/core/src/UI/UIStroke.ts b/pkgs/core/src/UI/UIStroke.ts index 095f0cd6..301c90cf 100644 --- a/pkgs/core/src/UI/UIStroke.ts +++ b/pkgs/core/src/UI/UIStroke.ts @@ -50,13 +50,20 @@ export class UIStroke { } } + /** + * @param tolerance + * 補正強度(0=なし,1=元の線に忠実,5=かなりなめらか くらいのニュアンス) + * + * SEE: https://luncheon.github.io/simplify-svg-path/index.html + */ public toSimplefiedPath({ - tolerance = 1, + tolerance = 5, }: { tolerance?: number } = {}): VectorPath { const simplified = simplifySvgPath( this.points.map((p) => [p.x, p.y] as const), - { closed: false, precision: 3, tolerance } + { closed: false, precision: 5, tolerance } ) + const absoluted = abs(simplified) const pal = indexedPointAtLength(simplified) diff --git a/pkgs/core/src/VectorProcess/splinePoints.ts b/pkgs/core/src/VectorProcess/splinePoints.ts deleted file mode 100644 index d48eff31..00000000 --- a/pkgs/core/src/VectorProcess/splinePoints.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { UIStrokePoint } from '@/UI/UIStroke' -import { VectorPathPoints } from '@/Document/LayerEntity/VectorPath' - -export const splinePoints = ( - points: UIStrokePoint[], - isClosedCurve = false -): VectorPathPoints => { - const n = points.length - - if (n < 3) - return { - points: points.map((p): VectorPathPoints['points'][0] => ({ - x: p.x, - y: p.y, - in: null, - out: null, - deltaTime: p.deltaTimeMs, - pressure: p.pressure, - tilt: p.tilt ?? { x: 0, y: 0 }, - })), - } - - let p0 = isClosedCurve ? points[n - 1] : points[0] - let p1 = points[0] - let p2 = points[1] - let p3 = points[2] - let pts = [points[0]] - - for (let i = 1; i < n; i++) { - pts.push([]) - } - - return pts -} diff --git a/pkgs/core/src/fastsvg/CachedPointAtLength.spec.ts b/pkgs/core/src/fastsvg/CachedPointAtLength.spec.ts index 4cd907b4..b8813a88 100644 --- a/pkgs/core/src/fastsvg/CachedPointAtLength.spec.ts +++ b/pkgs/core/src/fastsvg/CachedPointAtLength.spec.ts @@ -19,6 +19,9 @@ describe('CachedPointAtLength', () => { expect(cached.at(100)[0]).toBeCloseTo(original.at(100)[0], 2) expect(cached.at(100)[1]).toBeCloseTo(original.at(100)[1], 2) + + expect(cached.at(length)[0]).toBeCloseTo(original.at(length)[0], 2) + expect(cached.at(length)[1]).toBeCloseTo(original.at(length)[1], 2) }) describe('getSequencialReader', () => { diff --git a/pkgs/core/src/index.ts b/pkgs/core/src/index.ts index db01823e..2bfcc9bb 100644 --- a/pkgs/core/src/index.ts +++ b/pkgs/core/src/index.ts @@ -6,6 +6,7 @@ export { Pipeline, RuntimeDocument, createCustomBrush, + StandardBrushes, type BrushClass, type BrushContext, type IBrush, @@ -17,4 +18,5 @@ export * as VectorProcess from './VectorProcess' export * as RasterProcess from './RasterProcess' export * as ExtraBrushes from './Extras/ExtraBrushes' export * as StrokeHelper from './StrokeHelper' +export * as PaplicoMath from './Math' export { installGlobally as _installPapDebugGlobaly } from './utils/DebugHelper' diff --git a/pkgs/core/src/thirdparty/ulid.ts b/pkgs/core/src/thirdparty/ulid.ts index 144aae98..cf692f95 100644 --- a/pkgs/core/src/thirdparty/ulid.ts +++ b/pkgs/core/src/thirdparty/ulid.ts @@ -57,7 +57,7 @@ export function replaceCharAt(str: string, index: number, char: string) { } export function incrementBase32(str: string): string { - let done: string = undefined + let done: string | undefined = undefined let index = str.length let char let charIndex @@ -200,7 +200,7 @@ export function monotonicFactory(currPrng?: PRNG): ULID { return encodeTime(lastTime, TIME_LEN) + incrementedRandom } lastTime = seedTime - const newRandom = (lastRandom = encodeRandom(RANDOM_LEN, currPrng)) + const newRandom = (lastRandom = encodeRandom(RANDOM_LEN, currPrng!)) return encodeTime(seedTime, TIME_LEN) + newRandom } } diff --git a/pkgs/core/src/utils/resque.ts b/pkgs/core/src/utils/resque.ts index 20173fb9..f30866f7 100644 --- a/pkgs/core/src/utils/resque.ts +++ b/pkgs/core/src/utils/resque.ts @@ -26,7 +26,7 @@ interface Resque { const createResult = (result: any = null, error: any = null): Result => Object.assign([result, error] as const, { result, error }) -const isExpectedError = (actual: Error, expects: any[]) => { +const isExpectedError = (actual: unknown, expects: any[]) => { if (expects.length === 0) return true return expects.some((E) => actual instanceof E) } diff --git a/pkgs/core/vite.config.ts b/pkgs/core/vite.config.ts index 975bba2c..6143137b 100644 --- a/pkgs/core/vite.config.ts +++ b/pkgs/core/vite.config.ts @@ -2,8 +2,8 @@ import type {} from 'vitest/config' import { defineConfig } from 'vite' import tsConfigPaths from 'vite-tsconfig-paths' -import ts from '@rollup/plugin-typescript' import { resolve } from 'path' +import dts from 'vite-plugin-dts' export default defineConfig({ define: { @@ -17,7 +17,7 @@ export default defineConfig({ }, build: { minify: false, - emptyOutDir: true, + emptyOutDir: false, lib: { entry: 'src/index.ts', name: 'PapCore', @@ -28,18 +28,9 @@ export default defineConfig({ output: { exports: 'named', }, - plugins: [ - // @ts-expect-error - ts({ - rootDir: resolve(__dirname, 'src'), - declaration: true, - declarationDir: 'dist', - exclude: ['node_modules'], - }), - ], }, }, - plugins: [tsConfigPaths()], + plugins: [dts({ rollupTypes: false })], test: { globals: true, includeSource: ['src/**/*{.spec.ts,.ts}'], diff --git a/pkgs/web/next.config.js b/pkgs/web/next.config.js index b6b85da8..72da6d8c 100644 --- a/pkgs/web/next.config.js +++ b/pkgs/web/next.config.js @@ -1,13 +1,21 @@ /** @type {import('webpack')} */ const webpack = require('webpack') const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin') -const withPWA = require('next-pwa')({ dest: 'public' }) +const withPWA = require('next-pwa')({ dest: 'public', disableDevLogs: true }) const runtimeCaching = require('next-pwa/cache') const path = require('path') const { i18n } = require('./next-i18next.config') +const withNextra = require('nextra')({ + theme: 'nextra-theme-docs', + themeConfig: './src/theme.config.jsx', +}) + /** @type {import('next').NextConfig} */ const config = { + experimental: { + externalDir: true, + }, i18n, transpilePackages: ['@paplico/core', '@paplico/core-new'], compiler: { @@ -56,7 +64,7 @@ const config = { config.resolve.alias = { ...config.resolve.alias, '@paplico/core': path.join(__dirname, '../paplicore/src/index.ts'), - // '@paplico/core-new': path.join(__dirname, '../core/src/index.ts'), + '@paplico/core-new': path.join(__dirname, '../core/dist/index.mjs'), // '@paplico/ui': 'paplico-ui/src/index.ts', '🙌': path.join(__dirname, './src'), 'audio-worklet': path.resolve(__dirname, 'src/utils/audio-worklet'), @@ -109,4 +117,4 @@ const config = { }, } -module.exports = withPWA(config) +module.exports = withPWA(withNextra(config)) diff --git a/pkgs/web/package.json b/pkgs/web/package.json index 114f7ea8..e7111d59 100644 --- a/pkgs/web/package.json +++ b/pkgs/web/package.json @@ -20,6 +20,7 @@ "@fleur/react": "^5.0.3", "@floating-ui/react-dom": "^0.7.1", "@hanakla/arma": "^0.3.3", + "@paplico/core-new": "workspace:^", "@popperjs/core": "^2.9.3", "@react-aria/select": "^3.6.4", "@react-stately/select": "^3.1.6", @@ -47,17 +48,21 @@ "nanoid": "^3.3.2", "next": "^13.1.0", "next-i18next": "^8.5.5", + "nextra": "^2.2.19", + "nextra-theme-docs": "^2.2.19", "object-fit-math": "^1.0.0", "polished": "^4.1.3", - "react": "^18.0.0", + "react": "18.2.0", "react-color": "^2.19.3", "react-contexify": "^5.0.0", - "react-dom": "^18.0.0", + "react-dom": "18.2.0", + "react-draggable-tree": "^0.7.0", "react-error-boundary": "^3.1.3", "react-popper": "^2.2.5", "react-rnd": "^10.3.5", "react-spring": "^9.2.4", "react-use": "^17.2.4", + "react-use-event-hook": "^0.9.5", "react-use-gesture": "^9.1.3", "slate": "^0.65.3", "slate-react": "^0.65.3", @@ -82,8 +87,9 @@ "@types/howler": "^2.2.7", "@types/jsdom": "^16.2.14", "@types/node": "^16.4.4", - "@types/react": "^17.0.15", + "@types/react": "18.0.28", "@types/react-color": "^3.0.6", + "@types/react-dom": "18.0.11", "@types/styled-components": "5.1.25", "@types/webpack": "^5.28.0", "@types/wicg-file-system-access": "^2020.9.5", diff --git a/pkgs/web/src/domains/index.ts b/pkgs/web/src/domains/index.ts index 81a78d1e..cfdc3497 100644 --- a/pkgs/web/src/domains/index.ts +++ b/pkgs/web/src/domains/index.ts @@ -1,12 +1,10 @@ import Fleur, { AppContext } from '@fleur/fleur' -// import { AppStore } from './App' -// import { EditorStore } from './EditorStable' -// import { NotifyStore } from './Notify' +import { AppStore } from './App' +import { EditorStore } from './EditorStable' +import { NotifyStore } from './Notify' const app = new Fleur({ - stores: [ - // AppStore, EditorStore, NotifyStore - ], + stores: [AppStore, EditorStore, NotifyStore], }) export const createContext = () => { diff --git a/pkgs/web/src/features/Paint/hooks.ts b/pkgs/web/src/features/Paint/hooks.ts index 7398d227..fa10ad68 100644 --- a/pkgs/web/src/features/Paint/hooks.ts +++ b/pkgs/web/src/features/Paint/hooks.ts @@ -96,6 +96,7 @@ export const useDocumentWatch = ( ) => { const rerender = useUpdate() + console.trace() useEffect(() => { document?.on('layersChanged', rerender) return () => document?.off('layersChanged', rerender) diff --git a/pkgs/web/src/features/PaintV2/constants.ts b/pkgs/web/src/features/PaintV2/constants.ts new file mode 100644 index 00000000..9f2cc38c --- /dev/null +++ b/pkgs/web/src/features/PaintV2/constants.ts @@ -0,0 +1,176 @@ +import { ExtraBrushes, StandardBrushes } from '@paplico/core-new' +import { nanoid } from 'nanoid' + +export const BRUSH_PRESETS = [ + { + id: nanoid(), + nameKey: 'circle', + brushId: StandardBrushes.CircleBrush.id, + size: 20, + opacity: 0.8, + specific: { + texture: 'circle', + inOutInfluence: 0, + randomRotation: 0, + randomScale: 0, + scatterRange: 0, + pressureInfluence: 0.5, + } as Partial, + }, + { + id: nanoid(), + nameKey: 'scatter', + brushId: ExtraBrushes.ScatterBrush.id, + size: 20, + opacity: 0.8, + specific: { + texture: 'pencil', + // inOutInfluence: 0, + // randomRotation: 0, + // randomScale: 0, + // scatterRange: 0, + // pressureInfluence: 0.5, + } satisfies Partial, + }, + // { + // id: nanoid(), + // nameKey: 'circle', + // brushId: PapBrushes.ScatterBrush.id, + // size: 20, + // opacity: 0.8, + // specific: { + // texture: 'circle', + // inOutInfluence: 0, + // randomRotation: 0, + // randomScale: 0, + // scatterRange: 0, + // pressureInfluence: 0.5, + // } as Partial, + // }, + // { + // id: nanoid(), + // nameKey: 'fade', + // brushId: PapBrushes.ScatterBrush.id, + // size: 20, + // opacity: 0.8, + // specific: { + // texture: 'fadeBrush', + // randomRotation: 0, + // randomScale: 0, + // inOutInfluence: 0.2, + // scatterRange: 0, + // } as Partial, + // }, + // { + // id: nanoid(), + // nameKey: 'pencil', + // brushId: PapBrushes.ScatterBrush.id, + // size: 20, + // opacity: 0.8, + // specific: { + // texture: 'pencil', + // inOutInfluence: 1, + // randomRotation: 1, + // randomScale: 0, + // scatterRange: 0.5, + // pressureInfluence: 0.5, + // } as Partial, + // }, + // { + // id: nanoid(), + // nameKey: 'sparkle', + // brushId: PapBrushes.ScatterBrush.id, + // size: 100, + // opacity: 1, + // specific: { + // texture: 'sparkle', + // divisions: 5, + // rotationAdjust: 0, + // inOutInfluence: 2, + // randomRotation: 0, + // randomScale: 0.2, + // scatterRange: 0, + // pressureInfluence: 0, + // } as Partial, + // }, + // { + // id: nanoid(), + // nameKey: 'pencil-enterexit', + // brushId: PapBrushes.ScatterBrush.id, + // size: 20, + // opacity: 1, + // specific: { + // texture: 'pencil', + // inOutInfluence: 1, + // randomRotation: 1, + // randomScale: 0, + // scatterRange: 1, + // pressureInfluence: 0.5, + // } as Partial, + // }, + // { + // id: nanoid(), + // nameKey: 'baribari', + // brushId: PapBrushes.ScatterBrush.id, + // size: 20, + // opacity: 0.8, + // specific: { + // texture: 'baribari', + // inOutInfluence: 0, + // randomRotation: 0, + // randomScale: 0, + // scatterRange: 0, + // pressureInfluence: 0.5, + // } as Partial, + // }, + // { + // id: nanoid(), + // nameKey: 'noise', + // brushId: PapBrushes.ScatterBrush.id, + // size: 20, + // opacity: 1, + // specific: { + // texture: 'noise', + // inOutInfluence: 1, + // randomRotation: 1, + // randomScale: 0, + // scatterRange: 1, + // pressureInfluence: 0.5, + // noiseInfluence: 0.5, + // } as Partial, + // }, +] + +export const FILTER_CATEGORIES = [ + // { + // category: 'filterCategory.blur', + // items: [ + // PapFilters.BloomFilter.id, + // PapFilters.KawaseBlurFilter.id, + // PapFilters.ZoomBlurFilter.id, + // PapFilters.TiltShiftFilter.id, + // ], + // }, + // { + // category: 'filterCategory.distortion', + // items: [ + // PapFilters.LowResoFilter.id, + // PapFilters.BinarizationFilter.id, + // PapFilters.UVReplaceFilter.id, + // PapFilters.GlitchJpegFilter.id, + // ], + // }, + // { + // category: 'filterCategory.coloring', + // items: [ + // PapFilters.HalftoneFilter.id, + // PapFilters.GradientMapFilter.id, + // PapFilters.ChromaticAberrationFilter.id, + // PapFilters.PosterizationFilter.id, + // ], + // }, + // { + // category: 'filterCategory.etc', + // items: [PapFilters.OutlineFilter.id, PapFilters.NoiseFilter.id], + // }, +] diff --git a/pkgs/web/src/features/PaintV2/containers/BrushPresets.tsx b/pkgs/web/src/features/PaintV2/containers/BrushPresets.tsx new file mode 100644 index 00000000..ea3fc5da --- /dev/null +++ b/pkgs/web/src/features/PaintV2/containers/BrushPresets.tsx @@ -0,0 +1,183 @@ +import { useStore } from '@fleur/react' +import { useFunk } from '@hanakla/arma' +import { useTranslation } from 'next-i18next' +import { rgba } from 'polished' +import { useAsync } from 'react-use' +import { PapValueTypes } from '@paplico/core' +import { Document } from '@paplico/core-new' +import { css, useTheme } from 'styled-components' + +import { SidebarPane } from '🙌/components/SidebarPane' +import { EditorOps, EditorSelector, EditorStore } from '🙌/domains/EditorStable' +import { centering } from '🙌/utils/mixins' +import { PropsOf } from '🙌/utils/types' +import { shallowEquals } from '🙌/utils/object' +import { generateBrushThumbnail } from '../helpers' +import { useFleur } from '🙌/utils/hooks' +import { BRUSH_PRESETS } from '../constants' +import { memo, useContext } from 'react' +import { PaplicoEngineContext, usePaplicoEngine } from '../contexts/engine' + +export const BrushPresets = memo(function BrushPresets() { + const { t } = useTranslation('app') + const { execute } = useFleur() + const engine = usePaplicoEngine() + + const handleSelectBrush = useFunk['onSelected']>( + (settings) => { + engine.strokeSetting = { + ...engine.strokeSetting, + ...settings, + } + } + ) + + return ( + children}> +
+ {BRUSH_PRESETS.map((preset) => ( + + ))} + + {/* + + + */} +
+
+ ) +}) + +const BrushItem = memo(function BrushItem({ + brushId, + name, + preset, + onSelected, +}: { + brushId: string + name: string + preset: Partial + onSelected: (setting: Partial) => void +}) { + const theme = useTheme() + const engine = useContext(PaplicoEngineContext) + + // const { engine, currentBrushSetting } = useStore((get) => ({ + // engine: get(EditorStore).state.engine, + // currentBrushSetting: { ...EditorSelector.currentBrushSetting(get) }, + // })) + + const brushSize = preset.size ?? 20 + + const handleClick = useFunk(() => { + onSelected(preset) + }) + + const { value, error } = useAsync(async () => { + if (!engine) return null + + console.log(engine) + return await generateBrushThumbnail(engine, brushId, { + brushSize: 12, + specific: preset.specific ?? null, + size: { + width: 96 * 2, + height: 40 * 2, + }, + }) + }, [engine, brushSize, brushId]) + + console.log(value, error) + + const isMatchToSetting = false + // const isMatchToSetting = currentBrushSetting + // ? shallowEquals(preset, currentBrushSetting) + // : false + + return ( +
theme.exactColors.black60}; + border-radius: 2px; + + ${centering({ x: false, y: true })} + `} + onClick={handleClick} + style={{ + color: isMatchToSetting ? theme.colors.white50 : undefined, + background: isMatchToSetting ? theme.colors.active80 : undefined, + }} + tabIndex={-1} + > + +
+
+ {brushSize} +
+
{name}
+
+
+ ) +}) diff --git a/pkgs/web/src/features/PaintV2/containers/ControlsOverlay.tsx b/pkgs/web/src/features/PaintV2/containers/ControlsOverlay.tsx new file mode 100644 index 00000000..1620008b --- /dev/null +++ b/pkgs/web/src/features/PaintV2/containers/ControlsOverlay.tsx @@ -0,0 +1,65 @@ +import { useStore } from '@fleur/react' +import { DOMRectReadOnly } from 'use-measure' +import { EditorSelector } from '🙌/domains/EditorStable' +import { VectorLayerControl } from './LayerControls/VectorLayerControl' +import { RasterLayerControl } from './LayerControls/RasterLayerControl' +import { TextLayerControl } from './LayerControls/TextLayerControl' +import { GroupLayerControl } from './LayerControls/GroupLayerControl' +import { ReferenceLayerControl } from './LayerControls/ReferenceLayerControl' +import { useMeasure } from 'react-use' + +export const ControlsOverlay = ({ + editorBound, +}: { + editorBound: DOMRectReadOnly +}) => { + const { + activeLayer, + currentDocument, + canvasScale, + canvasPosition: { x, y }, + } = useStore((get) => ({ + activeLayer: EditorSelector.activeLayer(get), + currentDocument: EditorSelector.currentDocument(get), + canvasScale: EditorSelector.canvasScale(get), + canvasPosition: EditorSelector.canvasPosition(get), + })) + + // const bbox = currentLayerBBox ?? { width: 0, height: 0 } + + const [ref, rect] = useMeasure() + + if (!currentDocument) return null + + return ( + + + {!activeLayer?.lock && ( + <> + {activeLayer?.layerType === 'raster' && } + {activeLayer?.layerType === 'reference' && ( + + )} + {activeLayer?.layerType === 'group' && } + {activeLayer?.layerType === 'vector' && } + {activeLayer?.layerType === 'text' && } + + )} + + + ) +} diff --git a/pkgs/web/src/features/PaintV2/containers/DesktopTreeView/TreeRow.tsx b/pkgs/web/src/features/PaintV2/containers/DesktopTreeView/TreeRow.tsx new file mode 100644 index 00000000..60117e86 --- /dev/null +++ b/pkgs/web/src/features/PaintV2/containers/DesktopTreeView/TreeRow.tsx @@ -0,0 +1,193 @@ +import { TreeViewItem, TreeViewItemRow } from 'react-draggable-tree' + +interface NodeTreeViewItem extends TreeViewItem { + readonly node: Node +} + +export const TreeRow: React.FC<{ + rows: readonly TreeViewItemRow[] + index: number + item: NodeTreeViewItem + depth: number + indentation: number + onChange: () => void +}> = ({ rows, index, item, depth, indentation, onChange }) => { + const node = item.node + + const onCollapseButtonClick = (e: React.MouseEvent) => { + e.stopPropagation() + node.collapsed = !node.collapsed + onChange() + } + + const onClick = (event: React.MouseEvent) => { + if (event.metaKey) { + if (node.selected) { + node.deselect() + } else { + node.select() + } + } else if (event.shiftKey) { + let minSelectedIndex = index + let maxSelectedIndex = index + + for (const [i, row] of rows.entries()) { + if (row.item.node.selected) { + minSelectedIndex = Math.min(minSelectedIndex, i) + maxSelectedIndex = Math.max(maxSelectedIndex, i) + } + } + + for (let i = minSelectedIndex; i <= maxSelectedIndex; ++i) { + rows[i].item.node.select() + } + } else { + node.root.deselect() + node.select() + } + + onChange() + } + + return ( +
+ {node.firstChild !== undefined && ( + + {node.collapsed ? '[+]' : '[-]'} + + )} + {node.name} +
+ ) +} + +export class Node { + readonly key = (Math.random() * 0xfffffffff).toString(36).slice(2) + type: 'leaf' | 'branch' = 'leaf' + name = '' + selected = false + collapsed = false + parent: Node | undefined = undefined + nextSibling: Node | undefined = undefined + previousSibling: Node | undefined = undefined + firstChild: Node | undefined = undefined + lastChild: Node | undefined = undefined + + get children(): readonly Node[] { + const children: Node[] = [] + let node = this.firstChild + while (node) { + children.push(node) + node = node.nextSibling as Node | undefined + } + return children + } + + remove(): void { + const parent = this.parent + if (!parent) { + return + } + + const prev = this.previousSibling + const next = this.nextSibling + + if (prev) { + prev.nextSibling = next + } else { + parent.firstChild = next + } + if (next) { + next.previousSibling = prev + } else { + parent.lastChild = prev + } + this.parent = undefined + this.previousSibling = undefined + this.nextSibling = undefined + } + + insertBefore(child: Node, next: Node | undefined): void { + if (child === next) { + return + } + if (child.includes(this)) { + throw new Error('Cannot insert node to its descendant') + } + if (next && next.parent !== this) { + throw new Error('The ref node is not a child of this node') + } + child.remove() + + let prev = next ? next.previousSibling : this.lastChild + if (prev) { + prev.nextSibling = child + } else { + this.firstChild = child + } + if (next) { + next.previousSibling = child + } else { + this.lastChild = child + } + child.previousSibling = prev + child.nextSibling = next + child.parent = this + } + + append(...children: Node[]): void { + for (const child of children) { + this.insertBefore(child, undefined) + } + } + + includes(other: Node): boolean { + if (this === other.parent) { + return true + } + if (!other.parent) { + return false + } + return this.includes(other.parent) + } + + get root(): Node { + return this.parent?.root ?? this + } + + select() { + this.selected = true + for (const child of this.children) { + child.deselect() + } + } + + deselect() { + this.selected = false + for (const child of this.children) { + child.deselect() + } + } + + get ancestorSelected(): boolean { + return this.selected || (this.parent?.ancestorSelected ?? false) + } + + get selectedDescendants(): Node[] { + if (this.selected) { + return [this] + } + return this.children.flatMap((child) => child.selectedDescendants) + } +} diff --git a/pkgs/web/src/features/PaintV2/containers/FilterSettings.tsx b/pkgs/web/src/features/PaintV2/containers/FilterSettings.tsx new file mode 100644 index 00000000..37b66cab --- /dev/null +++ b/pkgs/web/src/features/PaintV2/containers/FilterSettings.tsx @@ -0,0 +1,928 @@ +import { useFleurContext, useStore } from '@fleur/react' +import { useFunk } from '@hanakla/arma' +import { useTranslation } from 'next-i18next' +import { ChangeEvent, memo, MouseEvent, ReactNode, useEffect } from 'react' + +import { useClickAway, useToggle, useUpdate } from 'react-use' +import { PapCommands, PapDOM, PapDOMDigger, PapFilters } from '@paplico/core' + +import { DeltaRange } from '🙌/components/DeltaRange' +import { RangeInput } from '🙌/components/RangeInput' +import { SelectBox } from '🙌/components/SelectBox' +import { EditorOps } from '🙌/domains/EditorStable' +import { useFleur } from '🙌/utils/hooks' +import { roundString } from '🙌/utils/StringUtils' + +import { EditorSelector } from '🙌/domains/EditorStable' +import { usePapFilterWatch, useTransactionCommand } from '../hooks' + +import { Stack } from '🙌/components/Stack' +import { + Column, + LayerSelector, + ColorInput, + OpacityColumn, +} from './FilterSettings/_components' +import { Noise } from './FilterSettings/Noise' +import { GradientMap } from './FilterSettings/GradientMap' +import { HueShift } from './FilterSettings/HueShift' + +type Props = { layer: PapDOM.LayerTypes; filter: PapDOM.Filter } + +export const FilterSettings = ({ layer, filter }: Props) => { + const filterId = filter.filterId as { + [K in keyof typeof PapFilters]: typeof PapFilters[K] + }[keyof typeof PapFilters]['id'] + + switch (filterId) { + case '@paplico/filters/bloom': { + return + } + case '@paplico/filters/gauss-blur': { + return + } + case '@paplico/filters/chromatic-aberration': { + return + } + case '@paplico/filters/halftone': { + return + } + case '@paplico/filters/binarization': { + return + } + case '@paplico/filters/glitch-jpeg': { + return + } + case '@paplico/filters/low-reso': { + return + } + case '@paplico/filters/noise': { + return + } + case '@paplico/filters/outline': { + return + } + case '@paplico/filters/zoom-blur': { + return + } + case '@paplico/filters/kawase-blur': { + return + } + case '@paplico/filters/uvreplace': { + return + } + case '@paplico/filters/gradient-map': { + return + } + case '@paplico/filters/hue-shift': { + return + } + default: { + return <>🤔 + } + } +} + +const BloomSetting = ({ layer, filter }: Props) => { + const { executeOperation } = useFleurContext() + + // const handleChange = useFunk(() => { + // editorActions.updateFilter(layer.id, filter.uid, (filter) => {}) + // }, [layer, filter]) + + const handleChangeComplete = useFunk(() => { + executeOperation(EditorOps.rerenderCanvas) + }) + + return ( +
+ + {/* */} + + +
+ ) +} + +const GaussBlur = ({ layer, filter }: Props) => { + const { executeOperation } = useFleurContext() + + const handleChangeRadius = useFunk((value: number) => { + executeOperation( + EditorOps.updateFilter, + layer.uid, + filter.uid, + (filter) => { + filter.settings.radius = value + } + ) + }) + + const handleChangePower = useFunk((value: number) => { + executeOperation( + EditorOps.updateFilter, + layer.uid, + filter.uid, + (filter) => { + filter.settings.power = value + } + ) + }) + + const handleChangeComplete = useFunk(() => { + executeOperation(EditorOps.rerenderCanvas) + }) + + return ( +
+ + + + + {process.env.NODE_ENV === 'development' && ( + + + + )} + +
+ ) +} + +const ChromaticAberration = ({ layer, filter }: Props) => { + const { execute } = useFleur() + const activeLayerPath = useStore(EditorSelector.activeLayerPath) + const trnsCommand = useTransactionCommand({ threshold: 2000 }) + + const handleChangeDistance = useFunk( + ({ currentTarget }: ChangeEvent) => { + if (!activeLayerPath) return + + trnsCommand.autoStartAndDoAdd( + new PapCommands.Filter.PatchAttr({ + pathToTargetLayer: activeLayerPath, + filterUid: filter.uid, + patcher: (attr) => { + attr.settings.distance = currentTarget.valueAsNumber + }, + }) + ) + } + ) + + const handleChangeAngleDeg = useFunk( + ({ currentTarget }: ChangeEvent) => { + if (!activeLayerPath) return + + trnsCommand.autoStartAndDoAdd( + new PapCommands.Filter.PatchAttr({ + pathToTargetLayer: activeLayerPath, + filterUid: filter.uid, + patcher: (attr) => { + attr.settings.angleDeg = currentTarget.valueAsNumber + }, + }) + ) + } + ) + + const handleChangeComplete = useFunk(() => { + execute(EditorOps.rerenderCanvas) + }) + + return ( +
+ + + + + + + +
+ ) +} + +const Halftone = ({ layer, filter }: Props) => { + const { t } = useTranslation('app') + const activeLayerPath = useStore(EditorSelector.activeLayerPath) + const trnsCommand = useTransactionCommand({ threshold: 2000 }) + + const handleChangeShape = useFunk((value: string) => { + if (!activeLayerPath) return + + trnsCommand.autoStartAndDoAdd( + new PapCommands.Filter.PatchAttr({ + pathToTargetLayer: activeLayerPath, + filterUid: filter.uid, + patcher: (attr) => { + attr.settings.shape = +value + }, + }) + ) + }) + + const handleChangeRadius = useFunk( + ({ currentTarget }: ChangeEvent) => { + if (!activeLayerPath) return + + trnsCommand.autoStartAndDoAdd( + new PapCommands.Filter.PatchAttr({ + pathToTargetLayer: activeLayerPath, + filterUid: filter.uid, + patcher: (attr) => { + attr.settings.radius = currentTarget.valueAsNumber + }, + }) + ) + } + ) + + const handleChangeScatter = useFunk( + ({ currentTarget }: ChangeEvent) => { + if (!activeLayerPath) return + + trnsCommand.autoStartAndDoAdd( + new PapCommands.Filter.PatchAttr({ + pathToTargetLayer: activeLayerPath, + filterUid: filter.uid, + patcher: (attr) => { + attr.settings.scatter = currentTarget.valueAsNumber + }, + }) + ) + } + ) + + return ( +
+ + + + + + + + + + +
+ ) +} + +const GlitchJpeg = ({ layer, filter }: Props) => { + const { execute } = useFleur() + + const handleChangeCopies = useFunk( + ({ currentTarget }: ChangeEvent) => { + execute(EditorOps.updateFilter, layer.uid, filter.uid, (filter) => { + filter.settings.copies = currentTarget.valueAsNumber + }) + } + ) + + const handleChangeQuality = useFunk( + ({ currentTarget }: ChangeEvent) => { + execute(EditorOps.updateFilter, layer.uid, filter.uid, (filter) => { + filter.settings.quality = currentTarget.valueAsNumber / 100 + }) + } + ) + + return ( +
+ + + + + + + +
+ ) +} + +const Binarization = ({ layer, filter }: Props) => { + const { execute } = useFleur() + + const handleChangeThreshold = useFunk( + ({ currentTarget }: ChangeEvent) => { + execute(EditorOps.updateFilter, layer.uid, filter.uid, (filter) => { + filter.settings.level = currentTarget.valueAsNumber + }) + } + ) + + return ( +
+ + + + +
+ ) +} + +const LowReso = ({ layer, filter }: Props) => { + const { execute } = useFleur() + + const handleChangeSameBlocks = useFunk( + ({ currentTarget }: ChangeEvent) => { + execute(EditorOps.updateFilter, layer.uid, filter.uid, (filter) => { + filter.settings.sameBlocks = currentTarget.checked + }) + } + ) + + const handleChangeLevelX = useFunk( + ({ currentTarget }: ChangeEvent) => { + execute(EditorOps.updateFilter, layer.uid, filter.uid, (filter) => { + filter.settings.levelX = currentTarget.valueAsNumber + }) + } + ) + + const handleChangeLevelY = useFunk( + ({ currentTarget }: ChangeEvent) => { + execute(EditorOps.updateFilter, layer.uid, filter.uid, (filter) => { + filter.settings.levelY = currentTarget.valueAsNumber + }) + } + ) + + return ( +
+ + + + + + + + + + +
+ ) +} + +const Outline = ({ layer, filter }: Props) => { + const { execute } = useFleur() + + const handleChangeThickness = useFunk( + ({ currentTarget }: ChangeEvent) => { + execute(EditorOps.updateFilter, layer.uid, filter.uid, (filter) => { + filter.settings.thickness = currentTarget.valueAsNumber + }) + } + ) + + const handleChangeColor = useFunk((color: any) => { + execute(EditorOps.updateFilter, layer.uid, filter.uid, (filter) => { + filter.settings.color = color + }) + }) + + return ( +
+ + + + + + + +
+ ) +} + +const ZoomBlur = memo(function ZoomBlur({ layer, filter }: Props) { + const activeLayerPath = useStore(EditorSelector.activeLayerPath) + const trnsCommand = useTransactionCommand({ threshold: 2000 }) + + const handleChangeStrength = useFunk( + ({ currentTarget }: ChangeEvent) => { + if (!activeLayerPath) return + + trnsCommand.autoStartAndDoAdd( + new PapCommands.Filter.PatchAttr({ + pathToTargetLayer: activeLayerPath, + filterUid: filter.uid, + patcher: (filter) => { + filter.settings.strength = currentTarget.valueAsNumber / 100 + }, + }) + ) + + trnsCommand.debouncedCommit() + } + ) + const handleChangeCenterX = useFunk( + ({ currentTarget }: ChangeEvent) => { + if (!activeLayerPath) return + + trnsCommand.autoStartAndDoAdd( + new PapCommands.Filter.PatchAttr({ + pathToTargetLayer: activeLayerPath, + filterUid: filter.uid, + patcher: (filter) => { + filter.settings.center[0] = currentTarget.valueAsNumber / 100 + }, + }) + ) + + trnsCommand.debouncedCommit() + } + ) + const handleChangeCenterY = useFunk( + ({ currentTarget }: ChangeEvent) => { + if (!activeLayerPath) return + + trnsCommand.autoStartAndDoAdd( + new PapCommands.Filter.PatchAttr({ + pathToTargetLayer: activeLayerPath, + filterUid: filter.uid, + patcher: (filter) => { + filter.settings.center[1] = currentTarget.valueAsNumber / 100 + }, + }) + ) + + trnsCommand.debouncedCommit() + } + ) + + return ( +
+ + + + + + + + + + +
+ ) +}) + +const KawaseBlur = memo(function KawaseBlur({ layer, filter }: Props) { + const activeLayerPath = useStore(EditorSelector.activeLayerPath) + const commandTransaction = useTransactionCommand({ threshold: 2000 }) + + usePapFilterWatch(filter) + + const handleChangeBlurSize = useFunk( + ({ currentTarget }: ChangeEvent) => { + if (!activeLayerPath) return + + commandTransaction.autoStartAndDoAdd( + new PapCommands.Filter.PatchAttr({ + pathToTargetLayer: activeLayerPath, + filterUid: filter.uid, + patcher: (filter) => { + filter.settings.blurSize = currentTarget.valueAsNumber + }, + }) + ) + } + ) + const handleChangeQuality = useFunk( + ({ currentTarget }: ChangeEvent) => { + if (!activeLayerPath) return + + commandTransaction.autoStartAndDoAdd( + new PapCommands.Filter.PatchAttr({ + pathToTargetLayer: activeLayerPath, + filterUid: filter.uid, + patcher: (filter) => { + filter.settings.quality = currentTarget.valueAsNumber + }, + }) + ) + } + ) + + const handleChangeComplete = useFunk(() => { + commandTransaction.commit() + }) + + return ( +
+ + + + + + +
+ ) +}) + +const UVReplace = memo(function UVReplace({ layer, filter }: Props) { + const { t } = useTranslation('app') + const rerender = useUpdate() + const { execute } = useFleur() + + const { activeLayerPath, currentDocument } = useStore((get) => ({ + activeLayerPath: EditorSelector.activeLayerPath(get), + currentDocument: EditorSelector.currentDocument(get), + })) + + const commandTransaction = useTransactionCommand({ threshold: 2000 }) + + const handleChangeReplacement: SelectBox.OnChangeHandler = useFunk( + (value) => { + if (!activeLayerPath) return + + execute( + EditorOps.runCommand, + new PapCommands.Filter.PatchAttr({ + pathToTargetLayer: activeLayerPath, + filterUid: filter.uid, + patcher: (filter) => { + filter.settings.replacement = value + }, + }) + ) + + rerender() + } + ) + + const handleChangeReplaceMapLayerUid = useFunk((layerUid: string) => { + if (!activeLayerPath) return + + execute( + EditorOps.runCommand, + new PapCommands.Filter.PatchAttr({ + pathToTargetLayer: activeLayerPath, + filterUid: filter.uid, + patcher: (filter) => { + filter.settings.replaceMapLayerUid = layerUid + }, + }) + ) + + rerender() + }) + + const handleChangeMovement = useFunk( + ({ currentTarget }: ChangeEvent) => { + if (!activeLayerPath) return + + commandTransaction.autoStartAndDoAdd( + new PapCommands.Filter.PatchAttr({ + pathToTargetLayer: activeLayerPath, + filterUid: filter.uid, + patcher: (filter) => { + const index = currentTarget.dataset.dimention === 'x' ? 0 : 1 + filter.settings.movementPx[index] = currentTarget.valueAsNumber + }, + }) + ) + + commandTransaction.debouncedCommit() + + rerender() + } + ) + + const handleChangeClamping: SelectBox.OnChangeHandler = useFunk( + (value: string) => { + if (!activeLayerPath) return + + execute( + EditorOps.runCommand, + new PapCommands.Filter.PatchAttr({ + pathToTargetLayer: activeLayerPath, + filterUid: filter.uid, + patcher: (filter) => { + filter.settings.clamping = value + }, + }) + ) + + rerender() + } + ) + + return ( + + + + + + + + + + + + + + + + + {/* + + */} + + ) +}) diff --git a/pkgs/web/src/features/PaintV2/containers/FilterSettings/GradientMap.tsx b/pkgs/web/src/features/PaintV2/containers/FilterSettings/GradientMap.tsx new file mode 100644 index 00000000..eb7197bb --- /dev/null +++ b/pkgs/web/src/features/PaintV2/containers/FilterSettings/GradientMap.tsx @@ -0,0 +1,181 @@ +import { useStore } from '@fleur/react' +import { useFunk } from '@hanakla/arma' +import { PapCommands, PapFilters, PapValueTypes } from '@paplico/core' +import { nanoid } from 'nanoid' +import { ChangeEvent, memo } from 'react' +import { useUpdate } from 'react-use' +import { RangeInput } from '🙌/components/RangeInput' +import { EditorOps, EditorSelector } from '🙌/domains/EditorStable' +import { useFleur } from '🙌/utils/hooks' +import { GradientSlider } from '../../../../components/GradientSlider' +import { SelectBox } from '../../../../components/SelectBox' +import { TextInput } from '../../../../components/TextInput' +import { roundString } from '../../../../utils/StringUtils' +import { useTransactionCommand } from '../../hooks' +import { Column, OpacityColumn } from './_components' +import { FilterPaneProps } from './_shared' + +const PRESETS: Array<{ + id: string + value: PapValueTypes.ColorStop1D[] +}> = [ + { + id: nanoid(), + value: [ + { position: 0, color: { r: 0, g: 0, b: 0, a: 1 } }, + { position: 1, color: { r: 1, g: 1, b: 1, a: 1 } }, + ], + }, + { + id: nanoid(), + value: [ + { position: 0, color: { r: 0.3, g: 0, b: 0, a: 1 } }, + { position: 1, color: { r: 1, g: 0, b: 0, a: 1 } }, + ], + }, +] + +export const GradientMap = memo(function GradientMap({ + layer, + filter, +}: FilterPaneProps) { + const settings = filter.settings as PapFilters.GradientMapFilter.Params + + // const rerender = useUpdate() + const { execute } = useFleur() + + const activeLayerPath = useStore(EditorSelector.activeLayerPath) + const transCommand = useTransactionCommand() + + // const handleChangeColor = useFunk( + // ({ currentTarget }: ChangeEvent) => { + // if (!activeLayerPath) return + + // execute( + // EditorOps.runCommand, + // new PapCommands.Filter.PatchAttr({ + // pathToTargetLayer: activeLayerPath, + // filterUid: filter.uid, + // patcher: (attrs) => { + // attrs.settings.color = currentTarget.checked + // }, + // }) + // ) + // } + // ) + + const handleChangeMap = useFunk((value) => { + if (!activeLayerPath) return + + const map = PRESETS.find((entry) => entry.id === value) + if (!map) return + + transCommand.autoStartAndDoAdd( + new PapCommands.Filter.PatchAttr({ + pathToTargetLayer: activeLayerPath, + filterUid: filter.uid, + patcher: (attrs) => { + attrs.settings.map = map.value + }, + }) + ) + + transCommand.rerenderCanvas() + }) + + const handleChangeMixRatio = useFunk( + ({ currentTarget }: ChangeEvent) => { + if (!activeLayerPath) return + + transCommand.autoStartAndDoAdd( + new PapCommands.Filter.PatchAttr({ + pathToTargetLayer: activeLayerPath, + filterUid: filter.uid, + patcher: (attrs) => { + attrs.settings.mixRatio = currentTarget.valueAsNumber + }, + }) + ) + + transCommand.rerenderCanvas() + } + ) + + // const handleChangeSeed = useFunk( + // ({ currentTarget }: ChangeEvent) => { + // if (!activeLayerPath) return + + // transCommand.autoStartAndDoAdd( + // new PapCommands.Filter.PatchAttr({ + // pathToTargetLayer: activeLayerPath, + // filterUid: filter.uid, + // patcher: (attrs) => { + // attrs.settings.seed = currentTarget.valueAsNumber + // }, + // }) + // ) + + // transCommand.rerenderCanvas() + // } + // ) + + const handleChangeComplete = useFunk(() => { + transCommand.commit() + }) + + const handleChangeGradientStop = useFunk( + (stops) => { + if (!activeLayerPath) return + + transCommand.autoStartAndDoAdd( + new PapCommands.Filter.PatchAttr({ + pathToTargetLayer: activeLayerPath, + filterUid: filter.uid, + patcher: (attrs) => { + attrs.settings.map = stops + }, + }) + ) + } + ) + + const noop = useFunk(() => {}) + + return ( +
+ + + + +
+ ) +}) diff --git a/pkgs/web/src/features/PaintV2/containers/FilterSettings/HueShift.tsx b/pkgs/web/src/features/PaintV2/containers/FilterSettings/HueShift.tsx new file mode 100644 index 00000000..ed666902 --- /dev/null +++ b/pkgs/web/src/features/PaintV2/containers/FilterSettings/HueShift.tsx @@ -0,0 +1,99 @@ +import { useStore } from '@fleur/react' +import { useFunk } from '@hanakla/arma' +import { PapCommands } from '@paplico/core' +import { ChangeEvent, memo } from 'react' +import { useUpdate } from 'react-use' +import { RangeInput } from '🙌/components/RangeInput' +import { EditorOps, EditorSelector } from '🙌/domains/EditorStable' +import { useFleur } from '🙌/utils/hooks' +import { SelectBox } from '🙌/components/SelectBox' +import { TextInput } from '🙌/components/TextInput' +import { roundString } from '🙌/utils/StringUtils' +import { useTransactionCommand } from '../../hooks' +import { Column, OpacityColumn } from './_components' +import { FilterPaneProps } from './_shared' +import { useTranslation } from 'next-i18next' +import { Stack } from '../../../../components/Stack' + +export const HueShift = memo(function HueShift({ + layer, + filter, +}: FilterPaneProps) { + const { t } = useTranslation('app') + + const activeLayerPath = useStore(EditorSelector.activeLayerPath) + const transCommand = useTransactionCommand() + + const handleChangeColorSpace = useFunk((value) => { + if (!activeLayerPath) return + + transCommand.autoStartAndDoAdd( + new PapCommands.Filter.PatchAttr({ + pathToTargetLayer: activeLayerPath, + filterUid: filter.uid, + patcher: (attr) => { + attr.settings.colorSpace = value + }, + }) + ) + }) + + const handleChangeDistance = useFunk( + ({ currentTarget }: ChangeEvent) => { + if (!activeLayerPath) return + + transCommand.autoStartAndDoAdd( + new PapCommands.Filter.PatchAttr({ + pathToTargetLayer: activeLayerPath, + filterUid: filter.uid, + patcher: (attr) => { + attr.settings.shift = currentTarget.valueAsNumber + }, + }) + ) + } + ) + + const handleChangeComplete = useFunk(() => { + transCommand.commit() + }) + + return ( + + + + + + + + + + ) +}) diff --git a/pkgs/web/src/features/PaintV2/containers/FilterSettings/Noise.tsx b/pkgs/web/src/features/PaintV2/containers/FilterSettings/Noise.tsx new file mode 100644 index 00000000..a32dd748 --- /dev/null +++ b/pkgs/web/src/features/PaintV2/containers/FilterSettings/Noise.tsx @@ -0,0 +1,111 @@ +import { useStore } from '@fleur/react' +import { useFunk } from '@hanakla/arma' +import { PapCommands } from '@paplico/core' +import { ChangeEvent, memo } from 'react' +import { useUpdate } from 'react-use' +import { RangeInput } from '🙌/components/RangeInput' +import { EditorOps, EditorSelector } from '🙌/domains/EditorStable' +import { useFleur } from '🙌/utils/hooks' +import { TextInput } from '../../../../components/TextInput' +import { useTransactionCommand } from '../../hooks' +import { Column, OpacityColumn } from './_components' +import { FilterPaneProps } from './_shared' + +export const Noise = memo(function Noise({ layer, filter }: FilterPaneProps) { + // const rerender = useUpdate() + const { execute } = useFleur() + + const activeLayerPath = useStore(EditorSelector.activeLayerPath) + const transCommand = useTransactionCommand() + + const handleChangeColor = useFunk( + ({ currentTarget }: ChangeEvent) => { + if (!activeLayerPath) return + + execute( + EditorOps.runCommand, + new PapCommands.Filter.PatchAttr({ + pathToTargetLayer: activeLayerPath, + filterUid: filter.uid, + patcher: (attrs) => { + attrs.settings.color = currentTarget.checked + }, + }) + ) + } + ) + + const handleChangeScale = useFunk( + ({ currentTarget }: ChangeEvent) => { + if (!activeLayerPath) return + + transCommand.autoStartAndDoAdd( + new PapCommands.Filter.PatchAttr({ + pathToTargetLayer: activeLayerPath, + filterUid: filter.uid, + patcher: (attrs) => { + attrs.settings.scale = currentTarget.valueAsNumber + }, + }) + ) + + transCommand.rerenderCanvas() + } + ) + + const handleChangeSeed = useFunk( + ({ currentTarget }: ChangeEvent) => { + if (!activeLayerPath) return + + transCommand.autoStartAndDoAdd( + new PapCommands.Filter.PatchAttr({ + pathToTargetLayer: activeLayerPath, + filterUid: filter.uid, + patcher: (attrs) => { + attrs.settings.seed = currentTarget.valueAsNumber + }, + }) + ) + + transCommand.rerenderCanvas() + } + ) + + const handleBlur = useFunk(() => { + transCommand.commit() + }) + + return ( +
+ + + + + + + + + + +
+ ) +}) diff --git a/pkgs/web/src/features/PaintV2/containers/FilterSettings/_components.tsx b/pkgs/web/src/features/PaintV2/containers/FilterSettings/_components.tsx new file mode 100644 index 00000000..8f8bcc23 --- /dev/null +++ b/pkgs/web/src/features/PaintV2/containers/FilterSettings/_components.tsx @@ -0,0 +1,423 @@ +import { flip, offset, shift, useFloating } from '@floating-ui/react-dom' +import { useFunk } from '@hanakla/arma' +import { PapCommands, PapDOM, PapDOMDigger } from '@paplico/core' +import { useTranslation } from 'next-i18next' +import { ChangeEvent, memo, MouseEvent, ReactNode, useEffect } from 'react' +import { useClickAway, useToggle } from 'react-use' + +import { centering } from '🙌/utils/mixins' +import { DOMUtils } from '🙌/utils/dom' +import { ChromePicker, ColorChangeHandler } from 'react-color' +import { useAutoUpdateFloating, useFleur } from '../../../../utils/hooks' +import { Portal } from '../../../../components/Portal' +import { EditorOps, EditorSelector } from '../../../../domains/EditorStable' +import { useDocumentWatch, useTransactionCommand } from '../../hooks' +import { LayerTypes } from '@paplico/core/dist/DOM' +import { ThemeProp, tm } from '../../../../utils/theme' +import { rgba } from 'polished' +import { LayerNameText } from '../../../../components/LayerNameText' +import { useHover } from 'react-use-gesture' +import { ArrowDownS, ErrorWarning } from '@styled-icons/remix-line' +import { useStore } from '@fleur/react' +import { RangeInput } from '../../../../components/RangeInput' +import { roundString } from '../../../../utils/StringUtils' + +export const Column = memo( + ({ + nameKey, + value, + filter: { filterId }, + children, + }: { + nameKey: string + value?: string + filter: PapDOM.Filter + children: ReactNode + }) => { + const { t } = useTranslation('app') + + return ( +
+
+ {t(`filterOptions.${filterId}.${nameKey}`)} +
+ +
+
+ {children} +
+ + {value !== undefined && ( +
+ {value} +
+ )} +
+
+ ) + } +) + +export const ColorInput = memo( + ({ + alpha, + value, + onChange, + }: + | { + alpha: true + value: { r: number; g: number; b: number; a: number } + onChange: (value: { + r: number + g: number + b: number + a: number + }) => void + } + | { + alpha?: false + value: { r: number; g: number; b: number } + onChange: (value: { r: number; g: number; b: number }) => void + }) => { + const [open, toggleOpen] = useToggle(false) + const fl = useFloating({ + strategy: 'fixed', + placement: 'right', + middleware: [shift(), offset(4)], + }) + + const handleClick = useFunk((e: MouseEvent) => { + if (!DOMUtils.isSameElement(e.target, e.currentTarget)) return + toggleOpen() + }) + + const handleChange = useFunk(({ rgb }) => { + onChange( + (alpha + ? { + r: rgb.r / 255, + g: rgb.g / 255, + b: rgb.b / 255, + a: rgb.a!, + } + : { + r: rgb.r / 255, + g: rgb.g / 255, + b: rgb.b / 255, + }) as any + ) + }) + + useAutoUpdateFloating(fl) + + useClickAway(fl.refs.floating, () => { + toggleOpen(false) + }) + + return ( +
+ +
+ +
+
+
+ ) + } +) + +export const LayerSelector = memo(function LayerSelector({ + valueLayerUid, + onChange, +}: { + valueLayerUid: string | null + onChange: (layerUid: string) => void +}) { + const { execute } = useFleur() + + const { currentDocument, thumbnailUrlOfLayer } = useStore((get) => ({ + currentDocument: EditorSelector.currentDocument(get), + thumbnailUrlOfLayer: EditorSelector.thumbnailUrlOfLayer(get), + })) + + const [listOpened, toggleListOpened] = useToggle(false) + + if (!currentDocument) return null + + useDocumentWatch(currentDocument) + + const listFl = useFloating({ + strategy: 'fixed', + placement: 'bottom', + middleware: [shift(), flip(), offset(4)], + }) + + const handleClickBox = useFunk(() => { + toggleListOpened() + }) + + const handleItemClick = useFunk((e: MouseEvent) => { + e.stopPropagation() + + toggleListOpened(false) + onChange(e.currentTarget.dataset.layerUid!) + }) + + const bindLayerHover = useHover(({ hovering }) => { + if (!valueLayerUid) return + + execute(EditorOps.setHighlightedLayers, (ids) => { + if (hovering) return [...ids, valueLayerUid] + else return ids.filter((id) => id !== valueLayerUid) + }) + }) + + const bindItemHover = useHover(({ hovering, event: { currentTarget } }) => { + const layerUid = (currentTarget as HTMLDivElement).dataset.layerUid! + + execute(EditorOps.setHighlightedLayers, (ids) => { + if (hovering) return [...ids, layerUid] + else return ids.filter((id) => id !== layerUid) + }) + }) + + useClickAway(listFl.refs.floating, () => toggleListOpened(false)) + useAutoUpdateFloating(listFl) + + let selectedLayer = null as LayerTypes | null + const layers: LayerTypes[] = [] + + PapDOMDigger.traverseLayers( + currentDocument, + { kind: ['raster', 'vector', 'text'] }, + (layer) => { + if (valueLayerUid === layer.uid) selectedLayer = layer + layers.push(layer) + } + ) + + return ( +
+
theme.color.surface3}; + color: ${({ theme }: ThemeProp) => theme.color.text2}; + + &::placeholder { + color: ${({ theme }: ThemeProp) => theme.exactColors.black30}; + } + `} + onClick={handleClickBox} + {...bindLayerHover()} + > + {/* {currentItem?.label ?? ( + + {placeholder} + + )} */} + + + {selectedLayer ? ( + + ) : ( + '----' + )} + + + + + +
+ rgba(theme.color.background2, 1)}; + filter: drop-shadow(0 0 5px ${rgba('#000', 0.5)}); + border-radius: 4px; + overflow: hidden; + `} + style={{ + position: listFl.strategy, + left: listFl.x ?? 0, + top: listFl.y ?? 0, + ...(listOpened + ? { visibility: 'visible', pointerEvents: 'all' } + : { visibility: 'hidden', pointerEvents: 'none' }), + }} + > + {layers.map((layer) => ( +
+
+ + +
+ ))} +
+ +
+ + {valueLayerUid != null && !selectedLayer && ( +
[o.typography(12), o.font.text2])} + `} + > + Target layer not found. +
+ )} +
+ ) +}) + +export const OpacityColumn = memo(function OpacityColumn({ + filter, +}: { + filter: PapDOM.Filter +}) { + const transCommand = useTransactionCommand() + + const activeLayerPath = useStore(EditorSelector.activeLayerPath) + + const handleChangeOpacity = useFunk( + ({ currentTarget }: ChangeEvent) => { + if (!activeLayerPath) return + + transCommand.autoStartAndDoAdd( + new PapCommands.Filter.PatchAttr({ + pathToTargetLayer: activeLayerPath, + filterUid: filter.uid, + patcher: (attrs) => { + attrs.opacity = currentTarget.valueAsNumber + }, + }) + ) + + transCommand.rerenderCanvas() + } + ) + + const handleChangeComplete = useFunk(() => { + transCommand.commit() + }) + + return ( + + + + ) +}) diff --git a/pkgs/web/src/features/PaintV2/containers/FilterSettings/_shared.ts b/pkgs/web/src/features/PaintV2/containers/FilterSettings/_shared.ts new file mode 100644 index 00000000..ce52e6a5 --- /dev/null +++ b/pkgs/web/src/features/PaintV2/containers/FilterSettings/_shared.ts @@ -0,0 +1,6 @@ +import { PapDOM } from '@paplico/core' + +export type FilterPaneProps = { + layer: PapDOM.LayerTypes + filter: PapDOM.Filter +} diff --git a/pkgs/web/src/features/PaintV2/containers/FilterView.tsx b/pkgs/web/src/features/PaintV2/containers/FilterView.tsx new file mode 100644 index 00000000..3b6f16da --- /dev/null +++ b/pkgs/web/src/features/PaintV2/containers/FilterView.tsx @@ -0,0 +1,456 @@ +import { Add, ArrowDownS } from '@styled-icons/remix-line' +import { Eye, EyeClose } from '@styled-icons/remix-fill' +import { useTranslation } from 'next-i18next' +import { rgba } from 'polished' +import { memo, MouseEvent, useState } from 'react' +import { useClickAway, useToggle } from 'react-use' +import { useTheme } from 'styled-components' +import { css } from 'styled-components' +import { PapCommands, PapDOM } from '@paplico/core' +import { offset, shift, size, useFloating } from '@floating-ui/react-dom' +import { + closestCenter, + DndContext, + DragEndEvent, + useSensor, + useSensors, +} from '@dnd-kit/core' +import { + SortableContext, + useSortable, + verticalListSortingStrategy, +} from '@dnd-kit/sortable' +import { + restrictToFirstScrollableAncestor, + restrictToVerticalAxis, +} from '@dnd-kit/modifiers' +import { useFleurContext, useStore } from '@fleur/react' +import { useFunk } from '@hanakla/arma' +import { CSS } from '@dnd-kit/utilities' + +import { + ContextMenu, + ContextMenuItem, + useContextMenu, +} from '🙌/components/ContextMenu' +import { Portal } from '🙌/components/Portal' +import { DOMUtils } from '🙌/utils/dom' +import { centering } from '🙌/utils/mixins' +import { FilterSettings } from './FilterSettings' +import { EditorOps, EditorSelector, EditorStore } from '🙌/domains/EditorStable' +import { isEventIgnoringTarget } from '../helpers' +import { SidebarPane } from '🙌/components/SidebarPane' +import { useFleur, useAutoUpdateFloating } from '🙌/utils/hooks' +import { useLayerWatch } from '../hooks' +import { DisableOnInputPointerSensor } from '../dndkit-helper' +import { pick } from '🙌/utils/object' +import { DragDots } from '🙌/components/icons/DragDots' +import { ThemeProp, tm } from '🙌/utils/theme' + +export const FilterView = memo(() => { + const { t } = useTranslation('app') + + const { executeOperation, getStore } = useFleurContext() + const { activeLayer, activeLayerPath, registeredFilters } = useStore( + (get) => ({ + activeLayer: EditorSelector.activeLayer(get), + activeLayerPath: EditorSelector.activeLayerPath(get), + registeredFilters: EditorSelector.getAvailableFilters(get), + }) + ) + + useLayerWatch(activeLayer) + + const [listOpened, toggleListOpened] = useToggle(false) + const [sortingFilterUid, setSortingFilterUid] = useState(null) + + const listFl = useFloating({ + placement: 'bottom-end', + strategy: 'fixed', + middleware: [ + offset(4), + shift(), + size({ + padding: 8, + apply: ({ availableWidth, availableHeight, elements }) => { + Object.assign(elements.floating.style, { + // maxWidth: `${availableWidth}px`, + maxHeight: `${availableHeight}px`, + }) + }, + }), + ], + }) + + const sensors = useSensors( + useSensor(DisableOnInputPointerSensor, { + activationConstraint: { distance: 4 }, + }) + ) + + const handleClickOpenFilter = useFunk((e: MouseEvent) => { + toggleListOpened() + }) + + const handleClickAddFilter = useFunk( + ({ currentTarget }: MouseEvent) => { + toggleListOpened(false) + + if (!activeLayerPath) return + + const filterId = currentTarget.dataset.filterId! + const filter = EditorSelector.getFilterInstance(getStore, filterId) + if (!filter) return + + executeOperation( + EditorOps.runCommand, + new PapCommands.Layer.AddFilter({ + pathToTargetLayer: activeLayerPath, + filter: PapDOM.Filter.create({ + filterId, + settings: filter.initialConfig, + }), + }) + ) + } + ) + + const handleFilterSortStart = useFunk(({ active }: DragEndEvent) => { + setSortingFilterUid(active.id) + }) + + const handleFilterSortEnd = useFunk(({ active, over }: DragEndEvent) => { + if (!activeLayer || !activeLayerPath || !over) return + + const oldIndex = activeLayer.filters.findIndex((f) => f.uid === active.id) + const newIndex = activeLayer.filters.findIndex((f) => f.uid === over.id) + if (oldIndex === newIndex) return + + executeOperation( + EditorOps.runCommand, + new PapCommands.Layer.ReorderFilter({ + pathToTargetLayer: activeLayerPath, + filterUid: active.id, + newIndex: { exactly: newIndex }, + }) + ) + }) + + useClickAway(listFl.refs.floating, (e) => { + if (isEventIgnoringTarget(e.target)) return + toggleListOpened(false) + }) + + useAutoUpdateFloating(listFl) + + return ( + + {t('layerFilter')} + +
+ + + +
    theme.color.surface3}; + color: ${({ theme }) => theme.text.white}; + box-shadow: 0 0 4px ${rgba('#000', 0.5)}; + border-radius: 4px; + overflow: auto; + + li { + padding: 8px; + user-select: none; + } + li:hover { + background-color: rgba(255, 255, 255, 0.2); + } + `} + style={{ + position: listFl.strategy, + left: listFl.x ?? 0, + top: listFl.y ?? 0, + ...(listOpened + ? { visibility: 'visible', pointerEvents: 'all' } + : { visibility: 'hidden', pointerEvents: 'none' }), + }} + > + {registeredFilters.map((filter) => ( +
  • + {t(`filters.${filter.id}`)} +
  • + ))} +
+
+
+ + } + container={(children) => ( +
+ {children} +
+ )} + > +
+ theme.exactColors.blackFade20}; */ + `} + > +
+ {activeLayer && ( + + uid)} + strategy={verticalListSortingStrategy} + > + {activeLayer.filters.map((filter) => ( + + ))} + + {/* + {sortingFilterUid && ( + f.uid === sortingFilterUid + )! + } + /> + )} + */} + + + )} +
+
+
+ ) +}) + +const FilterItem = memo(function FilterItem({ + layer, + filter, +}: { + layer: PapDOM.LayerTypes + filter: PapDOM.Filter +}) { + const { t } = useTranslation('app') + const theme = useTheme() + const { execute } = useFleur() + + const contextMenu = useContextMenu() + const { + attributes, + listeners, + setNodeRef, + transform, + transition, + isDragging, + } = useSortable({ id: filter.uid }) + + const { activeLayer, activeLayerPath, selectedFilterIds } = useStore( + (get) => ({ + activeLayer: EditorSelector.activeLayer(get), + activeLayerPath: EditorSelector.activeLayerPath(get), + selectedFilterIds: get(EditorStore).state.selectedFilterIds, + }) + ) + + const active = selectedFilterIds[filter.uid] + const [propsOpened, togglePropsOpened] = useToggle(false) + + const handleClick = useFunk((e: MouseEvent) => { + if (DOMUtils.closestOrSelf(e.target, '[data-ignore-click]')) return + + execute(EditorOps.setSelectedFilterIds, { [filter.uid]: true }) + }) + + const handleDoubleClick = useFunk((e: MouseEvent) => { + if (DOMUtils.closestOrSelf(e.target, '[data-ignore-click]')) return + togglePropsOpened() + }) + + const handleToggleVisibility = useFunk(() => { + if (!activeLayer || !activeLayerPath) return + + execute(EditorOps.updateLayer, activeLayerPath, (layer) => { + const targetFilter = layer.filters.find((f) => f.uid === filter.uid) + if (!targetFilter) return + + targetFilter.visible = !targetFilter.visible + }) + + execute(EditorOps.rerenderCanvas) + }) + + const handleContextMenu = useFunk((e: MouseEvent) => { + contextMenu.show(e) + }) + + const handleClickRemove = useFunk(() => { + if (!activeLayer || !activeLayerPath) return + + execute( + EditorOps.runCommand, + new PapCommands.Layer.RemoveFilter({ + pathToTargetLayer: activeLayerPath, + filterUid: filter.uid, + }) + ) + }) + + return ( +
theme.text.white}; + + & + & { + ${tm((o) => [o.border.default.top])} + } + `} + onClick={handleClick} + onDoubleClick={handleDoubleClick} + onContextMenu={handleContextMenu} + style={{ + transform: CSS.Transform.toString({ + scaleX: 1, + scaleY: 1, + ...pick(transform ?? { x: 0, y: 0 }, ['x', 'y']), + }), + transition: transition, + }} + > +
+
+ +
+ +
+ {filter.visible ? ( + theme.colors.white10}; + `} + width={16} + /> + ) : ( + + )} +
+ +
+ {t(`filters.${filter.filterId}`)} +
+ +
+ +
+
+ +
+ + {propsOpened && ( +
+ +
+ )} + + + + + {t('remove')} + + + +
+ ) +}) diff --git a/pkgs/web/src/features/PaintV2/containers/LayerControls/GroupLayerControl.tsx b/pkgs/web/src/features/PaintV2/containers/LayerControls/GroupLayerControl.tsx new file mode 100644 index 00000000..d6e984a5 --- /dev/null +++ b/pkgs/web/src/features/PaintV2/containers/LayerControls/GroupLayerControl.tsx @@ -0,0 +1,81 @@ +import { useStore } from '@fleur/react' +import { useRef } from 'react' +import { useDrag } from 'react-use-gesture' +import { PapCommands } from '@paplico/core' +import { EditorOps, EditorSelector } from '🙌/domains/EditorStable' +import { DOMUtils } from '🙌/utils/dom' +import { useFleur } from '🙌/utils/hooks' +import { useLayerWatch } from '../../hooks' + +export const GroupLayerControl = () => { + const { execute } = useFleur() + + const rootRef = useRef(null) + + const { activeLayer, activeLayerPath, currentDocument, currentTool } = + useStore((get) => ({ + activeLayer: EditorSelector.activeLayer(get), + activeLayerPath: EditorSelector.activeLayerPath(get), + canvasScale: EditorSelector.canvasScale(get), + currentDocument: EditorSelector.currentDocument(get), + currentTool: EditorSelector.currentTool(get), + })) + + const bbox = currentDocument?.getLayerSize(activeLayer!) ?? null + + const bindDrag = useDrag(({ initial, xy }) => { + if (currentTool !== 'cursor' || !activeLayerPath) return + + const initP = DOMUtils.domPointToSvgPoint(rootRef.current!, { + x: initial[0], + y: initial[1], + }) + const point = DOMUtils.domPointToSvgPoint(rootRef.current!, { + x: xy[0], + y: xy[1], + }) + + execute( + EditorOps.runCommand, + new PapCommands.Layer.PatchLayerAttr({ + pathToTargetLayer: activeLayerPath, + patch: { + x: point.x - initP.x, + y: point.y - initP.y, + }, + }) + ) + }) + + useLayerWatch(activeLayer) + + if (activeLayer?.layerType !== 'group' || !bbox || !currentDocument) + return null + + return ( + + {currentTool === 'cursor' && ( + + )} + + ) +} diff --git a/pkgs/web/src/features/PaintV2/containers/LayerControls/RasterLayerControl.tsx b/pkgs/web/src/features/PaintV2/containers/LayerControls/RasterLayerControl.tsx new file mode 100644 index 00000000..366b27f9 --- /dev/null +++ b/pkgs/web/src/features/PaintV2/containers/LayerControls/RasterLayerControl.tsx @@ -0,0 +1,92 @@ +import { useStore } from '@fleur/react' +import { useRef } from 'react' +import { useDrag } from 'react-use-gesture' +import { PapCommands } from '@paplico/core' +import { EditorOps, EditorSelector } from '🙌/domains/EditorStable' +import { DOMUtils } from '🙌/utils/dom' +import { useFleur } from '🙌/utils/hooks' +import { useLayerWatch, useTransactionCommand } from '../../hooks' + +export const RasterLayerControl = () => { + const { execute } = useFleur() + + const { + activeLayer, + activeLayerPath, + canvasScale, + currentDocument, + currentTool, + } = useStore((get) => ({ + activeLayer: EditorSelector.activeLayer(get), + activeLayerPath: EditorSelector.activeLayerPath(get), + canvasScale: EditorSelector.canvasScale(get), + currentDocument: EditorSelector.currentDocument(get), + currentTool: EditorSelector.currentTool(get), + })) + + const bbox = currentDocument?.getLayerSize(activeLayer!) ?? null + + const rootRef = useRef(null) + + const trnsCommand = useTransactionCommand() + + const bindDrag = useDrag(({ initial, xy, delta, last }) => { + if (currentTool !== 'cursor' || !activeLayer || !activeLayerPath) return + + // const initP = DOMUtils.domPointToSvgPoint(rootRef.current!, { + // x: initial[0], + // y: initial[1], + // }) + + // const point = DOMUtils.domPointToSvgPoint(rootRef.current!, { + // x: xy[0], + // y: xy[1], + // }) + + trnsCommand.autoStartAndDoAdd( + new PapCommands.Layer.PatchLayerAttr({ + pathToTargetLayer: activeLayerPath, + patch: { + x: activeLayer.x + delta[0] * (1 / canvasScale), + y: activeLayer.y + delta[1] * (1 / canvasScale), + }, + }) + ) + + if (last) { + trnsCommand.commit() + } + }) + + useLayerWatch(activeLayer) + + if (activeLayer?.layerType !== 'raster' || !bbox || !currentDocument) + return null + + return ( + + {currentTool === 'cursor' && ( + + )} + + ) +} diff --git a/pkgs/web/src/features/PaintV2/containers/LayerControls/ReferenceLayerControl.tsx b/pkgs/web/src/features/PaintV2/containers/LayerControls/ReferenceLayerControl.tsx new file mode 100644 index 00000000..e7648636 --- /dev/null +++ b/pkgs/web/src/features/PaintV2/containers/LayerControls/ReferenceLayerControl.tsx @@ -0,0 +1,96 @@ +import { useStore } from '@fleur/react' +import { useRef, useMemo } from 'react' +import { useDrag } from 'react-use-gesture' +import { PapCommands, PapDOMDigger } from '@paplico/core' +import { EditorOps, EditorSelector } from '🙌/domains/EditorStable' +import { DOMUtils } from '🙌/utils/dom' +import { useFleur } from '🙌/utils/hooks' +import { useLayerWatch, useTransactionCommand } from '../../hooks' + +export const ReferenceLayerControl = () => { + const { execute } = useFleur() + + const { + activeLayer, + activeLayerPath, + canvasScale, + currentDocument, + currentTool, + } = useStore((get) => ({ + activeLayer: EditorSelector.activeLayer(get), + activeLayerPath: EditorSelector.activeLayerPath(get), + canvasScale: EditorSelector.canvasScale(get), + currentDocument: EditorSelector.currentDocument(get), + currentTool: EditorSelector.currentTool(get), + })) + + const rootRef = useRef(null) + + const trnsCommand = useTransactionCommand() + + const bindDrag = useDrag(({ initial, xy, delta, last }) => { + if (currentTool !== 'cursor' || !activeLayer || !activeLayerPath) return + + // const initP = DOMUtils.domPointToSvgPoint(rootRef.current!, { + // x: initial[0], + // y: initial[1], + // }) + + // const point = DOMUtils.domPointToSvgPoint(rootRef.current!, { + // x: xy[0], + // y: xy[1], + // }) + + trnsCommand.autoStartAndDoAdd( + new PapCommands.Layer.PatchLayerAttr({ + pathToTargetLayer: activeLayerPath, + patch: { + x: activeLayer.x + delta[0] * (1 / canvasScale), + y: activeLayer.y + delta[1] * (1 / canvasScale), + }, + }) + ) + + if (last) { + trnsCommand.commit() + } + }) + + const realLayer = useMemo(() => { + return PapDOMDigger.findLayerRecursive(currentDocument!, activeLayer!.uid) + }, [currentDocument?.uid, activeLayer?.uid]) + + const bbox = realLayer ? currentDocument?.getLayerSize(realLayer) : null + + useLayerWatch(activeLayer) + + if (activeLayer?.layerType !== 'reference' || !bbox || !currentDocument) + return null + + return ( + + {currentTool === 'cursor' && ( + + )} + + ) +} diff --git a/pkgs/web/src/features/PaintV2/containers/LayerControls/TextLayerControl.tsx b/pkgs/web/src/features/PaintV2/containers/LayerControls/TextLayerControl.tsx new file mode 100644 index 00000000..806a9cab --- /dev/null +++ b/pkgs/web/src/features/PaintV2/containers/LayerControls/TextLayerControl.tsx @@ -0,0 +1,45 @@ +import { useStore } from '@fleur/react' +import { useMemo, useState } from 'react' +import { createEditor, Descendant } from 'slate' +import { Editable, Slate, withReact } from 'slate-react' +import { EditorSelector } from '🙌/domains/EditorStable' + +export const TextLayerControl = ({}) => { + const currentLayerBBox = useStore((get) => + EditorSelector.activeLayerBBox(get) + ) + + const editor = useMemo(() => withReact(createEditor()), []) + // Add the initial value when setting up our state. + const [value, setValue] = useState([ + { + type: 'paragraph', + children: [{ text: 'A line of text in a paragraph.' }], + }, + ]) + + const bbox = currentLayerBBox ?? null + + return ( +
+ setValue(newValue)} + > + + +
+ ) +} diff --git a/pkgs/web/src/features/PaintV2/containers/LayerControls/VectorLayerControl.tsx b/pkgs/web/src/features/PaintV2/containers/LayerControls/VectorLayerControl.tsx new file mode 100644 index 00000000..745da17d --- /dev/null +++ b/pkgs/web/src/features/PaintV2/containers/LayerControls/VectorLayerControl.tsx @@ -0,0 +1,1628 @@ +import { useFleurContext, useStore } from '@fleur/react' +import { useFunk, useObjectState } from '@hanakla/arma' +import { + forwardRef, + Fragment, + memo, + MouseEvent, + useEffect, + useMemo, + useRef, + useState, +} from 'react' +import { useClickAway, useToggle } from 'react-use' +import { useDrag, useHover } from 'react-use-gesture' +import { PapCommands, PapDOM } from '@paplico/core' +import { rgba } from 'polished' +import { useTranslation } from 'next-i18next' +import { DragMove2 } from '@styled-icons/remix-fill' +import { css } from 'styled-components' +import { useLongPress } from 'use-long-press' +import useMeasure from 'use-measure' +import { offset, shift, flip, useFloating } from '@floating-ui/react-dom' +import { ArrowDown, ArrowUp } from '@styled-icons/remix-line' + +import { the } from '🙌/utils/anyOf' +import { PapWebMath } from '🙌/utils/PapWebMath' +import { EditorOps, EditorSelector, EditorStore } from '🙌/domains/EditorStable' +import { useFunkyMouseTrap } from '🙌/hooks/useMouseTrap' +import { assign } from '🙌/utils/object' +import { deepClone } from '🙌/utils/clone' +import { normalRgbToRgbArray } from '../../helpers' +import { + useAutoUpdateFloating, + useDeepCompareMemo, + useFleur, +} from '🙌/utils/hooks' +import { DOMUtils } from '🙌/utils/dom' +import { + useLayerWatch, + useVectorObjectWatch, + usePaintCanvasRef, + useTransactionCommand, +} from '../../hooks' +import { Portal } from '🙌/components/Portal' +import { + ContextMenu, + ContextMenuItem, + ContextMenuParam, + useContextMenu, +} from '🙌/components/ContextMenu' +import { tm } from '🙌/utils/theme' +import { floatingDropShadow } from '🙌/utils/mixins' + +const POINT_SIZE = 8 + +// const VectorEditorSelector = { +// // isDiplayPointForObject: selector([EditorSelector.currentTool], (tool) => tool) +// } + +export const VectorLayerControl = () => { + const { t } = useTranslation('app') + const { executeOperation } = useFleurContext() + const { execute, getStore } = useFleur() + + const { + canvasScale, + canvasPosition, + activeLayer, + activeLayerPath, + brushSizeChanging, + currentDocument, + currentTool, + vectorStroking, + currentStroke, + currentFill, + activeObjectPointIndices, + activeObjectId, + vectorFocusing, + activeObject, + } = useStore((get) => ({ + canvasScale: EditorSelector.canvasScale(get), + canvasPosition: EditorSelector.canvasPosition(get), + brushSizeChanging: EditorSelector.brushSizeChanging(get), + activeLayer: EditorSelector.activeLayer(get), + activeLayerPath: EditorSelector.activeLayerPath(get), + currentDocument: EditorSelector.currentDocument(get), + currentTool: get(EditorStore).state.currentTool, + vectorStroking: get(EditorStore).state.vectorStroking, + currentStroke: get(EditorStore).state.currentStroke, + currentFill: get(EditorStore).state.currentFill, + activeObjectPointIndices: get(EditorStore).state.activeObjectPointIndices, + activeObjectId: get(EditorStore).state.activeObjectId, + vectorFocusing: get(EditorStore).state.vectorFocusing, + activeObject: EditorSelector.activeObject(get), + })) + + useLayerWatch(activeLayer) + + const contextMenu = useContextMenu() + const trsnCommand = useTransactionCommand() + + const canvasRef = usePaintCanvasRef() + const rootRef = useRef(null) + const makePathTransactionRef = useRef(null) + const canvasOverlayRef = useRef('canvas-overlays') + + if (!activeLayer || activeLayer.layerType !== 'vector') throw new Error('') + + const [isHoverOnPath, toggleIsHoverOnPath] = useToggle(false) + const [hoveredObjectUid, setHoveredObjectUid] = useState(null) + const currentControllDirection = useRef<{ x: number; y: number }>({ + x: 0, + y: 0, + }) + + const objActionFl = useFloating({ + strategy: 'absolute', + placement: 'bottom', + middleware: [ + offset(8), + shift({ + padding: { top: 8, right: 8, bottom: 64, left: 8 }, + crossAxis: true, + }), + flip({ boundary: canvasOverlayRef.current }), + ], + }) + + const handleClickRoot = useFunk((e: MouseEvent) => { + if (currentTool === 'shape-pen') return + if (!DOMUtils.isSameElement(e.target, e.currentTarget)) return + if (DOMUtils.closestOrSelf(e.target, '[data-disable-object-unfocus]')) + return + + execute(EditorOps.setActiveObject, null) + }) + + const handleHoverChangePath = useFunk( + ({ hovering, objectId }: { hovering: boolean; objectId: string }) => { + toggleIsHoverOnPath(hovering) + setHoveredObjectUid(objectId) + } + ) + + // const handleClickPath = useFunk((e: MouseEvent) => { + // e.stopPropagation() + // if (currentTool !== 'shape-pen') return + + // if (!activeLayerPath) return + + // // Insert point to current path + // // SEE: http://polymathprogrammer.com/2007/06/27/reverse-engineering-bezier-curves/ + // executeOperation( + // EditorOps.runCommand, + // new PapCommands.VectorLayer.PatchPathPoints({ + // pathToTargetLayer: activeLayerPath, + // objectUid: objectId, + // patcher: (points) => { + // points.splice(segmentIndex, 0, { + // x, + // y, + // in: { x: x + 2, y: y - 2 }, + // out: { x: x - 2, y: y + 2 }, + // }) + // }, + // }) + // ) + // }) + + const handleDoubleClickPath = useFunk( + ( + objectId: string, + segmentIndex: number, + { x, y }: { x: number; y: number } + ) => { + if (currentTool !== 'shape-pen') return + if (!activeLayerPath) return + + // Insert point to current path + // SEE: http://polymathprogrammer.com/2007/06/27/reverse-engineering-bezier-curves/ + executeOperation( + EditorOps.runCommand, + new PapCommands.VectorLayer.PatchPathPoints({ + pathToTargetLayer: activeLayerPath, + objectUid: objectId, + patcher: (points) => { + points.splice(segmentIndex, 0, { + x, + y, + in: { x: x + 2, y: y - 2 }, + out: { x: x - 2, y: y + 2 }, + }) + }, + }) + ) + } + ) + + const handleClickObjectOutline = useFunk((e: MouseEvent) => { + e.stopPropagation() + + execute( + EditorOps.setActiveObject, + e.currentTarget.dataset.objectUid ?? null + ) + }) + + const bindRootDrag = useDrag( + ({ initial, first, last, xy, event: e }) => { + assertVectorLayer(activeLayer) + + if (currentTool !== 'shape-pen' || !activeLayerPath) return + + // Add point + // SEE: https://stackoverflow.com/a/42711775 + const svg = (e.currentTarget as SVGRectElement).ownerSVGElement! + const initialPt = DOMUtils.domPointToSvgPoint(svg, { + x: initial[0], + y: initial[1], + }) + const { x, y } = DOMUtils.domPointToSvgPoint(svg, { + x: xy[0], + y: xy[1], + }) + + // Finish vector stroking + if (last) { + trsnCommand.commit() + return + } + + if (first) { + // Create new Object and add point + const newPoint: PapDOM.Path.PathPoint = { + in: null, + out: null, + x, + y, + pressure: 1, + } + + trsnCommand.startIfNotStarted() + + let nextPointIndex: number = -1 + let nextObjectId: string = '' + + if (vectorStroking == null) { + // Create new object with point + if (!activeLayerPath) return + + // When click clear space, add new VectorObject + const object = PapDOM.VectorObject.create({ + x: 0, + y: 0, + path: PapDOM.Path.create({ + points: [newPoint], + closed: false, + }), + brush: currentStroke ? deepClone(currentStroke) : null, + fill: currentFill ? deepClone(currentFill) : null, + }) + + trsnCommand.doAndAdd( + new PapCommands.VectorLayer.AddObject({ + object, + pathToTargetLayer: activeLayerPath, + }) + ) + + nextObjectId = object.uid + execute(EditorOps.setActiveObject, object.uid) + execute(EditorOps.setSelectedObjectPoints, [0]) + execute(EditorOps.markVectorLastUpdate) + nextPointIndex = 0 + } else { + if (!activeLayerPath) return + + // Add point to active path + nextObjectId = vectorStroking.objectId + + trsnCommand.doAndAdd( + new PapCommands.VectorLayer.PatchPathPoints({ + pathToTargetLayer: activeLayerPath, + objectUid: vectorStroking.objectId, + patcher: (points) => { + if (vectorStroking.isHead) { + points.unshift(newPoint) + nextPointIndex = 0 + } else if (vectorStroking.isTail) { + points.push(newPoint) + nextPointIndex = points.length - 1 + } + }, + }) + ) + + execute(EditorOps.markVectorLastUpdate) + } + + executeOperation(EditorOps.setVectorStroking, { + objectId: nextObjectId, + selectedPointIndex: nextPointIndex, + isHead: true, + isTail: false, + }) + } else { + // Update point or curve for current path + if (!vectorStroking) return + + execute( + EditorOps.runCommand, + new PapCommands.VectorLayer.PatchPathPoints({ + pathToTargetLayer: activeLayerPath, + objectUid: vectorStroking.objectId, + patcher: (points) => { + const targetPointIndex = vectorStroking.selectedPointIndex + const point = points[targetPointIndex] + if (!point) return + + // SEE: https://qiita.com/Hoshi_7/items/d04936883ff3eb1eed2d + const distance = Math.hypot(x - initialPt.x, y - initialPt.y) + + const rad = Math.atan2(y - initialPt.y, x - initialPt.x) + const degree = PapWebMath.normalizeDegree((rad * 180) / Math.PI) + + const oppeseDegree = PapWebMath.normalizeDegree(degree + 180) + const oppeseRad = oppeseDegree * (Math.PI / 180) + + const c1x = Math.cos(oppeseRad) * distance + const c1y = Math.sin(oppeseRad) * distance + + assign(point, { + in: { + x, + y, + }, + out: { x: c1x + (point?.x ?? 0), y: c1y + (point?.y ?? 0) }, + }) + }, + }) + ) + + execute(EditorOps.markVectorLastUpdate) + + // Add point + // activeObject.path.points.push({ + // c1x: 0, + // c1y: 100, + // c2x: x - 4, + // c2y: y, + // x, + // y, + // }) + } + }, + { useTouch: true } + ) + + const bindObjectHover = useHover(({ hovering, event: { currentTarget } }) => { + toggleIsHoverOnPath(hovering) + setHoveredObjectUid( + hovering ? (currentTarget as SVGPathElement)!.dataset.objectUid! : null + ) + }) + + const bindObjectDrag = useDrag( + (e) => { + const { event, delta, initial, xy, last } = e + if (!activeLayerPath || !the(currentTool).in('cursor', 'point-cursor')) + return + + if (e.touches > 2) { + trsnCommand.cancel() + return + } + + const objectUid = (event.target as SVGPathElement).dataset.objectUid! + + trsnCommand.autoStartAndDoAdd( + new PapCommands.VectorLayer.TransformObject({ + pathToTargetLayer: activeLayerPath, + objectUid: objectUid, + skipDo: false, + transform: { + movement: { + x: delta[0] * (1 / canvasScale), + y: delta[1] * (1 / canvasScale), + }, + }, + }) + ) + + if (last) { + trsnCommand.commit() + } + }, + { threshold: 4 } + ) + + const handleContextMenu = useFunk((e: MouseEvent) => { + contextMenu.show(e, { + props: { + objectUid: (e.currentTarget as SVGPathElement).dataset!.objectUid, + }, + }) + }) + + const moveDownObjectOrder = useFunk((objectUid: string) => { + if (!activeLayerPath) return + + execute( + EditorOps.runCommand, + new PapCommands.VectorLayer.ReorderObjects({ + pathToTargetLayer: activeLayerPath, + objectUid: objectUid, + newIndex: { delta: 1 }, + }) + ) + }) + + const moveUpObjectOrder = useFunk((objectUid: string) => { + if (!activeLayerPath) return + + execute( + EditorOps.runCommand, + new PapCommands.VectorLayer.ReorderObjects({ + pathToTargetLayer: activeLayerPath, + objectUid: objectUid, + newIndex: { delta: -1 }, + }) + ) + }) + + const handleClickMoveUp = useFunk((e: MouseEvent) => { + moveUpObjectOrder(e.currentTarget.dataset.objectUid!) + }) + + const handleClickMoveDown = useFunk((e: MouseEvent) => { + moveDownObjectOrder(e.currentTarget.dataset.objectUid!) + }) + + const handleContextClickMoveUp = useFunk( + (e: ContextMenuParam<{ objectUid: string }>) => { + moveUpObjectOrder(e.props!.objectUid) + } + ) + + const handleContextClickMoveDown = useFunk( + (e: ContextMenuParam<{ objectUid: string }>) => { + moveDownObjectOrder(e.props!.objectUid) + } + ) + + useClickAway(rootRef as any, ({ target }) => { + execute(EditorOps.setVectorStroking, null) + execute(EditorOps.setSelectedObjectPoints, []) + }) + + useFunkyMouseTrap(rootRef, ['del', 'backspace'], () => { + if (!activeLayerPath) return + if (activeObjectId == null) return + + if (activeObjectPointIndices.length === 0) { + execute( + EditorOps.runCommand, + new PapCommands.VectorLayer.DeleteObject({ + pathToTargetLayer: activeLayerPath, + objectUid: activeObjectId, + }) + ) + } else { + execute(EditorOps.deleteSelectedObjectPoints) + } + }) + + useFunkyMouseTrap(rootRef, ['command+c', 'ctrl+c'], () => { + if (!activeLayerPath) return + if (activeObjectId == null) return + + // execute(ClipboardOps.copyObject, ) + }) + + const bindControllerDrag = useDrag((e) => { + if (!activeLayerPath) return + + if (e.first) { + trsnCommand.startIfNotStarted() + } + + currentControllDirection.current = { + x: PapWebMath.clamp((e.xy[0] - e.initial[0]) / 2, -10, 10), + y: PapWebMath.clamp((e.xy[1] - e.initial[1]) / 2, -10, 10), + } + + if (e.last) { + currentControllDirection.current = { x: 0, y: 0 } + trsnCommand.commit() + } + }) + + // Apply delta each frame for transform controller + useEffect(() => { + if (!activeLayerPath || !activeObject || !activeObjectId) return + + const id = window.setInterval(() => { + if (!trsnCommand.isStarted) return + + trsnCommand.doAndAdd( + new PapCommands.VectorLayer.TransformObject({ + pathToTargetLayer: activeLayerPath, + objectUid: activeObjectId, + transform: { + movement: { + x: currentControllDirection.current.x, + y: currentControllDirection.current.y, + }, + }, + skipDo: false, + }) + ) + + execute(EditorOps.rerenderCanvas) + }, 100) + + return () => window.clearInterval(id) + }, [activeLayerPath, activeObject, activeObjectId]) + + useAutoUpdateFloating(objActionFl) + + useEffect(() => { + setInterval(() => { + objActionFl?.update() + }, 10) + }, []) + + const activeBBox = useMemo( + () => activeObject?.getBoundingBox(), + [activeObject?.cacheKeyObject.key] + ) + + if (!currentDocument) return null + if (!activeLayer.visible || activeLayer.lock) return null + + const zoom = 1 / canvasScale + + const skipHoverEffect = + vectorStroking || the(currentTool).in('draw', 'erase', 'shape-pen') + + // MEMO: これ見て https://codepen.io/osublake/pen/ggYxvp + return ( + + + {activeLayer.objects.map((object) => ( + + + + + ))} + + {activeObject && ( + + + + {currentTool === 'cursor' && ( + + )} + {currentTool === 'point-cursor' && + activeObject?.fill?.type === 'linear-gradient' && ( + + )} + + )} + + + + + {t('vectorControl.context.moveup')} + + + {t('vectorControl.context.movedown')} + + + + + + {activeObject && ( +
[ + o.bg.surface1, + o.border.default, + o.borderRadius('oval'), + ])} + `} + style={{ + // top: activeBBox!.bottom * canvasScale + canvasPosition.y, + // left: activeBBox!.left * canvasScale + canvasPosition.x, + position: objActionFl.strategy, + left: objActionFl.x ?? 0, + top: objActionFl.y ?? 0, + }} + data-disable-object-unfocus + > + [o.bg.surface1])} + `} + style={{ + transform: `translate(${currentControllDirection.current.x}px, ${currentControllDirection.current.y}px)`, + }} + {...bindControllerDrag()} + > + [o.font.text2])} + `} + width={24} + /> + + + + + + + + + +
+ )} + + + ) +} + +const GradientControl = ({ + object, + scale, +}: { + object: PapDOM.VectorObject + scale: number +}) => { + const zoom = 1 / scale + const objectBBox = useMemo( + () => object?.getBoundingBox(), + [object.lastUpdatedAt] + ) + + if (object?.fill?.type !== 'linear-gradient') return null + + const defId = `pplc-ui-linear-gradient-${object.uid}` + const { start, end } = object.fill + const distance = { x: end.x - start.x, y: end.y - start.y } + + useVectorObjectWatch(object) + + return ( + <> + + + {object.fill.colorStops.map(({ color, position }) => ( + + ))} + + + + + + + + {object.fill.colorStops.map((stop, idx) => ( + + ))} + + + ) +} + +const PathSegments = ({ + object, + scale, + // isHoverOnPath, + // hoverObjectId, + // onClickPath, + onDoubleClickPath, + onHoverStateChange, + showPaths, + showPoints, + showControls, +}: { + object: PapDOM.VectorObject + scale: number + // isHoverOnPath: boolean + // hoverObjectId: string | null + // onClickPath: (objectId: string) => void + onDoubleClickPath: ( + objectId: string, + segmentIndex: number, + point: { x: number; y: number } + ) => void + onHoverStateChange: (e: { hovering: boolean; objectId: string }) => void + showPaths: boolean + showPoints: boolean + showControls: boolean +}) => { + const { t } = useTranslation('app') + const { executeOperation } = useFleurContext() + const { execute } = useFleur() + const { + activeLayer, + activeLayerPath, + activeObject, + currentTool, + vectorStroking, + activeObjectPointIndices, + lastUpdated, + } = useStore((get) => ({ + activeLayer: EditorSelector.activeLayer(get), + activeLayerPath: EditorSelector.activeLayerPath(get), + activeObject: EditorSelector.activeObject(get), + currentTool: get(EditorStore).state.currentTool, + vectorStroking: get(EditorStore).state.vectorStroking, + currentStroke: get(EditorStore).state.currentStroke, + currentFill: get(EditorStore).state.currentFill, + activeObjectPointIndices: get(EditorStore).state.activeObjectPointIndices, + + // Get for rerender on update + lastUpdated: get(EditorStore).state.vectorLastUpdated, + })) + + if (activeLayer?.layerType !== 'vector') + throw new Error('Invalid layerType in PathSegment component') + + useVectorObjectWatch(object) + + const contextMenu = useContextMenu() + + const bindDragStartInAnchor = useDrag(({ delta, event }) => { + event.stopPropagation() + if (!activeLayerPath) return + + const { dataset } = event.currentTarget as SVGCircleElement + const pointIndex = +dataset.pointIndex! + + execute( + EditorOps.runCommand, + new PapCommands.VectorLayer.PatchPathPoints({ + pathToTargetLayer: activeLayerPath, + objectUid: object.uid, + patcher: (points) => { + const point = points[pointIndex] + if (!point?.in) return + + point.in.x += delta[0] * (1 / scale) + point.in.y += delta[1] * (1 / scale) + }, + }) + ) + }) + + const bindDragOutAnchor = useDrag(({ delta, event }) => { + event.stopPropagation() + if (!activeLayerPath) return + + const { dataset } = event.currentTarget as SVGCircleElement + const pointIndex = +dataset.pointIndex! + + execute( + EditorOps.runCommand, + new PapCommands.VectorLayer.PatchPathPoints({ + pathToTargetLayer: activeLayerPath, + objectUid: object.uid, + patcher: (points) => { + const point = points[pointIndex] + if (!point?.out) return + + point.out.x += delta[0] * (1 / scale) + point.out.y += delta[1] * (1 / scale) + }, + }) + ) + }) + + const bindDragPoint = useDrag( + ({ delta, event, first, last }) => { + event.stopPropagation() + + const { dataset } = event.currentTarget as SVGElement + const pointIndex = +dataset.pointIndex! + + if (!activeLayerPath) return + + execute( + EditorOps.runCommand, + new PapCommands.VectorLayer.PatchPathPoints({ + pathToTargetLayer: activeLayerPath, + objectUid: object.uid, + patcher: (points) => { + const point = points[pointIndex] + if (!point) return + + const deltaX = delta[0] * (1 / scale) + const deltaY = delta[1] * (1 / scale) + + point.x += deltaX + point.y += deltaY + + if (point.in) { + point.in.x += deltaX + point.in.y += deltaY + } + + if (point.out) { + point.out.x += deltaX + point.out.y += deltaY + } + + // if (last) { + // object.path.freeze() + // } + }, + }) + ) + + execute(EditorOps.markVectorLastUpdate) + }, + { threshold: 2 } + ) + + const handleClickPoint = useFunk((e: MouseEvent) => { + e.stopPropagation() + console.log('hi') + + const { dataset } = e.currentTarget + const pointIndex = +dataset.pointIndex! + const isFirstPoint = dataset.isFirstPoint != null + const isLastPoint = dataset.isLastPoint != null + + if (the(currentTool).notIn('point-cursor', 'shape-pen') || !activeLayerPath) + return + + if (vectorStroking && (isLastPoint || isFirstPoint)) { + if (!activeLayer || !activeObject) return + if (vectorStroking.objectId !== object.uid) return + if (!vectorStroking.isTail && !vectorStroking.isHead) return + + execute( + EditorOps.runCommand, + new PapCommands.VectorLayer.PatchPathAttr({ + pathToTargetLayer: activeLayerPath, + objectUid: vectorStroking.objectId, + patch: { closed: true }, + }) + ) + + execute(EditorOps.setVectorStroking, null) + + return + } + + if (activeObject && (isLastPoint || isFirstPoint)) { + execute(EditorOps.setVectorStroking, { + objectId: object.uid, + selectedPointIndex: pointIndex, + isHead: isFirstPoint, + isTail: isLastPoint, + }) + return + } + + const nextIndices = e.shiftKey + ? [...activeObjectPointIndices, pointIndex] + : [pointIndex] + + executeOperation(EditorOps.setSelectedObjectPoints, nextIndices) + }) + + const handleClickPath = useFunk((e: MouseEvent) => { + e.stopPropagation() + if (!activeLayerPath) return + + const { dataset } = e.currentTarget + const pointIndex = +dataset.pointIndex! + const point = object.path.points[pointIndex] + const prevPoint = object.path.points[pointIndex - 1] + + console.log('hi', { pointIndex, point, prevPoint }) + + if (currentTool === 'shape-pen') { + if (!prevPoint) return + + const svg = (e.target as SVGPathElement).ownerSVGElement! + const pt = assign(svg.createSVGPoint(), { + x: e.clientX, + y: e.clientY, + }).matrixTransform(svg.getScreenCTM()!.inverse()) + + const angle = PapWebMath.angleOfPoints(prevPoint, point) + const reverseAngle = PapWebMath.degToRad( + PapWebMath.deg(PapWebMath.radToDeg(angle) + 180) + ) + const distance = PapWebMath.distanceOfPoint(prevPoint, point) + + executeOperation( + EditorOps.runCommand, + new PapCommands.VectorLayer.PatchPathPoints({ + pathToTargetLayer: activeLayerPath, + objectUid: object.uid, + patcher: (points) => { + points.splice(pointIndex, 0, { + x: pt.x, + y: pt.y, + in: PapWebMath.pointByAngleAndDistance({ + angle: reverseAngle, + distance: distance / 2, + base: pt, + }), + out: PapWebMath.pointByAngleAndDistance({ + angle: angle, + distance: distance / 2, + base: pt, + }), + }) + }, + }) + ) + + return + } + + // onClickPath(object.uid) + }) + + const handleDoubleClickPath = useFunk((e: MouseEvent) => { + e.stopPropagation() + + const { dataset } = e.currentTarget + const pointIndex = +dataset.pointIndex! + + // SEE: https://stackoverflow.com/a/42711775 + const svg = (e.target as SVGPathElement).ownerSVGElement! + const cursorPt = DOMUtils.domPointToSvgPoint(svg, { + x: e.clientX, + y: e.clientY, + }) + + onDoubleClickPath(object.uid, pointIndex, { x: cursorPt.x, y: cursorPt.y }) + }) + + const handleDoubleClickInPoint = useFunk( + (e: MouseEvent) => { + const { dataset } = e.currentTarget + const pointIndex = +dataset.pointIndex! + + executeOperation(EditorOps.updateActiveObject, (object) => { + object.path.points[pointIndex].in = null + }) + } + ) + + const handleDoubleClickOutPoint = useFunk( + (e: MouseEvent) => { + const { dataset } = e.currentTarget + const pointIndex = +dataset.pointIndex! + + executeOperation(EditorOps.updateActiveObject, (object) => { + object.path.points[pointIndex].out = null + }) + } + ) + + const bindLongPressPoint = useLongPress( + (e) => { + // stopPropagation for shape-pen(add point) + e.stopPropagation() + + if (!activeLayerPath) return + + // console.log({ ...e }, , e.target?.dataset) + const { dataset } = e.target as SVGRectElement + const objectUid = dataset.objectId! + const pointIndex = +dataset.pointIndex! + // console.log(objectUid, pointIndex) + + execute( + EditorOps.runCommand, + new PapCommands.VectorLayer.PatchPathPoints({ + pathToTargetLayer: activeLayerPath, + objectUid: object.uid, + patcher: (points) => { + points.splice(pointIndex, 1) + }, + }) + ) + }, + { threshold: 600, cancelOnMovement: 2 } + ) + + // const pathHoverBind = useHover((e) => { + // const { dataset } = e.event.currentTarget as any as SVGPathElement + // const object = objects.find((obj) => obj.uid === dataset.objectId!)! + + // onHoverStateChange({ hovering: e.hovering, objectId: object.uid }) + // }) + + const zoom = 1 / scale + + const pathSegments = useDeepCompareMemo(() => { + return object.path + .mapPoints((point, prevPoint, pointIdx, points) => { + const isActive = activeObject?.uid === object.uid + const isPointSelected = activeObjectPointIndices.includes(pointIdx) + const isFirstPoint = pointIdx === 0 + const isLastPoint = pointIdx === points.length - 1 + // const hovering = isHoverOnPath && object.uid === hoverObjectId + const renderPoint = + pointIdx !== points.length - 1 || !object.path.closed + + // prettier-ignore + const segmentPath = prevPoint + ? ` + M${prevPoint.x},${prevPoint.y} + C${prevPoint.out?.x ?? prevPoint.x}, + ${prevPoint.out?.y ?? prevPoint.y} ${point.in?.x ?? point.x}, + ${point.in?.y ?? point.y} ${point.x}, + ${point.y} + ` + : '' + + return { + object, + line: ( + + + + ), + + inControl: ( + + {isActive && + isPointSelected && + !(object.path.closed && isLastPoint) && ( + <> + {/* handle from previous to current */} + {point.in && ( + <> + + + + )} + + )} + + ), + + outControl: ( + + {/* handle current to previous */} + {isPointSelected && point.out && ( + <> + + + + )} + + ), + point: + !renderPoint || !isActive ? null : ( + + + + + + ), + } + }) + .flat(1) + }, [activeObject?.uid, object, lastUpdated, activeObjectPointIndices]) + + const elements = ( + <> + {pathSegments.map(({ line, inControl, outControl, object }, idx) => ( + + + {showPaths && line} + {showControls && inControl} + {outControl && outControl} + + + ))} + {pathSegments.map(({ point, object }, idx) => ( + + {showPoints && point} + + ))} + + ) + + return elements +} + +const ObjectBoundingBox = memo( + forwardRef< + SVGGElement, + { + object: PapDOM.VectorObject + scale: number + active: boolean + } + >(function ObjectBoundingBox({ object, scale, active }, ref) { + const { execute } = useFleur() + const transCommand = useTransactionCommand() + + const bbox = useMemo( + () => object.getBoundingBox(), + [object.cacheKeyObject.key] + ) + + const [{ left, top, right, bottom, width, height }, setBBox] = + useObjectState(bbox) + + const rotateRef = useRef(object.rotate) + + const { canvasScale, canvasPosition } = useStore((get) => ({ + canvasScale: EditorSelector.canvasScale(get), + canvasPosition: EditorSelector.canvasPosition(get), + })) + + const [dragPoint, setDragPoint] = useState<[number, number] | null>(null) + + const zoom = 1 / scale + const controlSize = POINT_SIZE * zoom + + const bindRotateDrag = useDrag((e) => { + const { initial, xy, last, delta } = e + + e.memo ??= { rotate: object.rotate } + + const svg = (e.event.target as SVGElement).closest('svg')! + + setBBox((state) => { + state.left += delta[0] + state.top += delta[1] + }) + + const xyIni = assign(svg.createSVGPoint(), { + x: initial[0], + y: initial[1], + }).matrixTransform(svg.getScreenCTM()!.inverse()) + + const xyPt = assign(svg.createSVGPoint(), { + x: xy[0], + y: xy[1], + }).matrixTransform(svg.getScreenCTM()!.inverse()) + + const { x, y } = xyPt + + setDragPoint([x, y]) + + execute(EditorOps.updateActiveObject, (o) => { + const bbox = o.getBoundingBox() + + const angle = PapWebMath.angleOfPoints( + { x: bbox.centerX, y: bbox.centerY }, + { x: x - xyIni.x, y: y - xyIni.y } + ) + + o.rotate = e.memo!.rotate - PapWebMath.radToDeg(angle) + o.x += delta[0] * zoom + o.y += delta[1] * zoom + }) + + if (last) { + setDragPoint(null) + } + + return e.memo + }) + + const bindLeftTopDrag = useDrag((e) => { + const { initial, xy, last, delta } = e + + // e.memo ??= { rotate: object.rotate } + + // const svg = (e.event.target as SVGElement).closest('svg')! + + setBBox((state) => { + state.left += delta[0] + state.top += delta[1] + }) + + // const xyIni = assign(svg.createSVGPoint(), { + // x: initial[0], + // y: initial[1], + // }).matrixTransform(svg.getScreenCTM()!.inverse()) + + // const xyPt = assign(svg.createSVGPoint(), { + // x: xy[0], + // y: xy[1], + // }).matrixTransform(svg.getScreenCTM()!.inverse()) + + // const { x, y } = xyPt + + // setDragPoint([x, y]) + + // execute(EditorOps.updateActiveObject, (o) => { + // const bbox = o.getBoundingBox() + + // const angle = PapWebMath.angleOfPoints( + // { x: bbox.centerX, y: bbox.centerY }, + // { x: x - xyIni.x, y: y - xyIni.y } + // ) + + // o.rotate = e.memo!.rotate - PapWebMath.radToDeg(angle) + // // o.x += delta[0] * zoom + // // o.y += delta[1] * zoom + // }) + + // if (last) { + // setDragPoint(null) + // } + + return e.memo + }) + + useVectorObjectWatch(object) + + return ( + + + + {dragPoint && ( + + )} + + + + + + + + + ) + }), + (prev, next) => + prev.object.cacheKeyObject.key === next.object.cacheKeyObject.key && + prev.scale === next.scale && + prev.active === next.active +) + +const anchorRect = css` + stroke: #4e7fff; + fill: #fff; +` + +function assertVectorLayer(layer: any): asserts layer is PapDOM.VectorLayer { + if (layer?.layerType !== 'vector') + throw new Error('Expect VectorLayer but RasterLayer given') +} diff --git a/pkgs/web/src/features/PaintV2/containers/LayerView.tsx b/pkgs/web/src/features/PaintV2/containers/LayerView.tsx new file mode 100644 index 00000000..b22f24cb --- /dev/null +++ b/pkgs/web/src/features/PaintV2/containers/LayerView.tsx @@ -0,0 +1,1264 @@ +import { + ChangeEvent, + memo, + MouseEvent, + ReactNode, + useEffect, + useRef, + useState, +} from 'react' +import { TreeView, TreeViewItemRow, TreeViewItem } from 'react-draggable-tree' +// import { PapCommands, PapDOM, PapHelper } from '@paplico/core' +import { useClickAway, useToggle, useUpdate } from 'react-use' +import { + loadImageFromBlob, + selectFile, + useFunk, + useObjectState, +} from '@hanakla/arma' +import { rgba } from 'polished' +import { + Add, + Brush, + DeleteBin, + Filter3, + Guide, + Lock, + LockUnlock, + Magic, + Shape, +} from '@styled-icons/remix-fill' +import { css } from 'styled-components' +import { Portal } from '🙌/components/Portal' +import { centering, rangeThumb } from '🙌/utils/mixins' +import { useTranslation } from 'next-i18next' +import { ArrowDownS, Stack } from '@styled-icons/remix-line' +import { Eye, EyeClose } from '@styled-icons/remix-fill' +import { + ContextMenu, + ContextMenuItem, + ContextMenuParam, + Separator, + useContextMenu, +} from '🙌/components/ContextMenu' +import { combineRef } from '🙌/utils/react' +import { SelectBox } from '🙌/components/SelectBox' + +import { FakeInput } from '🙌/components/FakeInput' +import { DOMUtils } from '🙌/utils/dom' +import { timesMap } from '🙌/utils/array' +import { useMouseTrap } from '🙌/hooks/useMouseTrap' +import { useTheme } from 'styled-components' +import { useFleurContext, useStore } from '@fleur/react' +import { EditorOps, EditorSelector, EditorStore } from '🙌/domains/EditorStable' +import { + calcLayerMove, + FlatLayerEntry, + flattenLayers, + isEventIgnoringTarget, +} from '../helpers' +import { SidebarPane } from '🙌/components/SidebarPane' +import { ThemeProp, tm } from '🙌/utils/theme' +import { + closestCenter, + DndContext, + DragEndEvent, + PointerSensor, + useSensor, + useSensors, +} from '@dnd-kit/core' +import { + SortableContext, + useSortable, + verticalListSortingStrategy, +} from '@dnd-kit/sortable' + +import { CSS } from '@dnd-kit/utilities' +import { Tooltip2 } from '🙌/components/Tooltip2' +import { useBufferedState, useDebouncedFunk, useFleur } from '🙌/utils/hooks' +import { shallowEquals } from '🙌/utils/object' +import { useHover } from 'react-use-gesture' +import { CommandOps } from '../../../domains/Commands' +import { + useLayerWatch, + useActiveLayerPane, + useDocumentWatch, + useLayerListWatch, + useVectorObjectWatch, +} from '../hooks' +import { shift, useFloating } from '@floating-ui/react-dom' +import { LayerNameText } from '../../../components/LayerNameText' +import { TreeRow } from './DesktopTreeView/TreeRow' + +export const LayerView = memo(function LayerView() { + const { t } = useTranslation('app') + + const rerender = useUpdate() + const { execute } = useFleur() + const { executeOperation } = useFleurContext() + const { activeLayer, currentDocument, layers } = useStore((get) => ({ + activeLayer: EditorSelector.activeLayer(get), + layers: EditorSelector.layers(get), + currentDocument: EditorSelector.currentDocument(get), + })) + + const [layerSorting, setLayerSorting] = useState(false) + const [layerTypeOpened, toggleAddLayerOpened] = useToggle(false) + + const sensors = useSensors( + useSensor(PointerSensor, { activationConstraint: { distance: 4 } }) + ) + + const addLayerFl = useFloating({ + strategy: 'absolute', + placement: 'bottom-end', + middleware: [shift()], + }) + + useClickAway(addLayerFl.refs.floating, (e) => { + toggleAddLayerOpened(false) + }) + + const handleClickAddLayerItem = useFunk( + async ({ currentTarget }: MouseEvent) => { + if (!currentDocument) return + + const layerType = currentTarget.dataset.layerType! + const lastLayerId = activeLayer?.uid + const { width, height } = currentDocument + + let layer: PapDOM.LayerTypes + switch (layerType) { + case 'raster': { + layer = PapDOM.RasterLayer.create({ width, height }) + break + } + case 'vector': { + layer = PapDOM.VectorLayer.create({}) + break + } + case 'filter': { + layer = PapDOM.FilterLayer.create({}) + break + } + case 'image': { + const [file] = await selectFile({ + extensions: ['.jpg', '.jpeg', '.png'], + }) + if (!file) return + + const { image } = await loadImageFromBlob(file) + layer = await PapHelper.imageToLayer(image) + break + } + default: + throw new Error('なんかおかしなっとるで') + } + + execute( + EditorOps.runCommand, + new PapCommands.Document.AddLayer({ + layer, + aboveOnLayerId: lastLayerId, + }) + ) + execute(EditorOps.setActiveLayer, [layer.uid]) + + toggleAddLayerOpened(false) + } + ) + + const handleLayerDragStart = useFunk(() => { + setLayerSorting(true) + }) + + const handleLayerDragEnd = useFunk(({ active, over }: DragEndEvent) => { + const moves = calcLayerMove(flatLayers, { active, over }) + if (!moves) return + + console.log(moves) + + if (moves.type === 'layer') { + setLayerSorting(false) + execute( + EditorOps.runCommand, + new PapCommands.Layer.MoveLayer({ + moves: [ + { + layerPath: moves.sourcePath, + targetContainerPath: moves.targetParentPath, + targetIndex: moves.targetIndex, + }, + ], + }) + ) + } else if (moves.type === 'object') { + execute( + EditorOps.runCommand, + new PapCommands.VectorLayer.TransferObject({ + moves: [ + { + sourceContainerPath: moves.sourcePath, + destContainerPath: moves.targetParentPath, + destIndex: moves.targetIndex, + objectUid: moves.objectUid, + }, + ], + }) + ) + } + }) + + const container = useFunk((children: ReactNode) => ( +
+ {children} +
+ )) + + const [collapsedEntries, setCollapsed] = useObjectState< + Record + >({}) + const flatLayers = flattenLayers(layers, (entry, _, list) => { + let collapse = false + let current = entry + let nextParent = list.find((e) => e.id === entry.parentId) + + while (nextParent != null && !collapse) { + collapse = nextParent?.id + ? collapse || (collapsedEntries[nextParent.id] ?? true) + : true + + current = nextParent + nextParent = list.find((e) => e.id === current.parentId) + } + + return !collapse + }) + + useDocumentWatch(currentDocument) + useLayerListWatch( + flatLayers.filter((e) => e.type === 'layer').map((e) => e.layer) + ) + + const update = () => { + setItem(createItem(root)) + } + + return ( + + + レイヤー + + } + container={container} + > + +
[o.border.default.bottom])} + `} + > +
+
+ +
+
+ + +
    theme.color.surface3}; + box-shadow: 0 0 4px ${rgba('#000', 0.5)}; + color: ${({ theme }) => theme.text.white}; + z-index: 1; + border-radius: 4px; + overflow: hidden; + + li { + padding: 8px; + user-select: none; + } + li:hover { + /* color: #000; */ + background-color: ${({ theme }) => theme.exactColors.active40}; + } + `} + style={{ + position: addLayerFl.strategy, + left: addLayerFl.x ?? 0, + top: addLayerFl.y ?? 0, + ...(layerTypeOpened + ? { visibility: 'visible', pointerEvents: 'all' } + : { visibility: 'hidden', pointerEvents: 'none' }), + }} + > +
  • + {t('layerType.raster')} +
  • +
  • + {t('layerType.vector')} +
  • +
  • + {t('layerType.filter')} +
  • +
  • + {t('addFromImage')} +
  • +
+
+
+ hey +
+ theme.exactColors.blackFade20}; */ + `} + style={{ overflow: layerSorting ? 'hidden' : 'auto' }} + > + {/* console.log(item)} + dropBetweenIndicator={() => <>} + dropOverIndicator={() => <>} + dropBetweenIndicator={({ top, left }) => ( +
+ )} + dropOverIndicator={({ top, height }) => ( +
+ )} + renderRow={(props) => } + /> */} + {/* + entry.id)} + strategy={verticalListSortingStrategy} + > + {flatLayers.map((entry) => + entry.type === 'layer' ? ( + + setCollapsed((state) => { + state[id] = !(state[id] ?? true) + }) + } + /> + ) : ( + + ) + )} + + */} +
+ {/* */} + + ) +}) + +// const TreeRow: React.FC<{ +// rows: readonly TreeViewItemRow[] +// index: number +// item: TreeViewItem +// depth: number +// indentation: number +// onChange: () => void +// }> = ({ rows, index, item, depth, indentation, onChange }) => { +// const node = item.node + +// const onCollapseButtonClick = (e: React.MouseEvent) => { +// e.stopPropagation() +// node.collapsed = !node.collapsed +// onChange() +// } + +// const onClick = (event: React.MouseEvent) => { +// if (event.metaKey) { +// if (node.selected) { +// node.deselect() +// } else { +// node.select() +// } +// } else if (event.shiftKey) { +// let minSelectedIndex = index +// let maxSelectedIndex = index + +// for (const [i, row] of rows.entries()) { +// if (row.item.node.selected) { +// minSelectedIndex = Math.min(minSelectedIndex, i) +// maxSelectedIndex = Math.max(maxSelectedIndex, i) +// } +// } + +// for (let i = minSelectedIndex; i <= maxSelectedIndex; ++i) { +// rows[i].item.node.select() +// } +// } else { +// node.root.deselect() +// node.select() +// } + +// onChange() +// } + +// return ( +//
+// {node.firstChild !== undefined && ( +// +// {node.collapsed ? '[+]' : '[-]'} +// +// )} +// {node.name} +//
+// ) +// } + +const SortableLayerItem = memo( + ({ + entry, + onToggleCollapse, + }: { + entry: FlatLayerEntry + onToggleCollapse: (id: string) => void + }) => { + if (entry.type !== 'layer') return null + const { layer, parentPath, depth } = entry + + const contextMenu = useContextMenu() + const { attributes, listeners, setNodeRef, transform, transition } = + useSortable({ + id: entry.id, + animateLayoutChanges: ({ isSorting, wasDragging }) => + isSorting || wasDragging ? false : true, + }) + + const style = { + transform: CSS.Transform.toString(transform), + transition, + } + + const { t } = useTranslation('app') + const theme = useTheme() + + const { execute } = useFleur() + + const { + activeLayer, + thumbnailUrlOfLayer, + isInHighlightedLayer, + selectedLayerUids, + } = useStore((get) => ({ + activeLayer: EditorSelector.activeLayer(get), + thumbnailUrlOfLayer: EditorSelector.thumbnailUrlOfLayer(get), + isInHighlightedLayer: EditorSelector.isInHighlightedLayer(get), + selectedLayerUids: EditorSelector.selectedLayerUids(get), + })) + + const [objectsOpened, toggleObjectsOpened] = useToggle(false) + + const rootRef = useRef(null) + + const handleToggleVisibility = useFunk(() => { + execute( + EditorOps.runCommand, + new PapCommands.Layer.PatchLayerAttr({ + patch: { visible: !layer.visible }, + pathToTargetLayer: [...parentPath, layer.uid], + }) + ) + }) + + const handleToggleLock = useFunk(() => { + execute( + EditorOps.runCommand, + new PapCommands.Layer.PatchLayerAttr({ + patch: { lock: !layer.lock }, + pathToTargetLayer: [...parentPath, layer.uid], + }) + ) + }) + + const handleClickRoot = useFunk((e: MouseEvent) => { + if ( + DOMUtils.closestOrSelf(e.target, '[data-ignore-click]') || + DOMUtils.closestOrSelf(e.target, '[data-ignore-layer-click]') || + DOMUtils.closestOrSelf(e.target, '[data-prevent-active-layer-change]') + ) + return + + if (e.shiftKey || e.metaKey || e.ctrlKey) { + execute(EditorOps.setLayerSelection, (uids) => { + if (uids.includes(layer.uid)) { + return uids.filter((uid) => uid !== layer.uid) + } else { + return [...uids, layer.uid] + } + }) + } else { + execute(EditorOps.setLayerSelection, () => [layer.uid]) + execute(EditorOps.setActiveLayer, [...parentPath, layer.uid]) + } + }) + + const handleChangeActiveLayerToReferenceTarget = useFunk( + (e: MouseEvent) => { + if ( + DOMUtils.closestOrSelf(e.target, '[data-ignore-click]') || + DOMUtils.closestOrSelf(e.target, '[data-ignore-layer-click]') || + layer.layerType !== 'reference' + ) + return + + e.stopPropagation() + + execute( + EditorOps.setActiveLayerToReferenceTarget, + layer.referencedLayerId + ) + } + ) + + const handleChangeLayerName = useFunk( + ({ currentTarget }: ChangeEvent) => { + execute( + EditorOps.runCommand, + new PapCommands.Layer.PatchLayerAttr({ + patch: { name: currentTarget.value }, + pathToTargetLayer: [...parentPath, layer.uid], + }) + ) + } + ) + + const handleContextMenu = useFunk((e: MouseEvent) => { + contextMenu.show(e, { props: { layerPath: [...parentPath, layer.uid] } }) + }) + + const handleClickConvertToGroup = useFunk(() => { + execute(CommandOps.convertToGroups) + }) + + const handleClickConvertToSubstance = useFunk(() => { + execute( + EditorOps.runCommand, + new PapCommands.ReferenceLayer.ConvertToSubstance({ + pathToReference: [...parentPath, layer.uid], + }) + ) + }) + + const handleClickMakeReferenceLayer = useFunk( + ({ props }: LayerContextMenuParam) => { + const layer = PapDOM.ReferenceLayer.create({ + referencedLayerId: props!.layerPath.slice(-1)[0], + }) + + execute( + EditorOps.runCommand, + new PapCommands.Document.AddLayer({ + layer, + aboveOnLayerId: props!.layerPath.slice(-1)[0], + }) + ) + } + ) + + const handleClickDuplicateLayer = useFunk(() => { + execute( + EditorOps.runCommand, + new PapCommands.Layer.DuplicateLayer({ + pathToSourceLayer: [...parentPath, layer.uid], + }) + ) + }) + + const handleClickTrimByDocument = useFunk( + ({ props }: LayerContextMenuParam) => { + execute( + EditorOps.runCommand, + new PapCommands.RasterLayer.TrimToDocumentArea({ + pathToTargetLayer: props!.layerPath, + }) + ) + } + ) + + const handleClickRemoveLayer = useFunk( + ({ props }: LayerContextMenuParam) => { + execute( + EditorOps.runCommand, + new PapCommands.Layer.DeleteLayer({ + pathToTargetLayer: props!.layerPath, + }) + ) + + execute(EditorOps.setActiveLayer, null) + } + ) + + const handleClickTruncateLayer = useFunk( + ({ props }: LayerContextMenuParam) => { + if (layer.layerType === 'raster') { + execute( + EditorOps.runCommand, + new PapCommands.RasterLayer.UpdateBitmap({ + pathToTargetLayer: props!.layerPath, + update: (bitmap) => { + bitmap.fill(0, 0) + }, + }) + ) + } else if (layer.layerType === 'vector') { + execute( + EditorOps.runCommand, + new PapCommands.VectorLayer.TruncateContent({ + pathToTargetLayer: props!.layerPath, + }) + ) + } else if (layer.layerType === 'filter') { + execute( + EditorOps.runCommand, + new PapCommands.Layer.TruncateFilters({ + pathToTargetLayer: props!.layerPath, + }) + ) + } + } + ) + + const handleClickCollapse = useFunk(() => { + toggleObjectsOpened() + onToggleCollapse(entry.id) + }) + + useMouseTrap( + rootRef, + [ + { + key: ['del', 'backspace'], + handler: () => { + execute( + EditorOps.runCommand, + new PapCommands.Layer.DeleteLayer({ + pathToTargetLayer: [...parentPath, layer.uid], + }) + ) + }, + }, + ], + [layer] + ) + + const bindReferenceHover = useHover(({ hovering }) => { + if (layer.layerType !== 'reference') return + + execute(EditorOps.setHighlightedLayers, (ids) => { + if (hovering) return [...ids, layer.referencedLayerId] + else return ids.filter((id) => id !== layer.referencedLayerId) + }) + }) + + useLayerWatch(layer) + + return ( +
+ {process.env.NODE_ENV === 'development' && ( + + <>uid: {layer.uid} + + )} +
+ { + <> + {timesMap(depth, (i) => ( +
+ ))} + + } +
+
+ {(layer.layerType === 'vector' || + layer.layerType === 'group') && ( + + )} +
+ +
theme.colors.white10}; + opacity: 0.6; + &:hover { + opacity: 1; + } + `} + onClick={handleToggleVisibility} + data-ignore-click + > + + {layer.visible ? : } + +
+ +
theme.colors.white10}; + opacity: 0.6; + &:hover { + opacity: 1; + } + `} + onClick={handleToggleLock} + data-ignore-click + > + + {layer.lock ? : } + +
+ +
+ } + {layer.layerType === 'raster' && } + {layer.layerType === 'text' && + // + 'T'} + {layer.layerType === 'vector' && } + {layer.layerType === 'reference' && ( + + )} +
+ +
+ +
+
+
+ + + {selectedLayerUids.length >= 1 && + selectedLayerUids.includes(layer.uid) && ( + <> + + {t('layerView.context.makeGroup')} + + + + )} + + + + {t('layerView.context.duplicate')} + + + + + + + + + + {t('remove')} + + +
+ ) + }, + (prev, next) => + shallowEquals({ ...prev.entry.layer }, { ...next.entry.layer }) && + prev.entry.depth === next.entry.depth && + shallowEquals(prev.entry.path, next.entry.path) +) + +const SortableObjectItem = memo(function SortableObjectItem({ + entry, +}: { + entry: FlatLayerEntry +}) { + if (entry.type !== 'object') return null + const { object, parentPath, depth } = entry + + const { t } = useTranslation('app') + const theme = useTheme() + const { execute } = useFleur() + const { activeObject } = useStore((get) => ({ + activeObject: EditorSelector.activeObject(get), + })) + + const objectMenu = useContextMenu() + + useVectorObjectWatch(object) + + const { + attributes, + listeners, + setNodeRef, + transform, + transition, + isDragging, + isSorting, + } = useSortable({ + id: entry.id, + animateLayoutChanges: ({ isSorting, wasDragging }) => + isSorting || wasDragging ? false : true, + }) + + const handleClickObject = useFunk( + ({ currentTarget }: MouseEvent) => { + execute( + EditorOps.setActiveObject, + currentTarget.dataset.objectId ?? null, + parentPath + ) + } + ) + + const handleObjectContextMenu = useFunk((e: MouseEvent) => { + e.stopPropagation() + + objectMenu.show(e, { + props: { + layerPath: parentPath, + objectId: e.currentTarget.dataset.objectId!, + }, + }) + }) + + const handleToggleVisibility = useFunk(() => { + execute( + EditorOps.runCommand, + new PapCommands.VectorLayer.PatchObjectAttr({ + pathToTargetLayer: parentPath, + objectUid: object.uid, + patcher: (attrs) => { + attrs.visible = !attrs.visible + }, + }) + ) + }) + + const handleClickDeleteObject = useFunk( + (e: ContextMenuParam<{ layerPath: string[]; objectId: string }>) => { + execute( + EditorOps.runCommand, + new PapCommands.VectorLayer.DeleteObject({ + pathToTargetLayer: parentPath, + objectUid: e.props!.objectId, + }) + ) + } + ) + + const handleClickRemoveObject = useFunk(() => { + execute( + EditorOps.runCommand, + new PapCommands.VectorLayer.DeleteObject({ + pathToTargetLayer: parentPath, + objectUid: object.uid, + }) + ) + }) + + const style = { + transform: CSS.Transform.toString(transform), + transition, + } + + return ( + <> +
+ rgba(theme.surface.sidebarListActive, 0.1)}; + } + `} + style={{ + ...style, + paddingLeft: 24 + depth * 16, + backgroundColor: + activeObject?.uid === object.uid + ? theme.surface.sidebarListActive + : undefined, + }} + > +
theme.colors.white10}; + `} + style={{ + ...(object.visible ? {} : { opacity: 0.5 }), + }} + onClick={handleToggleVisibility} + data-ignore-click + > + {object.visible ? : } +
+ +
+ {t('layerView.object.path')} +
+ +
+ +
+
+ + + + {t('layerView.object.context.remove')} + + + + ) +}) + +const ActiveLayerPane = memo(function ActiveLayerPane() { + const { t } = useTranslation('app') + const { + state: { activeLayer, layerName }, + handleChangeLayerName, + handleChangeCompositeMode, + handleChangeOpacity, + } = useActiveLayerPane() + + return ( +
[o.border.default.bottom])} + `} + > +
+ ` + : '<未選択>' + } + onChange={handleChangeLayerName} + /> +
+
+ + {t('blend')} + + + +
+
+ {t('opacity')} +
+ +
+
+
+ ) +}) + +type LayerContextMenuParam = ContextMenuParam<{ layerPath: string[] }> diff --git a/pkgs/web/src/features/PaintV2/containers/MainActions/AnotherMenus.tsx b/pkgs/web/src/features/PaintV2/containers/MainActions/AnotherMenus.tsx new file mode 100644 index 00000000..3d57fd96 --- /dev/null +++ b/pkgs/web/src/features/PaintV2/containers/MainActions/AnotherMenus.tsx @@ -0,0 +1,175 @@ +import { useStore } from '@fleur/react' +import { + autoPlacement, + autoUpdate, + shift, + useFloating, +} from '@floating-ui/react-dom' +import { match, useFunk } from '@hanakla/arma' +import { Moon, Sun } from '@styled-icons/remix-fill' +import { rgba } from 'polished' +import { FC, memo, MouseEvent, useEffect } from 'react' +import { css } from 'styled-components' +import { + ActionSheet, + ActionSheetItem, + ActionSheetItemGroup, +} from '🙌/components/ActionSheet' +import { EditorOps, EditorSelector, EditorStore } from '🙌/domains/EditorStable' +import { useFleur } from '🙌/utils/hooks' +import { centering } from '🙌/utils/mixins' +import { darkTheme } from '🙌/utils/theme' +import { usePaplicoExporter } from '../../hooks' + +export const AnotherMenus = memo(function AnotherMenus({}: {}) { + const { execute } = useFleur() + const { engine, currentTheme, currentDocument } = useStore((get) => ({ + engine: get(EditorStore).state.engine, + currentTheme: EditorSelector.currentTheme(get), + currentDocument: EditorSelector.currentDocument(get), + })) + + const exporter = usePaplicoExporter() + + const float = useFloating({ + strategy: 'absolute', + placement: 'top-end', + middleware: [shift()], + }) + + const handleClickExport = useFunk(async () => { + if (!currentDocument) return + exporter.exportDocument() + }) + + const handleClickExportAs = useFunk( + async ({ currentTarget }: MouseEvent) => { + const type = currentTarget.dataset.type! + exporter.exportAs(type as any) + } + ) + + const handleClickDarkTheme = useFunk(() => { + execute(EditorOps.setTheme, 'dark') + }) + + const handleClickLightTheme = useFunk(() => { + execute(EditorOps.setTheme, 'light') + }) + + useEffect(() => { + float.reference(float.refs.floating.current!.parentElement!) + + if (!float.refs.reference.current || !float.refs.floating.current) return + autoUpdate( + float.refs.reference.current, + float.refs.floating.current, + float.update + ) + }, [float.refs.reference, float.refs.floating, float.update]) + + return ( +
theme.surface.floatWhite}; + border-radius: 4px; + box-shadow: 0 0 5px ${rgba('#000', 0.5)}; + `} + style={{ + position: float.strategy, + left: float.x ?? 0, + top: float.y ?? 0, + }} + > + + 保存する + + + + ファイルを保存 + + {/* ファイルを保存 */} + + + + +
+ テーマ +
+
+ + + + + + +
+
+
+ ) +}) + +const MenuItem: FC = ({ children }) => { + return ( +
+ {children} +
+ ) +} diff --git a/pkgs/web/src/features/PaintV2/containers/MainActions/LayerFloatMenu.tsx b/pkgs/web/src/features/PaintV2/containers/MainActions/LayerFloatMenu.tsx new file mode 100644 index 00000000..6496fead --- /dev/null +++ b/pkgs/web/src/features/PaintV2/containers/MainActions/LayerFloatMenu.tsx @@ -0,0 +1,1278 @@ +import type {} from '🙌/utils/styled-theme' + +import { + Add, + DeleteBin, + Eye, + EyeClose, + Menu, + Magic, + ArrowDownS, +} from '@styled-icons/remix-line' +import { Magic as MagicFill } from '@styled-icons/remix-fill' +import { useTranslation } from 'next-i18next' +import { rgba } from 'polished' +import { ChangeEvent, forwardRef, memo, MouseEvent, useRef } from 'react' +import { css, useTheme } from 'styled-components' +import { PapCommands, PapDOM, PapHelper } from '@paplico/core' +import { useStore } from '@fleur/react' +import { + loadImageFromBlob, + selectFile, + useFunk, + useObjectState, +} from '@hanakla/arma' +import { CSS } from '@dnd-kit/utilities' +import { useClickAway, useToggle } from 'react-use' +import { + closestCenter, + DndContext, + DragEndEvent, + PointerSensor, + useSensor, + useSensors, +} from '@dnd-kit/core' +import { + SortableContext, + useSortable, + verticalListSortingStrategy, +} from '@dnd-kit/sortable' +import deepEqual from 'fast-deep-equal' + +import { Portal } from '🙌/components/Portal' +import { centering, checkerBoard, rangeThumb } from '🙌/utils/mixins' +import { SelectBox } from '🙌/components/SelectBox' +import { FakeInput } from '🙌/components/FakeInput' +import { + ActionSheet, + ActionSheetItem, + ActionSheetItemGroup, +} from '🙌/components/ActionSheet' +import { DOMUtils } from '🙌/utils/dom' +import { EditorOps, EditorSelector, EditorStore } from '🙌/domains/EditorStable' +import { useFleur } from '🙌/utils/hooks' +import { Checkbox } from '🙌/components/Checkbox' +import { tm } from '🙌/utils/theme' +import { FilterSettings } from '../FilterSettings' +import { calcLayerMove, FlatLayerEntry, flattenLayers } from '../../helpers' +import { + useActiveLayerPane, + useDocumentWatch, + useLayerWatch, +} from '../../hooks' +import { useLongPress } from 'use-long-press' +import { + restrictToFirstScrollableAncestor, + restrictToVerticalAxis, +} from '@dnd-kit/modifiers' +import { createSlice, useLysSlice, useLysSliceRoot } from '@fleur/lys' +import { DragDots } from '🙌/components/icons/DragDots' +import { + ContextMenu, + ContextMenuItem, + ContextMenuParam, + useContextMenu, +} from '🙌/components/ContextMenu' + +export const LayerFloatMenu = memo( + forwardRef(function LayerFloatMenu(_, ref) { + const { t } = useTranslation('app') + useLysSliceRoot(layerFloatSlice) + + const { execute } = useFleur() + const { currentDocument, layers, activeLayer, activeLayerPath } = useStore( + (get) => ({ + currentDocument: EditorSelector.currentDocument(get), + layers: EditorSelector.layers(get), + activeLayer: EditorSelector.activeLayer(get), + activeLayerPath: EditorSelector.activeLayerPath(get), + }) + ) + useDocumentWatch(currentDocument) + + const [addLayerSheetOpened, toggleAddLayerSheetOpened] = useToggle(false) + + const handleLayerDragEnd = useFunk(({ active, over }: DragEndEvent) => { + const moves = calcLayerMove(flatLayers, { active, over }) + if (!moves) return + + execute( + EditorOps.runCommand, + new PapCommands.Layer.MoveLayer({ + moves: [ + { + layerPath: moves.sourcePath, + targetContainerPath: moves.targetParentPath, + targetIndex: moves.targetIndex, + }, + ], + }) + ) + }) + + const handleToggleLayerCollapse = useFunk((id) => { + setCollapsed((state) => { + state[id] = !(state[id] ?? true) + console.log(state, id) + }) + }) + + const handleClickAddLayer = useFunk(() => { + toggleAddLayerSheetOpened(true) + }) + + const handleClickAddLayerItem = useFunk( + async ({ currentTarget }: MouseEvent) => { + if (!currentDocument) return + + const layerType = currentTarget.dataset.layerType! as + | PapDOM.LayerTypes['layerType'] + | 'image' + const { width, height } = currentDocument + + let layer: PapDOM.LayerTypes + switch (layerType) { + case 'raster': { + layer = PapDOM.RasterLayer.create({ width, height }) + break + } + case 'vector': { + layer = PapDOM.VectorLayer.create({}) + break + } + case 'filter': { + layer = PapDOM.FilterLayer.create({}) + break + } + case 'image': { + const [file] = await selectFile({ + extensions: ['.jpg', '.jpeg', '.png'], + }) + if (!file) return + + const { image } = await loadImageFromBlob(file) + layer = await PapHelper.imageToLayer(image) + break + } + default: + throw new Error('なんかおかしなっとるで') + } + + execute( + EditorOps.runCommand, + new PapCommands.Document.AddLayer({ + layer, + aboveOnLayerId: activeLayer?.uid ?? null, + }) + ) + toggleAddLayerSheetOpened(false) + } + ) + + const addLayerSheetRef = useRef(null) + const handleCloseAddLayerSheet = useFunk(() => { + toggleAddLayerSheetOpened(false) + }) + + useClickAway(addLayerSheetRef, (e) => { + // Reduce rerendering + if (addLayerSheetOpened) toggleAddLayerSheetOpened(false) + }) + + const [collapsedEntries, setCollapsed] = useObjectState< + Record + >({}) + const flatLayers = flattenLayers(layers, (entry, _, list) => { + let collapse = false + let current = entry + let nextParent = list.find((e) => e.id === entry.parentId) + + while (nextParent != null && !collapse) { + collapse = nextParent?.id + ? collapse || (collapsedEntries[nextParent.id] ?? true) + : true + + current = nextParent + nextParent = list.find((e) => e.id === current.parentId) + } + + return !collapse + }) + + return ( +
+
+
+ + entry.id)} + strategy={verticalListSortingStrategy} + > + {flatLayers.map((entry) => + entry.type === 'layer' ? ( + + ) : ( + + ) + )} + + +
+
+ +
+ + レイヤーを追加 +
+ + + +
+ +
+ {t('layerType.raster')} +
+
+ {t('layerType.vector')} +
+
+ {t('layerType.filter')} +
+
+ {t('addFromImage')} +
+
+ + +
+ {t('cancel')} +
+
+
+
+
+ +
+ +
+
+ ) + }) +) + +const SortableLayerItem = memo( + function SortableLayerItem({ + entry, + childrenOpened, + onToggleCollapse, + }: { + entry: FlatLayerEntry + childrenOpened: boolean + onToggleCollapse: (id: string) => void + }) { + if (entry.type !== 'layer') return null + const { layer, parentPath, depth } = entry + useLayerWatch(layer) + + const { t } = useTranslation('app') + const theme = useTheme() + const { execute } = useFleur() + const [showMenu, toggleMenu] = useToggle(false) + const [{ sortingTargetIndex }, sliceActions] = useLysSlice(layerFloatSlice) + + const bindLongPress = useLongPress((e) => { + if (DOMUtils.closestOrSelf(e.target, '[data-ignore-open-layer-menu]')) + return + + toggleMenu(true) + }) + + const { attributes, listeners, setNodeRef, transform, transition } = + useSortable({ id: layer.uid }) + + const { + activeLayer, + activeLayerPath, + thumbnailUrlOfLayer, + selectedLayerUids, + } = useStore((get) => ({ + activeLayer: EditorSelector.activeLayer(get), + activeLayerPath: EditorSelector.activeLayerPath(get), + thumbnailUrlOfLayer: EditorSelector.thumbnailUrlOfLayer(get), + selectedLayerUids: EditorSelector.selectedLayerUids(get), + })) + + const [filtersSheetOpened, setFiltersSheetOpen] = useToggle(false) + + const handleClick = useFunk((e: MouseEvent) => { + if (DOMUtils.closestOrSelf(e.target, '[data-dont-close-layer-float]')) + return + + execute(EditorOps.setActiveLayer, [...parentPath, layer.uid]) + }) + + const handleClickToggleVisible = useFunk((e: MouseEvent) => { + if (!activeLayerPath) return + + execute( + EditorOps.runCommand, + new PapCommands.Layer.PatchLayerAttr({ + pathToTargetLayer: [...parentPath, layer.uid], + patch: { visible: !layer.visible }, + }) + ) + }) + + const handleClickLayerConfig = useFunk((e: MouseEvent) => { + e.stopPropagation() + setFiltersSheetOpen() + }) + + const handleClickLayerCheckBox = useFunk( + (e: ChangeEvent) => { + e.preventDefault() + e.stopPropagation() + + execute(EditorOps.setLayerSelection, (uids) => { + if (e.currentTarget.checked) { + return [...uids, layer.uid] + } else { + return uids.filter((uid) => uid !== layer.uid) + } + }) + } + ) + + const handleClickTrimByDocument = useFunk(() => { + execute( + EditorOps.runCommand, + new PapCommands.RasterLayer.TrimToDocumentArea({ + pathToTargetLayer: [...parentPath, layer.uid], + }) + ) + + toggleMenu(false) + }) + + const handleClickTruncateLayer = useFunk(() => { + if (layer.layerType === 'raster') { + execute( + EditorOps.runCommand, + new PapCommands.RasterLayer.UpdateBitmap({ + pathToTargetLayer: [...parentPath, layer.uid], + update: (bitmap) => { + bitmap.fill(0) + }, + }) + ) + } else if (layer.layerType === 'vector') { + execute( + EditorOps.runCommand, + new PapCommands.VectorLayer.TruncateContent({ + pathToTargetLayer: [...parentPath, layer.uid], + }) + ) + } else if (layer.layerType === 'filter') { + execute( + EditorOps.runCommand, + new PapCommands.Layer.TruncateFilters({ + pathToTargetLayer: [...parentPath, layer.uid], + }) + ) + } + }) + + const handleClickCollapseChildren = useFunk(() => { + onToggleCollapse(entry.id) + }) + + const handleCloseLayerMenu = useFunk(() => { + toggleMenu(false) + }) + + const handleClickDuplicateLayer = useFunk(() => { + execute( + EditorOps.runCommand, + new PapCommands.Layer.DuplicateLayer({ + pathToSourceLayer: [...parentPath, layer.uid], + }) + ) + }) + + const handleClickMakeReferenceLayer = useFunk(() => { + const refLayer = PapDOM.ReferenceLayer.create({ + referencedLayerId: [...parentPath, layer.uid], + }) + + execute( + EditorOps.runCommand, + new PapCommands.Document.AddLayer({ + layer: refLayer, + aboveOnLayerId: layer.uid, + }) + ) + }) + + const handleClickConvertToSubstance = useFunk(() => { + execute( + EditorOps.runCommand, + new PapCommands.ReferenceLayer.ConvertToSubstance({ + pathToReference: [...parentPath, layer.uid], + }) + ) + }) + + const handleCloseFilterSheet = useFunk(() => { + setFiltersSheetOpen(false) + }) + + return ( +
+
+ {(layer.layerType === 'vector' || layer.layerType === 'group') && ( + + )} +
+ + +
+ {layer.visible ? : } +
+
+ `} + disabled + /> +
[o.font.text2])} + `} + > + {t(`compositeModes.${layer.compositeMode}`)} + {Math.round(layer.opacity)}% +
+
+ +
+ {layer.filters.length > 0 ? ( + + ) : ( + + )} +
+ +
+ [o.font.text3])} + `} + /> +
+ + + + + + {t('layerView.context.duplicate')} + + + {layer.layerType === 'reference' && ( + + {t('layerView.context.convertToSubstance')} + + )} + + {layer.layerType !== 'reference' && ( + + {t('layerView.context.makeReference')} + + )} + + {layer.layerType === 'raster' && ( + + {t('layerView.context.trimByCanvasRect')} + + )} + {layer.layerType !== 'reference' && + layer.layerType !== 'group' && + layer.layerType !== 'text' && ( + + {t('layerView.context.truncate')} + + )} + + + + + {t('cancel')} + + + + + + + + +
+ ) + }, + (prev, next) => { + return deepEqual(prev, next) + } +) + +const SortableObjectItem = memo(function SortableObjectItem({ + entry, +}: { + entry: FlatLayerEntry +}) { + if (entry.type !== 'object') return null + const { id, object, parentPath, depth } = entry + + const { t } = useTranslation('app') + const theme = useTheme() + const { execute } = useFleur() + const { activeObject } = useStore((get) => ({ + activeObject: EditorSelector.activeObject(get), + })) + + const { attributes, listeners, setNodeRef, transform, transition } = + useSortable({ + id: entry.id, + animateLayoutChanges: ({ isSorting, wasDragging }) => + isSorting || wasDragging ? false : true, + }) + + const handleClickObject = useFunk( + ({ currentTarget }: MouseEvent) => { + execute( + EditorOps.setActiveObject, + currentTarget.dataset.objectId ?? null, + parentPath + ) + } + ) + + const handleClickRemoveObject = useFunk(() => { + execute( + EditorOps.runCommand, + new PapCommands.VectorLayer.DeleteObject({ + pathToTargetLayer: parentPath, + objectUid: object.uid, + }) + ) + }) + + const style = { + transform: CSS.Transform.toString(transform), + transition, + } + + return ( + <> +
+
+ {t('layerView.object.path')} +
+ +
+ +
+
+ + ) +}) + +const FiltersSheet = memo(function FiltersSheet({ + opened, + layer, + onClose, +}: { + opened: boolean + layer: PapDOM.LayerTypes + onClose: () => void +}) { + useLayerWatch(layer) + + const { t } = useTranslation('app') + const { execute, getStore } = useFleur() + const { + activeLayer, + activeLayerPath, + thumbnailUrlOfLayer, + registeredFilters, + } = useStore((get) => ({ + activeLayer: EditorSelector.activeLayer(get), + activeLayerPath: EditorSelector.activeLayerPath(get), + thumbnailUrlOfLayer: EditorSelector.thumbnailUrlOfLayer(get), + registeredFilters: EditorSelector.getAvailableFilters(get), + })) + + const rootRef = useRef(null) + const [addFilterSheetOpened, toggleAddFilterSheet] = useToggle(false) + + const sensors = useSensors( + useSensor(PointerSensor, { activationConstraint: { distance: 4 } }) + ) + + const handleFilterSortEnd = useFunk(({ active, over }: DragEndEvent) => { + if (!activeLayer || !activeLayerPath || !over) return + + const oldIndex = activeLayer.filters.findIndex((f) => f.uid === active.id) + const newIndex = activeLayer.filters.findIndex((f) => f.uid === over.id) + if (oldIndex === newIndex) return + + execute( + EditorOps.runCommand, + new PapCommands.Layer.ReorderFilter({ + pathToTargetLayer: activeLayerPath, + filterUid: active.id, + newIndex: { exactly: newIndex }, + }) + ) + }) + + const handleClickAddFilterButton = useFunk(() => { + toggleAddFilterSheet(true) + }) + + const handleCloseAddFilterSheet = useFunk(() => { + toggleAddFilterSheet(false) + }) + + const handleClickAddFilter = useFunk( + ({ currentTarget }: MouseEvent) => { + toggleAddFilterSheet(false) + + if (!activeLayerPath) return + + const filterId = currentTarget.dataset.filterId! + const filter = EditorSelector.getFilterInstance(getStore, filterId) + if (!filter) return + + execute( + EditorOps.runCommand, + new PapCommands.Layer.AddFilter({ + pathToTargetLayer: activeLayerPath, + filter: PapDOM.Filter.create({ + filterId, + settings: filter.initialConfig, + }), + }) + ) + } + ) + + const handleCloseFilterSheet = useFunk(() => { + onClose() + }) + + useClickAway(rootRef, () => { + onClose() + }) + + return ( + + {t('filter')} + + } + fill + backdrop={false} + // data-ignore-open-layer-menu + > +
+
theme.exactColors.blackFade30}; + `} + > + +
+
+ + {t('filter')} + + {layer.name === '' + ? `<${t(`layerType.${layer.layerType}`)}>` + : layer.name} +
+
+ {t(`compositeModes.${layer.compositeMode}`)} + {Math.round(layer.opacity)}% +
+
+
+ +
+ + entry.uid)} + strategy={verticalListSortingStrategy} + > + {layer.filters.map((filter) => ( + + ))} + + +
+ +
+ + フィルターを追加 +
+
+ + + +
+ + {registeredFilters.map((filter) => ( + + {t(`filters.${filter.id}`)} + + ))} + +
+
+
+
+ ) +}) + +const SortableFilterItem = ({ + layer, + filter, +}: { + layer: PapDOM.LayerTypes + filter: PapDOM.Filter +}) => { + const { t } = useTranslation('app') + + const { execute } = useFleur() + const { activeLayerPath, selectedFilterIds } = useStore((get) => ({ + activeLayerPath: EditorSelector.activeLayerPath(get), + selectedFilterIds: get(EditorStore).state.selectedFilterIds, + })) + const selected = selectedFilterIds[filter.uid] + + const { attributes, listeners, setNodeRef, transform, transition } = + useSortable({ id: filter.uid }) + + const handleClick = useFunk((e: MouseEvent) => { + if (DOMUtils.closestOrSelf(e.target, '[data-ignore-click]')) return + + execute(EditorOps.setSelectedFilterIds, { [filter.uid]: true }) + }) + + const handleToggleVisibility = useFunk(() => { + execute(EditorOps.updateLayer, [layer.uid], (layer) => { + const targetFilter = layer.filters.find((f) => f.uid === filter.uid) + if (!targetFilter) return + + targetFilter.visible = !targetFilter.visible + }) + + execute(EditorOps.rerenderCanvas) + }) + + const handleClickRemoveFilter = useFunk(() => { + if (!activeLayerPath) return + + execute( + EditorOps.runCommand, + new PapCommands.Layer.RemoveFilter({ + pathToTargetLayer: activeLayerPath, + filterUid: filter.uid, + }) + ) + }) + + return ( +
+
+ {filter.visible ? ( + + ) : ( + + )} +
+ +
+ {t(`filters.${filter.filterId}`)} +
+ +
+ +
+ +
+ +
+ + {selected && ( +
+ +
+ )} +
+ ) +} + +const ActiveLayerPane = memo(function ActiveLayerPane() { + const { t } = useTranslation('app') + + const { + state: { activeLayer, layerName }, + handleChangeLayerName, + handleChangeCompositeMode, + handleChangeOpacity, + handleClickRemoveLayer, + } = useActiveLayerPane() + + return ( +
div { + padding: 2px 0; + } + `} + > +
+ ` + : '<未選択>' + } + value={layerName} + onChange={handleChangeLayerName} + /> +
+ +
+
+
+ {t('blend')} + + +
+
+ {t('opacity')} +
+ +
+
+
+ ) +}) + +const layerFloatSlice = createSlice( + { + actions: {}, + }, + () => ({ + sortingTargetIndex: null as number | null, + }) +) diff --git a/pkgs/web/src/features/PaintV2/containers/MainActions/MainActions.tsx b/pkgs/web/src/features/PaintV2/containers/MainActions/MainActions.tsx new file mode 100644 index 00000000..ff1d29fe --- /dev/null +++ b/pkgs/web/src/features/PaintV2/containers/MainActions/MainActions.tsx @@ -0,0 +1,1798 @@ +import { + ChangeEvent, + KeyboardEvent, + memo, + MouseEvent, + MutableRefObject, + useEffect, + useRef, + useState, +} from 'react' +import { useStore } from '@fleur/react' +import { + ChromePicker, + Color, + ColorChangeHandler, + CustomPicker, + RGBColor, +} from 'react-color' +import { Alpha, Hue, Saturation } from 'react-color/lib/components/common' +import { + rgba, + readableColor, + rgb, + parseToRgb, + rgbToColorString, +} from 'polished' +import { PapCommands, PapValueTypes } from '@paplico/core' +import { useTranslation } from 'next-i18next' +import { useClickAway, useToggle } from 'react-use' +import { + Brush, + Close, + Eraser, + Pencil, + Stack, + Sip, +} from '@styled-icons/remix-line' +import { Cursor, Menu } from '@styled-icons/remix-fill' +import { Cursor as CursorLine } from '@styled-icons/remix-line' +import styled, { css, useTheme } from 'styled-components' +import { Portal } from '🙌/components/Portal' +import { FloatMenu, FloatMenuArrow } from '🙌/components/FloatMenu' +import { LayerFloatMenu } from './LayerFloatMenu' +import { useDrag } from 'react-use-gesture' +import { DOMUtils } from '🙌/utils/dom' +import { pick } from '🙌/utils/object' +import { EditorOps, EditorSelector, EditorStore } from '🙌/domains/EditorStable' +import { letDownload, useFunk } from '@hanakla/arma' + +import { + arrow, + autoPlacement, + autoUpdate, + flip, + offset, + shift, + useFloating, +} from '@floating-ui/react-dom' +import { Tab, TabBar } from '🙌/components/TabBar' +import { + useAutoUpdateFloating, + useBufferedState, + useDelayedLeave, + useFleur, +} from '🙌/utils/hooks' +import { + colorStopsToCssGradient, + normalRGBAToRGBA, + normalRgbToRgbArray, +} from '../../helpers' +import { GradientSlider } from '🙌/components/GradientSlider' +import { tm } from '🙌/utils/theme' +import { deepClone } from '🙌/utils/clone' +import { PapWebMath } from '🙌/utils/PapWebMath' +import { media } from '🙌/utils/responsive' +import { centering, checkerBoard } from '🙌/utils/mixins' +import { + ActionSheet, + ActionSheetItem, + ActionSheetItemGroup, +} from '🙌/components/ActionSheet' +import { exportProject } from '🙌/domains/EditorStable/exportProject' +import { NotifyOps } from '🙌/domains/Notify' +import { RangeInput } from '🙌/components/RangeInput' +import { useTransactionCommand, useVectorObjectWatch } from '../../hooks' +import { Tooltip2 } from '🙌/components/Tooltip2' +import { the } from '🙌/utils/anyOf' +import { BrushPresets } from '../BrushPresets' +import { DragDots } from '🙌/components/icons/DragDots' +import { TextInput } from '../../../../components/TextInput' +import { usePaplicoEngine } from '../../contexts/engine' + +export const MainActions = memo(function MainActions() { + const theme = useTheme() + // const isNarrowMedia = useMedia(`(max-width: ${narrow})`) + const engine = usePaplicoEngine() + + const { execute } = useFleur() + const { + displayingBrushSetting, + currentVectorBrush, + currentVectorFill, + currentTool, + activeLayer, + vectorColorTarget, + } = useStore((get) => ({ + displayingBrushSetting: EditorSelector.displayingBrushSetting(get), + currentVectorBrush: EditorSelector.currentVectorBrush(get), + currentVectorFill: EditorSelector.currentVectorFill(get), + currentTool: get(EditorStore).state.currentTool, + activeLayer: EditorSelector.activeLayer(get), + vectorColorTarget: EditorSelector.vectorColorTarget(get), + })) + + const [color, setColor] = useBufferedState( + displayingBrushSetting?.color + ? { + r: Math.round(displayingBrushSetting.color.r * 255), + g: Math.round(displayingBrushSetting.color.g * 255), + b: Math.round(displayingBrushSetting.color.b * 255), + } + : { r: 30, g: 30, b: 30 } + ) + const [appMenuOpened, toggleAppMenuOpened] = useToggle(false) + const [pickerOpened, toggleBrushColorPicker] = useToggle(false) + const [brushOpened, toggleBrush] = useToggle(false) + const [layersOpened, toggleLayers] = useToggle(false) + const [vectorColorOpened, toggleVectorColorOpened] = useToggle(false) + + const [brushSize, setBrushSize] = useBufferedState( + displayingBrushSetting?.size ?? 20 + ) + const [brushOpacity, setBrushOpacity] = useBufferedState( + displayingBrushSetting?.opacity ?? 1 + ) + + const handleCloseAppMenu = useFunk(() => { + toggleAppMenuOpened(false) + }) + + const handleChangeColor: ColorChangeHandler = useFunk((color) => { + setColor(color.rgb) + }) + + const handleChangeCompleteColor: ColorChangeHandler = useFunk((color) => { + setColor(color.rgb) + + engine.strokeSetting = { + ...engine.strokeSetting!, + color: { + r: color.rgb.r / 255, + g: color.rgb.g / 255, + b: color.rgb.b / 255, + }, + opacity: color.rgb.a ?? 1, + } + + execute(EditorOps.setBrushSetting, { + color: { + r: color.rgb.r / 255, + g: color.rgb.g / 255, + b: color.rgb.b / 255, + }, + }) + }) + + const handleChangeToCursorMode = useFunk(() => { + execute( + EditorOps.setTool, + currentTool === 'cursor' && activeLayer?.layerType === 'vector' + ? 'point-cursor' + : 'cursor' + ) + }) + + const handleChangeToShapePenMode = useFunk(() => { + execute(EditorOps.setTool, 'shape-pen') + }) + + const handleChangeToPencilMode = useFunk(() => { + if (currentTool === 'draw') { + brushesFl.update() + toggleBrush(true) + return + } + + execute(EditorOps.setTool, 'draw') + }) + + const handleChangeToDropperMode = useFunk(() => { + execute(EditorOps.setTool, 'dropper') + }) + + const handleChangeToEraceMode = useFunk(() => { + execute(EditorOps.setTool, 'erase') + }) + + const handleClickColor = useFunk((e: MouseEvent) => { + if ( + DOMUtils.isChildren( + e.target, + colorPickerFl.refs.reference.current as Element + ) + ) + return + + toggleBrushColorPicker() + }) + + const handleClickLayerIcon = useFunk((e: MouseEvent) => { + if ( + DOMUtils.closestOrSelf( + e.target, + '[data-ignore-click],[data-dont-close-layer-float]' + ) + ) + return + layersFl.update() + toggleLayers() + }) + + const handleClickVectorStrokeColor = useFunk( + (e: MouseEvent) => { + if (DOMUtils.isChildren(e.target, e.currentTarget)) return + + if (!vectorColorOpened) { + execute(EditorOps.setVectorColorTarget, 'stroke') + } + + toggleVectorColorOpened() + } + ) + + const handleClickVectorFillColor = useFunk( + (e: MouseEvent) => { + if (DOMUtils.isChildren(e.target, e.currentTarget)) return + + if (!vectorColorOpened) { + execute(EditorOps.setVectorColorTarget, 'fill') + } + + toggleVectorColorOpened() + } + ) + + const handleCloseVectorColorPicker = useFunk(() => { + toggleVectorColorOpened(false) + }) + + const appMenuOpenerRef = useRef(null) + + const brushesArrowRef = useRef(null) + const brushesFl = useFloating({ + placement: 'top', + strategy: 'fixed', + middleware: [ + offset(12), + shift({ padding: 8 }), + flip(), + arrow({ element: brushesArrowRef }), + ], + }) + + const colorPickerArrowRef = useRef(null) + const colorPickerFl = useFloating({ + placement: 'top', + strategy: 'absolute', + middleware: [ + offset(12), + shift({ padding: 8 }), + flip(), + arrow({ element: colorPickerArrowRef }), + ], + }) + + const layersArrowRef = useRef(null) + const layersFl = useFloating({ + strategy: 'absolute', + placement: 'top', + middleware: [ + offset(12), + shift({ padding: 8 }), + flip(), + // autoPlacement({ alignment: 'start', allowedPlacements: ['top'] }), + arrow({ element: layersArrowRef }), + ], + }) + + const vectorColorRootRef = useRef(null) + + useAutoUpdateFloating(brushesFl) + useAutoUpdateFloating(colorPickerFl) + useAutoUpdateFloating(layersFl) + + useClickAway(appMenuOpenerRef, (e) => { + if (DOMUtils.isChildren(e.target, e.currentTarget)) return + toggleAppMenuOpened(false) + }) + + useClickAway(colorPickerFl.refs.floating, (e) => { + if ( + DOMUtils.childrenOrSelf( + e.target, + colorPickerFl.refs.reference.current as HTMLElement | null + ) + ) + return + + if (pickerOpened) toggleBrushColorPicker(false) + }) + + useClickAway(brushesFl.refs.floating, (e) => { + if ( + DOMUtils.childrenOrSelf( + e.target, + brushesFl.refs.reference.current as HTMLElement | null + ) + ) + return + if (brushOpened) toggleBrush(false) + }) + + useClickAway( + layersFl.refs.reference as MutableRefObject, + (e) => { + if (DOMUtils.closestOrSelf(e.target, '[data-dont-close-layer-float]')) + return + + toggleLayers(false) + } + ) + + useDelayedLeave(colorPickerFl.refs.floating, 1300, () => { + toggleBrushColorPicker(false) + }) + + // useDelayedLeave(vectorColorRootRef, 2000, () => { + // toggleVectorFillColorOpened(false) + // }) + + const bindBrushSizeDrag = useDrag(({ delta, first, last, event }) => { + if (first) { + execute(EditorOps.setBrushSizeChanging, true) + ;(event.currentTarget as HTMLDivElement).requestPointerLock?.() + } + + if (last) { + execute(EditorOps.setBrushSizeChanging, false) + document.exitPointerLock?.() + } + + const changed = delta[0] < 0 ? delta[0] * 1.2 : delta[0] * 0.4 + + setBrushSize((size) => { + const next = Math.max(0, size + changed) + // execute(EditorOps.setBrushSetting, { size: next }) + engine.strokeSetting = { ...engine.strokeSetting!, size: next } + return next + }) + }) + + const bindBrushOpacityDrag = useDrag(({ delta, first, last, event }) => { + if (first) { + ;(event.currentTarget as HTMLDivElement).requestPointerLock?.() + } + + if (last) { + document.exitPointerLock?.() + } + + const changed = delta[0] * 0.003 + setBrushOpacity((opacity) => { + const next = Math.min(Math.max(0, opacity + changed), 1) + execute(EditorOps.setBrushSetting, { opacity: next }) + return next + }) + }) + + const [menuPos, setMenuPos] = useState<{ x: number; y: number }>({ + x: 0, + y: 0, + }) + + const bindMenuPosDrag = useDrag( + ({ delta }) => { + setMenuPos((pos) => ({ x: pos.x + delta[0], y: pos.y + delta[1] })) + brushesFl.update() + colorPickerFl.update() + layersFl.update() + }, + { + threshold: 2, + } + ) + + return ( +
theme.color.surface3}; + border-radius: 100px; + color: ${({ theme }) => theme.color.text1}; + border: 1px solid ${({ theme }) => theme.color.surface7}; + white-space: nowrap; + touch-action: manipulation; + + ${media.narrow` + border: none; + border-top: 1px solid #aaa; + border-radius: 0; + padding-bottom: 24px; + padding-bottom: max(env(safe-area-inset-bottom, 24px), 24px); + `} + `} + style={{ + transform: `translate(${menuPos.x}px, ${menuPos.y}px)`, + }} + data-ignore-canvas-wheel + > + +
+
+ + + +
+ +
theme.color.surface8}; + border-radius: 64px; + overflow: hidden; + `} + {...bindBrushSizeDrag()} + > +
+
+ {(Math.round(brushSize * 10) / 10).toString(10)} +
+
+
theme.color.surface8}; + border-radius: 64px; + overflow: hidden; + + background: linear-gradient( + 45deg, + rgba(0, 0, 0, 0.2) 25%, + transparent 25%, + transparent 75%, + rgba(0, 0, 0, 0.2) 75% + ), + linear-gradient( + 45deg, + rgba(0, 0, 0, 0.2) 25%, + transparent 25%, + transparent 75%, + rgba(0, 0, 0, 0.2) 75% + ); + background-size: 4px 4px; + background-position: 0 0, 2px 2px; ; + `} + {...bindBrushOpacityDrag()} + > +
+ {Math.round(brushOpacity * 100)} + % +
+
+
+ +
+ {activeLayer?.layerType === 'raster' ? ( +
+
+ + + + + +
+
+ ) : ( +
+ {/* VectorColor */} +
+ {!currentVectorBrush && } +
+ +
+ {!currentVectorFill && } +
+ + +
+ )} +
+ +
theme.color.surface7}; + overflow: hidden; + + div:first-of-type { + padding-left: 8px; + } + + div:last-of-type { + padding-right: 8px; + } + `} + > +
+ {currentTool === 'point-cursor' ? ( + + ) : ( + + )} +
+ + {activeLayer?.layerType === 'vector' && ( +
+ +
+ )} + + {(activeLayer?.layerType === 'raster' || + activeLayer?.layerType === 'vector') && ( +
+ + +
+ )} + + {(activeLayer?.layerType === 'raster' || + activeLayer?.layerType === 'vector') && ( +
+ +
+ )} + + {activeLayer?.layerType === 'raster' && ( +
+ +
+ )} + + + + + + + +
+ + {/* {isNarrowMedia && ( */} +
+ <> + {layersOpened ? ( + + ) : ( + + )} + + +
+ + + + +
+
+ {/* )} */} +
+ ) +}) + +const AppMenu = memo(function AppMenu({ + opened, + onClose, +}: { + opened: boolean + onClose: () => void +}) { + const { t } = useTranslation('app') + const { execute, getStore } = useFleur() + + const { engine, currentTheme, currentDocument } = useStore((get) => ({ + engine: get(EditorStore).state.engine, + currentTheme: EditorSelector.currentTheme(get), + currentDocument: EditorSelector.currentDocument(get), + })) + + const fl = useFloating({ + placement: 'top', + middleware: [shift({ padding: 8 })], + }) + + const handleClickBackToHome = useFunk(() => { + execute(EditorOps.disposeEngineAndSession, { + withSave: true, + switchToHome: true, + }) + }) + + const handleClickChangeTheme = useFunk(() => { + execute(EditorOps.setTheme, currentTheme === 'dark' ? 'light' : 'dark') + onClose() + }) + + const handleClickSave = useFunk(() => { + if (!currentDocument) return + + execute(EditorOps.saveCurrentDocumentToIdb, { notify: true }) + onClose() + }) + + const handleClickSaveAs = useFunk(() => { + if (!currentDocument) return + + execute(NotifyOps.create, { + area: 'loadingLock', + lock: true, + messageKey: 'appMenu.saving', + timeout: 0, + }) + + const { blob } = exportProject(currentDocument, getStore) + const url = URL.createObjectURL(blob) + letDownload( + url, + !currentDocument.title + ? `${t('untitled')}.paplc` + : `${currentDocument.title}.paplc` + ) + + onClose() + + window.setTimeout(() => { + execute(NotifyOps.create, { + area: 'loadingLock', + lock: false, + messageKey: 'appMenu.saved', + timeout: 0, + }) + }, /* フィードバックが早すぎると何が起きたかわからないので */ 1000) + + window.setTimeout(() => URL.revokeObjectURL(url), 3000) + }) + + const handleClickExportAs = useFunk(async () => { + if (!currentDocument || !engine) return + + execute(NotifyOps.create, { + area: 'loadingLock', + lock: true, + messageKey: 'appMenu.exporting', + timeout: 0, + }) + + const exporter = await engine.renderAndExport(currentDocument) + const blob = await exporter.export('image/png') + const url = URL.createObjectURL(blob) + letDownload( + url, + !currentDocument.title + ? `${t('untitled')}.png` + : `${currentDocument.title}.png` + ) + + onClose() + + window.setTimeout(() => { + execute(NotifyOps.create, { + area: 'loadingLock', + lock: false, + messageKey: 'appMenu.exported', + timeout: 0, + }) + }, /* フィードバックが早すぎると何が起きたかわからないので */ 1000) + + window.setTimeout(() => { + URL.revokeObjectURL(url) + }, 3000) + }) + + const handleClickReload = useFunk(async () => { + execute(NotifyOps.create, { + area: 'loadingLock', + lock: true, + messageKey: 'リロード中', + timeout: 0, + }) + + window.location.reload() + }) + + useAutoUpdateFloating(fl) + + return ( +
+ + theme.colors.blue50}; + `} + opened={opened} + onClose={onClose} + fill={false} + > + + + テーマ切り替え + + + + + 保存 + + アイテムをダウンロードして保存 + + + 画像に書き出し + + + + + + 保存してホームへ戻る + + + + {process.env.NODE_ENV === 'development' && ( + + + リロード + + + )} + + + キャンセル + + + +
+ ) +}) + +const VectorColorPicker = memo(function VectorColorPicker({ + opened, + target, + onClose, +}: { + opened: boolean + target: 'fill' | 'stroke' + // color: Color + // onChange: ColorChangeHandler + // onChangeComplete: ColorChangeHandler + onClose: () => void +}) { + const { t } = useTranslation('app') + + const { execute } = useFleur() + const { + currentVectorBrush, + currentVectorFill, + defaultVectorBrush, + activeObject, + activeLayerPath, + } = useStore((get) => ({ + currentVectorBrush: EditorSelector.currentVectorBrush(get), + currentVectorFill: EditorSelector.currentVectorFill(get), + defaultVectorBrush: EditorSelector.defaultVectorBrush(get), + activeObject: EditorSelector.activeObject(get), + activeLayerPath: EditorSelector.activeLayerPath(get), + })) + + // const targetSurface = target === 'fill' ? activeObject.fill?.type : activeObject!.brush + + const [currentTab, setTab] = useBufferedState<'solid' | 'gradient' | 'none'>( + // prettier-ignore + (target === 'fill' ? ( + activeObject?.fill?.type === 'fill' ? 'solid' + : activeObject?.fill?.type === 'linear-gradient' ? 'gradient' + : null + ) + : target === 'stroke' ? 'solid' : null) ?? 'solid' + ) + const [gradientIndices, setGradientIndices] = useState([]) + const [currentColor, setFillColor] = useBufferedState(() => + currentVectorFill?.type === 'fill' + ? normalRGBAToRGBA(currentVectorFill.color) + : { + r: 0, + g: 0, + b: 0, + } + ) + + const [gradientLength, setGradientLength] = useBufferedState(() => { + if (activeObject?.fill?.type !== 'linear-gradient') return 0 + + const fill = activeObject?.fill + return PapWebMath.distanceOfPoint(fill.start, fill.end) + }) + + const [gradientAngle, setGradientAngle] = useBufferedState(() => { + if (activeObject?.fill?.type !== 'linear-gradient') return 0 + + const fill = activeObject?.fill + const rad = PapWebMath.angleOfPoints(fill.start, fill.end) + return PapWebMath.normalizeDegree(PapWebMath.radToDeg(rad) + 180) + }) + + const arrowRef = useRef(null) + const fl = useFloating({ + placement: 'top', + middleware: [ + shift(), + offset(8), + // autoPlacement({ alignment: 'start' }), + arrow({ element: arrowRef }), + ], + }) + + const [showGradLen, toggleShowGradLen] = useToggle(false) + const [showGradAngle, toggleShowGradAngle] = useToggle(false) + + const cmdTransaction = useTransactionCommand() + + const handleClickTab = useFunk((nextTab) => { + setTab(nextTab) + + if (!activeLayerPath || !activeObject) return + + if (target === 'fill') { + if (currentTab === 'solid' && nextTab === 'gradient') { + execute(EditorOps.updateActiveObject, (o) => { + const fill = o.fill + if (fill?.type !== 'fill') return + + o.fill = { + type: 'linear-gradient', + colorStops: [{ position: 0, color: { ...fill.color, a: 1 } }], + opacity: 1, + start: { x: -10, y: -10 }, + end: { x: 10, y: 10 }, + } + }) + } + + if (currentTab === 'gradient' && nextTab === 'solid') { + // Swap gradient and solid in object fill + execute(EditorOps.updateActiveObject, (o) => { + const fill = o.fill + if (fill?.type !== 'linear-gradient') return + + o.fill = { + type: 'fill', + color: pick(fill.colorStops[0].color, ['r', 'g', 'b']), + opacity: 1, + } + }) + } + + if (nextTab === 'none') { + if (!activeLayerPath || !activeObject) return + + execute( + EditorOps.runCommand, + new PapCommands.VectorLayer.PatchObjectAttr({ + pathToTargetLayer: activeLayerPath, + objectUid: activeObject.uid, + patcher: (attrs) => { + attrs.fill = null + }, + }) + ) + } + } + + if (target === 'stroke') { + if (nextTab === 'none') { + execute( + EditorOps.runCommand, + new PapCommands.VectorLayer.PatchObjectAttr({ + pathToTargetLayer: activeLayerPath, + objectUid: activeObject.uid, + patcher: (attrs) => { + attrs.brush = null + }, + }) + ) + } + + if (nextTab === 'solid') { + execute( + EditorOps.runCommand, + new PapCommands.VectorLayer.PatchObjectAttr({ + pathToTargetLayer: activeLayerPath, + objectUid: activeObject.uid, + patcher: (attrs) => { + attrs.brush = { + ...defaultVectorBrush, + ...attrs.brush, + color: toRationalColor(currentColor), + opacity: currentColor.a != null ? currentColor.a * 100 : 100, + } + }, + }) + ) + } + + if (currentTab === 'solid' && nextTab === 'gradient') { + // Unsupported currently + } + + if (currentTab === 'gradient' && nextTab === 'solid') { + // Unsupported currently + // execute(EditorOps.updateActiveObject, (o) => { + // const stroke = + // if (stroke?.type !== 'linear-gradient') return + // o.stroke = { + // type: 'fill', + // color: pick(stroke.colorStops[0].color, ['r', 'g', 'b']), + // opacity: 1, + // } + // }) + } + } + + // fl.update() + }) + + const handleChangeColor: ColorChangeHandler = useFunk(({ rgb }) => { + setFillColor(rgb) + }) + + const handleChangeCompleteColor: ColorChangeHandler = useFunk(({ rgb }) => { + setFillColor(rgb) + + if (!activeLayerPath || !activeObject) return + + if (target === 'fill') { + execute(EditorOps.updateActiveObject, (obj) => { + if (!obj.fill) { + obj.fill = { + type: 'fill', + opacity: rgb.a ?? 1, + color: toRationalColor(rgb), + } + } + + if (obj.fill.type === 'fill') { + obj.fill = { ...obj.fill, color: toRationalColor(rgb) } + } else if (obj.fill.type === 'linear-gradient') { + const nextColorStops = deepClone(obj.fill.colorStops) + gradientIndices.forEach((i) => { + nextColorStops[i].color = toRationalRgba(rgb) + }) + + obj.fill = { + ...obj.fill, + colorStops: nextColorStops, + } + } + }) + } else { + // Stroke + + execute( + EditorOps.runCommand, + new PapCommands.VectorLayer.PatchObjectAttr({ + pathToTargetLayer: activeLayerPath, + objectUid: activeObject.uid, + patcher: (attrs) => { + attrs.brush = { + ...defaultVectorBrush, + ...attrs.brush, + color: toRationalColor(rgb), + opacity: rgb.a ?? 1, + } + }, + }) + ) + } + }) + + const handleChangeGradient = useFunk( + (colorStops: PapValueTypes.ColorStop[]) => { + execute(EditorOps.updateActiveObject, (obj) => { + if (obj.fill?.type !== 'linear-gradient') return + + obj.fill = { + ...obj.fill, + colorStops, + } + }) + } + ) + + const handleChangeGradientIndices = useFunk((indices: number[]) => { + if (activeObject?.fill?.type !== 'linear-gradient') return + + setGradientIndices(indices) + + const { color } = activeObject.fill.colorStops[indices[0]] + setFillColor(normalRGBAToRGBA(color)) + }) + + const handleChangeGradientLength = useFunk( + ({ currentTarget }: ChangeEvent) => { + if (!activeLayerPath || activeObject?.fill?.type !== 'linear-gradient') + return + + setGradientLength(currentTarget.valueAsNumber) + toggleShowGradLen(true) + cmdTransaction.startIfNotStarted() + + const length = currentTarget.valueAsNumber / 2 + const fill = deepClone({ ...activeObject.fill }) + + const rad = PapWebMath.angleOfPoints(fill.start, fill.end) + const pointS = PapWebMath.pointByAngleAndDistance({ + angle: rad, + distance: length, + base: { x: 0, y: 0 }, + }) + + const pointE = PapWebMath.pointByAngleAndDistance({ + angle: PapWebMath.degToRad( + PapWebMath.deg(PapWebMath.radToDeg(rad) + 180) + ), + distance: length, + base: { x: 0, y: 0 }, + }) + + fill.start = pointS + fill.end = pointE + + if (target === 'fill') { + cmdTransaction.doAndAdd( + new PapCommands.VectorLayer.PatchObjectAttr({ + pathToTargetLayer: activeLayerPath, + objectUid: activeObject.uid, + patcher: (attrs) => { + attrs.fill = fill + }, + }) + ) + } + } + ) + + const handleCompleteGradientLength = useFunk(() => { + toggleShowGradLen(false) + cmdTransaction.commit() + }) + + const handleChangeGradientAngle = useFunk( + ({ currentTarget }: ChangeEvent) => { + if (!activeLayerPath || activeObject?.fill?.type !== 'linear-gradient') + return + + toggleShowGradAngle(true) + setGradientAngle(currentTarget.valueAsNumber) + cmdTransaction.startIfNotStarted() + + const nextFill = deepClone({ ...activeObject.fill }) + const rad = PapWebMath.degToRad( + PapWebMath.deg(currentTarget.valueAsNumber) + ) + const length = PapWebMath.distanceOfPoint(nextFill.start, nextFill.end) + + const pointS = PapWebMath.pointByAngleAndDistance({ + angle: rad, + distance: length / 2, + base: { x: 0, y: 0 }, + }) + + const pointE = PapWebMath.pointByAngleAndDistance({ + angle: PapWebMath.degToRad( + PapWebMath.deg(PapWebMath.radToDeg(rad) + 180) + ), + distance: length / 2, + base: { x: 0, y: 0 }, + }) + + nextFill.start = pointS + nextFill.end = pointE + + // console.log({ + // expect: currentTarget.valueAsNumber, + // act: SilkWebMath.radToDeg(SilkWebMath.angleOfPoints(pointS, pointE)), + // }) + + if (target === 'fill') { + cmdTransaction.doAndAdd( + new PapCommands.VectorLayer.PatchObjectAttr({ + pathToTargetLayer: activeLayerPath, + objectUid: activeObject.uid, + patcher: (attrs) => { + attrs.fill = nextFill + }, + }) + ) + } + } + ) + + const handleCompleteGradientAngle = useFunk(() => { + toggleShowGradAngle(false) + cmdTransaction.commit() + }) + + useClickAway(fl.refs.floating, onClose) + + useEffect(() => { + fl.reference(fl.refs.floating.current!.parentElement) + + if (!fl.refs.floating.current || !fl.refs.reference.current) return + autoUpdate(fl.refs.reference.current, fl.refs.floating.current, fl.update) + }, [fl.refs.floating.current]) + + useVectorObjectWatch(activeObject) + + return ( +
theme.colors.surface6}); + ${tm((o) => [o.bg.surface2])} + `} + style={{ + position: fl.strategy, + left: fl.x ?? 0, + top: fl.y ?? 0, + ...(opened + ? { opacity: 1, pointerEvents: 'all' } + : { opacity: 0, pointerEvents: 'none' }), + }} + data-ignore-click + > +
+
+ {currentTab === 'gradient' && ( + <> + + + + + + )} + + +
+ {/* )} */} + {/* {target === 'stroke' && currentVectorBrush && ( + + )} */} + + + + {t('vectorColorPicker.modes.none')} + + + {t('vectorColorPicker.modes.solid')} + + {target === 'fill' && ( + + {t('vectorColorPicker.modes.gradient')} + + )} + + +
+ `${theme.color.surface2} transparent transparent transparent`}; + `} + style={{ + left: fl.middlewareData.arrow?.x ?? 0, + // top: fl.middlewareData.arrow?.y ?? 0, + }} + /> +
+
+ ) +}) + +const BrushColorPicker = memo( + CustomPicker<{ disableAlpha?: boolean }>(function BrushColorPicker(props) { + return ( +
theme.colors.surface6}); + ${tm((o) => [o.bg.surface2])} + `} + data-ignore-click + > + +
+ ) + }) +) + +const CustomColorPicker = CustomPicker<{ disableAlpha?: boolean }>( + function CustomColorPicker(props) { + const [stringValue, setStringValue] = useBufferedState( + rgbToColorString({ + red: props.rgb?.r ?? 0, + green: props.rgb?.g ?? 0, + blue: props.rgb?.b ?? 0, + }).slice(1) + ) + + const handleChangeColor = useFunk( + ({ currentTarget }: ChangeEvent) => { + const color = currentTarget.value.replace(/#/g, '') + console.log(color) + setStringValue(color) + } + ) + + const handleCompleteColor = useFunk( + ({ currentTarget }: KeyboardEvent) => { + const color = currentTarget.value.replace(/#/g, '') + setStringValue(color) + props.onChange?.(`#${color}`) + } + ) + + return ( +
+ {/* */} +
+ +
+
+ +
+ + {!props.disableAlpha && ( +
+ +
+ )} + +
+ # + +
+ + {/* */} +
+ ) + } +) + +const HuePointer = styled.div.withConfig({ + shouldForwardProp: (prop, valid) => prop !== 'color' && valid(prop), +})` + width: 10px; + height: 10px; + border: 1px solid #fff; + box-shadow: inset 0 0 0 0.5px #000, 0 0 0 0.5px #000; + border-radius: 100px; + transform: translate(-50%, -50%); +` + +const Pointer = styled.div.withConfig({ + shouldForwardProp: (prop, valid) => prop !== 'color' && valid(prop), +})` + width: 12px; + height: 12px; + border-radius: 100px; + background-color: ${({ theme }) => theme.exactColors.white60}; + box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15); + transform: translateY(-2px); +` + +const NoColorSlash = styled.div` + position: absolute; + top: 50%; + left: 50%; + width: 180%; + height: 5px; + background-color: hsl(0, 75%, 60%); + border: 1px solid hsl(0, 0%, 85%); + transform: translate(-50%, -50%) rotate(-45deg); + border-radius: 4px; + pointer-events: none; +` + +const toRationalColor = ({ r, g, b }: { r: number; g: number; b: number }) => ({ + r: r / 255, + g: g / 255, + b: b / 255, +}) + +const toRationalRgba = (color: { + r: number + g: number + b: number + a?: number +}) => { + return { ...toRationalColor(color), a: color.a ?? 1 } +} diff --git a/pkgs/web/src/features/PaintV2/contexts/engine.ts b/pkgs/web/src/features/PaintV2/contexts/engine.ts new file mode 100644 index 00000000..54c8b72e --- /dev/null +++ b/pkgs/web/src/features/PaintV2/contexts/engine.ts @@ -0,0 +1,6 @@ +import { Paplico } from '@paplico/core-new' +import { createContext, useContext } from 'react' + +export const PaplicoEngineContext = createContext(null!) +export const PaplicoEngineProvider = PaplicoEngineContext.Provider +export const usePaplicoEngine = () => useContext(PaplicoEngineContext) diff --git a/pkgs/web/src/features/PaintV2/devToolsAPI.ts b/pkgs/web/src/features/PaintV2/devToolsAPI.ts new file mode 100644 index 00000000..0a1271db --- /dev/null +++ b/pkgs/web/src/features/PaintV2/devToolsAPI.ts @@ -0,0 +1,24 @@ +import { StoreGetter } from '@fleur/fleur' +import { EditorSelector } from '../../domains/EditorStable' + +export const bindDevToolAPI = (getStore: StoreGetter) => { + const api = {} + + Object.defineProperties(api, { + activeLayer: { + enumerable: true, + configurable: false, + get() { + return EditorSelector.activeLayer(getStore) + }, + }, + activeObject: { + enumerable: true, + configurable: false, + get() { + return EditorSelector.activeObject(getStore) + }, + }, + }) + ;(window as any).paplico = api +} diff --git a/pkgs/web/src/features/PaintV2/dndkit-helper.ts b/pkgs/web/src/features/PaintV2/dndkit-helper.ts new file mode 100644 index 00000000..37c40ca0 --- /dev/null +++ b/pkgs/web/src/features/PaintV2/dndkit-helper.ts @@ -0,0 +1,18 @@ +import { PointerSensor } from '@dnd-kit/core' +import { PointerEvent } from 'react' +import { DOMUtils } from '../../utils/dom' + +export class DisableOnInputPointerSensor extends PointerSensor { + static activators = [ + { + eventName: 'onPointerDown' as const, + handler: ({ nativeEvent: event }: PointerEvent) => { + console.log('pointerDown', event, event.target) + return !DOMUtils.closestOrSelf( + event.target, + 'input, textarea, selectbox' + ) + }, + }, + ] +} diff --git a/pkgs/web/src/features/PaintV2/helpers.spec.ts b/pkgs/web/src/features/PaintV2/helpers.spec.ts new file mode 100644 index 00000000..3b187060 --- /dev/null +++ b/pkgs/web/src/features/PaintV2/helpers.spec.ts @@ -0,0 +1,24 @@ +import { PapDOM } from '@paplico/core' +import { calcLayerMove, flattenLayers } from './helpers' + +describe('Paint/helpers', () => { + const structure = [ + PapDOM.VectorLayer.create({ name: 'root-1' }), + PapDOM.GroupLayer.create({ + name: 'root-2-group', + layers: [ + PapDOM.VectorLayer.create({ name: 'group-1' }), + PapDOM.VectorLayer.create({ name: 'group-2' }), + ], + }), + PapDOM.VectorLayer.create({ name: 'root-3' }), + ] as const + const flatten = flattenLayers(structure as unknown as PapDOM.LayerTypes[]) + + describe('calcLayerMove', () => { + calcLayerMove(flatten, { + active: { id: structure[1].layers[1].uid }, + target: { id: structure[1].uid }, + }) + }) +}) diff --git a/pkgs/web/src/features/PaintV2/helpers.ts b/pkgs/web/src/features/PaintV2/helpers.ts new file mode 100644 index 00000000..e918302a --- /dev/null +++ b/pkgs/web/src/features/PaintV2/helpers.ts @@ -0,0 +1,308 @@ +import { Active, Over } from '@dnd-kit/core' +import { rgba } from 'polished' +import { Document, Paplico } from '@paplico/core-new' +import { assign } from '🙌/utils/object' +import arrayMove from 'array-move' + +export const isEventIgnoringTarget = (target: EventTarget | null) => { + return (target as HTMLElement)?.dataset?.isPaintCanvas != null +} + +export type FlatLayerEntry = { + id: string + parentId: string | null + index: number + indexInParent: number + depth: number + parentPath: string[] +} & ( + | { + type: 'layer' + layer: PapDOM.LayerTypes + } + | { + type: 'object' + object: PapDOM.VectorObject + } +) + +export const calcLayerMove = ( + flattenLayers: FlatLayerEntry[], + { + active, + over, + }: { + active: Active + over: Over | null + } +) => { + if (!over || active.id === over.id) return + + const movedEntryIndex = flattenLayers.findIndex((l) => l.id === active.id) + const newIndexOnFlatten = flattenLayers.findIndex((l) => l.id === over.id) + + const movedEntry = flattenLayers[movedEntryIndex] + const entryAtNewIndex = flattenLayers[newIndexOnFlatten] + + let parent: FlatLayerEntry | null = + // prettier-ignore + newIndexOnFlatten === 0 ? null + : (entryAtNewIndex.type === 'layer' && entryAtNewIndex.layer.layerType === 'group') ? entryAtNewIndex + : entryAtNewIndex.parentId != null ? (flattenLayers.find(e => e.id === entryAtNewIndex.parentId) ?? null) + : null + + const entryAtPrevIndex = flattenLayers[newIndexOnFlatten - 1] + if (entryAtPrevIndex) { + console.log(active.rect.current) + if (entryAtPrevIndex.depth !== movedEntry.depth) { + parent = flattenLayers.find( + (entry) => entry.id === entryAtPrevIndex.parentId + )! + } + } + + if (movedEntry.type === 'layer') { + if ( + parent != null && // not in document root + (parent.type !== 'layer' || parent?.layer.layerType !== 'group') // out of group + ) + return null + + return { + type: 'layer' as const, + sourcePath: [...movedEntry.parentPath, movedEntry.layer.uid], + targetParentPath: + parent?.type === 'layer' + ? [...parent.parentPath, parent.layer.uid] + : [], + targetIndex: entryAtNewIndex.indexInParent, + } + } else if (movedEntry.type === 'object') { + if (parent?.type !== 'layer') return null + + return { + type: 'object' as const, + sourcePath: movedEntry.parentPath, + targetParentPath: [...parent.parentPath, parent.layer.uid], + targetIndex: entryAtNewIndex.indexInParent, + objectUid: movedEntry.object.uid, + } + } + + return null +} + +const findLastIndexFrom = ( + arr: T[], + { from }: { from: number }, + predicate: (v: T, index: number, list: T[]) => boolean +) => { + for (let idx = from; idx >= 0; idx--) { + if (predicate(arr[idx], idx, arr)) return idx + } + + return null +} + +export const flattenLayers = ( + layers: PapDOM.LayerTypes[], + filter: ( + entry: FlatLayerEntry, + idx: number, + list: FlatLayerEntry[] + ) => boolean = () => true +): FlatLayerEntry[] => { + const flatter = ( + layers: PapDOM.LayerTypes[], + parentPath: string[], + parentIndex: number | null, + entries: FlatLayerEntry[] + ) => { + layers.forEach((layer, index) => { + const entry: FlatLayerEntry = { + id: layer.uid, + parentId: parentPath.slice(-1)[0], + type: 'layer', + layer, + parentPath, + indexInParent: index, + depth: parentPath.length, + index: entries.length, + } + + entries.push(entry) + + if (layer.layerType === 'group') { + flatter( + layer.layers, + [...parentPath, layer.uid], + entries.length - 1, + entries + ) + } else if (layer.layerType === 'vector') { + const parentIdx = entries.indexOf(entry) + + entries.push( + ...layer.objects.map((o, i) => ({ + id: o.uid, + parentId: layer.uid, + indexInParent: i, + type: 'object' as const, + object: o, + parentPath: [...parentPath, layer.uid], + depth: parentPath.length + 1, + index: entries.length + 1 + i, + })) + ) + } + }) + + return entries + } + + return flatter(layers, [], null, []) + .filter(filter) + .map((entry, index) => assign(entry, { index })) + + // return layers + // .map((l, idx) => { + // const self = { + // parentPath: [], + // layer: l, + // depth: 0, + // index: idx, + // parentIdx: null, + // } + + // return l.layerType === 'group' + // ? + // [ + // self, + // ...l.layers.map((sl, subIdx) => ({ + // parentPath: [l.uid], + // layer: sl, + // depth: 1, + // index: subIdx, + // parentIdx: idx, + // })), + // ] + // l.layerType === 'vector' ? + // [ + // self, + + // ] + // : self + // }) + // .flat(2) +} + +export const generateBrushThumbnail = async ( + engine: Paplico, + brushId: string, + { + size, + brushSize, + specific, + }: { + size: { width: number; height: number } + brushSize: number + specific: Record | null + } +) => { + const ctx = document.createElement('canvas').getContext('2d')! + assign(ctx.canvas, size) + + const path = Document.createVectorPath({ + points: [ + { + x: size.width / 6, + y: size.height / 2, + in: null, + out: { x: size.width / 2, y: size.height - size.height / 5 }, + pressure: 1, + }, + { + x: size.width - size.width / 6, + y: size.height / 2, + in: { x: size.width - size.width / 2, y: size.height / 5 }, + out: null, + pressure: 1, + }, + ], + closed: false, + randomSeed: 0, + }) + + await engine.renderer.renderStroke( + ctx.canvas, + path, + { + brushId, + color: { r: 0, g: 0, b: 0 }, + opacity: 1, + size: brushSize, + specific: specific ?? {}, + }, + { rotate: 0, scale: { x: 1, y: 1 }, translate: { x: 0, y: 0 } } + ) + + return ctx.canvas.toDataURL('image/png') +} + +export const swapObjectBrushAndFill = ( + obj: Document, + { fallbackBrushId }: { fallbackBrushId: string } +) => { + const fill = obj.fill + + obj.fill = obj.brush + ? { type: 'fill', color: obj.brush.color, opacity: obj.brush.opacity } + : null + + obj.brush = fill + ? { + brushId: fallbackBrushId, + color: + fill.type === 'fill' + ? { ...fill.color } + : { ...fill.colorStops[0].color }, + opacity: fill.opacity, + size: 1, + } + : null +} + +export const normalRgbToRgbArray = (color: { + r: number + g: number + b: number +}) => { + const c = normalRGBAToRGBA(color) + return [c.r, c.g, c.b] as const +} + +export const normalRGBAToRGBA = (color: { + r: number + g: number + b: number + a?: number +}) => { + return { + r: Math.round(color.r * 255), + g: Math.round(color.g * 255), + b: Math.round(color.b * 255), + ...(color.a != null ? { a: color.a } : {}), + } +} + +export const colorStopsToCssGradient = ( + degree: number, + colorStops: PapValueTypes.ColorStop[] +): string => { + return `linear-gradient(${degree}deg, ${colorStops + .map( + ({ color, position }) => + `${rgba(...normalRgbToRgbArray(color), color.a)} ${position * 100}%` + ) + .join(', ')})` +} diff --git a/pkgs/web/src/features/PaintV2/hooks.ts b/pkgs/web/src/features/PaintV2/hooks.ts new file mode 100644 index 00000000..0acc01f9 --- /dev/null +++ b/pkgs/web/src/features/PaintV2/hooks.ts @@ -0,0 +1,316 @@ +import { useStore } from '@fleur/react' +import { letDownload, match, useFunk } from '@hanakla/arma' +import { useTranslation } from 'next-i18next' +import { + useEffect, + useRef, + useMemo, + ChangeEvent, + createContext, + useContext, + MutableRefObject, + createRef, +} from 'react' +import { usePrevious, useUpdate } from 'react-use' +import { PapDOM, PapSerializer, PapCommands } from '@paplico/core' +import { EditorOps, EditorSelector, EditorStore } from '🙌/domains/EditorStable' +import { NotifyOps } from '🙌/domains/Notify' +import { useBufferedState, useDebouncedFunk, useFleur } from '🙌/utils/hooks' +import { debounce } from '🙌/utils/func' + +export const usePaplicoExporter = () => { + const { t } = useTranslation('app') + + const { execute, getStore } = useFleur() + const { engine, currentDocument } = useStore((get) => ({ + engine: get(EditorStore).state.engine, + currentDocument: EditorSelector.currentDocument(get), + })) + + return { + exportDocument: useFunk(async () => { + if (!currentDocument) return + + const bin = PapSerializer.exportDocument( + currentDocument as PapDOM.Document + )! + const blob = new Blob([bin], { type: 'application/octet-stream' }) + const url = URL.createObjectURL(blob) + + if (typeof window.showSaveFilePicker === 'undefined') { + letDownload(url, 'test.paplc') + } else { + const handler = + getStore(EditorStore).state.currentFileHandler ?? + (await window.showSaveFilePicker({ + types: [ + { + description: 'Paplico project', + accept: { 'application/octet-stream': '.paplc' }, + }, + ], + })) + + await ( + await handler.createWritable({ keepExistingData: false }) + ).write(blob) + + execute(EditorOps.setCurrentFileHandler, handler) + } + + setTimeout(() => URL.revokeObjectURL(url), 10000) + }), + exportAs: useFunk(async (type: 'png' | 'jpeg') => { + if (!currentDocument || !engine) return + + const mime = match(type) + .when('png', 'image/png') + .when('jpeg', 'image/jpeg') + ._(new Error(`Unexpected type ${type}`)) + + const exporter = await engine.renderAndExport(currentDocument) + + const blob = await exporter.export(mime, 100) + const url = URL.createObjectURL(blob) + + letDownload( + url, + !currentDocument.title + ? `${t('untitled')}.${type}` + : `${currentDocument.title}.${type}` + ) + + execute(NotifyOps.create, { + area: 'save', + timeout: 3000, + messageKey: t('exports.exported'), + }) + + setTimeout(() => URL.revokeObjectURL(url), 10000) + }), + } +} + +export const useDocumentWatch = ( + document: PapDOM.Document | null | undefined +) => { + const rerender = useUpdate() + + // useEffect(() => { + // document?.on('layersChanged', rerender) + // return () => document?.off('layersChanged', rerender) + // }, [document?.uid, rerender]) +} + +export const useLayerWatch = (layer: PapDOM.LayerTypes | null | undefined) => { + const rerender = useUpdate() + + // useEffect(() => { + // layer?.on('updated', rerender) + // return () => layer?.off('updated', rerender) + // }, [layer?.uid, rerender]) +} + +export const useLayerListWatch = ( + layers: Array +) => { + const rerender = useUpdate() + // const prev = usePrevious(layers) + + useEffect(() => { + layers.forEach((l) => l?.on('updated', rerender)) + return () => layers.forEach((l) => l?.off('updated', rerender)) + }) +} + +export const useVectorObjectWatch = ( + object: PapDOM.VectorObject | null | undefined +) => { + const rerender = useUpdate() + + useEffect(() => { + object?.on('updated', rerender) + return () => object?.off('updated', rerender) + }) +} + +export const usePapFilterWatch = (filter: PapDOM.Filter | null | undefined) => { + const rerender = useUpdate() + + useEffect(() => { + filter?.on('updated', rerender) + return () => filter?.off('updated', rerender) + }) +} + +export const useTransactionCommand = ({ + threshold = 500, +}: { + threshold?: number +} = {}) => { + const rerender = useUpdate() + const { execute } = useFleur() + + const ref = useRef(null) + const debouncedCommit = useMemo( + () => + debounce(() => { + ref.current = null + execute(EditorOps.rerenderCanvas) + }, threshold), + [threshold] + ) + + return useMemo( + () => ({ + get isStarted() { + return !!ref.current + }, + startIfNotStarted: () => { + if (ref.current) return + ref.current = new PapCommands.Transaction({ commands: [] }) + execute(EditorOps.runCommand, ref.current) + }, + doAndAdd: (command: PapCommands.AnyCommandType) => { + ref.current?.doAndAddCommand(command) + rerender() + }, + autoStartAndDoAdd: (command: PapCommands.AnyCommandType) => { + if (!ref.current) { + ref.current = new PapCommands.Transaction({ commands: [] }) + execute(EditorOps.runCommand, ref.current) + } + + ref.current?.doAndAddCommand(command) + rerender() + }, + cancel: () => { + if (!ref.current) return + execute(EditorOps.revertCommand, { whenCommandIs: ref.current }) + ref.current = null + }, + rerenderCanvas: () => { + execute(EditorOps.rerenderCanvas) + }, + commit: () => { + ref.current = null + setTimeout(() => { + execute(EditorOps.rerenderCanvas) + }, 1000) + }, + debouncedCommit, + }), + [debouncedCommit] + ) +} + +export const useActiveLayerPane = () => { + const { execute } = useFleur() + const { activeLayer, activeLayerPath } = useStore((get) => ({ + activeLayer: EditorSelector.activeLayer(get), + activeLayerPath: EditorSelector.activeLayerPath(get), + })) + + useLayerWatch(activeLayer) + + const [layerName, setLayerName] = useBufferedState(activeLayer?.name) + + const handleChangeLayerName = useFunk( + ({ currentTarget }: ChangeEvent) => { + if (!activeLayerPath) return + + setLayerName(currentTarget.value) + commitChangeLayerName(activeLayerPath, currentTarget.value) + } + ) + + const commitChangeLayerName = useDebouncedFunk( + (path: string[], layerName: string) => { + if (!activeLayerPath) return + execute( + EditorOps.runCommand, + new PapCommands.Layer.PatchLayerAttr({ + patch: { name: layerName }, + pathToTargetLayer: path, + }) + ) + }, + 1000 + ) + + const handleChangeCompositeMode = useFunk((value: string) => { + if (!activeLayerPath) return + + execute( + EditorOps.updateLayer, + activeLayerPath, + (layer) => (layer.compositeMode = value as any) + ) + }) + + const handleChangeOpacity = useFunk( + ({ currentTarget }: ChangeEvent) => { + if (!activeLayer) return + + execute(EditorOps.updateLayer, activeLayerPath, (layer) => { + layer.opacity = currentTarget.valueAsNumber + }) + } + ) + + const handleClickRemoveLayer = useFunk(() => { + if (!activeLayerPath) return + + execute( + EditorOps.runCommand, + new PapCommands.Layer.DeleteLayer({ + pathToTargetLayer: activeLayerPath, + }) + ) + }) + + return { + handleChangeLayerName, + handleChangeCompositeMode, + handleChangeOpacity, + handleClickRemoveLayer, + state: { layerName, activeLayer }, + } +} + +export const PaintCanvasContext = createContext< + MutableRefObject +>(createRef()) + +export const usePaintCanvasRef = () => { + return useContext(PaintCanvasContext) +} + +export const useWhiteNoise = () => { + useEffect(() => { + // if (!WhiteNoiseNode) return + // const Worker = function (url: URL) { + // return url + // } + // const ctx = new AudioContext() + // const modulator = new OscillatorNode(ctx) + // ctx.audioWorklet + // .addModule( + // new AudioWorklet( + // new URL('./sounds/WhiteNoiseWorklet.js', import.meta.url) + // ) + // ) + // .then(() => { + // if (!WhiteNoiseNode) return + // const node = new AudioWorkletNode(ctx, 'PaplicoWhiteNoise') + // node.connect(ctx.destination) + // ctx.resume() + // // modulator.connect(node) + // }) + // modulator.connect(ctx.destination) + // modulator.start() + // return () => { + // ctx.close() + // } + }, []) +} diff --git a/pkgs/web/src/features/PaintV2/hooks/usePaplico.ts b/pkgs/web/src/features/PaintV2/hooks/usePaplico.ts new file mode 100644 index 00000000..90e8309a --- /dev/null +++ b/pkgs/web/src/features/PaintV2/hooks/usePaplico.ts @@ -0,0 +1,26 @@ +import { Paplico } from '@paplico/core-new' +import { createContext, useContext, useRef, useSyncExternalStore } from 'react' +import { useEffectOnce } from 'react-use' + +const PapReactContext = createContext(null) + +export const PaplicoProvider = PapReactContext.Provider + +export function usePaplicoState(pap: Paplico | undefined | null) { + const engine = useContext(PapReactContext) + + useSyncExternalStore( + (callback) => { + if (engine == null) return () => {} + + engine.on('stateChanged', callback) + + return () => { + engine.off('stateChanged', callback) + } + }, + () => ({ + ...engine?.state, + }) + ) +} diff --git a/pkgs/web/src/features/PaintV2/index.tsx b/pkgs/web/src/features/PaintV2/index.tsx new file mode 100644 index 00000000..fe71eb36 --- /dev/null +++ b/pkgs/web/src/features/PaintV2/index.tsx @@ -0,0 +1,1452 @@ +import useEvent from 'react-use-event-hook' +import { useFleurContext, useStore } from '@fleur/react' +import { + letDownload, + loadImageFromBlob, + match, + useAsyncEffect, +} from '@hanakla/arma' +import { + ChangeEvent, + forwardRef, + memo, + MouseEvent, + PointerEvent, + RefObject, + MutableRefObject, + useEffect, + useRef, + useState, +} from 'react' +import { + useClickAway, + useDropArea, + useInterval, + useToggle, + useUpdate, +} from 'react-use' +import { useGesture } from 'react-use-gesture' +import { autoPlacement, shift, useFloating } from '@floating-ui/react-dom' +import { useRouter } from 'next/router' +import { css, keyframes, useTheme } from 'styled-components' +import useMeasure from 'use-measure' +import { Moon, Sun } from '@styled-icons/remix-fill' +import { Menu } from '@styled-icons/remix-line' +import { useTranslation } from 'next-i18next' +import { fit } from 'object-fit-math' + +import { + Paplico, + CanvasFactory, + ExtraBrushes, + Document, +} from '@paplico/core-new' +import isiOS from 'is-ios' + +import { Sidebar } from '🙌/components/Sidebar' +import { FilterView } from './containers/FilterView' +import { LayerView } from './containers/LayerView' +import { EditorOps, EditorSelector, EditorStore } from '🙌/domains/EditorStable' +import { useFunkyGlobalMouseTrap } from '🙌/hooks/useMouseTrap' +import { + useFleur, + useIsiPadOS, + useMedia, + useMultiFingerTouch, +} from '🙌/utils/hooks' +import { ControlsOverlay } from './containers/ControlsOverlay' +import { MainActions } from './containers/MainActions/MainActions' + +import { centering, checkerBoard } from '🙌/utils/mixins' +import { rgba } from 'polished' +import { darkTheme, ThemeProp } from '🙌/utils/theme' +import { Button } from '🙌/components/Button' +import { media, narrow } from '🙌/utils/responsive' +import { + isEventIgnoringTarget, + normalRgbToRgbArray, + swapObjectBrushAndFill, +} from './helpers' +import { Tooltip } from '🙌/components/Tooltip' +import { NotifyOps, useNotifyConsumer } from '🙌/domains/Notify' +import { SidebarPane } from '🙌/components/SidebarPane' +import { BrushPresets } from './containers/BrushPresets' +import { Dropzone } from '🙌/components/Dropzone' +import { log } from '🙌/utils/log' +import { DOMUtils } from '🙌/utils/dom' +import { FloatingWindow } from '🙌/components/FloatingWindow' +import { exportProject } from '🙌/domains/EditorStable/exportProject' +import { combineRef } from '../../utils/react' +import { PaintCanvasContext, useWhiteNoise } from './hooks' +import { Testing } from './testing' +import useSound from 'use-sound' +import { Howl } from 'howler' +import { debounce } from '../../utils/func' +import { bindDevToolAPI } from './devToolsAPI' +import { ReferenceImageWindow } from './views/ReferenceImageWindow' +import { HistoryFlash } from './views/HistoryFlash' +import { PaplicoEngineContext, PaplicoEngineProvider } from './contexts/engine' + +export const PaintV2 = memo(function PaintPage({}) { + const { t } = useTranslation('app') + const theme = useTheme() + const router = useRouter() + + const { executeOperation } = useFleurContext() + const { execute, getStore } = useFleur() + const { + currentDocument, + activeLayer, + currentTool, + currentTheme, + renderSetting, + } = useStore((get) => ({ + currentDocument: EditorSelector.currentDocument(get), + activeLayer: EditorSelector.activeLayer(get), + currentTool: get(EditorStore).state.currentTool, + currentTheme: get(EditorStore).state.currentTheme, + renderSetting: get(EditorStore).state.renderSetting, + })) + + const isNarrowMedia = useMedia(`(max-width: ${narrow})`, false) + const isIpadOS = useIsiPadOS() + + const engine = useRef(null) + + const rootRef = useRef(null) + const canvasRef = useRef(null) + const editAreaRef = useRef(null) + const sidebarRef = useRef(null) + + const saveFloat = useFloating({ + placement: 'top', + middleware: [shift(), autoPlacement({ allowedPlacements: ['top-start'] })], + }) + + const rerender = useUpdate() + const [sidebarOpened, sidebarToggle] = useToggle(!isNarrowMedia) + const [stream, setStream] = useState(null) + + const [saveMessage] = useNotifyConsumer('save', 1) + + const handleOnDrop = useEvent(async ([file]: File[]) => { + if (!currentDocument) return + + const { image } = await loadImageFromBlob(file) + const layer = await PapHelper.imageToLayer(image) + + execute( + EditorOps.runCommand, + new PapCommands.Document.AddLayer({ + layer, + }) + ) + execute(EditorOps.setActiveLayer, [layer.uid]) + }) + + const handleChangeDisableFilters = useEvent( + ({ currentTarget }: ChangeEvent) => { + executeOperation(EditorOps.setRenderSetting, { + disableAllFilters: currentTarget.checked, + }) + } + ) + + const handleClickExport = useEvent(async () => { + if (!currentDocument) return + + const { blob } = exportProject(currentDocument, getStore) + const url = URL.createObjectURL(blob) + + if (typeof window.showSaveFilePicker === 'undefined') { + letDownload(url, 'test.paplc') + } else { + const handler = + getStore(EditorStore).state.currentFileHandler ?? + (await window.showSaveFilePicker({ + types: [ + { + description: 'Paplico project', + accept: { 'application/octet-stream': '.paplc' }, + }, + ], + })) + + await ( + await handler.createWritable({ keepExistingData: false }) + ).write(blob) + + executeOperation(EditorOps.setCurrentFileHandler, handler) + } + + setTimeout(() => URL.revokeObjectURL(url), 10000) + }) + + const handleClickExportAs = useEvent( + async (e: MouseEvent) => { + const { currentTarget } = e + e.stopPropagation() + + if (!currentDocument || !engine.current) return + + const type = currentTarget.dataset.type! + const mime = match(type) + .when('png', 'image/png') + .when('jpeg', 'image/jpeg') + ._(new Error(`Unexpected type ${type}`)) + + const exporter = await engine.current.renderAndExport(currentDocument) + + const blob = await exporter.export(mime, 1.0) + const url = URL.createObjectURL(blob) + + letDownload( + url, + !currentDocument.title + ? `${t('untitled')}.${type}` + : `${currentDocument.title}.${type}` + ) + + executeOperation(NotifyOps.create, { + area: 'save', + timeout: 3000, + messageKey: t('exports.exported'), + }) + setTimeout(() => URL.revokeObjectURL(url), 10000) + } + ) + + const handleClickExportAsPsd = useEvent(async (e: MouseEvent) => { + e.stopPropagation() + + if (!currentDocument) return + + execute(NotifyOps.create, { + area: 'loadingLock', + timeout: 0, + messageKey: t('appMenu.exporting'), + lock: true, + }) + + try { + const exporter = new PapExporter.PSD() + const blob = await exporter.export(engine.current!, currentDocument) + const url = URL.createObjectURL(blob) + + letDownload( + url, + !currentDocument.title + ? `${t('untitled')}.psd` + : `${currentDocument.title}.psd` + ) + + setTimeout(() => URL.revokeObjectURL(url), 10000) + } finally { + execute(NotifyOps.create, { + area: 'loadingLock', + timeout: 0, + messageKey: t('appMenu.exported'), + lock: false, + }) + } + }) + + const handleClickDarkTheme = useEvent(() => { + executeOperation(EditorOps.setTheme, 'dark') + }) + + const handleClickLightTheme = useEvent(() => { + executeOperation(EditorOps.setTheme, 'light') + }) + + const [bindDrop, dragState] = useDropArea({ onFiles: handleOnDrop }) + + useFunkyGlobalMouseTrap(['ctrl+s', 'command+s'], (e) => { + e.preventDefault() + + executeOperation(EditorOps.saveCurrentDocumentToIdb, { notify: true }) + }) + useFunkyGlobalMouseTrap(['ctrl+shift+s', 'command+shift+s'], (e) => { + e.preventDefault() + handleClickExport() + }) + + useFunkyGlobalMouseTrap(['ctrl+z', 'command+z'], () => { + executeOperation(EditorOps.undoCommand) + }) + + useFunkyGlobalMouseTrap(['ctrl+shift+z', 'command+shift+z', 'ctrl+y'], () => { + executeOperation(EditorOps.redoCommand) + }) + + useFunkyGlobalMouseTrap(['v'], () => + executeOperation(EditorOps.setTool, 'cursor') + ) + + useFunkyGlobalMouseTrap(['i'], () => + executeOperation(EditorOps.setTool, 'dropper') + ) + + useFunkyGlobalMouseTrap(['a'], () => + executeOperation(EditorOps.setTool, 'point-cursor') + ) + + useFunkyGlobalMouseTrap(['b'], () => { + executeOperation(EditorOps.setTool, 'draw') + }) + + useFunkyGlobalMouseTrap(['x'], () => { + const vectorColorTarget = EditorSelector.vectorColorTarget(getStore) + executeOperation( + EditorOps.setVectorColorTarget, + vectorColorTarget === 'fill' ? 'stroke' : 'fill' + ) + }) + + useFunkyGlobalMouseTrap(['e'], () => + executeOperation(EditorOps.setTool, 'erase') + ) + + useFunkyGlobalMouseTrap(['p'], () => + executeOperation(EditorOps.setTool, 'shape-pen') + ) + + useFunkyGlobalMouseTrap(['tab'], (e) => { + e.preventDefault() + sidebarToggle() + }) + + useFunkyGlobalMouseTrap(['command+up', 'ctrl+up'], (e) => { + const activeLayer = EditorSelector.activeLayer(getStore) + const activeLayerPath = EditorSelector.activeLayerPath(getStore) + const activeObject = EditorSelector.activeObject(getStore) + + if ( + activeLayer?.layerType !== 'vector' || + !activeLayerPath || + !activeObject + ) + return + + execute( + EditorOps.runCommand, + new PapCommands.VectorLayer.ReorderObjects({ + pathToTargetLayer: activeLayerPath, + objectUid: activeObject.uid, + newIndex: { delta: -1 }, + }) + ) + }) + + useFunkyGlobalMouseTrap(['command+down', 'ctrl+down'], (e) => { + const activeLayer = EditorSelector.activeLayer(getStore) + const activeLayerPath = EditorSelector.activeLayerPath(getStore) + const activeObject = EditorSelector.activeObject(getStore) + + if ( + activeLayer?.layerType !== 'vector' || + !activeLayerPath || + !activeObject + ) + return + + execute( + EditorOps.runCommand, + new PapCommands.VectorLayer.ReorderObjects({ + pathToTargetLayer: activeLayerPath, + objectUid: activeObject.uid, + newIndex: { delta: 1 }, + }) + ) + }) + + useFunkyGlobalMouseTrap(['ctrl+0', 'command+0'], (e) => { + executeOperation(EditorOps.setCanvasTransform, { + scale: 1, + pos: { x: 0, y: 0 }, + }) + }) + + useFunkyGlobalMouseTrap(['/'], () => { + const path = EditorSelector.activeLayerPath(getStore) + const o = EditorSelector.activeObject(getStore) + const target = EditorSelector.vectorColorTarget(getStore) + + if (!path || !o) return + + executeOperation( + EditorOps.runCommand, + new PapCommands.VectorLayer.PatchObjectAttr({ + objectUid: o.uid, + pathToTargetLayer: path, + patcher: (attr) => { + if (target === 'fill') attr.fill = null + else attr.brush = null + }, + }) + ) + }) + + useFunkyGlobalMouseTrap(['shift+x'], () => { + executeOperation(EditorOps.updateActiveObject, (o) => { + swapObjectBrushAndFill(o, { fallbackBrushId: PapBrushes.Brush.id }) + }) + }) + + useClickAway(sidebarRef, (e) => { + if (!isNarrowMedia) return + if (isEventIgnoringTarget(e.target)) return + + sidebarToggle(false) + }) + + // Initialize Paplico + useAsyncEffect(async () => { + ;(window as any).engine = engine.current = new Paplico(canvasRef.current!) + await engine.current.brushes.register(ExtraBrushes.ScatterBrush) + console.log('hey') + + executeOperation(EditorOps.initEngine, engine.current) + + Object.defineProperty(window, '_session', { + configurable: true, + get() { + return getStore(EditorStore).state.session + }, + }) + + bindDevToolAPI(getStore) + + if (process.env.NODE_ENV !== 'development') { + } else { + const document = Document.createDocument({ width: 1000, height: 1000 }) + const vector = Document.createVectorLayerEntity({}) + + canvasRef.current!.width = 1000 + canvasRef.current!.height = 1000 + + vector.objects.push( + Document.createVectorObject({ + appearances: [ + { + kind: 'fill', + fill: { + type: 'fill', + color: { r: 1, g: 0, b: 0 }, + opacity: 1, + }, + }, + ], + path: Document.createVectorPath({ + closed: true, + points: [ + { x: 0, y: 0, in: null, out: null }, + { x: 100, y: 0, in: null, out: null }, + { x: 100, y: 100, in: null, out: null }, + { x: 0, y: 100, in: null, out: null }, + ], + }), + }) + ) + + const layers = [ + Document.createRasterLayerEntity({ width: 1000, height: 1000 }), + Document.createRasterLayerEntity({ width: 1000, height: 1000 }), + Document.createRasterLayerEntity({ width: 1000, height: 1000 }), + Document.createRasterLayerEntity({ width: 1000, height: 1000 }), + Document.createRasterLayerEntity({ width: 1000, height: 1000 }), + Document.createRasterLayerEntity({ width: 1000, height: 1000 }), + vector, + ] + + document.layerEntities.push(...layers) + document.layerTree = [ + { layerUid: layers[0].uid, children: [] }, + { layerUid: layers[1].uid, children: [] }, + { layerUid: layers[2].uid, children: [] }, + { layerUid: layers[3].uid, children: [] }, + { layerUid: layers[4].uid, children: [] }, + { layerUid: layers[5].uid, children: [] }, + { layerUid: layers[6].uid, children: [] }, + ] + + engine.current.strokeSetting = { + brushId: ExtraBrushes.ScatterBrush.id, + brushVersion: '1.0.0', + size: 5, + color: { r: 0, g: 0, b: 0 }, + opacity: 1, + specific: { + rotationAdjust: 1, + scatterRange: 3, + } satisfies ExtraBrushes.ScatterBrush.SpecificSetting, + } + engine.current.loadDocument(document) + engine.current.enterLayer([layers[0].uid]) + + // if (EditorSelector.currentDocument(getStore) != null) return + + // const document = Document.createDocument() + // // ({ width: 1000, height: 1000 }) + // executeOperation(EditorOps.createSession, document) + + // const layer = Document.createRasterLayerEntity({ + // width: 1000, + // height: 1000, + // }) + // const vector = Document.createVectorLayerEntity({ visible: true }) + + // { + // const path = Document.createVectorPath({ + // points: [ + // { x: 0, y: 0, in: null, out: null }, + // { x: 200, y: 500, in: null, out: null }, + // { x: 600, y: 500, in: null, out: null }, + // { + // x: 1000, + // y: 1000, + // in: null, + // out: null, + // }, + // ], + // closed: true, + // }) + + // const obj = Document.createVectorObject({ x: 0, y: 0, path }) + + // obj.appearances.push({ + // kind: 'stroke', + // stroke: { + // brushId: ExtraBrushes.ScatterBrush.id, + // color: { r: 0, g: 0, b: 0 }, + // opacity: 1, + // size: 2, + // }, + // }) + + // // obj.fill = null + // // obj.fill = { + // // type: 'linear-gradient', + // // opacity: 1, + // // start: { x: -100, y: -100 }, + // // end: { x: 100, y: 100 }, + // // colorStops: [ + // // { color: { r: 0, g: 1, b: 1, a: 1 }, position: 0 }, + // // { color: { r: 1, g: 0.2, b: 0.1, a: 1 }, position: 1 }, + // // ], + // // } + + // vector.objects.push(obj) + // } + + // const filter = Document.createFilterLayerEntity({ + // filters: [], + // }) + + // // const text = PapDOM.TextLayer.create({}) + // // const filter = PapDOM.FilterLayer.create({}) + // // const group = PapDOM.GroupLayer.create({ + // // layers: [ + // // PapDOM.RasterLayer.create({ width: 1000, height: 1000 }), + // // PapDOM.VectorLayer.create({}), + // // ], + // // }) + + // // const reference = PapDOM.ReferenceLayer.create({ + // // referencedLayerId: layer.uid, + // // }) + // // reference.x = 20 + + // // vector.filters + // // .push + // // PapDOM.Filter.create({ + // // filterId: '@silk-core/gauss-blur', + // // visible: true, + // // settings: engine.current.toolRegistry.getFilterInstance( + // // '@silk-core/gauss-blur' + // // )!.initialConfig, + // // }), + // // PapDOM.Filter.create({ + // // filterId: '@silk-core/chromatic-aberration', + // // visible: true, + // // settings: engine.current.toolRegistry.getFilterInstance( + // // '@silk-core/chromatic-aberration' + // // )!.initialConfig, + // // }), + // // PapDOM.Filter.create({ + // // filterId: '@silk-core/noise', + // // visible: true, + // // settings: + // // engine.current.toolRegistry.getFilterInstance('@silk-core/noise')! + // // .initialConfig, + // // }), + // // PapDOM.Filter.create({ + // // filterId: '@silk-core/binarization', + // // visible: true, + // // settings: engine.current.toolRegistry.getFilterInstance( + // // '@silk-core/binarization' + // // )!.initialConfig, + // // }), + // // PapDOM.Filter.create({ + // // filterId: '@silk-core/low-reso', + // // visible: true, + // // settings: engine.current.toolRegistry.getFilterInstance( + // // '@silk-core/low-reso' + // // )!.initialConfig, + // // }) + // // () + + // document.layerEntities.push(layer) + // document.layerEntities.push(vector) + // // document.layerEntities.push(text) + // document.layerEntities.push(filter) + // // document.layerEntities.push(group) + // // document.layerEntities.unshift(reference) + // document.layerTree = [ + // { layerUid: layer.uid, children: [] }, + // { layerUid: vector.uid, children: [] }, + // { layerUid: filter.uid, children: [] }, + // ] + + // executeOperation(EditorOps.createSession, document) + // executeOperation(EditorOps.setActiveLayer, [vector.uid]) + + executeOperation(EditorOps.setVectorFill, { + type: 'linear-gradient', + colorStops: [ + { color: { r: 0, g: 1, b: 1, a: 1 }, position: 0 }, + { color: { r: 0.5, g: 1, b: 0.8, a: 1 }, position: 1 }, + ], + start: { x: -100, y: -100 }, + end: { x: 100, y: 100 }, + opacity: 1, + }) + } + + if ('scrollRestoration' in history) { + history.scrollRestoration = 'manual' + } + + window.addEventListener('contextmenu', (e) => { + e.preventDefault() + }) + + const stream = canvasRef.current!.captureStream(5) + setStream(stream) + + rerender() + executeOperation(EditorOps.rerenderCanvas) + + return () => { + window + console.log('disposed') + engine.current!.dispose() + } + }, []) + + useInterval(() => { + if (process.env.NODE_ENV === 'development') { + const bytes = + getStore(EditorStore).state.session?.getDocumentUsedMemoryBytes() ?? 0 + + log( + `Document holding bytes: ${byteToMiB(bytes)} MiB${ + performance.memory + ? `, Heap: ${byteToMiB( + performance.memory!.usedJSHeapSize + )} MiB / ${byteToMiB(performance.memory.jsHeapSizeLimit)} MiB` + : '' + }, Canvas holdings ${byteToMiB( + CanvasFactory.getCanvasBytes() + )} MiB with ${CanvasFactory.activeCanvasesCount()} canvases + ` + ) + } + }, 4000) + + useEffect(() => { + if (!currentDocument) return + if (isiOS) { + execute(NotifyOps.create, { + area: 'save', + messageKey: 'exports.autoSaveDisabledInIOS', + timeout: 5000, + }) + + return + } + + const autoSave = () => { + executeOperation(EditorOps.autoSave, currentDocument!.uid) + executeOperation(NotifyOps.create, { + area: 'save', + messageKey: t('exports.autoSaved'), + timeout: 3000, + }) + } + + const id = window.setInterval(autoSave, 10000) + autoSave() + + return () => window.clearInterval(id) + }, [currentDocument?.uid]) + + useWhiteNoise() + + return ( + + + {/* {process.env.NODE_ENV === 'development' && } */} +
theme.color.background1}; + color: ${({ theme }) => theme.color.text2}; + touch-action: none; + `} + {...bindDrop} + > +
theme.colors.black60}; + } + `} + style={{ display: isIpadOS ? 'block' : 'none' }} + /> + +
+ + + + + <> +
+ +
+ + + +
+
+
+
+ +
+
+
+ + + + + <> + + + + + +
+ {/* +
+ +
+ {t('referenceColor')} +
+
+ +
*/} + +
+ `1px solid ${rgba(theme.colors.white50, 0.2)}`}; + `} + > +
+ + + + + + +
+ +
+ + + {t(saveMessage?.messageKey)} + + +
+ } + > + {t('export')} + +
+
+
+ + +
+
+
+
+ ) +}) + +const EditorArea = memo( + forwardRef< + HTMLDivElement, + { canvasRef: MutableRefObject } + >(function EditorArea({ canvasRef }) { + const theme = useTheme() + const { execute, getStore } = useFleur() + + const { currentTool } = useStore((get) => ({ + currentTool: get(EditorStore).state.currentTool, + })) + + const [cursorPos, setCursorPos] = useState<{ x: number; y: number } | null>( + null + ) + + const editAreaRef = useRef(null) + const editorBound = useMeasure(editAreaRef) + + const handleEditAreaPointerDown = useEvent((e: PointerEvent) => { + const box = editAreaRef.current!.getBoundingClientRect() + setCursorPos({ + x: e.clientX - box.left, + y: e.clientY - box.top, + }) + }) + + const handleEditAreaPointerMove = useEvent((e: PointerEvent) => { + const box = editAreaRef.current!.getBoundingClientRect() + + setCursorPos((prev) => + prev + ? { + x: e.clientX - box.left, + y: e.clientY - box.top, + } + : null + ) + }) + + const handleEditAreaPointerUp = useEvent(() => { + setTimeout(() => { + setCursorPos(null) + }, 100) + }) + + const editorMultiTouchRef = useMultiFingerTouch((e) => { + if (e.fingers == 2) execute(EditorOps.undoCommand) + if (e.fingers == 3) execute(EditorOps.redoCommand) + }) + + useGesture( + { + onPinch: ({ delta: [d, r] }) => { + execute(EditorOps.setCanvasTransform, { + scale: (prev) => Math.max(0.1, prev + d / 400), + }) + }, + + onDrag: (e) => { + if (e.touches < 2) return + + execute(EditorOps.setCanvasTransform, { + pos: ({ x, y }) => ({ + x: x + e.delta[0], + y: y + e.delta[1], + }), + }) + }, + }, + { domTarget: editAreaRef, eventOptions: { passive: false } } + ) + + useEffect(() => { + const handleCanvasWheel = (e: WheelEvent) => { + if (DOMUtils.closestOrSelf(e.target, '[data-ignore-canvas-wheel]')) + return + + e.preventDefault() + + execute(EditorOps.setCanvasTransform, { + pos: ({ x, y }) => ({ + x: x - e.deltaX * 0.5, + y: y - e.deltaY * 0.5, + }), + }) + } + + editAreaRef.current?.addEventListener('wheel', handleCanvasWheel, { + passive: false, + }) + + return () => + editAreaRef.current?.removeEventListener('wheel', handleCanvasWheel) + }, []) + + return ( +
+
theme.exactColors.white40}; + pointer-events: none; + `} + style={{ + // ...(dragState.over ? { opacity: 1 } : { opacity: 0 }), + opacity: 0, + }} + > + ドロップして画像を追加 +
+ + + + + + + +
+ +
+ +
+ +
+ +
+ +
+
+ ) + }) +) + +const PaintCanvas = memo(function PaintCanvas({ + canvasRef, +}: { + canvasRef: RefObject +}) { + const canvasHandler = useRef(null) + + const { execute, getStore } = useFleur() + const { + currentDocument, + session, + engine, + currentTool, + renderStrategy, + scale, + pos, + } = useStore((get) => ({ + currentDocument: EditorSelector.currentDocument(get), + currentTool: EditorSelector.currentTool(get), + session: get(EditorStore).state.session, + engine: get(EditorStore).state.engine, + renderStrategy: get(EditorStore).state.renderStrategy, + scale: get(EditorStore).state.canvasScale, + pos: get(EditorStore).state.canvasPosition, + })) + + const [play, { sound, stop }] = useSound( + require('./sounds/assets/Mechanical_Pen01-05(Straight).mp3'), + { volume: 0.2 } + ) + + const handleClickCanvas = useEvent( + ({ + currentTarget, + clientX, + clientY, + buttons, + }: PointerEvent) => { + if (buttons !== 1 || currentTool !== 'dropper') return + + const rect = currentTarget.getBoundingClientRect() + const [x, y] = [ + (clientX - rect.left) / scale, + (clientY - rect.top) / scale, + ] + + const ctx = canvasRef.current!.getContext('2d')! + const pixel = ctx.getImageData(x, y, 1, 1) + + // const data = ctx.createImageData(1, 1) + // data.data.set([255, 0, 0, 255]) + // canvasRef.current!.getContext('2d')!.putImageData(data, x, y) + + execute(EditorOps.setBrushSetting, { + color: { + r: pixel.data[0] / 255, + g: pixel.data[1] / 255, + b: pixel.data[2] / 255, + }, + }) + } + ) + + const measure = useMeasure(canvasRef) + + useEffect(() => { + if (!canvasHandler.current) return + canvasHandler.current!.scale = scale + }, [scale]) + + return ( +
+ +
+ ) +}) + +const CanvasPreviewWindow = memo(function CanvasPreviewWindow({ + stream, +}: { + stream: MediaProvider | null +}) { + const videoRef = useRef(null) + + const [mirror, toggleMirror] = useToggle(false) + + useEffect(() => { + videoRef.current!.srcObject = stream + }, [stream]) + + return ( + + + + + ) +}) + +const ColorHistoryPane = memo(function ColorHistoryPane() { + const { t } = useTranslation('app') + const { execute, getStore } = useFleur() + + const colorHistory = useStore(EditorSelector.colorHistory) + + const handleClickColor = useEvent(({ currentTarget }) => { + const id = currentTarget.dataset.id! + const entry = colorHistory.find((entry) => entry.id === id) + if (!entry) return + + const activeLayer = EditorSelector.activeLayer(getStore) + + if (activeLayer?.layerType === 'vector') { + const vectorColorTarget = EditorSelector.vectorColorTarget(getStore) + const pathToActiveLayer = EditorSelector.activeLayerPath(getStore) + const defaultVectorBrush = EditorSelector.defaultVectorBrush(getStore) + const activeObject = EditorSelector.activeObject(getStore) + + if (!activeObject) return + + if (vectorColorTarget === 'fill') { + execute(EditorOps.setVectorFill, { color: entry.color }) + } else if (vectorColorTarget === 'stroke') { + execute(EditorOps.setBrushSetting, { color: entry.color }) + } + + execute( + EditorOps.runCommand, + new PapCommands.VectorLayer.PatchObjectAttr({ + pathToTargetLayer: pathToActiveLayer!, + objectUid: activeObject.uid, + patcher: (attrs) => { + if (vectorColorTarget === 'stroke') { + attrs.brush = attrs.brush + ? { ...attrs.brush, color: entry.color } + : { + ...defaultVectorBrush, + color: entry.color, + } + } else if ( + vectorColorTarget === 'fill' && + attrs.fill?.type === 'fill' + ) { + attrs.fill.color = entry.color + } + }, + }) + ) + } else { + execute(EditorOps.setBrushSetting, { color: entry.color }) + } + }) + + return ( + +
+ {colorHistory.map((entry) => ( + <> +
+ + ))} +
+ + ) +}) + +const byteToMiB = (byte: number) => Math.round((byte / 1024 / 1024) * 100) / 100 diff --git a/pkgs/web/src/features/PaintV2/sounds/assets/LICENSE.md b/pkgs/web/src/features/PaintV2/sounds/assets/LICENSE.md new file mode 100644 index 00000000..1aae9388 --- /dev/null +++ b/pkgs/web/src/features/PaintV2/sounds/assets/LICENSE.md @@ -0,0 +1,4 @@ +- Below assets from OtoLogic シャープペン 01 (https://otologic.jp/free/se/writing_material02.html) + Licenced under CC-BY + - Mechanical_Pen01-04(Straight).mp3 + - Mechanical_Pen01-05(Straight).mp3 diff --git a/pkgs/web/src/features/PaintV2/sounds/assets/Mechanical_Pen01-04(Straight).mp3 b/pkgs/web/src/features/PaintV2/sounds/assets/Mechanical_Pen01-04(Straight).mp3 new file mode 100755 index 00000000..fddb353e Binary files /dev/null and b/pkgs/web/src/features/PaintV2/sounds/assets/Mechanical_Pen01-04(Straight).mp3 differ diff --git a/pkgs/web/src/features/PaintV2/sounds/assets/Mechanical_Pen01-05(Straight).mp3 b/pkgs/web/src/features/PaintV2/sounds/assets/Mechanical_Pen01-05(Straight).mp3 new file mode 100755 index 00000000..87195432 Binary files /dev/null and b/pkgs/web/src/features/PaintV2/sounds/assets/Mechanical_Pen01-05(Straight).mp3 differ diff --git a/pkgs/web/src/features/PaintV2/testing.tsx b/pkgs/web/src/features/PaintV2/testing.tsx new file mode 100644 index 00000000..b3a91b9c --- /dev/null +++ b/pkgs/web/src/features/PaintV2/testing.tsx @@ -0,0 +1,77 @@ +import { PapDOM } from '@paplico/core' +import { useEffect } from 'react' +import { describe, it } from '../Debug/clientSpec' +import { calcLayerMove, flattenLayers } from './helpers' + +export const Testing = () => { + useEffect(() => { + try { + console.group('testing') + + describe('calcLayerMove', () => { + describe('Move to out of group', () => { + const structure = [ + PapDOM.VectorLayer.create({ name: 'root-1' }), + PapDOM.GroupLayer.create({ + name: 'root-2-group', + layers: [ + PapDOM.VectorLayer.create({ name: 'group-1' }), + PapDOM.VectorLayer.create({ name: 'group-2' }), + ], + }), + PapDOM.VectorLayer.create({ name: 'root-3' }), + ] as const + + const flatten = flattenLayers( + structure as unknown as PapDOM.LayerTypes[] + ) + + const result = calcLayerMove(flatten, { + active: { id: structure[1].layers[1].uid }, + over: { id: structure[1].uid }, + }) + + it('sourcePath') + .expect(result?.sourcePath) + .toEqual([structure[1].uid, structure[1].layers[1].uid]) + it('targetIndex').expect(result?.targetIndex).toEqual(1) + it('targetParentPath').expect(result?.targetParentPath).toEqual([]) + }) + + describe('Move to inside of group', () => { + const structure = [ + PapDOM.VectorLayer.create({ name: 'root-1' }), + PapDOM.GroupLayer.create({ + name: 'root-2-group', + layers: [ + PapDOM.VectorLayer.create({ name: 'group-1' }), + PapDOM.VectorLayer.create({ name: 'group-2' }), + ], + }), + ] as const + + const flatten = flattenLayers( + structure as unknown as PapDOM.LayerTypes[] + ) + + const result = calcLayerMove(flatten, { + active: { id: structure[0].uid }, + over: { id: structure[1].layers[1].uid }, + }) + + it('sourcePath') + .expect(result?.sourcePath) + .toEqual([structure[0].uid]) + it('targetIndex').expect(result?.targetIndex).toEqual(1) + it('targetParentPath') + .expect(result?.targetParentPath) + .toEqual([structure[1].uid]) + }) + }) + + console.groupEnd() + } catch (e) {} + }, []) + + return null +} diff --git a/pkgs/web/src/features/PaintV2/views/HistoryFlash.tsx b/pkgs/web/src/features/PaintV2/views/HistoryFlash.tsx new file mode 100644 index 00000000..2a9ab7ae --- /dev/null +++ b/pkgs/web/src/features/PaintV2/views/HistoryFlash.tsx @@ -0,0 +1,62 @@ +import { useTranslation } from 'next-i18next' +import { memo } from 'react' +import { keyframes } from 'styled-components' +import { useNotifyConsumer } from '🙌/domains/Notify' +import { ThemeProp } from '🙌/utils/theme' + +export const HistoryFlash = memo(function HistoryFlash() { + const { t } = useTranslation('app') + const notifications = useNotifyConsumer('commandFlash', Infinity) + + return ( +
+ {notifications.map((notfiy) => ( +
theme.exactColors.white60}; + background-color: ${({ theme }: ThemeProp) => + theme.exactColors.blackFade50}; + transform: translateX(-50%); + animation: ${histolyFlashAnim} 1s ease-in-out 1 both; + `} + > + {t(`commandFlash.${notfiy.messageKey}`)} +
+ ))} +
+ ) +}) + +const histolyFlashAnim = keyframes` + from { + transform: translate(-50%, -16px); + opacity: 0; + } + + 30% { + transform: translate(-50%, 0); + opacity: 1; + } + + 70% { + transform: translate(-50%, 0); + opacity: 1; + } + + to { + transform: translate(-50%, -16px); + opacity: 0; + } +` diff --git a/pkgs/web/src/features/PaintV2/views/ReferenceImageWindow.tsx b/pkgs/web/src/features/PaintV2/views/ReferenceImageWindow.tsx new file mode 100644 index 00000000..e9d6d3ab --- /dev/null +++ b/pkgs/web/src/features/PaintV2/views/ReferenceImageWindow.tsx @@ -0,0 +1,43 @@ +import { loadImageFromBlob, useFunk } from '@hanakla/arma' +import { memo, useState } from 'react' +import { useToggle } from 'react-use' +import { Dropzone } from '🙌/components/Dropzone' +import { FloatingWindow } from '🙌/components/FloatingWindow' + +export const ReferenceImageWindow = memo(function ReferenceImageWindow() { + const [opened, toggleOpened] = useToggle(false) + const [referenceImage, setReferenceImage] = useState(null) + const [referencePosition, setReferencePosition] = useState({ x: 200, y: 0 }) + + const handleDropReference = useFunk(async (files: File[]) => { + if (referenceImage != null) URL.revokeObjectURL(referenceImage) + + const { image, url } = await loadImageFromBlob(files[0]) + setReferenceImage(url) + }) + + return ( + + {referenceImage ? ( + + ) : ( + + リファレンス画像を選ぶ + + )} + + ) +}) diff --git a/pkgs/web/src/pages/_meta.json b/pkgs/web/src/pages/_meta.json new file mode 100644 index 00000000..d828d03f --- /dev/null +++ b/pkgs/web/src/pages/_meta.json @@ -0,0 +1,5 @@ +{ + "docs": { + "display": "hidden" + } +} diff --git a/pkgs/web/src/pages/app.tsx b/pkgs/web/src/pages/app.tsx index 0f78b669..d5fd3260 100644 --- a/pkgs/web/src/pages/app.tsx +++ b/pkgs/web/src/pages/app.tsx @@ -37,9 +37,9 @@ import { Restart } from '@styled-icons/remix-fill' // return {} // } -declare module 'styled-components' { - export interface DefaultTheme extends ThemeType {} -} +// declare module 'styled-components' { +// export interface DefaultTheme extends ThemeType {} +// } const PageSwitch = memo(function PageSwitch() { const isNarrowMedia = useMedia(`(max-width: ${narrow})`, false) diff --git a/pkgs/web/src/pages/dev/new.tsx b/pkgs/web/src/pages/dev/new.tsx index e3561346..fb4c8241 100644 --- a/pkgs/web/src/pages/dev/new.tsx +++ b/pkgs/web/src/pages/dev/new.tsx @@ -43,7 +43,7 @@ export default function NewPage() { paplico.brushes.register(ExtraBrushes.ScatterBrush) console.log(paplico.brushes) - console.log(new ExtraBrushes.ScatterBrush().initialize()) + // console.log(new ExtraBrushes.ScatterBrush().initialize()) const doc = (window.__papdoc = new Document.PaplicoDocument()) doc.layerEntities.push( @@ -66,7 +66,8 @@ export default function NewPage() { paplico.strokeSetting = { // brushId: '@paplico/core/circle-brush', - brushId: ExtraBrushes.ScatterBrush.id, + brushId: '@paplico/core/circle-brush', + // brushId: ExtraBrushes.ScatterBrush.id, brushVersion: '1.0.0', color: { r: 0, g: 0, b: 0 }, opacity: 1, diff --git a/pkgs/web/src/pages/docs/_meta.json b/pkgs/web/src/pages/docs/_meta.json new file mode 100644 index 00000000..70b96a25 --- /dev/null +++ b/pkgs/web/src/pages/docs/_meta.json @@ -0,0 +1,5 @@ +{ + "develop": { + "title": "Development" + } +} diff --git a/pkgs/web/src/pages/docs/develop/_meta.json b/pkgs/web/src/pages/docs/develop/_meta.json new file mode 100644 index 00000000..37ea52c9 --- /dev/null +++ b/pkgs/web/src/pages/docs/develop/_meta.json @@ -0,0 +1,5 @@ +{ + "index": { + "title": "@paplico/core-new" + } +} diff --git a/pkgs/web/src/pages/docs/develop/index.md b/pkgs/web/src/pages/docs/develop/index.md new file mode 100644 index 00000000..e69de29b diff --git a/pkgs/web/src/pages/docs/index.mdx b/pkgs/web/src/pages/docs/index.mdx new file mode 100644 index 00000000..84b65b4a --- /dev/null +++ b/pkgs/web/src/pages/docs/index.mdx @@ -0,0 +1 @@ +# Paplico paint diff --git a/pkgs/web/src/pages/index.tsx b/pkgs/web/src/pages/index.tsx index 81e11637..75643d13 100644 --- a/pkgs/web/src/pages/index.tsx +++ b/pkgs/web/src/pages/index.tsx @@ -13,7 +13,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => { return { redirect: { permanent: false, - destination: '/app', + destination: '/v2', }, } } diff --git a/pkgs/web/src/pages/v2.tsx b/pkgs/web/src/pages/v2.tsx new file mode 100644 index 00000000..d13e0331 --- /dev/null +++ b/pkgs/web/src/pages/v2.tsx @@ -0,0 +1,41 @@ +import { ThemeProvider } from 'styled-components' +import { PaintV2 } from '🙌/features/PaintV2' +import { CharcoalTheme, dark, light } from '@charcoal-ui/theme' +import { GlobalStyle } from '🙌/components/GlobalStyle' +import { FleurContext } from '@fleur/react' +import { useMemo } from 'react' +import { createContext } from '🙌/domains' +import { LysContext } from '@fleur/lys' +import { serverSideTranslations } from 'next-i18next/serverSideTranslations' +import { getStaticPropsWithFleur } from '🙌/lib/fleur' +import nextI18nextConfig from '../../next-i18next.config' + +// const InfiniteCanvas: typeof import('ef-infinite-canvas').InfiniteCanvas = require('ef-infinite-canvas') + +export default function V2Page() { + const fleurContext = useMemo(() => createContext(), []) + + return ( + + + + + + + + + ) +} + +export const getStaticProps = getStaticPropsWithFleur(async ({ locale }) => { + return { + props: { + ...(await serverSideTranslations( + locale!, + ['app', 'index-home'], + nextI18nextConfig + )), + // Will be passed to the page component as props + }, + } +}) diff --git a/pkgs/web/src/theme.config.jsx b/pkgs/web/src/theme.config.jsx new file mode 100644 index 00000000..32e1ea72 --- /dev/null +++ b/pkgs/web/src/theme.config.jsx @@ -0,0 +1,15 @@ +/** @satisfies {typeof import('nextra').} */ +export default { + logo: Paplico, + project: { + link: 'https://github.com/hanakla/paplico-paint', + }, + footer: { + component: null, + }, + useNextSeoProps() { + return { + titleTemplate: '%s – Paplico', + } + }, +} diff --git a/pkgs/web/src/utils/theme.ts b/pkgs/web/src/utils/theme.ts index cdc5b1ba..5beeecb1 100644 --- a/pkgs/web/src/utils/theme.ts +++ b/pkgs/web/src/utils/theme.ts @@ -204,7 +204,7 @@ export const tm = createTheme(styled) declare module 'styled-components' { // interface DefaultTheme extends ThemeType {} - interface DefaultTheme extends ThemeType {} + interface DefaultTheme extends CharcoalTheme {} } export type ThemeProp = T & { theme: ThemeType } diff --git a/pkgs/web/tsconfig.json b/pkgs/web/tsconfig.json index 8f4c7c19..60070a5a 100644 --- a/pkgs/web/tsconfig.json +++ b/pkgs/web/tsconfig.json @@ -20,6 +20,7 @@ "isolatedModules": true, "strict": true, "jsx": "preserve", + "downlevelIteration": true, "paths": { "🙌/*": ["./src/*"] }, diff --git a/yarn.lock b/yarn.lock index eb1c1021..5ff4d6b5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1014,6 +1014,15 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.20.15": + version: 7.21.3 + resolution: "@babel/parser@npm:7.21.3" + bin: + parser: ./bin/babel-parser.js + checksum: a71e6456a1260c2a943736b56cc0acdf5f2a53c6c79e545f56618967e51f9b710d1d3359264e7c979313a7153741b1d95ad8860834cc2ab4ce4f428b13cc07be + languageName: node + linkType: hard + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.16.7": version: 7.16.7 resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.16.7" @@ -2739,6 +2748,15 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.13.10": + version: 7.21.0 + resolution: "@babel/runtime@npm:7.21.0" + dependencies: + regenerator-runtime: ^0.13.11 + checksum: 7b33e25bfa9e0e1b9e8828bb61b2d32bdd46b41b07ba7cb43319ad08efc6fda8eb89445193e67d6541814627df0ca59122c0ea795e412b99c5183a0540d338ab + languageName: node + linkType: hard + "@babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.6.2": version: 7.17.9 resolution: "@babel/runtime@npm:7.17.9" @@ -3375,7 +3393,7 @@ __metadata: languageName: node linkType: hard -"@floating-ui/react-dom@npm:^0.7.1": +"@floating-ui/react-dom@npm:0.7.2, @floating-ui/react-dom@npm:^0.7.1": version: 0.7.2 resolution: "@floating-ui/react-dom@npm:0.7.2" dependencies: @@ -3463,6 +3481,18 @@ __metadata: languageName: node linkType: hard +"@headlessui/react@npm:^1.7.10": + version: 1.7.13 + resolution: "@headlessui/react@npm:1.7.13" + dependencies: + client-only: ^0.0.1 + peerDependencies: + react: ^16 || ^17 || ^18 + react-dom: ^16 || ^17 || ^18 + checksum: 132e5fce86978b9a6fd9d669fb556acec783debcd8f510c99226b3b3229dad50a13aeed69fc9af66465fc7029487ecfc46e6226f14218b760d5a309e08c0d059 + languageName: node + linkType: hard + "@icons/material@npm:^0.2.4": version: 0.2.4 resolution: "@icons/material@npm:0.2.4" @@ -4228,7 +4258,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/sourcemap-codec@npm:1.4.14": +"@jridgewell/sourcemap-codec@npm:1.4.14, @jridgewell/sourcemap-codec@npm:^1.4.13": version: 1.4.14 resolution: "@jridgewell/sourcemap-codec@npm:1.4.14" checksum: 61100637b6d173d3ba786a5dff019e1a74b1f394f323c1fee337ff390239f053b87266c7a948777f4b1ee68c01a8ad0ab61e5ff4abb5a012a0b091bec391ab97 @@ -4343,6 +4373,214 @@ __metadata: languageName: node linkType: hard +"@mdx-js/mdx@npm:^2.2.1, @mdx-js/mdx@npm:^2.3.0": + version: 2.3.0 + resolution: "@mdx-js/mdx@npm:2.3.0" + dependencies: + "@types/estree-jsx": ^1.0.0 + "@types/mdx": ^2.0.0 + estree-util-build-jsx: ^2.0.0 + estree-util-is-identifier-name: ^2.0.0 + estree-util-to-js: ^1.1.0 + estree-walker: ^3.0.0 + hast-util-to-estree: ^2.0.0 + markdown-extensions: ^1.0.0 + periscopic: ^3.0.0 + remark-mdx: ^2.0.0 + remark-parse: ^10.0.0 + remark-rehype: ^10.0.0 + unified: ^10.0.0 + unist-util-position-from-estree: ^1.0.0 + unist-util-stringify-position: ^3.0.0 + unist-util-visit: ^4.0.0 + vfile: ^5.0.0 + checksum: d918766a326502ec0b54adee61dc2930daf5b748acb9107f9bfd1ab0dbc4d7b1a4d0dbb9e21da9dd2a9fc2f9950b2973a43c6ba62d3a72eb67a30f6c953e5be8 + languageName: node + linkType: hard + +"@mdx-js/react@npm:^2.2.1, @mdx-js/react@npm:^2.3.0": + version: 2.3.0 + resolution: "@mdx-js/react@npm:2.3.0" + dependencies: + "@types/mdx": ^2.0.0 + "@types/react": ">=16" + peerDependencies: + react: ">=16" + checksum: f45fe779556e6cd9a787f711274480e0638b63c460f192ebdcd77cc07ffa61e23c98cb46dd46e577093e1cb4997a232a848d1fb0ba850ae204422cf603add524 + languageName: node + linkType: hard + +"@microsoft/api-extractor-model@npm:7.26.4": + version: 7.26.4 + resolution: "@microsoft/api-extractor-model@npm:7.26.4" + dependencies: + "@microsoft/tsdoc": 0.14.2 + "@microsoft/tsdoc-config": ~0.16.1 + "@rushstack/node-core-library": 3.55.2 + checksum: 0b27f9b248396422f3044f2472d86c111a6a4d34b4fd2c67d9995e96e5b144ec41ac35dbf2a005f144766814b824518bc42b424d93e3075d3dc1b17c2f8791d6 + languageName: node + linkType: hard + +"@microsoft/api-extractor@npm:^7.33.5": + version: 7.34.4 + resolution: "@microsoft/api-extractor@npm:7.34.4" + dependencies: + "@microsoft/api-extractor-model": 7.26.4 + "@microsoft/tsdoc": 0.14.2 + "@microsoft/tsdoc-config": ~0.16.1 + "@rushstack/node-core-library": 3.55.2 + "@rushstack/rig-package": 0.3.18 + "@rushstack/ts-command-line": 4.13.2 + colors: ~1.2.1 + lodash: ~4.17.15 + resolve: ~1.22.1 + semver: ~7.3.0 + source-map: ~0.6.1 + typescript: ~4.8.4 + bin: + api-extractor: bin/api-extractor + checksum: 855a04237e30f425553aab661b77bc0cbeb493510d769691d713e5bfdf02439fa7bc2076750271d154237f8c9d4451b209f30724c9ee2a2c4ea307c5db093d42 + languageName: node + linkType: hard + +"@microsoft/tsdoc-config@npm:~0.16.1": + version: 0.16.2 + resolution: "@microsoft/tsdoc-config@npm:0.16.2" + dependencies: + "@microsoft/tsdoc": 0.14.2 + ajv: ~6.12.6 + jju: ~1.4.0 + resolve: ~1.19.0 + checksum: 12b0d703154076bcaac75ca42e804e4fc292672396441e54346d7eadd0d6b57f90980eda2b1bab89b224af86da34a2389f9054002e282011e795ca5919a4386f + languageName: node + linkType: hard + +"@microsoft/tsdoc@npm:0.14.2": + version: 0.14.2 + resolution: "@microsoft/tsdoc@npm:0.14.2" + checksum: b167c89e916ba73ee20b9c9d5dba6aa3a0de25ed3d50050e8a344dca7cd43cb2e1059bd515c820369b6e708901dd3fda476a42bc643ca74a35671ce77f724a3a + languageName: node + linkType: hard + +"@napi-rs/simple-git-android-arm-eabi@npm:0.1.8": + version: 0.1.8 + resolution: "@napi-rs/simple-git-android-arm-eabi@npm:0.1.8" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@napi-rs/simple-git-android-arm64@npm:0.1.8": + version: 0.1.8 + resolution: "@napi-rs/simple-git-android-arm64@npm:0.1.8" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/simple-git-darwin-arm64@npm:0.1.8": + version: 0.1.8 + resolution: "@napi-rs/simple-git-darwin-arm64@npm:0.1.8" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/simple-git-darwin-x64@npm:0.1.8": + version: 0.1.8 + resolution: "@napi-rs/simple-git-darwin-x64@npm:0.1.8" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/simple-git-linux-arm-gnueabihf@npm:0.1.8": + version: 0.1.8 + resolution: "@napi-rs/simple-git-linux-arm-gnueabihf@npm:0.1.8" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@napi-rs/simple-git-linux-arm64-gnu@npm:0.1.8": + version: 0.1.8 + resolution: "@napi-rs/simple-git-linux-arm64-gnu@npm:0.1.8" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/simple-git-linux-arm64-musl@npm:0.1.8": + version: 0.1.8 + resolution: "@napi-rs/simple-git-linux-arm64-musl@npm:0.1.8" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@napi-rs/simple-git-linux-x64-gnu@npm:0.1.8": + version: 0.1.8 + resolution: "@napi-rs/simple-git-linux-x64-gnu@npm:0.1.8" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/simple-git-linux-x64-musl@npm:0.1.8": + version: 0.1.8 + resolution: "@napi-rs/simple-git-linux-x64-musl@npm:0.1.8" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@napi-rs/simple-git-win32-arm64-msvc@npm:0.1.8": + version: 0.1.8 + resolution: "@napi-rs/simple-git-win32-arm64-msvc@npm:0.1.8" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/simple-git-win32-x64-msvc@npm:0.1.8": + version: 0.1.8 + resolution: "@napi-rs/simple-git-win32-x64-msvc@npm:0.1.8" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/simple-git@npm:^0.1.8": + version: 0.1.8 + resolution: "@napi-rs/simple-git@npm:0.1.8" + dependencies: + "@napi-rs/simple-git-android-arm-eabi": 0.1.8 + "@napi-rs/simple-git-android-arm64": 0.1.8 + "@napi-rs/simple-git-darwin-arm64": 0.1.8 + "@napi-rs/simple-git-darwin-x64": 0.1.8 + "@napi-rs/simple-git-linux-arm-gnueabihf": 0.1.8 + "@napi-rs/simple-git-linux-arm64-gnu": 0.1.8 + "@napi-rs/simple-git-linux-arm64-musl": 0.1.8 + "@napi-rs/simple-git-linux-x64-gnu": 0.1.8 + "@napi-rs/simple-git-linux-x64-musl": 0.1.8 + "@napi-rs/simple-git-win32-arm64-msvc": 0.1.8 + "@napi-rs/simple-git-win32-x64-msvc": 0.1.8 + dependenciesMeta: + "@napi-rs/simple-git-android-arm-eabi": + optional: true + "@napi-rs/simple-git-android-arm64": + optional: true + "@napi-rs/simple-git-darwin-arm64": + optional: true + "@napi-rs/simple-git-darwin-x64": + optional: true + "@napi-rs/simple-git-linux-arm-gnueabihf": + optional: true + "@napi-rs/simple-git-linux-arm64-gnu": + optional: true + "@napi-rs/simple-git-linux-arm64-musl": + optional: true + "@napi-rs/simple-git-linux-x64-gnu": + optional: true + "@napi-rs/simple-git-linux-x64-musl": + optional: true + "@napi-rs/simple-git-win32-arm64-msvc": + optional: true + "@napi-rs/simple-git-win32-x64-msvc": + optional: true + checksum: 82cdb16257d038e1c3fafb35dcb95ab530db416142f629c9fec5bba35299d71aea8734d963dd77845ccbab46d281d7d298abcf36818e6b0d172a93334d5b825d + languageName: node + linkType: hard + "@next/env@npm:13.0.4": version: 13.0.4 resolution: "@next/env@npm:13.0.4" @@ -4586,7 +4824,7 @@ __metadata: languageName: node linkType: hard -"@paplico/core-new@workspace:pkgs/core": +"@paplico/core-new@workspace:^, @paplico/core-new@workspace:pkgs/core": version: 0.0.0-use.local resolution: "@paplico/core-new@workspace:pkgs/core" dependencies: @@ -4606,8 +4844,9 @@ __metadata: mitt: ^3.0.0 polished: ^4.2.2 three: ^0.146.0 - typescript: ^4.9.3 + typescript: ^5.0.3 vite: ^4.0.3 + vite-plugin-dts: ^2.1.0 vite-tsconfig-paths: ^4.0.3 vitest: ^0.26.2 zod: ^3.19.1 @@ -4748,6 +4987,7 @@ __metadata: "@fleur/react": ^5.0.3 "@floating-ui/react-dom": ^0.7.1 "@hanakla/arma": ^0.3.3 + "@paplico/core-new": "workspace:^" "@popperjs/core": ^2.9.3 "@react-aria/select": ^3.6.4 "@react-stately/select": ^3.1.6 @@ -4764,8 +5004,9 @@ __metadata: "@types/jsdom": ^16.2.14 "@types/mousetrap": ^1.6.8 "@types/node": ^16.4.4 - "@types/react": ^17.0.15 + "@types/react": 18.0.28 "@types/react-color": ^3.0.6 + "@types/react-dom": 18.0.11 "@types/styled-components": 5.1.25 "@types/webpack": ^5.28.0 "@types/wicg-file-system-access": ^2020.9.5 @@ -4799,18 +5040,22 @@ __metadata: next-i18next: ^8.5.5 next-pwa: ^5.6.0 next-transpile-modules: 9.0.0 + nextra: ^2.2.19 + nextra-theme-docs: ^2.2.19 object-fit-math: ^1.0.0 polished: ^4.1.3 - react: ^18.0.0 + react: 18.2.0 react-color: ^2.19.3 react-contexify: ^5.0.0 react-devtools: ^4.24.5 - react-dom: ^18.0.0 + react-dom: 18.2.0 + react-draggable-tree: ^0.7.0 react-error-boundary: ^3.1.3 react-popper: ^2.2.5 react-rnd: ^10.3.5 react-spring: ^9.2.4 react-use: ^17.2.4 + react-use-event-hook: ^0.9.5 react-use-gesture: ^9.1.3 slate: ^0.65.3 slate-react: ^0.65.3 @@ -4829,6 +5074,13 @@ __metadata: languageName: unknown linkType: soft +"@popperjs/core@npm:^2.11.6": + version: 2.11.6 + resolution: "@popperjs/core@npm:2.11.6" + checksum: 47fb328cec1924559d759b48235c78574f2d71a8a6c4c03edb6de5d7074078371633b91e39bbf3f901b32aa8af9b9d8f82834856d2f5737a23475036b16817f0 + languageName: node + linkType: hard + "@popperjs/core@npm:^2.9.3": version: 2.9.3 resolution: "@popperjs/core@npm:2.9.3" @@ -4836,6 +5088,326 @@ __metadata: languageName: node linkType: hard +"@radix-ui/number@npm:1.0.0": + version: 1.0.0 + resolution: "@radix-ui/number@npm:1.0.0" + dependencies: + "@babel/runtime": ^7.13.10 + checksum: 517ac0790e05cceb41401154d1bc55d4738accd51095e2a918ef9bcedac6a455cd7179201e88e76121bedec19cd93a37b2c20288b084fb224b69c74e67935457 + languageName: node + linkType: hard + +"@radix-ui/primitive@npm:1.0.0": + version: 1.0.0 + resolution: "@radix-ui/primitive@npm:1.0.0" + dependencies: + "@babel/runtime": ^7.13.10 + checksum: 72996afaf346ec4f4c73422f14f6cb2d0de994801ba7cbb9a4a67b0050e0cd74625182c349ef8017ccae1406579d4b74a34a225ef2efe61e8e5337decf235deb + languageName: node + linkType: hard + +"@radix-ui/react-arrow@npm:1.0.2": + version: 1.0.2 + resolution: "@radix-ui/react-arrow@npm:1.0.2" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-primitive": 1.0.2 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + checksum: d9c4a810376b686edfedfab15c3ef739bb2b388211fbf33191648149aba5064bfff8a5de05b264ad0b76c4a4df98fd8267002580e3515b7f5ad31b9495bfda21 + languageName: node + linkType: hard + +"@radix-ui/react-compose-refs@npm:1.0.0": + version: 1.0.0 + resolution: "@radix-ui/react-compose-refs@npm:1.0.0" + dependencies: + "@babel/runtime": ^7.13.10 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + checksum: fb98be2e275a1a758ccac647780ff5b04be8dcf25dcea1592db3b691fecf719c4c0700126da605b2f512dd89caa111352b9fad59528d736b4e0e9a0e134a74a1 + languageName: node + linkType: hard + +"@radix-ui/react-context@npm:1.0.0": + version: 1.0.0 + resolution: "@radix-ui/react-context@npm:1.0.0" + dependencies: + "@babel/runtime": ^7.13.10 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + checksum: 43c6b6f2183398161fe6b109e83fff240a6b7babbb27092b815932342a89d5ca42aa9806bfae5927970eed5ff90feed04c67aa29c6721f84ae826f17fcf34ce0 + languageName: node + linkType: hard + +"@radix-ui/react-direction@npm:1.0.0": + version: 1.0.0 + resolution: "@radix-ui/react-direction@npm:1.0.0" + dependencies: + "@babel/runtime": ^7.13.10 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + checksum: 92a40de4087b161a56957872daf204a7735bd21f2fccbd42deff322d759977d085ad3dcdae05af437b7e64e628e939e0d67e5bc468a3027e1b02e0a7dc90c485 + languageName: node + linkType: hard + +"@radix-ui/react-dismissable-layer@npm:1.0.3": + version: 1.0.3 + resolution: "@radix-ui/react-dismissable-layer@npm:1.0.3" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/primitive": 1.0.0 + "@radix-ui/react-compose-refs": 1.0.0 + "@radix-ui/react-primitive": 1.0.2 + "@radix-ui/react-use-callback-ref": 1.0.0 + "@radix-ui/react-use-escape-keydown": 1.0.2 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + checksum: cb2a38a65dd129d1fd58436bedee765f46f6a6edc2ec15d534a1499c10f768ae06ad874704e030c85869b3ee4b61103076a116dfdb7e0c761a8c8cdc30a5c951 + languageName: node + linkType: hard + +"@radix-ui/react-focus-guards@npm:1.0.0": + version: 1.0.0 + resolution: "@radix-ui/react-focus-guards@npm:1.0.0" + dependencies: + "@babel/runtime": ^7.13.10 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + checksum: 8c714e8caa6032f5402eecb0323addd7456d3496946dbad1b9ee8ebf5845943876945e7af9bca179e9f8ffe5100e61cb4ba54a185873949125c310c406be5aa4 + languageName: node + linkType: hard + +"@radix-ui/react-focus-scope@npm:1.0.2": + version: 1.0.2 + resolution: "@radix-ui/react-focus-scope@npm:1.0.2" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-compose-refs": 1.0.0 + "@radix-ui/react-primitive": 1.0.2 + "@radix-ui/react-use-callback-ref": 1.0.0 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + checksum: f04f7412c8d9d2e0f431a0360ec10415f085a530322f693220e0ad36ad8ffd9f637c4b0d9bc35da09621ac0ad97ff33d382c84853c827825a9f9c924843fd339 + languageName: node + linkType: hard + +"@radix-ui/react-id@npm:1.0.0": + version: 1.0.0 + resolution: "@radix-ui/react-id@npm:1.0.0" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-use-layout-effect": 1.0.0 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + checksum: ba323cedd6a6df6f6e51ed1f7f7747988ce432b47fd94d860f962b14b342dcf049eae33f8ad0b72fd7df6329a7375542921132271fba64ab0a271c93f09c48d1 + languageName: node + linkType: hard + +"@radix-ui/react-popover@npm:latest": + version: 1.0.5 + resolution: "@radix-ui/react-popover@npm:1.0.5" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/primitive": 1.0.0 + "@radix-ui/react-compose-refs": 1.0.0 + "@radix-ui/react-context": 1.0.0 + "@radix-ui/react-dismissable-layer": 1.0.3 + "@radix-ui/react-focus-guards": 1.0.0 + "@radix-ui/react-focus-scope": 1.0.2 + "@radix-ui/react-id": 1.0.0 + "@radix-ui/react-popper": 1.1.1 + "@radix-ui/react-portal": 1.0.2 + "@radix-ui/react-presence": 1.0.0 + "@radix-ui/react-primitive": 1.0.2 + "@radix-ui/react-slot": 1.0.1 + "@radix-ui/react-use-controllable-state": 1.0.0 + aria-hidden: ^1.1.1 + react-remove-scroll: 2.5.5 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + checksum: 5a748af34b9889350c0f4dc0a15b1d09c3ab8a8bacc6e7a358c70341de94acbbdb07aafbeadfbe0371cf77dabdaf4e273bbcf742f5d67f29543eb8cea58ee2a9 + languageName: node + linkType: hard + +"@radix-ui/react-popper@npm:1.1.1": + version: 1.1.1 + resolution: "@radix-ui/react-popper@npm:1.1.1" + dependencies: + "@babel/runtime": ^7.13.10 + "@floating-ui/react-dom": 0.7.2 + "@radix-ui/react-arrow": 1.0.2 + "@radix-ui/react-compose-refs": 1.0.0 + "@radix-ui/react-context": 1.0.0 + "@radix-ui/react-primitive": 1.0.2 + "@radix-ui/react-use-callback-ref": 1.0.0 + "@radix-ui/react-use-layout-effect": 1.0.0 + "@radix-ui/react-use-rect": 1.0.0 + "@radix-ui/react-use-size": 1.0.0 + "@radix-ui/rect": 1.0.0 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + checksum: d9be4be002116efa50dc91b4e2e99150cb64db5ac440c4ad85efbd8d36a99615fc316bddae5ccad6a06807242b1ba23ab04e57cf82f5c92f1bcc5d662c77be94 + languageName: node + linkType: hard + +"@radix-ui/react-portal@npm:1.0.2": + version: 1.0.2 + resolution: "@radix-ui/react-portal@npm:1.0.2" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-primitive": 1.0.2 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + checksum: 1165b4bced8057021ea9ac4f568c6e0ea6f190936f07dc96780d67488b9222021444bbb8e04e506eea84d9219a5caacae8d0974e745182d4f398aa903b982e19 + languageName: node + linkType: hard + +"@radix-ui/react-presence@npm:1.0.0": + version: 1.0.0 + resolution: "@radix-ui/react-presence@npm:1.0.0" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-compose-refs": 1.0.0 + "@radix-ui/react-use-layout-effect": 1.0.0 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + checksum: a607d67795aa265e88f1765dcc7c18bebf6d88d116cb7f529ebe5a3fbbe751a42763aff0c1c89cdd8ce7f7664355936c4070fd3d4685774aff1a80fa95f4665b + languageName: node + linkType: hard + +"@radix-ui/react-primitive@npm:1.0.2": + version: 1.0.2 + resolution: "@radix-ui/react-primitive@npm:1.0.2" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-slot": 1.0.1 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + checksum: 070b1770749eb629425ef959c4cdbd86957b83c5286ae4423e55ab1a89fa286a51f5aeee44e3a13eb6ca44771415ac1acbaeb0ba03013b49ecb5253e1a5a8996 + languageName: node + linkType: hard + +"@radix-ui/react-scroll-area@npm:^1.0.3": + version: 1.0.3 + resolution: "@radix-ui/react-scroll-area@npm:1.0.3" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/number": 1.0.0 + "@radix-ui/primitive": 1.0.0 + "@radix-ui/react-compose-refs": 1.0.0 + "@radix-ui/react-context": 1.0.0 + "@radix-ui/react-direction": 1.0.0 + "@radix-ui/react-presence": 1.0.0 + "@radix-ui/react-primitive": 1.0.2 + "@radix-ui/react-use-callback-ref": 1.0.0 + "@radix-ui/react-use-layout-effect": 1.0.0 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + checksum: 54f6d604f9ef6112ce44f0ccaf2dea96ec22ae94d67fe822d8e6388c15b647a33bbdc448d1fc86d870ee6e79a8f531bd4b171b51d0b140272d429e1b941ee12a + languageName: node + linkType: hard + +"@radix-ui/react-slot@npm:1.0.1": + version: 1.0.1 + resolution: "@radix-ui/react-slot@npm:1.0.1" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-compose-refs": 1.0.0 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + checksum: a20693f8ce532bd6cbff12ba543dfcf90d451f22923bd60b57dc9e639f6e53348915e182002b33444feb6ab753434e78e2a54085bf7092aadda4418f0423763f + languageName: node + linkType: hard + +"@radix-ui/react-use-callback-ref@npm:1.0.0": + version: 1.0.0 + resolution: "@radix-ui/react-use-callback-ref@npm:1.0.0" + dependencies: + "@babel/runtime": ^7.13.10 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + checksum: a8dda76ba0a26e23dc6ab5003831ad7439f59ba9d696a517643b9ee6a7fb06b18ae7a8f5a3c00c530d5c8104745a466a077b7475b99b4c0f5c15f5fc29474471 + languageName: node + linkType: hard + +"@radix-ui/react-use-controllable-state@npm:1.0.0": + version: 1.0.0 + resolution: "@radix-ui/react-use-controllable-state@npm:1.0.0" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-use-callback-ref": 1.0.0 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + checksum: 35f1e714bbe3fc9f5362a133339dd890fb96edb79b63168a99403c65dd5f2b63910e0c690255838029086719e31360fa92544a55bc902cfed4442bb3b55822e2 + languageName: node + linkType: hard + +"@radix-ui/react-use-escape-keydown@npm:1.0.2": + version: 1.0.2 + resolution: "@radix-ui/react-use-escape-keydown@npm:1.0.2" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-use-callback-ref": 1.0.0 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + checksum: 5bec1b73ed6c38139bf1db3c626c0474ca6221ae55f154ef83f1c6429ea866280b2a0ba9436b807334d0215bb4389f0b492c65471cf565635957a8ee77cce98a + languageName: node + linkType: hard + +"@radix-ui/react-use-layout-effect@npm:1.0.0": + version: 1.0.0 + resolution: "@radix-ui/react-use-layout-effect@npm:1.0.0" + dependencies: + "@babel/runtime": ^7.13.10 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + checksum: fcdc8cfa79bd45766ebe3de11039c58abe3fed968cb39c12b2efce5d88013c76fe096ea4cee464d42576d02fe7697779b682b4268459bca3c4e48644f5b4ac5e + languageName: node + linkType: hard + +"@radix-ui/react-use-rect@npm:1.0.0": + version: 1.0.0 + resolution: "@radix-ui/react-use-rect@npm:1.0.0" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/rect": 1.0.0 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + checksum: c755cee1a8846a74d4f6f486c65134a552c65d0bfb934d1d3d4f69f331c32cfd8b279c08c8907d64fbb68388fc3683f854f336e4f9549e1816fba32156bb877b + languageName: node + linkType: hard + +"@radix-ui/react-use-size@npm:1.0.0": + version: 1.0.0 + resolution: "@radix-ui/react-use-size@npm:1.0.0" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-use-layout-effect": 1.0.0 + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + checksum: b319564668512bb5c8c64530e3c12810c4b7c75c19a00d5ef758c246e8d85cd5015df19688e174db1cc44b0584c8d7f22411eb00af5f8ac6c2e789aa5c8e34f5 + languageName: node + linkType: hard + +"@radix-ui/rect@npm:1.0.0": + version: 1.0.0 + resolution: "@radix-ui/rect@npm:1.0.0" + dependencies: + "@babel/runtime": ^7.13.10 + checksum: d5b54984148ac52e30c6a92834deb619cf74b4af02709a20eb43e7895f98fed098968b597a715bf5b5431ae186372e65499a801d93e835f53bbc39e3a549f664 + languageName: node + linkType: hard + "@react-aria/focus@npm:^3.5.4": version: 3.5.4 resolution: "@react-aria/focus@npm:3.5.4" @@ -5527,7 +6099,7 @@ __metadata: languageName: node linkType: hard -"@rollup/pluginutils@npm:^5.0.1": +"@rollup/pluginutils@npm:^5.0.1, @rollup/pluginutils@npm:^5.0.2": version: 5.0.2 resolution: "@rollup/pluginutils@npm:5.0.2" dependencies: @@ -5543,10 +6115,52 @@ __metadata: languageName: node linkType: hard -"@sinclair/typebox@npm:^0.23.3": - version: 0.23.5 - resolution: "@sinclair/typebox@npm:0.23.5" - checksum: c96056d35d9cb862aeb635ff8873e2e7633e668dd544e162aee2690a82c970d0b3f90aa2b3501fe374dfa8e792388559a3e3a86712b23ebaef10061add534f47 +"@rushstack/node-core-library@npm:3.55.2, @rushstack/node-core-library@npm:^3.53.2": + version: 3.55.2 + resolution: "@rushstack/node-core-library@npm:3.55.2" + dependencies: + colors: ~1.2.1 + fs-extra: ~7.0.1 + import-lazy: ~4.0.0 + jju: ~1.4.0 + resolve: ~1.22.1 + semver: ~7.3.0 + z-schema: ~5.0.2 + peerDependencies: + "@types/node": "*" + peerDependenciesMeta: + "@types/node": + optional: true + checksum: b6b289315cd6a3544471f534405479b7c80f3906b7506590d5eb83a605a0a5b65861bf678f3e6c2277c8db116b8e05f8e7b6864fdc75e0f1514c9ff224b83fe6 + languageName: node + linkType: hard + +"@rushstack/rig-package@npm:0.3.18": + version: 0.3.18 + resolution: "@rushstack/rig-package@npm:0.3.18" + dependencies: + resolve: ~1.22.1 + strip-json-comments: ~3.1.1 + checksum: 41e719fb14d99e0f79093523fede051dba2be5f53d63d5ae45c2ea1b8448e298e0303d8453be3f9ac5e8bb99e3dcf3ddbe4cc59d9be5eddb914286acbdd0a2f3 + languageName: node + linkType: hard + +"@rushstack/ts-command-line@npm:4.13.2": + version: 4.13.2 + resolution: "@rushstack/ts-command-line@npm:4.13.2" + dependencies: + "@types/argparse": 1.0.38 + argparse: ~1.0.9 + colors: ~1.2.1 + string-argv: ~0.3.1 + checksum: 3938e533e08d5cf4007a651d1aab658a7a60d6136a56414e2370b64434657a5d5a9eff442da4ddc260d5e6dc90f82428de64dbcfa1285e9ae176629f7fcd821d + languageName: node + linkType: hard + +"@sinclair/typebox@npm:^0.23.3": + version: 0.23.5 + resolution: "@sinclair/typebox@npm:0.23.5" + checksum: c96056d35d9cb862aeb635ff8873e2e7633e668dd544e162aee2690a82c970d0b3f90aa2b3501fe374dfa8e792388559a3e3a86712b23ebaef10061add534f47 languageName: node linkType: hard @@ -6164,6 +6778,18 @@ __metadata: languageName: node linkType: hard +"@ts-morph/common@npm:~0.18.0": + version: 0.18.1 + resolution: "@ts-morph/common@npm:0.18.1" + dependencies: + fast-glob: ^3.2.12 + minimatch: ^5.1.0 + mkdirp: ^1.0.4 + path-browserify: ^1.0.1 + checksum: 848fff5f7a6428d7c2f055de20cf8df864a967aac0cc03adc558d853442085a8fd9dec70429da24d67d263794b315edb0791c46d23ad9ae513251a7702df8031 + languageName: node + linkType: hard + "@tsconfig/node10@npm:^1.0.7": version: 1.0.8 resolution: "@tsconfig/node10@npm:1.0.8" @@ -6192,6 +6818,22 @@ __metadata: languageName: node linkType: hard +"@types/acorn@npm:^4.0.0": + version: 4.0.6 + resolution: "@types/acorn@npm:4.0.6" + dependencies: + "@types/estree": "*" + checksum: 60e1fd28af18d6cb54a93a7231c7c18774a9a8739c9b179e9e8750dca631e10cbef2d82b02830ea3f557b1d121e6406441e9e1250bd492dc81d4b3456e76e4d4 + languageName: node + linkType: hard + +"@types/argparse@npm:1.0.38": + version: 1.0.38 + resolution: "@types/argparse@npm:1.0.38" + checksum: 26ed7e3f1e3595efdb883a852f5205f971b798e4c28b7e30a32c5298eee596e8b45834ce831f014d250b9730819ab05acff5b31229666d3af4ba465b4697d0eb + languageName: node + linkType: hard + "@types/audioworklet@npm:^0.0.27": version: 0.0.27 resolution: "@types/audioworklet@npm:0.0.27" @@ -6329,7 +6971,7 @@ __metadata: languageName: node linkType: hard -"@types/debug@npm:^4.1.6": +"@types/debug@npm:^4.0.0, @types/debug@npm:^4.1.6": version: 4.1.7 resolution: "@types/debug@npm:4.1.7" dependencies: @@ -6374,6 +7016,15 @@ __metadata: languageName: node linkType: hard +"@types/estree-jsx@npm:^1.0.0": + version: 1.0.0 + resolution: "@types/estree-jsx@npm:1.0.0" + dependencies: + "@types/estree": "*" + checksum: 851d7afb63a89fb9ce7822563930660433f29106d72db279ce9c99f791ec996ef21b05adc6f545325cd1745b3041cc86422f0ffa39a06734305b90cfbc871765 + languageName: node + linkType: hard + "@types/estree@npm:*": version: 0.0.50 resolution: "@types/estree@npm:0.0.50" @@ -6462,6 +7113,15 @@ __metadata: languageName: node linkType: hard +"@types/hast@npm:^2.0.0": + version: 2.3.4 + resolution: "@types/hast@npm:2.3.4" + dependencies: + "@types/unist": "*" + checksum: fff47998f4c11e21a7454b58673f70478740ecdafd95aaf50b70a3daa7da9cdc57315545bf9c039613732c40b7b0e9e49d11d03fe9a4304721cdc3b29a88141e + languageName: node + linkType: hard + "@types/hoist-non-react-statics@npm:*, @types/hoist-non-react-statics@npm:^3.3.1": version: 3.3.1 resolution: "@types/hoist-non-react-statics@npm:3.3.1" @@ -6547,6 +7207,13 @@ __metadata: languageName: node linkType: hard +"@types/js-yaml@npm:^4.0.0": + version: 4.0.5 + resolution: "@types/js-yaml@npm:4.0.5" + checksum: 7dcac8c50fec31643cc9d6444b5503239a861414cdfaa7ae9a38bc22597c4d850c4b8cec3d82d73b3fbca408348ce223b0408d598b32e094470dfffc6d486b4d + languageName: node + linkType: hard + "@types/jsdom@npm:^16.2.14, @types/jsdom@npm:^16.2.4": version: 16.2.14 resolution: "@types/jsdom@npm:16.2.14" @@ -6572,6 +7239,13 @@ __metadata: languageName: node linkType: hard +"@types/katex@npm:^0.11.0": + version: 0.11.1 + resolution: "@types/katex@npm:0.11.1" + checksum: 1e51988b4b386a1b6fa8e22826ab4705bf3e6c9fb03461f2c91d28cb31095232bdeff491069ac9bc74bc4c26110be6a11a41e12ca77a2e4169f3afd8cd349355 + languageName: node + linkType: hard + "@types/keyv@npm:^3.1.1": version: 3.1.4 resolution: "@types/keyv@npm:3.1.4" @@ -6588,6 +7262,22 @@ __metadata: languageName: node linkType: hard +"@types/mdast@npm:^3.0.0": + version: 3.0.10 + resolution: "@types/mdast@npm:3.0.10" + dependencies: + "@types/unist": "*" + checksum: 3f587bfc0a9a2403ecadc220e61031b01734fedaf82e27eb4d5ba039c0eb54db8c85681ccc070ab4df3f7ec711b736a82b990e69caa14c74bf7ac0ccf2ac7313 + languageName: node + linkType: hard + +"@types/mdx@npm:^2.0.0": + version: 2.0.3 + resolution: "@types/mdx@npm:2.0.3" + checksum: 41deb51c29535913af01d25f0e1414cfb5a6948d0e60e77e4aca895694de48bf0ac69c5a81fe2d9617d726cb253001ef82a65b683d5ef51987d15aa1931d086b + languageName: node + linkType: hard + "@types/mime@npm:^1": version: 1.3.2 resolution: "@types/mime@npm:1.3.2" @@ -6736,6 +7426,15 @@ __metadata: languageName: node linkType: hard +"@types/react-dom@npm:18.0.11": + version: 18.0.11 + resolution: "@types/react-dom@npm:18.0.11" + dependencies: + "@types/react": "*" + checksum: 579691e4d5ec09688087568037c35edf8cfb1ab3e07f6c60029280733ee7b5c06d66df6fcc90786702c93ac8cb13bc7ff16c79ddfc75d082938fbaa36e1cdbf4 + languageName: node + linkType: hard + "@types/react-dom@npm:^16.9.9": version: 16.9.17 resolution: "@types/react-dom@npm:16.9.17" @@ -6754,7 +7453,7 @@ __metadata: languageName: node linkType: hard -"@types/react@npm:*, @types/react@npm:^17.0.15": +"@types/react@npm:*": version: 17.0.18 resolution: "@types/react@npm:17.0.18" dependencies: @@ -6765,6 +7464,17 @@ __metadata: languageName: node linkType: hard +"@types/react@npm:18.0.28, @types/react@npm:>=16": + version: 18.0.28 + resolution: "@types/react@npm:18.0.28" + dependencies: + "@types/prop-types": "*" + "@types/scheduler": "*" + csstype: ^3.0.2 + checksum: e752df961105e5127652460504785897ca6e77259e0da8f233f694f9e8f451cde7fa0709d4456ade0ff600c8ce909cfe29f9b08b9c247fa9b734e126ec53edd7 + languageName: node + linkType: hard + "@types/react@npm:^16, @types/react@npm:^16.9.9": version: 16.14.34 resolution: "@types/react@npm:16.14.34" @@ -6885,6 +7595,13 @@ __metadata: languageName: node linkType: hard +"@types/unist@npm:*, @types/unist@npm:^2.0.0": + version: 2.0.6 + resolution: "@types/unist@npm:2.0.6" + checksum: 25cb860ff10dde48b54622d58b23e66214211a61c84c0f15f88d38b61aa1b53d4d46e42b557924a93178c501c166aa37e28d7f6d994aba13d24685326272d5db + languageName: node + linkType: hard + "@types/uuid@npm:8.3.1": version: 8.3.1 resolution: "@types/uuid@npm:8.3.1" @@ -7243,7 +7960,7 @@ __metadata: languageName: node linkType: hard -"acorn-jsx@npm:^5.2.0": +"acorn-jsx@npm:^5.0.0, acorn-jsx@npm:^5.2.0": version: 5.3.2 resolution: "acorn-jsx@npm:5.3.2" peerDependencies: @@ -7284,6 +8001,15 @@ __metadata: languageName: node linkType: hard +"acorn@npm:^8.0.0": + version: 8.8.2 + resolution: "acorn@npm:8.8.2" + bin: + acorn: bin/acorn + checksum: f790b99a1bf63ef160c967e23c46feea7787e531292bb827126334612c234ed489a0dc2c7ba33156416f0ffa8d25bf2b0fdb7f35c2ba60eb3e960572bece4001 + languageName: node + linkType: hard + "acorn@npm:^8.2.4": version: 8.4.1 resolution: "acorn@npm:8.4.1" @@ -7362,7 +8088,7 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^6.10.0, ajv@npm:^6.12.0, ajv@npm:^6.12.4, ajv@npm:^6.12.5": +"ajv@npm:^6.10.0, ajv@npm:^6.12.0, ajv@npm:^6.12.4, ajv@npm:^6.12.5, ajv@npm:~6.12.6": version: 6.12.6 resolution: "ajv@npm:6.12.6" dependencies: @@ -7446,6 +8172,13 @@ __metadata: languageName: node linkType: hard +"ansi-sequence-parser@npm:^1.1.0": + version: 1.1.0 + resolution: "ansi-sequence-parser@npm:1.1.0" + checksum: 75f4d3a4c555655a698aec05b5763cbddcd16ccccdbfd178fb0aa471ab74fdf98e031b875ef26e64be6a95cf970c89238744b26de6e34af97f316d5186b1df53 + languageName: node + linkType: hard + "ansi-styles@npm:^2.2.1": version: 2.2.1 resolution: "ansi-styles@npm:2.2.1" @@ -7453,7 +8186,7 @@ __metadata: languageName: node linkType: hard -"ansi-styles@npm:^3.2.0, ansi-styles@npm:^3.2.1": +"ansi-styles@npm:^3.1.0, ansi-styles@npm:^3.2.0, ansi-styles@npm:^3.2.1": version: 3.2.1 resolution: "ansi-styles@npm:3.2.1" dependencies: @@ -7543,6 +8276,13 @@ __metadata: languageName: node linkType: hard +"arch@npm:^2.1.0": + version: 2.2.0 + resolution: "arch@npm:2.2.0" + checksum: e21b7635029fe8e9cdd5a026f9a6c659103e63fff423834323cdf836a1bb240a72d0c39ca8c470f84643385cf581bd8eda2cad8bf493e27e54bd9783abe9101f + languageName: node + linkType: hard + "are-we-there-yet@npm:^3.0.0": version: 3.0.1 resolution: "are-we-there-yet@npm:3.0.1" @@ -7553,6 +8293,13 @@ __metadata: languageName: node linkType: hard +"arg@npm:1.0.0": + version: 1.0.0 + resolution: "arg@npm:1.0.0" + checksum: 0a35939e3cb59d4f18207884563e00b0f71647becccec932348e9c5bdecfe6ca41b2ed854bbb267a2e87f97f605ed3ca936f2f0b3313bf88be6d7cdbb2d0a4b1 + languageName: node + linkType: hard + "arg@npm:^4.1.0": version: 4.1.3 resolution: "arg@npm:4.1.3" @@ -7560,7 +8307,7 @@ __metadata: languageName: node linkType: hard -"argparse@npm:^1.0.7": +"argparse@npm:^1.0.7, argparse@npm:~1.0.9": version: 1.0.10 resolution: "argparse@npm:1.0.10" dependencies: @@ -7576,6 +8323,15 @@ __metadata: languageName: node linkType: hard +"aria-hidden@npm:^1.1.1": + version: 1.2.3 + resolution: "aria-hidden@npm:1.2.3" + dependencies: + tslib: ^2.0.0 + checksum: 7d7d211629eef315e94ed3b064c6823d13617e609d3f9afab1c2ed86399bb8e90405f9bdd358a85506802766f3ecb468af985c67c846045a34b973bcc0289db9 + languageName: node + linkType: hard + "array-flatten@npm:1.1.1": version: 1.1.1 resolution: "array-flatten@npm:1.1.1" @@ -7652,6 +8408,15 @@ __metadata: languageName: node linkType: hard +"astring@npm:^1.8.0": + version: 1.8.4 + resolution: "astring@npm:1.8.4" + bin: + astring: bin/astring + checksum: bc0b98087350c4a0c8a510d491d648cf8b299ec904629d5e0f5ae8d2ccc515cd27475327bb9729c7e92f4a4873adcd05cef15379d0f6f7293f1320319f0d24f0 + languageName: node + linkType: hard + "async-exit-hook@npm:^2.0.1": version: 2.0.1 resolution: "async-exit-hook@npm:2.0.1" @@ -7992,6 +8757,13 @@ __metadata: languageName: node linkType: hard +"bail@npm:^2.0.0": + version: 2.0.2 + resolution: "bail@npm:2.0.2" + checksum: aab4e8ccdc8d762bf3fdfce8e706601695620c0c2eda256dd85088dc0be3cfd7ff126f6e99c2bee1f24f5d418414aacf09d7f9702f16d6963df2fa488cda8824 + languageName: node + linkType: hard + "balanced-match@npm:^1.0.0": version: 1.0.2 resolution: "balanced-match@npm:1.0.2" @@ -8594,6 +9366,13 @@ __metadata: languageName: node linkType: hard +"ccount@npm:^2.0.0": + version: 2.0.1 + resolution: "ccount@npm:2.0.1" + checksum: 48193dada54c9e260e0acf57fc16171a225305548f9ad20d5471e0f7a8c026aedd8747091dccb0d900cde7df4e4ddbd235df0d8de4a64c71b12f0d3303eeafd4 + languageName: node + linkType: hard + "chai@npm:^4.3.6": version: 4.3.6 resolution: "chai@npm:4.3.6" @@ -8624,6 +9403,17 @@ __metadata: languageName: node linkType: hard +"chalk@npm:2.3.0": + version: 2.3.0 + resolution: "chalk@npm:2.3.0" + dependencies: + ansi-styles: ^3.1.0 + escape-string-regexp: ^1.0.5 + supports-color: ^4.0.0 + checksum: d348fc0f4f8d27c068a6ac492e708fa35a75e273d5f0004da61ea694e958981658c96693790f4d23e7b3712f9e3e4ca0988136cb0403876de5459a4c0d13078f + languageName: node + linkType: hard + "chalk@npm:^1.1.3": version: 1.1.3 resolution: "chalk@npm:1.1.3" @@ -8675,6 +9465,34 @@ __metadata: languageName: node linkType: hard +"character-entities-html4@npm:^2.0.0": + version: 2.1.0 + resolution: "character-entities-html4@npm:2.1.0" + checksum: 7034aa7c7fa90309667f6dd50499c8a760c3d3a6fb159adb4e0bada0107d194551cdbad0714302f62d06ce4ed68565c8c2e15fdef2e8f8764eb63fa92b34b11d + languageName: node + linkType: hard + +"character-entities-legacy@npm:^3.0.0": + version: 3.0.0 + resolution: "character-entities-legacy@npm:3.0.0" + checksum: 7582af055cb488b626d364b7d7a4e46b06abd526fb63c0e4eb35bcb9c9799cc4f76b39f34fdccef2d1174ac95e53e9ab355aae83227c1a2505877893fce77731 + languageName: node + linkType: hard + +"character-entities@npm:^2.0.0": + version: 2.0.2 + resolution: "character-entities@npm:2.0.2" + checksum: cf1643814023697f725e47328fcec17923b8f1799102a8a79c1514e894815651794a2bffd84bb1b3a4b124b050154e4529ed6e81f7c8068a734aecf07a6d3def + languageName: node + linkType: hard + +"character-reference-invalid@npm:^2.0.0": + version: 2.0.1 + resolution: "character-reference-invalid@npm:2.0.1" + checksum: 98d3b1a52ae510b7329e6ee7f6210df14f1e318c5415975d4c9e7ee0ef4c07875d47c6e74230c64551f12f556b4a8ccc24d9f3691a2aa197019e72a95e9297ee + languageName: node + linkType: hard + "check-error@npm:^1.0.2": version: 1.0.2 resolution: "check-error@npm:1.0.2" @@ -8801,13 +9619,23 @@ __metadata: languageName: node linkType: hard -"client-only@npm:0.0.1": +"client-only@npm:0.0.1, client-only@npm:^0.0.1": version: 0.0.1 resolution: "client-only@npm:0.0.1" checksum: 0c16bf660dadb90610553c1d8946a7fdfb81d624adea073b8440b7d795d5b5b08beb3c950c6a2cf16279365a3265158a236876d92bce16423c485c322d7dfaf8 languageName: node linkType: hard +"clipboardy@npm:1.2.2": + version: 1.2.2 + resolution: "clipboardy@npm:1.2.2" + dependencies: + arch: ^2.1.0 + execa: ^0.8.0 + checksum: 7744a95e014e5945f17554b426fe78c6e1329ead85a61fa589dd4c3f4a596a037d45cad0bfaa3e60bdf77717f0dfda67c35454216f4dbb7832ec0eba42ba8a06 + languageName: node + linkType: hard + "cliui@npm:^5.0.0": version: 5.0.0 resolution: "cliui@npm:5.0.0" @@ -8871,6 +9699,13 @@ __metadata: languageName: node linkType: hard +"clsx@npm:^1.2.1": + version: 1.2.1 + resolution: "clsx@npm:1.2.1" + checksum: 30befca8019b2eb7dbad38cff6266cf543091dae2825c856a62a8ccf2c3ab9c2907c4d12b288b73101196767f66812365400a227581484a05f968b0307cfaf12 + languageName: node + linkType: hard + "co@npm:^4.6.0": version: 4.6.0 resolution: "co@npm:4.6.0" @@ -8889,6 +9724,13 @@ __metadata: languageName: node linkType: hard +"code-block-writer@npm:^11.0.3": + version: 11.0.3 + resolution: "code-block-writer@npm:11.0.3" + checksum: f0a2605f19963d7087267c9b0fd0b05a6638a50e7b29b70f97aa01a514f59475b0626f8aa092188df853ee6d96745426dfa132d6a677795df462c6ce32c21639 + languageName: node + linkType: hard + "collect-v8-coverage@npm:^1.0.0": version: 1.0.1 resolution: "collect-v8-coverage@npm:1.0.1" @@ -8978,6 +9820,13 @@ __metadata: languageName: node linkType: hard +"colors@npm:~1.2.1": + version: 1.2.5 + resolution: "colors@npm:1.2.5" + checksum: b6e23de735f68b72d5cdf6fd854ca43d1b66d82dcf54bda0b788083b910164a040f2c4edf23c670d36a7a2d8f1b7d6e62e3292703e4642691e6ccaa1c62d8f74 + languageName: node + linkType: hard + "combined-stream@npm:^1.0.8": version: 1.0.8 resolution: "combined-stream@npm:1.0.8" @@ -8987,6 +9836,13 @@ __metadata: languageName: node linkType: hard +"comma-separated-tokens@npm:^2.0.0": + version: 2.0.3 + resolution: "comma-separated-tokens@npm:2.0.3" + checksum: e3bf9e0332a5c45f49b90e79bcdb4a7a85f28d6a6f0876a94f1bb9b2bfbdbbb9292aac50e1e742d8c0db1e62a0229a106f57917e2d067fca951d81737651700d + languageName: node + linkType: hard + "commander@npm:2.9.0": version: 2.9.0 resolution: "commander@npm:2.9.0" @@ -9010,6 +9866,20 @@ __metadata: languageName: node linkType: hard +"commander@npm:^8.0.0": + version: 8.3.0 + resolution: "commander@npm:8.3.0" + checksum: 0f82321821fc27b83bd409510bb9deeebcfa799ff0bf5d102128b500b7af22872c0c92cb6a0ebc5a4cf19c6b550fba9cedfa7329d18c6442a625f851377bacf0 + languageName: node + linkType: hard + +"commander@npm:^9.4.1": + version: 9.5.0 + resolution: "commander@npm:9.5.0" + checksum: c7a3e27aa59e913b54a1bafd366b88650bc41d6651f0cbe258d4ff09d43d6a7394232a4dadd0bf518b3e696fdf595db1028a0d82c785b88bd61f8a440cecfade + languageName: node + linkType: hard + "common-tags@npm:^1.8.0": version: 1.8.0 resolution: "common-tags@npm:1.8.0" @@ -9038,6 +9908,13 @@ __metadata: languageName: node linkType: hard +"compute-scroll-into-view@npm:^3.0.0": + version: 3.0.0 + resolution: "compute-scroll-into-view@npm:3.0.0" + checksum: 06965595510d3190bfb58705cf74bacc0b6fea8021f56a6477ad134fadcd1971d2083a714c6e3c99f545cc72614d60a9a97d774ea81a37ad302efddc849d372c + languageName: node + linkType: hard + "concat-map@npm:0.0.1": version: 0.0.1 resolution: "concat-map@npm:0.0.1" @@ -9609,27 +10486,27 @@ __metadata: languageName: node linkType: hard -"debug@npm:^4.1.0": - version: 4.3.3 - resolution: "debug@npm:4.3.3" +"debug@npm:^4.0.0, debug@npm:^4.3.3, debug@npm:^4.3.4": + version: 4.3.4 + resolution: "debug@npm:4.3.4" dependencies: ms: 2.1.2 peerDependenciesMeta: supports-color: optional: true - checksum: 14472d56fe4a94dbcfaa6dbed2dd3849f1d72ba78104a1a328047bb564643ca49df0224c3a17fa63533fd11dd3d4c8636cd861191232a2c6735af00cc2d4de16 + checksum: 3dbad3f94ea64f34431a9cbf0bafb61853eda57bff2880036153438f50fb5a84f27683ba0d8e5426bf41a8c6ff03879488120cf5b3a761e77953169c0600a708 languageName: node linkType: hard -"debug@npm:^4.3.3, debug@npm:^4.3.4": - version: 4.3.4 - resolution: "debug@npm:4.3.4" +"debug@npm:^4.1.0": + version: 4.3.3 + resolution: "debug@npm:4.3.3" dependencies: ms: 2.1.2 peerDependenciesMeta: supports-color: optional: true - checksum: 3dbad3f94ea64f34431a9cbf0bafb61853eda57bff2880036153438f50fb5a84f27683ba0d8e5426bf41a8c6ff03879488120cf5b3a761e77953169c0600a708 + checksum: 14472d56fe4a94dbcfaa6dbed2dd3849f1d72ba78104a1a328047bb564643ca49df0224c3a17fa63533fd11dd3d4c8636cd861191232a2c6735af00cc2d4de16 languageName: node linkType: hard @@ -9647,6 +10524,15 @@ __metadata: languageName: node linkType: hard +"decode-named-character-reference@npm:^1.0.0": + version: 1.0.2 + resolution: "decode-named-character-reference@npm:1.0.2" + dependencies: + character-entities: ^2.0.0 + checksum: f4c71d3b93105f20076052f9cb1523a22a9c796b8296cd35eef1ca54239c78d182c136a848b83ff8da2071e3ae2b1d300bf29d00650a6d6e675438cc31b11d78 + languageName: node + linkType: hard + "decompress-response@npm:^3.3.0": version: 3.3.0 resolution: "decompress-response@npm:3.3.0" @@ -9787,6 +10673,13 @@ __metadata: languageName: node linkType: hard +"dequal@npm:^2.0.0": + version: 2.0.3 + resolution: "dequal@npm:2.0.3" + checksum: 8679b850e1a3d0ebbc46ee780d5df7b478c23f335887464023a631d1b9af051ad4a6595a44220f9ff8ff95a8ddccf019b5ad778a976fd7bbf77383d36f412f90 + languageName: node + linkType: hard + "destroy@npm:1.2.0": version: 1.2.0 resolution: "destroy@npm:1.2.0" @@ -9801,6 +10694,13 @@ __metadata: languageName: node linkType: hard +"detect-node-es@npm:^1.1.0": + version: 1.1.0 + resolution: "detect-node-es@npm:1.1.0" + checksum: e46307d7264644975b71c104b9f028ed1d3d34b83a15b8a22373640ce5ea630e5640b1078b8ea15f202b54641da71e4aa7597093bd4b91f113db520a26a37449 + languageName: node + linkType: hard + "detect-node@npm:^2.0.4": version: 2.1.0 resolution: "detect-node@npm:2.1.0" @@ -9850,6 +10750,13 @@ __metadata: languageName: node linkType: hard +"diff@npm:^5.0.0": + version: 5.1.0 + resolution: "diff@npm:5.1.0" + checksum: c7bf0df7c9bfbe1cf8a678fd1b2137c4fb11be117a67bc18a0e03ae75105e8533dbfb1cda6b46beb3586ef5aed22143ef9d70713977d5fb1f9114e21455fba90 + languageName: node + linkType: hard + "dir-compare@npm:^2.4.0": version: 2.4.0 resolution: "dir-compare@npm:2.4.0" @@ -10782,6 +11689,13 @@ __metadata: languageName: node linkType: hard +"escape-string-regexp@npm:^5.0.0": + version: 5.0.0 + resolution: "escape-string-regexp@npm:5.0.0" + checksum: 20daabe197f3cb198ec28546deebcf24b3dbb1a5a269184381b3116d12f0532e06007f4bc8da25669d6a7f8efb68db0758df4cd981f57bc5b57f521a3e12c59e + languageName: node + linkType: hard + "escodegen@npm:^1.11.1": version: 1.14.3 resolution: "escodegen@npm:1.14.3" @@ -10872,6 +11786,63 @@ __metadata: languageName: node linkType: hard +"estree-util-attach-comments@npm:^2.0.0": + version: 2.1.1 + resolution: "estree-util-attach-comments@npm:2.1.1" + dependencies: + "@types/estree": ^1.0.0 + checksum: c5c2c41c9a55a169fb4fba9627057843f0d2e21e47a2e3e24318a11ffcf6bc704c0f96f405a529bddac7969b7c44f6cf86711505faaf0c5862c2024419b19704 + languageName: node + linkType: hard + +"estree-util-build-jsx@npm:^2.0.0": + version: 2.2.2 + resolution: "estree-util-build-jsx@npm:2.2.2" + dependencies: + "@types/estree-jsx": ^1.0.0 + estree-util-is-identifier-name: ^2.0.0 + estree-walker: ^3.0.0 + checksum: d008ac36a45d797eadca696f41b4c1ac0587ec0e0b52560cfb0e76d14ef15fc18e526f9023b6e5457dafa9cf3f010c9bb1dfc9c727ebd7cf0ba2ebbaa43919ac + languageName: node + linkType: hard + +"estree-util-is-identifier-name@npm:^2.0.0": + version: 2.1.0 + resolution: "estree-util-is-identifier-name@npm:2.1.0" + checksum: cab317a071fafb99cf83b57df7924bccd2e6ab4e252688739e49f00b16cefd168e279c171442b0557c80a1c80ffaa927d670dadea65bb3c9b151efb8e772e89d + languageName: node + linkType: hard + +"estree-util-to-js@npm:^1.1.0": + version: 1.2.0 + resolution: "estree-util-to-js@npm:1.2.0" + dependencies: + "@types/estree-jsx": ^1.0.0 + astring: ^1.8.0 + source-map: ^0.7.0 + checksum: 93a75e1051a6a4f5c631597ecd2ed95129fadbc80a58a10475d6d6b1b076a69393ba4a8d2bb71f698401f64ccca47e3f3828dd0042cac81439b988fae0f5f8e0 + languageName: node + linkType: hard + +"estree-util-value-to-estree@npm:^1.3.0": + version: 1.3.0 + resolution: "estree-util-value-to-estree@npm:1.3.0" + dependencies: + is-plain-obj: ^3.0.0 + checksum: a13c65f0712f32897ecae128d30dedafc439e39ab3722b0a4e51c455a6a0f506fcf2cd4f4c5cce35dafb738f1f55a933a3a44ce277e2140d4a4133968b2becb3 + languageName: node + linkType: hard + +"estree-util-visit@npm:^1.0.0": + version: 1.2.1 + resolution: "estree-util-visit@npm:1.2.1" + dependencies: + "@types/estree-jsx": ^1.0.0 + "@types/unist": ^2.0.0 + checksum: 6feea4fdc43b0ba0f79faf1d57cf32373007e146d4810c7c09c13f5a9c1b8600c1ac57a8d949967cedd2a9a91dddd246e19b59bacfc01e417168b4ebf220f691 + languageName: node + linkType: hard + "estree-walker@npm:^0.6.1": version: 0.6.1 resolution: "estree-walker@npm:0.6.1" @@ -10893,6 +11864,15 @@ __metadata: languageName: node linkType: hard +"estree-walker@npm:^3.0.0": + version: 3.0.3 + resolution: "estree-walker@npm:3.0.3" + dependencies: + "@types/estree": ^1.0.0 + checksum: a65728d5727b71de172c5df323385755a16c0fdab8234dc756c3854cfee343261ddfbb72a809a5660fac8c75d960bb3e21aa898c2d7e9b19bb298482ca58a3af + languageName: node + linkType: hard + "esutils@npm:^2.0.2": version: 2.0.3 resolution: "esutils@npm:2.0.3" @@ -10936,6 +11916,21 @@ __metadata: languageName: node linkType: hard +"execa@npm:^0.8.0": + version: 0.8.0 + resolution: "execa@npm:0.8.0" + dependencies: + cross-spawn: ^5.0.1 + get-stream: ^3.0.0 + is-stream: ^1.1.0 + npm-run-path: ^2.0.0 + p-finally: ^1.0.0 + signal-exit: ^3.0.0 + strip-eof: ^1.0.0 + checksum: c2a4bf6e051737e46bee61a93ec286cb71a05f16650a1918c8d6262ba9f0bac031472252411baa8c78b7f432f10cb4c601349403774d69be2ebd864e9b1eca60 + languageName: node + linkType: hard + "execa@npm:^1.0.0": version: 1.0.0 resolution: "execa@npm:1.0.0" @@ -11053,15 +12048,31 @@ __metadata: languageName: node linkType: hard -"extensible-custom-error@npm:^0.0.7": - version: 0.0.7 - resolution: "extensible-custom-error@npm:0.0.7" - checksum: 10286941f7e3d6c6e475ce8b598911edba74d445630ea7e6a477a3105a75b995bcbe0a3790e9351b05858d1f19127d7e9c569088760809c6f84ce47fb7c9618e +"extend-shallow@npm:^2.0.1": + version: 2.0.1 + resolution: "extend-shallow@npm:2.0.1" + dependencies: + is-extendable: ^0.1.0 + checksum: 8fb58d9d7a511f4baf78d383e637bd7d2e80843bd9cd0853649108ea835208fb614da502a553acc30208e1325240bb7cc4a68473021612496bb89725483656d8 languageName: node linkType: hard -"extract-zip@npm:^1.0.3": - version: 1.7.0 +"extend@npm:^3.0.0": + version: 3.0.2 + resolution: "extend@npm:3.0.2" + checksum: a50a8309ca65ea5d426382ff09f33586527882cf532931cb08ca786ea3146c0553310bda688710ff61d7668eba9f96b923fe1420cdf56a2c3eaf30fcab87b515 + languageName: node + linkType: hard + +"extensible-custom-error@npm:^0.0.7": + version: 0.0.7 + resolution: "extensible-custom-error@npm:0.0.7" + checksum: 10286941f7e3d6c6e475ce8b598911edba74d445630ea7e6a477a3105a75b995bcbe0a3790e9351b05858d1f19127d7e9c569088760809c6f84ce47fb7c9618e + languageName: node + linkType: hard + +"extract-zip@npm:^1.0.3": + version: 1.7.0 resolution: "extract-zip@npm:1.7.0" dependencies: concat-stream: ^1.6.2 @@ -11113,6 +12124,19 @@ __metadata: languageName: node linkType: hard +"fast-glob@npm:^3.2.12": + version: 3.2.12 + resolution: "fast-glob@npm:3.2.12" + dependencies: + "@nodelib/fs.stat": ^2.0.2 + "@nodelib/fs.walk": ^1.2.3 + glob-parent: ^5.1.2 + merge2: ^1.3.0 + micromatch: ^4.0.4 + checksum: 0b1990f6ce831c7e28c4d505edcdaad8e27e88ab9fa65eedadb730438cfc7cde4910d6c975d6b7b8dc8a73da4773702ebcfcd6e3518e73938bb1383badfe01c2 + languageName: node + linkType: hard + "fast-json-stable-stringify@npm:2.x, fast-json-stable-stringify@npm:^2.0.0, fast-json-stable-stringify@npm:^2.1.0": version: 2.1.0 resolution: "fast-json-stable-stringify@npm:2.1.0" @@ -11292,6 +12316,20 @@ __metadata: languageName: node linkType: hard +"flexsearch@npm:^0.7.21": + version: 0.7.31 + resolution: "flexsearch@npm:0.7.31" + checksum: 9817d8909a0d07d98e2c31f8a39cbc91be7a900cc3f86d74214ee49d00b6f3b133b883a3a19154de1d2064519494002dcfb1a30fbc65c8ef44259c3655fa8f39 + languageName: node + linkType: hard + +"focus-visible@npm:^5.2.0": + version: 5.2.0 + resolution: "focus-visible@npm:5.2.0" + checksum: 876f646ef453680d3d34e9f9b23961527ffd5ccaed8690f423d4fbfa37ff023d98a490972bc1387850e37ec2e44958c81f6096ef95b67462e5c4b5404cf1dbb9 + languageName: node + linkType: hard + "foreach@npm:^2.0.5": version: 2.0.5 resolution: "foreach@npm:2.0.5" @@ -11405,6 +12443,17 @@ __metadata: languageName: node linkType: hard +"fs-extra@npm:~7.0.1": + version: 7.0.1 + resolution: "fs-extra@npm:7.0.1" + dependencies: + graceful-fs: ^4.1.2 + jsonfile: ^4.0.0 + universalify: ^0.1.0 + checksum: 141b9dccb23b66a66cefdd81f4cda959ff89282b1d721b98cea19ba08db3dcbe6f862f28841f3cf24bb299e0b7e6c42303908f65093cb7e201708e86ea5a8dcf + languageName: node + linkType: hard + "fs-minipass@npm:^2.0.0, fs-minipass@npm:^2.1.0": version: 2.1.0 resolution: "fs-minipass@npm:2.1.0" @@ -11530,6 +12579,13 @@ __metadata: languageName: node linkType: hard +"get-nonce@npm:^1.0.0": + version: 1.0.1 + resolution: "get-nonce@npm:1.0.1" + checksum: e2614e43b4694c78277bb61b0f04583d45786881289285c73770b07ded246a98be7e1f78b940c80cbe6f2b07f55f0b724e6db6fd6f1bcbd1e8bdac16521074ed + languageName: node + linkType: hard + "get-own-enumerable-property-symbols@npm:^3.0.0": version: 3.0.2 resolution: "get-own-enumerable-property-symbols@npm:3.0.2" @@ -11586,6 +12642,32 @@ __metadata: languageName: node linkType: hard +"git-up@npm:^7.0.0": + version: 7.0.0 + resolution: "git-up@npm:7.0.0" + dependencies: + is-ssh: ^1.4.0 + parse-url: ^8.1.0 + checksum: 2faadbab51e94d2ffb220e426e950087cc02c15d664e673bd5d1f734cfa8196fed8b19493f7bf28fe216d087d10e22a7fd9b63687e0ba7d24f0ddcfb0a266d6e + languageName: node + linkType: hard + +"git-url-parse@npm:^13.1.0": + version: 13.1.0 + resolution: "git-url-parse@npm:13.1.0" + dependencies: + git-up: ^7.0.0 + checksum: 212a9b0343e9199998b6a532efe2014476a7a1283af393663ca49ac28d4768929aad16d3322e2685236065ee394dbc93e7aa63a48956531e984c56d8b5edb54d + languageName: node + linkType: hard + +"github-slugger@npm:^2.0.0": + version: 2.0.0 + resolution: "github-slugger@npm:2.0.0" + checksum: 250375cde2058f21454872c2c79f72c4637340c30c51ff158ca4ec71cbc478f33d54477d787a662f9207aeb095a2060f155bc01f15329ba8a5fb6698e0fc81f8 + languageName: node + linkType: hard + "gl-matrix@npm:*, gl-matrix@npm:^3.4.0, gl-matrix@npm:^3.4.3": version: 3.4.3 resolution: "gl-matrix@npm:3.4.3" @@ -11942,7 +13024,7 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": +"graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.2.10, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": version: 4.2.10 resolution: "graceful-fs@npm:4.2.10" checksum: 3f109d70ae123951905d85032ebeae3c2a5a7a997430df00ea30df0e3a6c60cf6689b109654d6fdacd28810a053348c4d14642da1d075049e6be1ba5216218da @@ -11963,6 +13045,18 @@ __metadata: languageName: node linkType: hard +"gray-matter@npm:^4.0.3": + version: 4.0.3 + resolution: "gray-matter@npm:4.0.3" + dependencies: + js-yaml: ^3.13.1 + kind-of: ^6.0.2 + section-matter: ^1.0.0 + strip-bom-string: ^1.0.0 + checksum: 37717bd424344487d655392251ce8d8878a1275ee087003e61208fba3bfd59cbb73a85b2159abf742ae95e23db04964813fdc33ae18b074208428b2528205222 + languageName: node + linkType: hard + "has-ansi@npm:^2.0.0": version: 2.0.0 resolution: "has-ansi@npm:2.0.0" @@ -11993,6 +13087,13 @@ __metadata: languageName: node linkType: hard +"has-flag@npm:^2.0.0": + version: 2.0.0 + resolution: "has-flag@npm:2.0.0" + checksum: 7d060d142ef6740c79991cb99afe5962b267e6e95538bf8b607026b9b1e7451288927bc8e7b4a9484a8b99935c0af023070f91ee49faef791ecd401dc58b2e8d + languageName: node + linkType: hard + "has-flag@npm:^3.0.0": version: 3.0.0 resolution: "has-flag@npm:3.0.0" @@ -12055,6 +13156,106 @@ __metadata: languageName: node linkType: hard +"hash-obj@npm:^4.0.0": + version: 4.0.0 + resolution: "hash-obj@npm:4.0.0" + dependencies: + is-obj: ^3.0.0 + sort-keys: ^5.0.0 + type-fest: ^1.0.2 + checksum: 33ff78814accc4e7ac7a1227d8eff3c377ff24996b3329d2092e0e39e4cbbb5330c8e738f86130cea7980852bae4f3650d5b8ef89b5902e10571d2e8cb4f4d64 + languageName: node + linkType: hard + +"hast-util-from-parse5@npm:^7.0.0": + version: 7.1.2 + resolution: "hast-util-from-parse5@npm:7.1.2" + dependencies: + "@types/hast": ^2.0.0 + "@types/unist": ^2.0.0 + hastscript: ^7.0.0 + property-information: ^6.0.0 + vfile: ^5.0.0 + vfile-location: ^4.0.0 + web-namespaces: ^2.0.0 + checksum: 7b4ed5b508b1352127c6719f7b0c0880190cf9859fe54ccaf7c9228ecf623d36cef3097910b3874d2fe1aac6bf4cf45d3cc2303daac3135a05e9ade6534ddddb + languageName: node + linkType: hard + +"hast-util-is-element@npm:^2.0.0": + version: 2.1.3 + resolution: "hast-util-is-element@npm:2.1.3" + dependencies: + "@types/hast": ^2.0.0 + "@types/unist": ^2.0.0 + checksum: 9d988f6839a50566a895a3dd19222e6ab1591243f6a3c36bba835b7e9339a2845f1ff1c583425afd602de1a57a76c5bae8a6dc0ab1d6e5d1e252b422cdeadbb7 + languageName: node + linkType: hard + +"hast-util-parse-selector@npm:^3.0.0": + version: 3.1.1 + resolution: "hast-util-parse-selector@npm:3.1.1" + dependencies: + "@types/hast": ^2.0.0 + checksum: 511d373465f60dd65e924f88bf0954085f4fb6e3a2b062a4b5ac43b93cbfd36a8dce6234b5d1e3e63499d936375687e83fc5da55628b22bd6b581b5ee167d1c4 + languageName: node + linkType: hard + +"hast-util-to-estree@npm:^2.0.0": + version: 2.3.2 + resolution: "hast-util-to-estree@npm:2.3.2" + dependencies: + "@types/estree": ^1.0.0 + "@types/estree-jsx": ^1.0.0 + "@types/hast": ^2.0.0 + "@types/unist": ^2.0.0 + comma-separated-tokens: ^2.0.0 + estree-util-attach-comments: ^2.0.0 + estree-util-is-identifier-name: ^2.0.0 + hast-util-whitespace: ^2.0.0 + mdast-util-mdx-expression: ^1.0.0 + mdast-util-mdxjs-esm: ^1.0.0 + property-information: ^6.0.0 + space-separated-tokens: ^2.0.0 + style-to-object: ^0.4.1 + unist-util-position: ^4.0.0 + zwitch: ^2.0.0 + checksum: 721167e275c1b0b9b1dcb35964a39f6180e22983ee7b56748ecab9f6cc35fe5229fd6e30a8eb4826caeee7eed88014ce4710bd79146c080d4dd281058ba09a39 + languageName: node + linkType: hard + +"hast-util-to-text@npm:^3.1.0": + version: 3.1.2 + resolution: "hast-util-to-text@npm:3.1.2" + dependencies: + "@types/hast": ^2.0.0 + "@types/unist": ^2.0.0 + hast-util-is-element: ^2.0.0 + unist-util-find-after: ^4.0.0 + checksum: d17cf3344c1d584ddd811cbb78d25b6c9819e62c8edb9643b53be38083fd978a6fa9a5bf6e6cd7b5ea48d30d9edc2859acae40b8bb89e166bebcda6017d4703d + languageName: node + linkType: hard + +"hast-util-whitespace@npm:^2.0.0": + version: 2.0.1 + resolution: "hast-util-whitespace@npm:2.0.1" + checksum: 431be6b2f35472f951615540d7a53f69f39461e5e080c0190268bdeb2be9ab9b1dddfd1f467dd26c1de7e7952df67beb1307b6ee940baf78b24a71b5e0663868 + languageName: node + linkType: hard + +"hastscript@npm:^7.0.0": + version: 7.2.0 + resolution: "hastscript@npm:7.2.0" + dependencies: + "@types/hast": ^2.0.0 + comma-separated-tokens: ^2.0.0 + hast-util-parse-selector: ^3.0.0 + property-information: ^6.0.0 + space-separated-tokens: ^2.0.0 + checksum: 928a21576ff7b9a8c945e7940bcbf2d27f770edb4279d4d04b33dc90753e26ca35c1172d626f54afebd377b2afa32331e399feb3eb0f7b91a399dca5927078ae + languageName: node + linkType: hard + "hex-color-regex@npm:^1.1.0": version: 1.1.0 resolution: "hex-color-regex@npm:1.1.0" @@ -12389,6 +13590,13 @@ __metadata: languageName: node linkType: hard +"import-lazy@npm:~4.0.0": + version: 4.0.0 + resolution: "import-lazy@npm:4.0.0" + checksum: 22f5e51702134aef78890156738454f620e5fe7044b204ebc057c614888a1dd6fdf2ede0fdcca44d5c173fd64f65c985f19a51775b06967ef58cc3d26898df07 + languageName: node + linkType: hard + "import-local@npm:^3.0.2": version: 3.0.2 resolution: "import-local@npm:3.0.2" @@ -12453,6 +13661,13 @@ __metadata: languageName: node linkType: hard +"inline-style-parser@npm:0.1.1": + version: 0.1.1 + resolution: "inline-style-parser@npm:0.1.1" + checksum: 5d545056a3e1f2bf864c928a886a0e1656a3517127d36917b973de581bd54adc91b4bf1febcb0da054f204b4934763f1a4e09308b4d55002327cf1d48ac5d966 + languageName: node + linkType: hard + "inline-style-prefixer@npm:^6.0.0": version: 6.0.1 resolution: "inline-style-prefixer@npm:6.0.1" @@ -12473,6 +13688,13 @@ __metadata: languageName: node linkType: hard +"intersection-observer@npm:^0.12.2": + version: 0.12.2 + resolution: "intersection-observer@npm:0.12.2" + checksum: d1fa9ebbb1e0baafe88ad95545bbb93f92bdcb8789912a2bd11b2463ab177ef185052c9c79c26187833d633f404acb077d4b1249353c5f6e2ca5cd64f50e216b + languageName: node + linkType: hard + "intl-messageformat@npm:^9.6.12": version: 9.13.0 resolution: "intl-messageformat@npm:9.13.0" @@ -12522,6 +13744,23 @@ __metadata: languageName: node linkType: hard +"is-alphabetical@npm:^2.0.0": + version: 2.0.1 + resolution: "is-alphabetical@npm:2.0.1" + checksum: 56207db8d9de0850f0cd30f4966bf731eb82cedfe496cbc2e97e7c3bacaf66fc54a972d2d08c0d93bb679cb84976a05d24c5ad63de56fabbfc60aadae312edaa + languageName: node + linkType: hard + +"is-alphanumerical@npm:^2.0.0": + version: 2.0.1 + resolution: "is-alphanumerical@npm:2.0.1" + dependencies: + is-alphabetical: ^2.0.0 + is-decimal: ^2.0.0 + checksum: 87acc068008d4c9c4e9f5bd5e251041d42e7a50995c77b1499cf6ed248f971aadeddb11f239cabf09f7975ee58cac7a48ffc170b7890076d8d227b24a68663c9 + languageName: node + linkType: hard + "is-arrayish@npm:^0.2.1": version: 0.2.1 resolution: "is-arrayish@npm:0.2.1" @@ -12564,6 +13803,13 @@ __metadata: languageName: node linkType: hard +"is-buffer@npm:^2.0.0": + version: 2.0.5 + resolution: "is-buffer@npm:2.0.5" + checksum: 764c9ad8b523a9f5a32af29bdf772b08eb48c04d2ad0a7240916ac2688c983bf5f8504bf25b35e66240edeb9d9085461f9b5dae1f3d2861c6b06a65fe983de42 + languageName: node + linkType: hard + "is-callable@npm:^1.1.4, is-callable@npm:^1.2.3, is-callable@npm:^1.2.4": version: 1.2.4 resolution: "is-callable@npm:1.2.4" @@ -12607,6 +13853,15 @@ __metadata: languageName: node linkType: hard +"is-core-module@npm:^2.1.0": + version: 2.11.0 + resolution: "is-core-module@npm:2.11.0" + dependencies: + has: ^1.0.3 + checksum: f96fd490c6b48eb4f6d10ba815c6ef13f410b0ba6f7eb8577af51697de523e5f2cd9de1c441b51d27251bf0e4aebc936545e33a5d26d5d51f28d25698d4a8bab + languageName: node + linkType: hard + "is-core-module@npm:^2.2.0": version: 2.6.0 resolution: "is-core-module@npm:2.6.0" @@ -12634,6 +13889,13 @@ __metadata: languageName: node linkType: hard +"is-decimal@npm:^2.0.0": + version: 2.0.1 + resolution: "is-decimal@npm:2.0.1" + checksum: 97132de7acdce77caa7b797632970a2ecd649a88e715db0e4dbc00ab0708b5e7574ba5903962c860cd4894a14fd12b100c0c4ac8aed445cf6f55c6cf747a4158 + languageName: node + linkType: hard + "is-directory@npm:^0.3.1": version: 0.3.1 resolution: "is-directory@npm:0.3.1" @@ -12641,6 +13903,13 @@ __metadata: languageName: node linkType: hard +"is-extendable@npm:^0.1.0": + version: 0.1.1 + resolution: "is-extendable@npm:0.1.1" + checksum: 3875571d20a7563772ecc7a5f36cb03167e9be31ad259041b4a8f73f33f885441f778cee1f1fe0085eb4bc71679b9d8c923690003a36a6a5fdf8023e6e3f0672 + languageName: node + linkType: hard + "is-extglob@npm:^2.1.1": version: 2.1.1 resolution: "is-extglob@npm:2.1.1" @@ -12678,6 +13947,13 @@ __metadata: languageName: node linkType: hard +"is-hexadecimal@npm:^2.0.0": + version: 2.0.1 + resolution: "is-hexadecimal@npm:2.0.1" + checksum: 66a2ea85994c622858f063f23eda506db29d92b52580709eb6f4c19550552d4dcf3fb81952e52f7cf972097237959e00adc7bb8c9400cd12886e15bf06145321 + languageName: node + linkType: hard + "is-hotkey@npm:^0.1.6": version: 0.1.8 resolution: "is-hotkey@npm:0.1.8" @@ -12776,6 +14052,13 @@ __metadata: languageName: node linkType: hard +"is-obj@npm:^3.0.0": + version: 3.0.0 + resolution: "is-obj@npm:3.0.0" + checksum: 75e97a99ed0b0884778887f8e913791864151307774914283b068b06b57ca86f695b024aa1ba5ed04411918edef93e2bfd8f84d68c6b6aab417802cc76f5061b + languageName: node + linkType: hard + "is-path-cwd@npm:^2.0.0": version: 2.2.0 resolution: "is-path-cwd@npm:2.2.0" @@ -12810,6 +14093,20 @@ __metadata: languageName: node linkType: hard +"is-plain-obj@npm:^3.0.0": + version: 3.0.0 + resolution: "is-plain-obj@npm:3.0.0" + checksum: a6ebdf8e12ab73f33530641972a72a4b8aed6df04f762070d823808303e4f76d87d5ea5bd76f96a7bbe83d93f04ac7764429c29413bd9049853a69cb630fb21c + languageName: node + linkType: hard + +"is-plain-obj@npm:^4.0.0": + version: 4.1.0 + resolution: "is-plain-obj@npm:4.1.0" + checksum: 6dc45da70d04a81f35c9310971e78a6a3c7a63547ef782e3a07ee3674695081b6ca4e977fbb8efc48dae3375e0b34558d2bcd722aec9bddfa2d7db5b041be8ce + languageName: node + linkType: hard + "is-plain-object@npm:^3.0.0": version: 3.0.1 resolution: "is-plain-object@npm:3.0.1" @@ -12840,6 +14137,15 @@ __metadata: languageName: node linkType: hard +"is-reference@npm:^3.0.0": + version: 3.0.1 + resolution: "is-reference@npm:3.0.1" + dependencies: + "@types/estree": "*" + checksum: 12c316d16191961938057e949c9f59ecac3c00c8101005a81ee351fde0775590238939c294ecac3a371400eb85d4b2556675396ebd4db821b767c145df28623f + languageName: node + linkType: hard + "is-regex@npm:^1.1.3, is-regex@npm:^1.1.4": version: 1.1.4 resolution: "is-regex@npm:1.1.4" @@ -12887,6 +14193,15 @@ __metadata: languageName: node linkType: hard +"is-ssh@npm:^1.4.0": + version: 1.4.0 + resolution: "is-ssh@npm:1.4.0" + dependencies: + protocols: ^2.0.1 + checksum: 75eaa17b538bee24b661fbeb0f140226ac77e904a6039f787bea418431e2162f1f9c4c4ccad3bd169e036cd701cc631406e8c505d9fa7e20164e74b47f86f40f + languageName: node + linkType: hard + "is-stream@npm:^1.0.0, is-stream@npm:^1.1.0": version: 1.1.0 resolution: "is-stream@npm:1.1.0" @@ -14542,6 +15857,13 @@ __metadata: languageName: node linkType: hard +"jju@npm:~1.4.0": + version: 1.4.0 + resolution: "jju@npm:1.4.0" + checksum: 3790481bd2b7827dd6336e6e3dc2dcc6d425679ba7ebde7b679f61dceb4457ea0cda330972494de608571f4973c6dfb5f70fab6f3c5037dbab19ac449a60424f + languageName: node + linkType: hard + "jotai@npm:^1.2.2": version: 1.3.0 resolution: "jotai@npm:1.3.0" @@ -14606,7 +15928,7 @@ __metadata: languageName: node linkType: hard -"js-yaml@npm:^4.1.0": +"js-yaml@npm:^4.0.0, js-yaml@npm:^4.1.0": version: 4.1.0 resolution: "js-yaml@npm:4.1.0" dependencies: @@ -14846,6 +16168,39 @@ __metadata: languageName: node linkType: hard +"katex@npm:^0.13.0": + version: 0.13.24 + resolution: "katex@npm:0.13.24" + dependencies: + commander: ^8.0.0 + bin: + katex: cli.js + checksum: 1b7c8295867073d0db4f6fb41ef1c0e3418b8e23924ff61b446b36134cb74cdadc7242dfbfb922d9c32f0b15eda6160a08cd30948c4e78141966ca2991a1726b + languageName: node + linkType: hard + +"katex@npm:^0.15.0": + version: 0.15.6 + resolution: "katex@npm:0.15.6" + dependencies: + commander: ^8.0.0 + bin: + katex: cli.js + checksum: 2da808bbd1d3be27715006cd86767dd3fcce3e317fb3bbd64d407328d2d90de17b5d83062b2cfd0e0d0de32e340efbac214862bc96892a5d1492462e553728d4 + languageName: node + linkType: hard + +"katex@npm:^0.16.4": + version: 0.16.4 + resolution: "katex@npm:0.16.4" + dependencies: + commander: ^8.0.0 + bin: + katex: cli.js + checksum: 94eaf1fbd8365792308527695c09baa6d2d84e2d0170e4af44fb12be3ed403fb3430caff2410117f2f1a9dfdb329f61ab9611d97e645d9c89ee60940698a45cc + languageName: node + linkType: hard + "keyv@npm:^3.0.0": version: 3.1.0 resolution: "keyv@npm:3.1.0" @@ -14855,6 +16210,13 @@ __metadata: languageName: node linkType: hard +"kind-of@npm:^6.0.0, kind-of@npm:^6.0.2": + version: 6.0.3 + resolution: "kind-of@npm:6.0.3" + checksum: 3ab01e7b1d440b22fe4c31f23d8d38b4d9b91d9f291df683476576493d5dfd2e03848a8b05813dd0c3f0e835bc63f433007ddeceb71f05cb25c45ae1b19c6d3b + languageName: node + linkType: hard + "kleur@npm:^3.0.3": version: 3.0.3 resolution: "kleur@npm:3.0.3" @@ -14862,6 +16224,13 @@ __metadata: languageName: node linkType: hard +"kleur@npm:^4.0.3": + version: 4.1.5 + resolution: "kleur@npm:4.1.5" + checksum: 1dc476e32741acf0b1b5b0627ffd0d722e342c1b0da14de3e8ae97821327ca08f9fb944542fb3c126d90ac5f27f9d804edbe7c585bf7d12ef495d115e0f22c12 + languageName: node + linkType: hard + "klona@npm:^2.0.5": version: 2.0.5 resolution: "klona@npm:2.0.5" @@ -14869,6 +16238,13 @@ __metadata: languageName: node linkType: hard +"kolorist@npm:^1.6.0": + version: 1.7.0 + resolution: "kolorist@npm:1.7.0" + checksum: 0eb64d22a204be11ddd7d2017525bf2603de38b15c571f79d6f0c5f1c869b8adfd40c3aa71c12e0cfe5496c25b2bbe891267cbc11f5b3fbc858d74ae79bac293 + languageName: node + linkType: hard + "latest-version@npm:^3.0.0": version: 3.1.0 resolution: "latest-version@npm:3.1.0" @@ -15020,6 +16396,20 @@ __metadata: languageName: node linkType: hard +"lodash.get@npm:^4.4.2": + version: 4.4.2 + resolution: "lodash.get@npm:4.4.2" + checksum: e403047ddb03181c9d0e92df9556570e2b67e0f0a930fcbbbd779370972368f5568e914f913e93f3b08f6d492abc71e14d4e9b7a18916c31fa04bd2306efe545 + languageName: node + linkType: hard + +"lodash.isequal@npm:^4.5.0": + version: 4.5.0 + resolution: "lodash.isequal@npm:4.5.0" + checksum: da27515dc5230eb1140ba65ff8de3613649620e8656b19a6270afe4866b7bd461d9ba2ac8a48dcc57f7adac4ee80e1de9f965d89d4d81a0ad52bb3eec2609644 + languageName: node + linkType: hard + "lodash.memoize@npm:4.x, lodash.memoize@npm:^4.1.2": version: 4.1.2 resolution: "lodash.memoize@npm:4.1.2" @@ -15041,7 +16431,7 @@ __metadata: languageName: node linkType: hard -"lodash@npm:4.x, lodash@npm:^4, lodash@npm:^4.0.1, lodash@npm:^4.17.10, lodash@npm:^4.17.11, lodash@npm:^4.17.15, lodash@npm:^4.17.20, lodash@npm:^4.17.4, lodash@npm:^4.7.0": +"lodash@npm:4.x, lodash@npm:^4, lodash@npm:^4.0.1, lodash@npm:^4.17.10, lodash@npm:^4.17.11, lodash@npm:^4.17.15, lodash@npm:^4.17.20, lodash@npm:^4.17.4, lodash@npm:^4.7.0, lodash@npm:~4.17.15": version: 4.17.21 resolution: "lodash@npm:4.17.21" checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7 @@ -15057,6 +16447,13 @@ __metadata: languageName: node linkType: hard +"longest-streak@npm:^3.0.0": + version: 3.1.0 + resolution: "longest-streak@npm:3.1.0" + checksum: d7f952ed004cbdb5c8bcfc4f7f5c3d65449e6c5a9e9be4505a656e3df5a57ee125f284286b4bf8ecea0c21a7b3bf2b8f9001ad506c319b9815ad6a63a47d0fd0 + languageName: node + linkType: hard + "loose-envify@npm:^1.0.0, loose-envify@npm:^1.1.0, loose-envify@npm:^1.4.0": version: 1.4.0 resolution: "loose-envify@npm:1.4.0" @@ -15135,6 +16532,15 @@ __metadata: languageName: node linkType: hard +"magic-string@npm:^0.29.0": + version: 0.29.0 + resolution: "magic-string@npm:0.29.0" + dependencies: + "@jridgewell/sourcemap-codec": ^1.4.13 + checksum: 19e5398fcfc44804917127c72ad622c68a19a0a10cbdb8d4f9f9417584a087fe9e117140bfb2463d86743cf1ed9cf4182ae0b0ad1a7536f7fdda257ee4449ffb + languageName: node + linkType: hard + "make-dir@npm:^1.0.0": version: 1.3.0 resolution: "make-dir@npm:1.3.0" @@ -15211,6 +16617,30 @@ __metadata: languageName: node linkType: hard +"markdown-extensions@npm:^1.0.0": + version: 1.1.1 + resolution: "markdown-extensions@npm:1.1.1" + checksum: 8a6dd128be1c524049ea6a41a9193715c2835d3d706af4b8b714ff2043a82786dbcd4a8f1fa9ddd28facbc444426c97515aef2d1f3dd11d5e2d63749ba577b1e + languageName: node + linkType: hard + +"markdown-table@npm:^3.0.0": + version: 3.0.3 + resolution: "markdown-table@npm:3.0.3" + checksum: 8fcd3d9018311120fbb97115987f8b1665a603f3134c93fbecc5d1463380c8036f789e2a62c19432058829e594fff8db9ff81c88f83690b2f8ed6c074f8d9e10 + languageName: node + linkType: hard + +"match-sorter@npm:^6.3.1": + version: 6.3.1 + resolution: "match-sorter@npm:6.3.1" + dependencies: + "@babel/runtime": ^7.12.5 + remove-accents: 0.4.2 + checksum: a4b02b676ac4ce64a89a091539ee4a70a802684713bcf06f2b70787927f510fe8a2adc849f9288857a90906083ad303467e530e8723b4a9756df9994fc164550 + languageName: node + linkType: hard + "matcher@npm:^3.0.0": version: 3.0.0 resolution: "matcher@npm:3.0.0" @@ -15252,41 +16682,275 @@ __metadata: languageName: node linkType: hard -"mdn-data@npm:2.0.14": - version: 2.0.14 - resolution: "mdn-data@npm:2.0.14" - checksum: 9d0128ed425a89f4cba8f787dca27ad9408b5cb1b220af2d938e2a0629d17d879a34d2cb19318bdb26c3f14c77dd5dfbae67211f5caaf07b61b1f2c5c8c7dc16 +"mdast-util-definitions@npm:^5.0.0": + version: 5.1.2 + resolution: "mdast-util-definitions@npm:5.1.2" + dependencies: + "@types/mdast": ^3.0.0 + "@types/unist": ^2.0.0 + unist-util-visit: ^4.0.0 + checksum: 2544daccab744ea1ede76045c2577ae4f1cc1b9eb1ea51ab273fe1dca8db5a8d6f50f87759c0ce6484975914b144b7f40316f805cb9c86223a78db8de0b77bae languageName: node linkType: hard -"mdn-data@npm:2.0.4": - version: 2.0.4 - resolution: "mdn-data@npm:2.0.4" - checksum: add3c95e6d03d301b8a8bcfee3de33f4d07e4c5eee5b79f18d6d737de717e22472deadf67c1a8563983c0b603e10d7df40aa8e5fddf18884dfe118ccec7ae329 +"mdast-util-find-and-replace@npm:^2.0.0": + version: 2.2.2 + resolution: "mdast-util-find-and-replace@npm:2.2.2" + dependencies: + "@types/mdast": ^3.0.0 + escape-string-regexp: ^5.0.0 + unist-util-is: ^5.0.0 + unist-util-visit-parents: ^5.0.0 + checksum: b4ce463c43fe6e1c38a53a89703f755c84ab5437f49bff9a0ac751279733332ca11c85ed0262aa6c17481f77b555d26ca6d64e70d6814f5b8d12d34a3e53a60b languageName: node linkType: hard -"media-typer@npm:0.3.0": - version: 0.3.0 - resolution: "media-typer@npm:0.3.0" - checksum: af1b38516c28ec95d6b0826f6c8f276c58aec391f76be42aa07646b4e39d317723e869700933ca6995b056db4b09a78c92d5440dc23657e6764be5d28874bba1 +"mdast-util-from-markdown@npm:^1.0.0, mdast-util-from-markdown@npm:^1.1.0": + version: 1.3.0 + resolution: "mdast-util-from-markdown@npm:1.3.0" + dependencies: + "@types/mdast": ^3.0.0 + "@types/unist": ^2.0.0 + decode-named-character-reference: ^1.0.0 + mdast-util-to-string: ^3.1.0 + micromark: ^3.0.0 + micromark-util-decode-numeric-character-reference: ^1.0.0 + micromark-util-decode-string: ^1.0.0 + micromark-util-normalize-identifier: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + unist-util-stringify-position: ^3.0.0 + uvu: ^0.5.0 + checksum: cc971d1ad381150f6504fd753fbcffcc64c0abb527540ce343625c2bba76104505262122ef63d14ab66eb47203f323267017c6d09abfa8535ee6a8e14069595f languageName: node linkType: hard -"memfs@npm:^3.4.1": - version: 3.4.7 - resolution: "memfs@npm:3.4.7" +"mdast-util-gfm-autolink-literal@npm:^1.0.0": + version: 1.0.3 + resolution: "mdast-util-gfm-autolink-literal@npm:1.0.3" dependencies: - fs-monkey: ^1.0.3 - checksum: fab88266dc576dc4999e38bdf531d703fb798affac2e0dd3fc17470878486844027b2766008ba80c0103b443f52cf9068a5c00f4e1ecf04106f4b29c11855822 + "@types/mdast": ^3.0.0 + ccount: ^2.0.0 + mdast-util-find-and-replace: ^2.0.0 + micromark-util-character: ^1.0.0 + checksum: 1748a8727cfc533bac0c287d6e72d571d165bfa77ae0418be4828177a3ec73c02c3f2ee534d87eb75cbaffa00c0866853bbcc60ae2255babb8210f7636ec2ce2 languageName: node linkType: hard -"memorystream@npm:^0.3.1": - version: 0.3.1 - resolution: "memorystream@npm:0.3.1" - checksum: f18b42440d24d09516d01466c06adf797df7873f0d40aa7db02e5fb9ed83074e5e65412d0720901d7069363465f82dc4f8bcb44f0cde271567a61426ce6ca2e9 - languageName: node +"mdast-util-gfm-footnote@npm:^1.0.0": + version: 1.0.2 + resolution: "mdast-util-gfm-footnote@npm:1.0.2" + dependencies: + "@types/mdast": ^3.0.0 + mdast-util-to-markdown: ^1.3.0 + micromark-util-normalize-identifier: ^1.0.0 + checksum: 2d77505f9377ed7e14472ef5e6b8366c3fec2cf5f936bb36f9fbe5b97ccb7cce0464d9313c236fa86fb844206fd585db05707e4fcfb755e4fc1864194845f1f6 + languageName: node + linkType: hard + +"mdast-util-gfm-strikethrough@npm:^1.0.0": + version: 1.0.3 + resolution: "mdast-util-gfm-strikethrough@npm:1.0.3" + dependencies: + "@types/mdast": ^3.0.0 + mdast-util-to-markdown: ^1.3.0 + checksum: 17003340ff1bba643ec4a59fd4370fc6a32885cab2d9750a508afa7225ea71449fb05acaef60faa89c6378b8bcfbd86a9d94b05f3c6651ff27a60e3ddefc2549 + languageName: node + linkType: hard + +"mdast-util-gfm-table@npm:^1.0.0": + version: 1.0.7 + resolution: "mdast-util-gfm-table@npm:1.0.7" + dependencies: + "@types/mdast": ^3.0.0 + markdown-table: ^3.0.0 + mdast-util-from-markdown: ^1.0.0 + mdast-util-to-markdown: ^1.3.0 + checksum: 8b8c401bb4162e53f072a2dff8efbca880fd78d55af30601c791315ab6722cb2918176e8585792469a0c530cebb9df9b4e7fede75fdc4d83df2839e238836692 + languageName: node + linkType: hard + +"mdast-util-gfm-task-list-item@npm:^1.0.0": + version: 1.0.2 + resolution: "mdast-util-gfm-task-list-item@npm:1.0.2" + dependencies: + "@types/mdast": ^3.0.0 + mdast-util-to-markdown: ^1.3.0 + checksum: c9b86037d6953b84f11fb2fc3aa23d5b8e14ca0dfcb0eb2fb289200e172bb9d5647bfceb4f86606dc6d935e8d58f6a458c04d3e55e87ff8513c7d4ade976200b + languageName: node + linkType: hard + +"mdast-util-gfm@npm:^2.0.0": + version: 2.0.2 + resolution: "mdast-util-gfm@npm:2.0.2" + dependencies: + mdast-util-from-markdown: ^1.0.0 + mdast-util-gfm-autolink-literal: ^1.0.0 + mdast-util-gfm-footnote: ^1.0.0 + mdast-util-gfm-strikethrough: ^1.0.0 + mdast-util-gfm-table: ^1.0.0 + mdast-util-gfm-task-list-item: ^1.0.0 + mdast-util-to-markdown: ^1.0.0 + checksum: 7078cb985255208bcbce94a121906417d38353c6b1a9acbe56ee8888010d3500608b5d51c16b0999ac63ca58848fb13012d55f26930ff6c6f3450f053d56514e + languageName: node + linkType: hard + +"mdast-util-math@npm:^2.0.0": + version: 2.0.2 + resolution: "mdast-util-math@npm:2.0.2" + dependencies: + "@types/mdast": ^3.0.0 + longest-streak: ^3.0.0 + mdast-util-to-markdown: ^1.3.0 + checksum: 60b3c1ca8dd0f009504de17515249fa396818fd4d5f83ed2c9c4aa3523add080c9076b32299b9622a8b1eb6e249d65fd4a8c1f0ab231a7656396353c2898d0bd + languageName: node + linkType: hard + +"mdast-util-mdx-expression@npm:^1.0.0": + version: 1.3.2 + resolution: "mdast-util-mdx-expression@npm:1.3.2" + dependencies: + "@types/estree-jsx": ^1.0.0 + "@types/hast": ^2.0.0 + "@types/mdast": ^3.0.0 + mdast-util-from-markdown: ^1.0.0 + mdast-util-to-markdown: ^1.0.0 + checksum: e4c90f26deaa5eb6217b0a9af559a80de41da02ab3bcd864c56bed3304b056ae703896e9876bc6ded500f4aff59f4de5cbf6a4b109a5ba408f2342805fe6dc05 + languageName: node + linkType: hard + +"mdast-util-mdx-jsx@npm:^2.0.0": + version: 2.1.2 + resolution: "mdast-util-mdx-jsx@npm:2.1.2" + dependencies: + "@types/estree-jsx": ^1.0.0 + "@types/hast": ^2.0.0 + "@types/mdast": ^3.0.0 + "@types/unist": ^2.0.0 + ccount: ^2.0.0 + mdast-util-from-markdown: ^1.1.0 + mdast-util-to-markdown: ^1.3.0 + parse-entities: ^4.0.0 + stringify-entities: ^4.0.0 + unist-util-remove-position: ^4.0.0 + unist-util-stringify-position: ^3.0.0 + vfile-message: ^3.0.0 + checksum: 637e0bbd97c0c783f6b12bb05ccb1edaec076c5aa6d349147d77b8e6e10677f1be8e2870c05b1896f69095c9bc527f34be72b349b30737ab2e499bfc579b3a28 + languageName: node + linkType: hard + +"mdast-util-mdx@npm:^2.0.0": + version: 2.0.1 + resolution: "mdast-util-mdx@npm:2.0.1" + dependencies: + mdast-util-from-markdown: ^1.0.0 + mdast-util-mdx-expression: ^1.0.0 + mdast-util-mdx-jsx: ^2.0.0 + mdast-util-mdxjs-esm: ^1.0.0 + mdast-util-to-markdown: ^1.0.0 + checksum: 7303149230a26e524e319833b782bffca94e49cdab012996618701259bd056e014ca22a35d25ffa8880ba9064ee126a2a002f01e5c90a31ca726339ed775875e + languageName: node + linkType: hard + +"mdast-util-mdxjs-esm@npm:^1.0.0": + version: 1.3.1 + resolution: "mdast-util-mdxjs-esm@npm:1.3.1" + dependencies: + "@types/estree-jsx": ^1.0.0 + "@types/hast": ^2.0.0 + "@types/mdast": ^3.0.0 + mdast-util-from-markdown: ^1.0.0 + mdast-util-to-markdown: ^1.0.0 + checksum: ee78a4f58adfec38723cbc920f05481201ebb001eff3982f2d0e5f5ce5c75685e732e9d361ad4a1be8b936b4e5de0f2599cb96b92ad4bd92698ac0c4a09bbec3 + languageName: node + linkType: hard + +"mdast-util-phrasing@npm:^3.0.0": + version: 3.0.1 + resolution: "mdast-util-phrasing@npm:3.0.1" + dependencies: + "@types/mdast": ^3.0.0 + unist-util-is: ^5.0.0 + checksum: c5b616d9b1eb76a6b351d195d94318494722525a12a89d9c8a3b091af7db3dd1fc55d294f9d29266d8159a8267b0df4a7a133bda8a3909d5331c383e1e1ff328 + languageName: node + linkType: hard + +"mdast-util-to-hast@npm:^12.1.0": + version: 12.3.0 + resolution: "mdast-util-to-hast@npm:12.3.0" + dependencies: + "@types/hast": ^2.0.0 + "@types/mdast": ^3.0.0 + mdast-util-definitions: ^5.0.0 + micromark-util-sanitize-uri: ^1.1.0 + trim-lines: ^3.0.0 + unist-util-generated: ^2.0.0 + unist-util-position: ^4.0.0 + unist-util-visit: ^4.0.0 + checksum: ea40c9f07dd0b731754434e81c913590c611b1fd753fa02550a1492aadfc30fb3adecaf62345ebb03cea2ddd250c15ab6e578fffde69c19955c9b87b10f2a9bb + languageName: node + linkType: hard + +"mdast-util-to-markdown@npm:^1.0.0, mdast-util-to-markdown@npm:^1.3.0": + version: 1.5.0 + resolution: "mdast-util-to-markdown@npm:1.5.0" + dependencies: + "@types/mdast": ^3.0.0 + "@types/unist": ^2.0.0 + longest-streak: ^3.0.0 + mdast-util-phrasing: ^3.0.0 + mdast-util-to-string: ^3.0.0 + micromark-util-decode-string: ^1.0.0 + unist-util-visit: ^4.0.0 + zwitch: ^2.0.0 + checksum: 64338eb33e49bb0aea417591fd986f72fdd39205052563bb7ce9eb9ecc160824509bfacd740086a05af355c6d5c36353aafe95cab9e6927d674478757cee6259 + languageName: node + linkType: hard + +"mdast-util-to-string@npm:^3.0.0, mdast-util-to-string@npm:^3.1.0": + version: 3.1.1 + resolution: "mdast-util-to-string@npm:3.1.1" + dependencies: + "@types/mdast": ^3.0.0 + checksum: 5e9375e1757ebf2950e122ef3538e4257ed2b6f43ab1d3e9c45db5dd5d5b5d14fd041490afcde00934f1cdb4b99877597ae04eb810d313ec7b38c6009058dddd + languageName: node + linkType: hard + +"mdn-data@npm:2.0.14": + version: 2.0.14 + resolution: "mdn-data@npm:2.0.14" + checksum: 9d0128ed425a89f4cba8f787dca27ad9408b5cb1b220af2d938e2a0629d17d879a34d2cb19318bdb26c3f14c77dd5dfbae67211f5caaf07b61b1f2c5c8c7dc16 + languageName: node + linkType: hard + +"mdn-data@npm:2.0.4": + version: 2.0.4 + resolution: "mdn-data@npm:2.0.4" + checksum: add3c95e6d03d301b8a8bcfee3de33f4d07e4c5eee5b79f18d6d737de717e22472deadf67c1a8563983c0b603e10d7df40aa8e5fddf18884dfe118ccec7ae329 + languageName: node + linkType: hard + +"media-typer@npm:0.3.0": + version: 0.3.0 + resolution: "media-typer@npm:0.3.0" + checksum: af1b38516c28ec95d6b0826f6c8f276c58aec391f76be42aa07646b4e39d317723e869700933ca6995b056db4b09a78c92d5440dc23657e6764be5d28874bba1 + languageName: node + linkType: hard + +"memfs@npm:^3.4.1": + version: 3.4.7 + resolution: "memfs@npm:3.4.7" + dependencies: + fs-monkey: ^1.0.3 + checksum: fab88266dc576dc4999e38bdf531d703fb798affac2e0dd3fc17470878486844027b2766008ba80c0103b443f52cf9068a5c00f4e1ecf04106f4b29c11855822 + languageName: node + linkType: hard + +"memorystream@npm:^0.3.1": + version: 0.3.1 + resolution: "memorystream@npm:0.3.1" + checksum: f18b42440d24d09516d01466c06adf797df7873f0d40aa7db02e5fb9ed83074e5e65412d0720901d7069363465f82dc4f8bcb44f0cde271567a61426ce6ca2e9 + languageName: node linkType: hard "merge-descriptors@npm:1.0.1": @@ -15317,6 +16981,456 @@ __metadata: languageName: node linkType: hard +"micromark-core-commonmark@npm:^1.0.0, micromark-core-commonmark@npm:^1.0.1": + version: 1.0.6 + resolution: "micromark-core-commonmark@npm:1.0.6" + dependencies: + decode-named-character-reference: ^1.0.0 + micromark-factory-destination: ^1.0.0 + micromark-factory-label: ^1.0.0 + micromark-factory-space: ^1.0.0 + micromark-factory-title: ^1.0.0 + micromark-factory-whitespace: ^1.0.0 + micromark-util-character: ^1.0.0 + micromark-util-chunked: ^1.0.0 + micromark-util-classify-character: ^1.0.0 + micromark-util-html-tag-name: ^1.0.0 + micromark-util-normalize-identifier: ^1.0.0 + micromark-util-resolve-all: ^1.0.0 + micromark-util-subtokenize: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.1 + uvu: ^0.5.0 + checksum: 4b483c46077f696ed310f6d709bb9547434c218ceb5c1220fde1707175f6f68b44da15ab8668f9c801e1a123210071e3af883a7d1215122c913fd626f122bfc2 + languageName: node + linkType: hard + +"micromark-extension-gfm-autolink-literal@npm:^1.0.0": + version: 1.0.3 + resolution: "micromark-extension-gfm-autolink-literal@npm:1.0.3" + dependencies: + micromark-util-character: ^1.0.0 + micromark-util-sanitize-uri: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + uvu: ^0.5.0 + checksum: bb181972ac346ca73ca1ab0b80b80c9d6509ed149799d2217d5442670f499c38a94edff73d32fa52b390d89640974cfbd7f29e4ad7d599581d5e1cabcae636a2 + languageName: node + linkType: hard + +"micromark-extension-gfm-footnote@npm:^1.0.0": + version: 1.0.4 + resolution: "micromark-extension-gfm-footnote@npm:1.0.4" + dependencies: + micromark-core-commonmark: ^1.0.0 + micromark-factory-space: ^1.0.0 + micromark-util-character: ^1.0.0 + micromark-util-normalize-identifier: ^1.0.0 + micromark-util-sanitize-uri: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + uvu: ^0.5.0 + checksum: 8daa203f5cf753338d5ecdbaae6b3ab6319d34b6013b90ea6860bed299418cecf86e69e48dabe42562e334760c738c77c5acdb47e75ae26f5f01f02f3bf0952d + languageName: node + linkType: hard + +"micromark-extension-gfm-strikethrough@npm:^1.0.0": + version: 1.0.4 + resolution: "micromark-extension-gfm-strikethrough@npm:1.0.4" + dependencies: + micromark-util-chunked: ^1.0.0 + micromark-util-classify-character: ^1.0.0 + micromark-util-resolve-all: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + uvu: ^0.5.0 + checksum: f43d316b85fe93df1711cdcdc99a5320b941239349234bd262fc708cb67ad47bdfb41d1a7ebe2a5829816b0e9d3107380a5c1e558cb536a75354cbe4857823ba + languageName: node + linkType: hard + +"micromark-extension-gfm-table@npm:^1.0.0": + version: 1.0.5 + resolution: "micromark-extension-gfm-table@npm:1.0.5" + dependencies: + micromark-factory-space: ^1.0.0 + micromark-util-character: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + uvu: ^0.5.0 + checksum: f0aab3b4333cc24b1534b08dc4cce986dd606df8b7ed913e5a1de9fe2d3ae67b2435663c0bc271b528874af4928e580e1ad540ea9117d7f2d74edb28859c97ef + languageName: node + linkType: hard + +"micromark-extension-gfm-tagfilter@npm:^1.0.0": + version: 1.0.1 + resolution: "micromark-extension-gfm-tagfilter@npm:1.0.1" + dependencies: + micromark-util-types: ^1.0.0 + checksum: 63e8d68f25871722900a67a8001d5da21f19ea707f3566fc7d0b2eb1f6d52476848bb6a41576cf22470565124af9497c5aae842355faa4c14ec19cb1847e71ec + languageName: node + linkType: hard + +"micromark-extension-gfm-task-list-item@npm:^1.0.0": + version: 1.0.3 + resolution: "micromark-extension-gfm-task-list-item@npm:1.0.3" + dependencies: + micromark-factory-space: ^1.0.0 + micromark-util-character: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + uvu: ^0.5.0 + checksum: d320b0c5301f87e211c06a2330d1ee0fee6da14f0d6d44d5211055b465dadff34390cd6b258a5e0ca376fcda3364fef9a12fe6e26a0c858231fa3b98ddbf7785 + languageName: node + linkType: hard + +"micromark-extension-gfm@npm:^2.0.0": + version: 2.0.1 + resolution: "micromark-extension-gfm@npm:2.0.1" + dependencies: + micromark-extension-gfm-autolink-literal: ^1.0.0 + micromark-extension-gfm-footnote: ^1.0.0 + micromark-extension-gfm-strikethrough: ^1.0.0 + micromark-extension-gfm-table: ^1.0.0 + micromark-extension-gfm-tagfilter: ^1.0.0 + micromark-extension-gfm-task-list-item: ^1.0.0 + micromark-util-combine-extensions: ^1.0.0 + micromark-util-types: ^1.0.0 + checksum: b181479c87be38d5ae8d28e6dc52fab73c894fd2706876746f27a91fb186644ce03532a9c35dca2186327a0e2285cd5242ad0361dc89adedd4a50376ffd94e22 + languageName: node + linkType: hard + +"micromark-extension-math@npm:^2.0.0": + version: 2.0.2 + resolution: "micromark-extension-math@npm:2.0.2" + dependencies: + "@types/katex": ^0.11.0 + katex: ^0.13.0 + micromark-factory-space: ^1.0.0 + micromark-util-character: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + uvu: ^0.5.0 + checksum: c604d2e75443cd20988c485ecf35ff6799497b038d24e5c680107ebb6756031225df9292e449914178f04b837379b5f95dea0ad3fe4ae77ce60d194f102576a5 + languageName: node + linkType: hard + +"micromark-extension-mdx-expression@npm:^1.0.0": + version: 1.0.4 + resolution: "micromark-extension-mdx-expression@npm:1.0.4" + dependencies: + micromark-factory-mdx-expression: ^1.0.0 + micromark-factory-space: ^1.0.0 + micromark-util-character: ^1.0.0 + micromark-util-events-to-acorn: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + uvu: ^0.5.0 + checksum: d19a31f9813dd5d4ad96b99e35b7c48067e69d75f92ec670dad5242857fb7688ba8b7c6a15616797b5df25dd89fd3b54916f93cb60ce2cfe97aca84739b45954 + languageName: node + linkType: hard + +"micromark-extension-mdx-jsx@npm:^1.0.0": + version: 1.0.3 + resolution: "micromark-extension-mdx-jsx@npm:1.0.3" + dependencies: + "@types/acorn": ^4.0.0 + estree-util-is-identifier-name: ^2.0.0 + micromark-factory-mdx-expression: ^1.0.0 + micromark-factory-space: ^1.0.0 + micromark-util-character: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + uvu: ^0.5.0 + vfile-message: ^3.0.0 + checksum: 1a5566890aabc52fe96b78e3a3a507dee03a2232e44b9360b00617734e156f934e85bc6a477fbb856c793fe33c9fb7d2207a4f50e680168c0d04ba9c9336d960 + languageName: node + linkType: hard + +"micromark-extension-mdx-md@npm:^1.0.0": + version: 1.0.0 + resolution: "micromark-extension-mdx-md@npm:1.0.0" + dependencies: + micromark-util-types: ^1.0.0 + checksum: b4f205e1d5f0946b4755541ef44ffd0b3be8c7ecfc08d8b139b6a21fbd3ff62d8fdb6b7e6d17bd9a3b610450267f43a41703dc48b341da9addd743a28cdefa64 + languageName: node + linkType: hard + +"micromark-extension-mdxjs-esm@npm:^1.0.0": + version: 1.0.3 + resolution: "micromark-extension-mdxjs-esm@npm:1.0.3" + dependencies: + micromark-core-commonmark: ^1.0.0 + micromark-util-character: ^1.0.0 + micromark-util-events-to-acorn: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + unist-util-position-from-estree: ^1.1.0 + uvu: ^0.5.0 + vfile-message: ^3.0.0 + checksum: 756074656391a5e5bb96bc8a0e9c1df7d9f7be5299847c9719e6a90552e1c76a11876aa89986ad5da89ab485f776a4a43a61ea3acddd4f865a5cee43ac523ffd + languageName: node + linkType: hard + +"micromark-extension-mdxjs@npm:^1.0.0": + version: 1.0.0 + resolution: "micromark-extension-mdxjs@npm:1.0.0" + dependencies: + acorn: ^8.0.0 + acorn-jsx: ^5.0.0 + micromark-extension-mdx-expression: ^1.0.0 + micromark-extension-mdx-jsx: ^1.0.0 + micromark-extension-mdx-md: ^1.0.0 + micromark-extension-mdxjs-esm: ^1.0.0 + micromark-util-combine-extensions: ^1.0.0 + micromark-util-types: ^1.0.0 + checksum: ba836c6d2dfc67597886e88f533ffa02f2029dbe216a0651f1066e70f8529a700bcc7fa2bc4201ee12fd3d1cd7da7093d5a442442daeb84b27df96aaffb7699c + languageName: node + linkType: hard + +"micromark-factory-destination@npm:^1.0.0": + version: 1.0.0 + resolution: "micromark-factory-destination@npm:1.0.0" + dependencies: + micromark-util-character: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + checksum: 8e733ae9c1c2342f14ff290bf09946e20f6f540117d80342377a765cac48df2ea5e748f33c8b07501ad7a43414b1a6597c8510ede2052b6bf1251fab89748e20 + languageName: node + linkType: hard + +"micromark-factory-label@npm:^1.0.0": + version: 1.0.2 + resolution: "micromark-factory-label@npm:1.0.2" + dependencies: + micromark-util-character: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + uvu: ^0.5.0 + checksum: 957e9366bdc8dbc1437c0706ff96972fa985ab4b1274abcae12f6094f527cbf5c69e7f2304c23c7f4b96e311ff7911d226563b8b43dcfcd4091e8c985fb97ce6 + languageName: node + linkType: hard + +"micromark-factory-mdx-expression@npm:^1.0.0": + version: 1.0.7 + resolution: "micromark-factory-mdx-expression@npm:1.0.7" + dependencies: + micromark-factory-space: ^1.0.0 + micromark-util-character: ^1.0.0 + micromark-util-events-to-acorn: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + unist-util-position-from-estree: ^1.0.0 + uvu: ^0.5.0 + vfile-message: ^3.0.0 + checksum: e7893f21576bcb7755d341e45d3ff202ba466fa2278c6f31ae4db4002a28d6d13a4efad331ef46223372ec2010d9bc2ff27e2cd57a4580be6491e59ca21ba59d + languageName: node + linkType: hard + +"micromark-factory-space@npm:^1.0.0": + version: 1.0.0 + resolution: "micromark-factory-space@npm:1.0.0" + dependencies: + micromark-util-character: ^1.0.0 + micromark-util-types: ^1.0.0 + checksum: 70d3aafde4e68ef4e509a3b644e9a29e4aada00801279e346577b008cbca06d78051bcd62aa7ea7425856ed73f09abd2b36607803055f726f52607ee7cb706b0 + languageName: node + linkType: hard + +"micromark-factory-title@npm:^1.0.0": + version: 1.0.2 + resolution: "micromark-factory-title@npm:1.0.2" + dependencies: + micromark-factory-space: ^1.0.0 + micromark-util-character: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + uvu: ^0.5.0 + checksum: 9a9cf66babde0bad1e25d6c1087082bfde6dfc319a36cab67c89651cc1a53d0e21cdec83262b5a4c33bff49f0e3c8dc2a7bd464e991d40dbea166a8f9b37e5b2 + languageName: node + linkType: hard + +"micromark-factory-whitespace@npm:^1.0.0": + version: 1.0.0 + resolution: "micromark-factory-whitespace@npm:1.0.0" + dependencies: + micromark-factory-space: ^1.0.0 + micromark-util-character: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + checksum: 0888386e6ea2dd665a5182c570d9b3d0a172d3f11694ca5a2a84e552149c9f1429f5b975ec26e1f0fa4388c55a656c9f359ce5e0603aff6175ba3e255076f20b + languageName: node + linkType: hard + +"micromark-util-character@npm:^1.0.0": + version: 1.1.0 + resolution: "micromark-util-character@npm:1.1.0" + dependencies: + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + checksum: 504a4e3321f69bddf3fec9f0c1058239fc23336bda5be31d532b150491eda47965a251b37f8a7a9db0c65933b3aaa49cf88044fb1028be3af7c5ee6212bf8d5f + languageName: node + linkType: hard + +"micromark-util-chunked@npm:^1.0.0": + version: 1.0.0 + resolution: "micromark-util-chunked@npm:1.0.0" + dependencies: + micromark-util-symbol: ^1.0.0 + checksum: c1efd56e8c4217bcf1c6f1a9fb9912b4a2a5503b00d031da902be922fb3fee60409ac53f11739991291357b2784fb0647ddfc74c94753a068646c0cb0fd71421 + languageName: node + linkType: hard + +"micromark-util-classify-character@npm:^1.0.0": + version: 1.0.0 + resolution: "micromark-util-classify-character@npm:1.0.0" + dependencies: + micromark-util-character: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + checksum: 180446e6a1dec653f625ded028f244784e1db8d10ad05c5d70f08af9de393b4a03dc6cf6fa5ed8ccc9c24bbece7837abf3bf66681c0b4adf159364b7d5236dfd + languageName: node + linkType: hard + +"micromark-util-combine-extensions@npm:^1.0.0": + version: 1.0.0 + resolution: "micromark-util-combine-extensions@npm:1.0.0" + dependencies: + micromark-util-chunked: ^1.0.0 + micromark-util-types: ^1.0.0 + checksum: 5304a820ef75340e1be69d6ad167055b6ba9a3bafe8171e5945a935752f462415a9dd61eb3490220c055a8a11167209a45bfa73f278338b7d3d61fa1464d3f35 + languageName: node + linkType: hard + +"micromark-util-decode-numeric-character-reference@npm:^1.0.0": + version: 1.0.0 + resolution: "micromark-util-decode-numeric-character-reference@npm:1.0.0" + dependencies: + micromark-util-symbol: ^1.0.0 + checksum: f3ae2bb582a80f1e9d3face026f585c0c472335c064bd850bde152376f0394cb2831746749b6be6e0160f7d73626f67d10716026c04c87f402c0dd45a1a28633 + languageName: node + linkType: hard + +"micromark-util-decode-string@npm:^1.0.0": + version: 1.0.2 + resolution: "micromark-util-decode-string@npm:1.0.2" + dependencies: + decode-named-character-reference: ^1.0.0 + micromark-util-character: ^1.0.0 + micromark-util-decode-numeric-character-reference: ^1.0.0 + micromark-util-symbol: ^1.0.0 + checksum: 2dbb41c9691cc71505d39706405139fb7d6699429d577a524c7c248ac0cfd09d3dd212ad8e91c143a00b2896f26f81136edc67c5bda32d20446f0834d261b17a + languageName: node + linkType: hard + +"micromark-util-encode@npm:^1.0.0": + version: 1.0.1 + resolution: "micromark-util-encode@npm:1.0.1" + checksum: 9290583abfdc79ea3e7eb92c012c47a0e14327888f8aaa6f57ff79b3058d8e7743716b9d91abca3646f15ab3d78fdad9779fdb4ccf13349cd53309dfc845253a + languageName: node + linkType: hard + +"micromark-util-events-to-acorn@npm:^1.0.0": + version: 1.2.1 + resolution: "micromark-util-events-to-acorn@npm:1.2.1" + dependencies: + "@types/acorn": ^4.0.0 + "@types/estree": ^1.0.0 + estree-util-visit: ^1.0.0 + micromark-util-types: ^1.0.0 + uvu: ^0.5.0 + vfile-location: ^4.0.0 + vfile-message: ^3.0.0 + checksum: baf1cad66d860980cf20963f641c48c434e5be5802beabefdda21be136ae037845dd236b5e9ce5cf9409bf1b9ba8b4131a396d3a5bfa12098dae13e4a9724f2b + languageName: node + linkType: hard + +"micromark-util-html-tag-name@npm:^1.0.0": + version: 1.1.0 + resolution: "micromark-util-html-tag-name@npm:1.1.0" + checksum: a9b783cec89ec813648d59799464c1950fe281ae797b2a965f98ad0167d7fa1a247718eff023b4c015f47211a172f9446b8e6b98aad50e3cd44a3337317dad2c + languageName: node + linkType: hard + +"micromark-util-normalize-identifier@npm:^1.0.0": + version: 1.0.0 + resolution: "micromark-util-normalize-identifier@npm:1.0.0" + dependencies: + micromark-util-symbol: ^1.0.0 + checksum: d7c09d5e8318fb72f194af72664bd84a48a2928e3550b2b21c8fbc0ec22524f2a72e0f6663d2b95dc189a6957d3d7759b60716e888909710767cd557be821f8b + languageName: node + linkType: hard + +"micromark-util-resolve-all@npm:^1.0.0": + version: 1.0.0 + resolution: "micromark-util-resolve-all@npm:1.0.0" + dependencies: + micromark-util-types: ^1.0.0 + checksum: 409667f2bd126ef8acce009270d2aecaaa5584c5807672bc657b09e50aa91bd2e552cf41e5be1e6469244a83349cbb71daf6059b746b1c44e3f35446fef63e50 + languageName: node + linkType: hard + +"micromark-util-sanitize-uri@npm:^1.0.0, micromark-util-sanitize-uri@npm:^1.1.0": + version: 1.1.0 + resolution: "micromark-util-sanitize-uri@npm:1.1.0" + dependencies: + micromark-util-character: ^1.0.0 + micromark-util-encode: ^1.0.0 + micromark-util-symbol: ^1.0.0 + checksum: fe6093faa0adeb8fad606184d927ce37f207dcc2ec7256438e7f273c8829686245dd6161b597913ef25a3c4fb61863d3612a40cb04cf15f83ba1b4087099996b + languageName: node + linkType: hard + +"micromark-util-subtokenize@npm:^1.0.0": + version: 1.0.2 + resolution: "micromark-util-subtokenize@npm:1.0.2" + dependencies: + micromark-util-chunked: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.0 + uvu: ^0.5.0 + checksum: c32ee58a7e1384ab1161a9ee02fbb04ad7b6e96d0b8c93dba9803c329a53d07f22ab394c7a96b2e30d6b8fbe3585b85817dba07277b1317111fc234e166bd2d1 + languageName: node + linkType: hard + +"micromark-util-symbol@npm:^1.0.0": + version: 1.0.1 + resolution: "micromark-util-symbol@npm:1.0.1" + checksum: c6a3023b3a7432c15864b5e33a1bcb5042ac7aa097f2f452e587bef45433d42d39e0a5cce12fbea91e0671098ba0c3f62a2b30ce1cde66ecbb5e8336acf4391d + languageName: node + linkType: hard + +"micromark-util-types@npm:^1.0.0, micromark-util-types@npm:^1.0.1": + version: 1.0.2 + resolution: "micromark-util-types@npm:1.0.2" + checksum: 08dc901b7c06ee3dfeb54befca05cbdab9525c1cf1c1080967c3878c9e72cb9856c7e8ff6112816e18ead36ce6f99d55aaa91560768f2f6417b415dcba1244df + languageName: node + linkType: hard + +"micromark@npm:^3.0.0": + version: 3.1.0 + resolution: "micromark@npm:3.1.0" + dependencies: + "@types/debug": ^4.0.0 + debug: ^4.0.0 + decode-named-character-reference: ^1.0.0 + micromark-core-commonmark: ^1.0.1 + micromark-factory-space: ^1.0.0 + micromark-util-character: ^1.0.0 + micromark-util-chunked: ^1.0.0 + micromark-util-combine-extensions: ^1.0.0 + micromark-util-decode-numeric-character-reference: ^1.0.0 + micromark-util-encode: ^1.0.0 + micromark-util-normalize-identifier: ^1.0.0 + micromark-util-resolve-all: ^1.0.0 + micromark-util-sanitize-uri: ^1.0.0 + micromark-util-subtokenize: ^1.0.0 + micromark-util-symbol: ^1.0.0 + micromark-util-types: ^1.0.1 + uvu: ^0.5.0 + checksum: 5fe5bc3bf92e2ddd37b5f0034080fc3a4d4b3c1130dd5e435bb96ec75e9453091272852e71a4d74906a8fcf992d6f79d794607657c534bda49941e9950a92e28 + languageName: node + linkType: hard + "micromatch@npm:^4.0.4": version: 4.0.4 resolution: "micromatch@npm:4.0.4" @@ -15436,6 +17550,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^5.1.0": + version: 5.1.6 + resolution: "minimatch@npm:5.1.6" + dependencies: + brace-expansion: ^2.0.1 + checksum: 7564208ef81d7065a370f788d337cd80a689e981042cb9a1d0e6580b6c6a8c9279eba80010516e258835a988363f99f54a6f711a315089b8b42694f5da9d0d77 + languageName: node + linkType: hard + "minimist@npm:^1.2.0, minimist@npm:^1.2.5": version: 1.2.5 resolution: "minimist@npm:1.2.5" @@ -15586,6 +17709,13 @@ __metadata: languageName: node linkType: hard +"mri@npm:^1.1.0": + version: 1.2.0 + resolution: "mri@npm:1.2.0" + checksum: 83f515abbcff60150873e424894a2f65d68037e5a7fcde8a9e2b285ee9c13ac581b63cfc1e6826c4732de3aeb84902f7c1e16b7aff46cd3f897a0f757a894e85 + languageName: node + linkType: hard + "ms@npm:2.0.0": version: 2.0.0 resolution: "ms@npm:2.0.0" @@ -15710,6 +17840,21 @@ __metadata: languageName: node linkType: hard +"next-mdx-remote@npm:^4.2.1": + version: 4.4.1 + resolution: "next-mdx-remote@npm:4.4.1" + dependencies: + "@mdx-js/mdx": ^2.2.1 + "@mdx-js/react": ^2.2.1 + vfile: ^5.3.0 + vfile-matter: ^3.0.1 + peerDependencies: + react: ">=16.x <=18.x" + react-dom: ">=16.x <=18.x" + checksum: 95cd77d03ae8ad7ae691cde0e895597b35a2ddac99cbeb31f1307599b2c7e7628f9e2a0fa5ced8d55036f58d8a2006ae312e308d574b8c3b0948df7279fa393d + languageName: node + linkType: hard + "next-pwa@npm:^5.6.0": version: 5.6.0 resolution: "next-pwa@npm:5.6.0" @@ -15726,6 +17871,28 @@ __metadata: languageName: node linkType: hard +"next-seo@npm:^5.5.0": + version: 5.15.0 + resolution: "next-seo@npm:5.15.0" + peerDependencies: + next: ^8.1.1-canary.54 || >=9.0.0 + react: ">=16.0.0" + react-dom: ">=16.0.0" + checksum: 05b0769d9c9abcc6a0179e5d724e87b1a3a7591a9c2caa4ef5b791769fcf868cb97fbcd90264106101cf7fbc073b0375ab90026aa519d2e899fbabea12cafbfb + languageName: node + linkType: hard + +"next-themes@npm:^0.2.1": + version: 0.2.1 + resolution: "next-themes@npm:0.2.1" + peerDependencies: + next: "*" + react: "*" + react-dom: "*" + checksum: ebc248b956138e73436c4ed0a0f0a877a0a48a33156db577029b8b8469e48b5c777d61abf2baeb75953378febea74e067a4869ff90b4a3e94fce123289b862ba + languageName: node + linkType: hard + "next-transpile-modules@npm:9.0.0": version: 9.0.0 resolution: "next-transpile-modules@npm:9.0.0" @@ -15873,6 +18040,63 @@ next@latest: languageName: node linkType: hard +"nextra-theme-docs@npm:^2.2.19": + version: 2.2.19 + resolution: "nextra-theme-docs@npm:2.2.19" + dependencies: + "@headlessui/react": ^1.7.10 + "@popperjs/core": ^2.11.6 + clsx: ^1.2.1 + flexsearch: ^0.7.21 + focus-visible: ^5.2.0 + git-url-parse: ^13.1.0 + intersection-observer: ^0.12.2 + match-sorter: ^6.3.1 + next-seo: ^5.5.0 + next-themes: ^0.2.1 + scroll-into-view-if-needed: ^3.0.0 + zod: ^3.20.2 + peerDependencies: + next: ">=9.5.3" + nextra: 2.2.19 + react: ">=16.13.1" + react-dom: ">=16.13.1" + checksum: f6cfdcff6475c77c847e9a85b8691a7d09f3514d62619790551e08374dd25c744023874edae5fc5964bf3dceb2c0d7f4d207dc5a0bad8c958b037fb66eb8039a + languageName: node + linkType: hard + +"nextra@npm:^2.2.19": + version: 2.2.19 + resolution: "nextra@npm:2.2.19" + dependencies: + "@mdx-js/mdx": ^2.3.0 + "@mdx-js/react": ^2.3.0 + "@napi-rs/simple-git": ^0.1.8 + github-slugger: ^2.0.0 + graceful-fs: ^4.2.10 + gray-matter: ^4.0.3 + katex: ^0.16.4 + lodash.get: ^4.4.2 + next-mdx-remote: ^4.2.1 + p-limit: ^3.1.0 + rehype-katex: ^6.0.2 + rehype-pretty-code: 0.9.3 + remark-gfm: ^3.0.1 + remark-math: ^5.1.1 + remark-reading-time: ^2.0.1 + shiki: ^0.14.0 + slash: ^3.0.0 + title: ^3.5.3 + unist-util-remove: ^3.1.1 + unist-util-visit: ^4.1.1 + peerDependencies: + next: ">=9.5.3" + react: ">=16.13.1" + react-dom: ">=16.13.1" + checksum: a7c29d0afac95d98fcaab84eac332ed871f50698728eddebd4cd8c0a7d7b8a7bdc718e8ea2d5e3a38d21e86eeec6dd55335f1e73097f85cb2a9d42830bdddf20 + languageName: node + linkType: hard + "nice-try@npm:^1.0.4": version: 1.0.5 resolution: "nice-try@npm:1.0.5" @@ -16260,7 +18484,7 @@ next@latest: languageName: node linkType: hard -"p-limit@npm:^3.0.2": +"p-limit@npm:^3.0.2, p-limit@npm:^3.1.0": version: 3.1.0 resolution: "p-limit@npm:3.1.0" dependencies: @@ -16368,6 +18592,9 @@ next@latest: version: 0.0.0-use.local resolution: "paplico-paint@workspace:." dependencies: + "@radix-ui/react-popover": latest + "@radix-ui/react-scroll-area": ^1.0.3 + react-draggable-tree: ^0.7.0 wsrun: ^5.2.4 languageName: unknown linkType: soft @@ -16381,6 +18608,22 @@ next@latest: languageName: node linkType: hard +"parse-entities@npm:^4.0.0": + version: 4.0.1 + resolution: "parse-entities@npm:4.0.1" + dependencies: + "@types/unist": ^2.0.0 + character-entities: ^2.0.0 + character-entities-legacy: ^3.0.0 + character-reference-invalid: ^2.0.0 + decode-named-character-reference: ^1.0.0 + is-alphanumerical: ^2.0.0 + is-decimal: ^2.0.0 + is-hexadecimal: ^2.0.0 + checksum: 32a6ff5b9acb9d2c4d71537308521fd265e685b9215691df73feedd9edfe041bb6da9f89bd0c35c4a2bc7d58e3e76e399bb6078c2fd7d2a343ff1dd46edbf1bd + languageName: node + linkType: hard + "parse-json@npm:^4.0.0": version: 4.0.0 resolution: "parse-json@npm:4.0.0" @@ -16403,6 +18646,22 @@ next@latest: languageName: node linkType: hard +"parse-numeric-range@npm:^1.3.0": + version: 1.3.0 + resolution: "parse-numeric-range@npm:1.3.0" + checksum: 289ca126d5b8ace7325b199218de198014f58ea6895ccc88a5247491d07f0143bf047f80b4a31784f1ca8911762278d7d6ecb90a31dfae31da91cc1a2524c8ce + languageName: node + linkType: hard + +"parse-path@npm:^7.0.0": + version: 7.0.0 + resolution: "parse-path@npm:7.0.0" + dependencies: + protocols: ^2.0.0 + checksum: 244b46523a58181d251dda9b888efde35d8afb957436598d948852f416d8c76ddb4f2010f9fc94218b4be3e5c0f716aa0d2026194a781e3b8981924142009302 + languageName: node + linkType: hard + "parse-svg-path@npm:^0.1.2, parse-svg-path@npm:~0.1.1": version: 0.1.2 resolution: "parse-svg-path@npm:0.1.2" @@ -16410,7 +18669,16 @@ next@latest: languageName: node linkType: hard -"parse5@npm:6.0.1": +"parse-url@npm:^8.1.0": + version: 8.1.0 + resolution: "parse-url@npm:8.1.0" + dependencies: + parse-path: ^7.0.0 + checksum: b93e21ab4c93c7d7317df23507b41be7697694d4c94f49ed5c8d6288b01cba328fcef5ba388e147948eac20453dee0df9a67ab2012415189fff85973bdffe8d9 + languageName: node + linkType: hard + +"parse5@npm:6.0.1, parse5@npm:^6.0.0": version: 6.0.1 resolution: "parse5@npm:6.0.1" checksum: 7d569a176c5460897f7c8f3377eff640d54132b9be51ae8a8fa4979af940830b2b0c296ce75e5bd8f4041520aadde13170dbdec44889975f906098ea0002f4bd @@ -16424,6 +18692,13 @@ next@latest: languageName: node linkType: hard +"path-browserify@npm:^1.0.1": + version: 1.0.1 + resolution: "path-browserify@npm:1.0.1" + checksum: c6d7fa376423fe35b95b2d67990060c3ee304fc815ff0a2dc1c6c3cfaff2bd0d572ee67e18f19d0ea3bbe32e8add2a05021132ac40509416459fffee35200699 + languageName: node + linkType: hard + "path-exists@npm:^3.0.0": version: 3.0.0 resolution: "path-exists@npm:3.0.0" @@ -16542,6 +18817,17 @@ next@latest: languageName: node linkType: hard +"periscopic@npm:^3.0.0": + version: 3.1.0 + resolution: "periscopic@npm:3.1.0" + dependencies: + "@types/estree": ^1.0.0 + estree-walker: ^3.0.0 + is-reference: ^3.0.0 + checksum: 2153244352e58a0d76e7e8d9263e66fe74509495f809af388da20045fb30aa3e93f2f94468dc0b9166ecf206fcfc0d73d2c7641c6fbedc07b1de858b710142cb + languageName: node + linkType: hard + "picocolors@npm:^1.0.0": version: 1.0.0 resolution: "picocolors@npm:1.0.0" @@ -17304,6 +19590,13 @@ next@latest: languageName: node linkType: hard +"property-information@npm:^6.0.0": + version: 6.2.0 + resolution: "property-information@npm:6.2.0" + checksum: 23afce07ba821cbe7d926e63cdd680991961c82be4bbb6c0b17c47f48894359c1be6e51cd74485fc10a9d3fd361b475388e1e39311ed2b53127718f72aab1955 + languageName: node + linkType: hard + "proto-list@npm:~1.2.1": version: 1.2.4 resolution: "proto-list@npm:1.2.4" @@ -17311,6 +19604,13 @@ next@latest: languageName: node linkType: hard +"protocols@npm:^2.0.0, protocols@npm:^2.0.1": + version: 2.0.1 + resolution: "protocols@npm:2.0.1" + checksum: 4a9bef6aa0449a0245ded319ac3cbfd032c3e76ebb562777037a3a832c99253d0e8bc2847f7be350236df620a11f7d4fe683ea7f59a2cc14c69f746b6259eda4 + languageName: node + linkType: hard + "proxy-addr@npm:~2.0.7": version: 2.0.7 resolution: "proxy-addr@npm:2.0.7" @@ -17491,6 +19791,18 @@ next@latest: languageName: node linkType: hard +"react-dom@npm:18.2.0": + version: 18.2.0 + resolution: "react-dom@npm:18.2.0" + dependencies: + loose-envify: ^1.1.0 + scheduler: ^0.23.0 + peerDependencies: + react: ^18.2.0 + checksum: 7d323310bea3a91be2965f9468d552f201b1c27891e45ddc2d6b8f717680c95a75ae0bc1e3f5cf41472446a2589a75aed4483aee8169287909fcd59ad149e8cc + languageName: node + linkType: hard + "react-dom@npm:^17.0.2": version: 17.0.2 resolution: "react-dom@npm:17.0.2" @@ -17504,15 +19816,10 @@ next@latest: languageName: node linkType: hard -"react-dom@npm:^18.0.0": - version: 18.1.0 - resolution: "react-dom@npm:18.1.0" - dependencies: - loose-envify: ^1.1.0 - scheduler: ^0.22.0 - peerDependencies: - react: ^18.1.0 - checksum: bb0d48eeb0b297c79c2a03978baa29f5b3ff7ba3d070b21e34c9af1a6e7fdf0ca8b8d73e41f9214d91ad40eeb6d1f3559f884cbbc338713374a51320637c23df +"react-draggable-tree@npm:^0.7.0": + version: 0.7.0 + resolution: "react-draggable-tree@npm:0.7.0" + checksum: e3403bb723b4f3e2b630d020eac4fb028d501b0b251e25aeee31101f3e2615946fcc2f4ff35bdd6d26a70a92b82fa1e675f56042fc7bfbb2bab516ee7ea347b6 languageName: node linkType: hard @@ -17606,6 +19913,41 @@ next@latest: languageName: node linkType: hard +"react-remove-scroll-bar@npm:^2.3.3": + version: 2.3.4 + resolution: "react-remove-scroll-bar@npm:2.3.4" + dependencies: + react-style-singleton: ^2.2.1 + tslib: ^2.0.0 + peerDependencies: + "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: b5ce5f2f98d65c97a3e975823ae4043a4ba2a3b63b5ba284b887e7853f051b5cd6afb74abde6d57b421931c52f2e1fdbb625dc858b1cb5a32c27c14ab85649d4 + languageName: node + linkType: hard + +"react-remove-scroll@npm:2.5.5": + version: 2.5.5 + resolution: "react-remove-scroll@npm:2.5.5" + dependencies: + react-remove-scroll-bar: ^2.3.3 + react-style-singleton: ^2.2.1 + tslib: ^2.1.0 + use-callback-ref: ^1.3.0 + use-sidecar: ^1.1.2 + peerDependencies: + "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 2c7fe9cbd766f5e54beb4bec2e2efb2de3583037b23fef8fa511ab426ed7f1ae992382db5acd8ab5bfb030a4b93a06a2ebca41377d6eeaf0e6791bb0a59616a4 + languageName: node + linkType: hard + "react-rnd@npm:^10.3.5": version: 10.3.7 resolution: "react-rnd@npm:10.3.7" @@ -17634,6 +19976,23 @@ next@latest: languageName: node linkType: hard +"react-style-singleton@npm:^2.2.1": + version: 2.2.1 + resolution: "react-style-singleton@npm:2.2.1" + dependencies: + get-nonce: ^1.0.0 + invariant: ^2.2.4 + tslib: ^2.0.0 + peerDependencies: + "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 7ee8ef3aab74c7ae1d70ff34a27643d11ba1a8d62d072c767827d9ff9a520905223e567002e0bf6c772929d8ea1c781a3ba0cc4a563e92b1e3dc2eaa817ecbe8 + languageName: node + linkType: hard + "react-universal-interface@npm:^0.6.2": version: 0.6.2 resolution: "react-universal-interface@npm:0.6.2" @@ -17644,6 +20003,15 @@ next@latest: languageName: node linkType: hard +"react-use-event-hook@npm:^0.9.5": + version: 0.9.5 + resolution: "react-use-event-hook@npm:0.9.5" + peerDependencies: + react: ">=16.8.0" + checksum: 392999a1abaa7e7b2ed71db275b7a3272639b9f5131af2e88ff74615044faa820b2b72ef5744e8170b62fd3fa808865e98215a7cabcf826eac998ea34adb1ebf + languageName: node + linkType: hard + "react-use-gesture@npm:^9.1.3": version: 9.1.3 resolution: "react-use-gesture@npm:9.1.3" @@ -17678,22 +20046,22 @@ next@latest: languageName: node linkType: hard -"react@npm:^17.0.2": - version: 17.0.2 - resolution: "react@npm:17.0.2" +"react@npm:18.2.0": + version: 18.2.0 + resolution: "react@npm:18.2.0" dependencies: loose-envify: ^1.1.0 - object-assign: ^4.1.1 - checksum: b254cc17ce3011788330f7bbf383ab653c6848902d7936a87b09d835d091e3f295f7e9dd1597c6daac5dc80f90e778c8230218ba8ad599f74adcc11e33b9d61b + checksum: 88e38092da8839b830cda6feef2e8505dec8ace60579e46aa5490fc3dc9bba0bd50336507dc166f43e3afc1c42939c09fe33b25fae889d6f402721dcd78fca1b languageName: node linkType: hard -"react@npm:^18.0.0": - version: 18.1.0 - resolution: "react@npm:18.1.0" +"react@npm:^17.0.2": + version: 17.0.2 + resolution: "react@npm:17.0.2" dependencies: loose-envify: ^1.1.0 - checksum: 5bb296b561b43ef2220395da4faac86c14a087c8c80e1a7598a5740f01ee605c11eaf249985c1e2000971c4cd32ccb46d40f00479bbd9fb6b1c7cf857393b7d4 + object-assign: ^4.1.1 + checksum: b254cc17ce3011788330f7bbf383ab653c6848902d7936a87b09d835d091e3f295f7e9dd1597c6daac5dc80f90e778c8230218ba8ad599f74adcc11e33b9d61b languageName: node linkType: hard @@ -17777,6 +20145,13 @@ next@latest: languageName: node linkType: hard +"reading-time@npm:^1.3.0": + version: 1.5.0 + resolution: "reading-time@npm:1.5.0" + checksum: e27bc5a70ba0f4ac337896b18531b914d38f4bee67cbad48029d0c11dd0a7a847b2a6bba895ab7ce2ad3e7ecb86912bdc477d8fa2d48405a3deda964be54d09b + languageName: node + linkType: hard + "regenerate-unicode-properties@npm:^10.0.1": version: 10.0.1 resolution: "regenerate-unicode-properties@npm:10.0.1" @@ -17802,6 +20177,13 @@ next@latest: languageName: node linkType: hard +"regenerator-runtime@npm:^0.13.11": + version: 0.13.11 + resolution: "regenerator-runtime@npm:0.13.11" + checksum: 27481628d22a1c4e3ff551096a683b424242a216fee44685467307f14d58020af1e19660bf2e26064de946bad7eff28950eae9f8209d55723e2d9351e632bbb4 + languageName: node + linkType: hard + "regenerator-runtime@npm:^0.13.4": version: 0.13.9 resolution: "regenerator-runtime@npm:0.13.9" @@ -17935,6 +20317,122 @@ next@latest: languageName: node linkType: hard +"rehype-katex@npm:^6.0.2": + version: 6.0.2 + resolution: "rehype-katex@npm:6.0.2" + dependencies: + "@types/hast": ^2.0.0 + "@types/katex": ^0.11.0 + hast-util-to-text: ^3.1.0 + katex: ^0.15.0 + rehype-parse: ^8.0.0 + unified: ^10.0.0 + unist-util-remove-position: ^4.0.0 + unist-util-visit: ^4.0.0 + checksum: ac8b3486441697b8e22cb7ebf6ec58e06d190240f45b128fe60422b9eb887599f38406581e6e3356af967eb1d45d631b0c09387f060190641f402f56c78fa771 + languageName: node + linkType: hard + +"rehype-parse@npm:^8.0.0": + version: 8.0.4 + resolution: "rehype-parse@npm:8.0.4" + dependencies: + "@types/hast": ^2.0.0 + hast-util-from-parse5: ^7.0.0 + parse5: ^6.0.0 + unified: ^10.0.0 + checksum: e678a5f9fa7cb91d5957f5f38bc37bc9fb90b8011a1ed6a90541ba6fff9f243c752c88b7f422cba8f5ba83ccb22942b1825654e8c3040970c703b85a6037efdf + languageName: node + linkType: hard + +"rehype-pretty-code@npm:0.9.3": + version: 0.9.3 + resolution: "rehype-pretty-code@npm:0.9.3" + dependencies: + hash-obj: ^4.0.0 + parse-numeric-range: ^1.3.0 + peerDependencies: + shiki: "*" + checksum: 5e4c6341467ef37cc08cf90c12c05a10a9bf88015f70b7e6b07270963efb4b456a8d41740fb4a576501bc15860dafd51ba11076e3589aa9839d064b22cf2fcfa + languageName: node + linkType: hard + +"remark-gfm@npm:^3.0.1": + version: 3.0.1 + resolution: "remark-gfm@npm:3.0.1" + dependencies: + "@types/mdast": ^3.0.0 + mdast-util-gfm: ^2.0.0 + micromark-extension-gfm: ^2.0.0 + unified: ^10.0.0 + checksum: 02254f74d67b3419c2c9cf62d799ec35f6c6cd74db25c001361751991552a7ce86049a972107bff8122d85d15ae4a8d1a0618f3bc01a7df837af021ae9b2a04e + languageName: node + linkType: hard + +"remark-math@npm:^5.1.1": + version: 5.1.1 + resolution: "remark-math@npm:5.1.1" + dependencies: + "@types/mdast": ^3.0.0 + mdast-util-math: ^2.0.0 + micromark-extension-math: ^2.0.0 + unified: ^10.0.0 + checksum: 1baec5862e36bbb8645144a73740e63a3aed2d547a64b731bb1a0162658319679378fd70f3d3d534655c2a0fcc3f941adba31cca33808e134fa22202a5d314f9 + languageName: node + linkType: hard + +"remark-mdx@npm:^2.0.0": + version: 2.3.0 + resolution: "remark-mdx@npm:2.3.0" + dependencies: + mdast-util-mdx: ^2.0.0 + micromark-extension-mdxjs: ^1.0.0 + checksum: 98486986c5b6f6a8321eb2f3b13c70fcd5644821428c77b7bfeb5ee5d4605b9761b322b2f6b531e83883cd2d5bc7bc4623427149aee00e1eba012f538b3d5627 + languageName: node + linkType: hard + +"remark-parse@npm:^10.0.0": + version: 10.0.1 + resolution: "remark-parse@npm:10.0.1" + dependencies: + "@types/mdast": ^3.0.0 + mdast-util-from-markdown: ^1.0.0 + unified: ^10.0.0 + checksum: 505088e564ab53ff054433368adbb7b551f69240c7d9768975529837a86f1d0f085e72d6211929c5c42db315273df4afc94f3d3a8662ffdb69468534c6643d29 + languageName: node + linkType: hard + +"remark-reading-time@npm:^2.0.1": + version: 2.0.1 + resolution: "remark-reading-time@npm:2.0.1" + dependencies: + estree-util-is-identifier-name: ^2.0.0 + estree-util-value-to-estree: ^1.3.0 + reading-time: ^1.3.0 + unist-util-visit: ^3.1.0 + checksum: 330b2c65d95f2ac3c1fd938fa9e163c5dda5b4aa8c5c554556797f6d1a7d1b7dfb3b580451f30cace04c0a23b4803a42b6827a2627f5a1c479b06b9570f86a80 + languageName: node + linkType: hard + +"remark-rehype@npm:^10.0.0": + version: 10.1.0 + resolution: "remark-rehype@npm:10.1.0" + dependencies: + "@types/hast": ^2.0.0 + "@types/mdast": ^3.0.0 + mdast-util-to-hast: ^12.1.0 + unified: ^10.0.0 + checksum: b9ac8acff3383b204dfdc2599d0bdf86e6ca7e837033209584af2e6aaa6a9013e519a379afa3201299798cab7298c8f4b388de118c312c67234c133318aec084 + languageName: node + linkType: hard + +"remove-accents@npm:0.4.2": + version: 0.4.2 + resolution: "remove-accents@npm:0.4.2" + checksum: 84a6988555dea24115e2d1954db99509588d43fe55a1590f0b5894802776f7b488b3151c37ceb9e4f4b646f26b80b7325dcea2fae58bc3865df146e1fa606711 + languageName: node + linkType: hard + "require-directory@npm:^2.1.1": version: 2.1.1 resolution: "require-directory@npm:2.1.1" @@ -18017,7 +20515,7 @@ next@latest: languageName: node linkType: hard -"resolve@npm:^1.22.0, resolve@npm:^1.22.1": +"resolve@npm:^1.22.0, resolve@npm:^1.22.1, resolve@npm:~1.22.1": version: 1.22.1 resolution: "resolve@npm:1.22.1" dependencies: @@ -18030,6 +20528,16 @@ next@latest: languageName: node linkType: hard +"resolve@npm:~1.19.0": + version: 1.19.0 + resolution: "resolve@npm:1.19.0" + dependencies: + is-core-module: ^2.1.0 + path-parse: ^1.0.6 + checksum: a05b356e47b85ad3613d9e2a39a824f3c27f4fcad9c9ff6c7cc71a2e314c5904a90ab37481ad0069d03cab9eaaac6eb68aca1bc3355fdb05f1045cd50e2aacea + languageName: node + linkType: hard + "resolve@patch:resolve@1.20.0#~builtin, resolve@patch:resolve@^1.0.0#~builtin, resolve@patch:resolve@^1.1.5#~builtin, resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.11.0#~builtin, resolve@patch:resolve@^1.14.2#~builtin, resolve@patch:resolve@^1.16.1#~builtin, resolve@patch:resolve@^1.17.0#~builtin, resolve@patch:resolve@^1.19.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin": version: 1.20.0 resolution: "resolve@patch:resolve@npm%3A1.20.0#~builtin::version=1.20.0&hash=c3c19d" @@ -18047,7 +20555,7 @@ next@latest: languageName: node linkType: hard -"resolve@patch:resolve@^1.22.0#~builtin, resolve@patch:resolve@^1.22.1#~builtin": +"resolve@patch:resolve@^1.22.0#~builtin, resolve@patch:resolve@^1.22.1#~builtin, resolve@patch:resolve@~1.22.1#~builtin": version: 1.22.1 resolution: "resolve@patch:resolve@npm%3A1.22.1#~builtin::version=1.22.1&hash=c3c19d" dependencies: @@ -18060,6 +20568,16 @@ next@latest: languageName: node linkType: hard +"resolve@patch:resolve@~1.19.0#~builtin": + version: 1.19.0 + resolution: "resolve@patch:resolve@npm%3A1.19.0#~builtin::version=1.19.0&hash=c3c19d" + dependencies: + is-core-module: ^2.1.0 + path-parse: ^1.0.6 + checksum: 2443b94d347e6946c87c85faf13071f605e609e0b54784829b0ed2b917d050bfc1cbaf4ecc6453f224cfa7d0c5dcd97cbb273454cd210bee68e4af15c1a5abc9 + languageName: node + linkType: hard + "responselike@npm:^1.0.2": version: 1.0.2 resolution: "responselike@npm:1.0.2" @@ -18336,6 +20854,15 @@ next@latest: languageName: node linkType: hard +"sade@npm:^1.7.3": + version: 1.8.1 + resolution: "sade@npm:1.8.1" + dependencies: + mri: ^1.1.0 + checksum: 0756e5b04c51ccdc8221ebffd1548d0ce5a783a44a0fa9017a026659b97d632913e78f7dca59f2496aa996a0be0b0c322afd87ca72ccd909406f49dbffa0f45d + languageName: node + linkType: hard + "safe-buffer@npm:*, safe-buffer@npm:5.2.1, safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.1.1, safe-buffer@npm:^5.1.2, safe-buffer@npm:~5.2.0": version: 5.2.1 resolution: "safe-buffer@npm:5.2.1" @@ -18408,6 +20935,15 @@ next@latest: languageName: node linkType: hard +"scheduler@npm:^0.23.0": + version: 0.23.0 + resolution: "scheduler@npm:0.23.0" + dependencies: + loose-envify: ^1.1.0 + checksum: d79192eeaa12abef860c195ea45d37cbf2bbf5f66e3c4dcd16f54a7da53b17788a70d109ee3d3dde1a0fd50e6a8fc171f4300356c5aee4fc0171de526bf35f8a + languageName: node + linkType: hard + "schema-utils@npm:^2.6.5": version: 2.7.1 resolution: "schema-utils@npm:2.7.1" @@ -18446,6 +20982,25 @@ next@latest: languageName: node linkType: hard +"scroll-into-view-if-needed@npm:^3.0.0": + version: 3.0.6 + resolution: "scroll-into-view-if-needed@npm:3.0.6" + dependencies: + compute-scroll-into-view: ^3.0.0 + checksum: fb3a62748f44bc14ee5f6986e6c3fd1a571811ff14e894f5ff1045247cfd9bf94ebd2038319971a8657bdd4c70a165e523ec1a0b90f74ded175a77035ff2d109 + languageName: node + linkType: hard + +"section-matter@npm:^1.0.0": + version: 1.0.0 + resolution: "section-matter@npm:1.0.0" + dependencies: + extend-shallow: ^2.0.1 + kind-of: ^6.0.0 + checksum: 3cc4131705493b2955729b075dcf562359bba66183debb0332752dc9cad35616f6da7a23e42b6cab45cd2e4bb5cda113e9e84c8f05aee77adb6b0289a0229101 + languageName: node + linkType: hard + "semver-compare@npm:^1.0.0": version: 1.0.0 resolution: "semver-compare@npm:1.0.0" @@ -18511,7 +21066,7 @@ next@latest: languageName: node linkType: hard -"semver@npm:^7.3.7": +"semver@npm:^7.3.7, semver@npm:~7.3.0": version: 7.3.8 resolution: "semver@npm:7.3.8" dependencies: @@ -18665,6 +21220,18 @@ next@latest: languageName: node linkType: hard +"shiki@npm:^0.14.0": + version: 0.14.1 + resolution: "shiki@npm:0.14.1" + dependencies: + ansi-sequence-parser: ^1.1.0 + jsonc-parser: ^3.2.0 + vscode-oniguruma: ^1.7.0 + vscode-textmate: ^8.0.0 + checksum: b19ea337cc84da69d99ca39d109f82946e0c56c11cc4c67b3b91cc14a9479203365fd0c9e0dd87e908f493ab409dc6f1849175384b6ca593ce7da884ae1edca2 + languageName: node + linkType: hard + "side-channel@npm:^1.0.4": version: 1.0.4 resolution: "side-channel@npm:1.0.4" @@ -18795,6 +21362,15 @@ next@latest: languageName: node linkType: hard +"sort-keys@npm:^5.0.0": + version: 5.0.0 + resolution: "sort-keys@npm:5.0.0" + dependencies: + is-plain-obj: ^4.0.0 + checksum: 9c0b7a468312075be03770b260b2cc0e5d55149025e564edaed41c9ff619199698aad6712a6fe4bbc75c541efb081276ac6bbd4cf2723d742f272f7a8fe354f5 + languageName: node + linkType: hard + "source-list-map@npm:^2.0.0": version: 2.0.1 resolution: "source-list-map@npm:2.0.1" @@ -18860,6 +21436,13 @@ next@latest: languageName: node linkType: hard +"source-map@npm:^0.7.0": + version: 0.7.4 + resolution: "source-map@npm:0.7.4" + checksum: 01cc5a74b1f0e1d626a58d36ad6898ea820567e87f18dfc9d24a9843a351aaa2ec09b87422589906d6ff1deed29693e176194dc88bcae7c9a852dc74b311dbf5 + languageName: node + linkType: hard + "source-map@npm:^0.7.3, source-map@npm:~0.7.2": version: 0.7.3 resolution: "source-map@npm:0.7.3" @@ -18883,6 +21466,13 @@ next@latest: languageName: node linkType: hard +"space-separated-tokens@npm:^2.0.0": + version: 2.0.2 + resolution: "space-separated-tokens@npm:2.0.2" + checksum: 202e97d7ca1ba0758a0aa4fe226ff98142073bcceeff2da3aad037968878552c3bbce3b3231970025375bbba5aee00c5b8206eda408da837ab2dc9c0f26be990 + languageName: node + linkType: hard + "spdx-correct@npm:^3.0.0": version: 3.1.1 resolution: "spdx-correct@npm:3.1.1" @@ -19039,6 +21629,13 @@ next@latest: languageName: node linkType: hard +"string-argv@npm:~0.3.1": + version: 0.3.1 + resolution: "string-argv@npm:0.3.1" + checksum: efbd0289b599bee808ce80820dfe49c9635610715429c6b7cc50750f0437e3c2f697c81e5c390208c13b5d5d12d904a1546172a88579f6ee5cbaaaa4dc9ec5cf + languageName: node + linkType: hard + "string-hash@npm:^1.1.1": version: 1.1.3 resolution: "string-hash@npm:1.1.3" @@ -19193,6 +21790,16 @@ next@latest: languageName: node linkType: hard +"stringify-entities@npm:^4.0.0": + version: 4.0.3 + resolution: "stringify-entities@npm:4.0.3" + dependencies: + character-entities-html4: ^2.0.0 + character-entities-legacy: ^3.0.0 + checksum: 59e8f523b403bf7d415690e72ae52982decd6ea5426bd8b3f5c66225ddde73e766c0c0d91627df082d0794e30b19dd907ffb5864cef3602e4098d6777d7ca3c2 + languageName: node + linkType: hard + "stringify-object@npm:^3.3.0": version: 3.3.0 resolution: "stringify-object@npm:3.3.0" @@ -19249,6 +21856,13 @@ next@latest: languageName: node linkType: hard +"strip-bom-string@npm:^1.0.0": + version: 1.0.0 + resolution: "strip-bom-string@npm:1.0.0" + checksum: 5635a3656d8512a2c194d6c8d5dee7ef0dde6802f7be9413b91e201981ad4132506656d9cf14137f019fd50f0269390d91c7f6a2601b1bee039a4859cfce4934 + languageName: node + linkType: hard + "strip-bom@npm:^3.0.0": version: 3.0.0 resolution: "strip-bom@npm:3.0.0" @@ -19284,7 +21898,7 @@ next@latest: languageName: node linkType: hard -"strip-json-comments@npm:^3.1.1": +"strip-json-comments@npm:^3.1.1, strip-json-comments@npm:~3.1.1": version: 3.1.1 resolution: "strip-json-comments@npm:3.1.1" checksum: 492f73e27268f9b1c122733f28ecb0e7e8d8a531a6662efbd08e22cccb3f9475e90a1b82cab06a392f6afae6d2de636f977e231296400d0ec5304ba70f166443 @@ -19314,6 +21928,15 @@ next@latest: languageName: node linkType: hard +"style-to-object@npm:^0.4.1": + version: 0.4.1 + resolution: "style-to-object@npm:0.4.1" + dependencies: + inline-style-parser: 0.1.1 + checksum: 2ea213e98eed21764ae1d1dc9359231a9f2d480d6ba55344c4c15eb275f0809f1845786e66d4caf62414a5cc8f112ce9425a58d251c77224060373e0db48f8c2 + languageName: node + linkType: hard + "styled-components@npm:^5.3.0": version: 5.3.3 resolution: "styled-components@npm:5.3.3" @@ -19470,6 +22093,15 @@ next@latest: languageName: node linkType: hard +"supports-color@npm:^4.0.0": + version: 4.5.0 + resolution: "supports-color@npm:4.5.0" + dependencies: + has-flag: ^2.0.0 + checksum: 6da4f498d5c71e8619f06e4a11d16f044105faf7590b5b005fc84933fbefdf72c2b4e5b7174c66da6ddc68e7f6ef56cc960a5ebd6f2d542d910e259e61b02335 + languageName: node + linkType: hard + "supports-color@npm:^5.3.0, supports-color@npm:^5.4.0, supports-color@npm:^5.5.0": version: 5.5.0 resolution: "supports-color@npm:5.5.0" @@ -19869,6 +22501,27 @@ next@latest: languageName: node linkType: hard +"title@npm:^3.5.3": + version: 3.5.3 + resolution: "title@npm:3.5.3" + dependencies: + arg: 1.0.0 + chalk: 2.3.0 + clipboardy: 1.2.2 + titleize: 1.0.0 + bin: + title: bin/title.js + checksum: 3fcd2fffcd12292d2015b7bdacf092cb9ed716b2e0187fdaa5ab0556f636c795f23a698e8a3d2f87ab647df35ba7b4e0ddfc1675b4676c535fb877daafb0dae5 + languageName: node + linkType: hard + +"titleize@npm:1.0.0": + version: 1.0.0 + resolution: "titleize@npm:1.0.0" + checksum: a8234b7860077f7d391d06e13c645fe2bf60431012d78ca196d4d73e4768e33cfc3a0dd881bf2f9974c8bd8beeb9d85e7842ec35d267b7c2a06f287de1c4ea7e + languageName: node + linkType: hard + "tmp-promise@npm:^3.0.2": version: 3.0.3 resolution: "tmp-promise@npm:3.0.3" @@ -19976,6 +22629,20 @@ next@latest: languageName: node linkType: hard +"trim-lines@npm:^3.0.0": + version: 3.0.1 + resolution: "trim-lines@npm:3.0.1" + checksum: e241da104682a0e0d807222cc1496b92e716af4db7a002f4aeff33ae6a0024fef93165d49eab11aa07c71e1347c42d46563f91dfaa4d3fb945aa535cdead53ed + languageName: node + linkType: hard + +"trough@npm:^2.0.0": + version: 2.1.0 + resolution: "trough@npm:2.1.0" + checksum: a577bb561c2b401cc0e1d9e188fcfcdf63b09b151ff56a668da12197fe97cac15e3d77d5b51f426ccfd94255744a9118e9e9935afe81a3644fa1be9783c82886 + languageName: node + linkType: hard + "truncate-utf8-bytes@npm:^1.0.0": version: 1.0.2 resolution: "truncate-utf8-bytes@npm:1.0.2" @@ -20056,6 +22723,16 @@ next@latest: languageName: node linkType: hard +"ts-morph@npm:17.0.1": + version: 17.0.1 + resolution: "ts-morph@npm:17.0.1" + dependencies: + "@ts-morph/common": ~0.18.0 + code-block-writer: ^11.0.3 + checksum: 4748ab45d0fb0be235f69399ea217cf1c5984ad2ef3ff9eba5a417571f73098c6f1f765fc011eaadc48179471b977f1e44f72eb993932e5c74c5031ab6c60f3a + languageName: node + linkType: hard + "ts-node@npm:^10.7.0": version: 10.7.0 resolution: "ts-node@npm:10.7.0" @@ -20173,6 +22850,13 @@ next@latest: languageName: node linkType: hard +"type-fest@npm:^1.0.2": + version: 1.4.0 + resolution: "type-fest@npm:1.4.0" + checksum: b011c3388665b097ae6a109a437a04d6f61d81b7357f74cbcb02246f2f5bd72b888ae33631b99871388122ba0a87f4ff1c94078e7119ff22c70e52c0ff828201 + languageName: node + linkType: hard + "type-is@npm:~1.6.18": version: 1.6.18 resolution: "type-is@npm:1.6.18" @@ -20209,6 +22893,26 @@ next@latest: languageName: node linkType: hard +"typescript@npm:^5.0.3": + version: 5.0.3 + resolution: "typescript@npm:5.0.3" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 3cce0576d218cb4277ff8b6adfef1a706e9114a98b4261a38ad658a7642f1b274a8396394f6cbff8c0ba852996d7ed2e233e9b8431d5d55ac7c2f6fea645af02 + languageName: node + linkType: hard + +"typescript@npm:~4.8.4": + version: 4.8.4 + resolution: "typescript@npm:4.8.4" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 3e4f061658e0c8f36c820802fa809e0fd812b85687a9a2f5430bc3d0368e37d1c9605c3ce9b39df9a05af2ece67b1d844f9f6ea8ff42819f13bcb80f85629af0 + languageName: node + linkType: hard + "typescript@patch:typescript@4.9.3#~builtin, typescript@patch:typescript@^4.9.3#~builtin": version: 4.9.3 resolution: "typescript@patch:typescript@npm%3A4.9.3#~builtin::version=4.9.3&hash=d73830" @@ -20219,6 +22923,26 @@ next@latest: languageName: node linkType: hard +"typescript@patch:typescript@^5.0.3#~builtin": + version: 5.0.3 + resolution: "typescript@patch:typescript@npm%3A5.0.3#~builtin::version=5.0.3&hash=d73830" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 9ec0a8eed38d46cc2c8794555b7674e413604c56c159f71b8ff21ce7f17334a44127a68724cb2ef8221ff3b19369f8f05654e8a5266621d7d962aeed889bd630 + languageName: node + linkType: hard + +"typescript@patch:typescript@~4.8.4#~builtin": + version: 4.8.4 + resolution: "typescript@patch:typescript@npm%3A4.8.4#~builtin::version=4.8.4&hash=0102e9" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 301459fc3eb3b1a38fe91bf96d98eb55da88a9cb17b4ef80b4d105d620f4d547ba776cc27b44cc2ef58b66eda23fe0a74142feb5e79a6fb99f54fc018a696afa + languageName: node + linkType: hard + "ufo@npm:^1.0.0": version: 1.0.1 resolution: "ufo@npm:1.0.1" @@ -20312,6 +23036,21 @@ next@latest: languageName: node linkType: hard +"unified@npm:^10.0.0": + version: 10.1.2 + resolution: "unified@npm:10.1.2" + dependencies: + "@types/unist": ^2.0.0 + bail: ^2.0.0 + extend: ^3.0.0 + is-buffer: ^2.0.0 + is-plain-obj: ^4.0.0 + trough: ^2.0.0 + vfile: ^5.0.0 + checksum: 053e7c65ede644607f87bd625a299e4b709869d2f76ec8138569e6e886903b6988b21cd9699e471eda42bee189527be0a9dac05936f1d069a5e65d0125d5d756 + languageName: node + linkType: hard + "uniq@npm:^1.0.1": version: 1.0.1 resolution: "uniq@npm:1.0.1" @@ -20362,6 +23101,122 @@ next@latest: languageName: node linkType: hard +"unist-util-find-after@npm:^4.0.0": + version: 4.0.1 + resolution: "unist-util-find-after@npm:4.0.1" + dependencies: + "@types/unist": ^2.0.0 + unist-util-is: ^5.0.0 + checksum: bed7e7a1a87539bea0b33ddc9ce8e2f3fdd4a7c87e143a848ed5bbb4cf9c563ade7ecf80b3ee5a38f9ad9e6af29cdb8cdde9001eea92542cbb14784f5add7019 + languageName: node + linkType: hard + +"unist-util-generated@npm:^2.0.0": + version: 2.0.1 + resolution: "unist-util-generated@npm:2.0.1" + checksum: 6221ad0571dcc9c8964d6b054f39ef6571ed59cc0ce3e88ae97ea1c70afe76b46412a5ffaa91f96814644ac8477e23fb1b477d71f8d70e625728c5258f5c0d99 + languageName: node + linkType: hard + +"unist-util-is@npm:^5.0.0": + version: 5.2.1 + resolution: "unist-util-is@npm:5.2.1" + dependencies: + "@types/unist": ^2.0.0 + checksum: ae76fdc3d35352cd92f1bedc3a0d407c3b9c42599a52ab9141fe89bdd786b51f0ec5a2ab68b93fb532e239457cae62f7e39eaa80229e1cb94875da2eafcbe5c4 + languageName: node + linkType: hard + +"unist-util-position-from-estree@npm:^1.0.0, unist-util-position-from-estree@npm:^1.1.0": + version: 1.1.2 + resolution: "unist-util-position-from-estree@npm:1.1.2" + dependencies: + "@types/unist": ^2.0.0 + checksum: e3f4060e2a9e894c6ed63489c5a7cb58ff282e5dae9497cbc2073033ca74d6e412af4d4d342c97aea08d997c908b8bce2fe43a2062aafc2bb3f266533016588b + languageName: node + linkType: hard + +"unist-util-position@npm:^4.0.0": + version: 4.0.4 + resolution: "unist-util-position@npm:4.0.4" + dependencies: + "@types/unist": ^2.0.0 + checksum: e7487b6cec9365299695e3379ded270a1717074fa11fd2407c9b934fb08db6fe1d9077ddeaf877ecf1813665f8ccded5171693d3d9a7a01a125ec5cdd5e88691 + languageName: node + linkType: hard + +"unist-util-remove-position@npm:^4.0.0": + version: 4.0.2 + resolution: "unist-util-remove-position@npm:4.0.2" + dependencies: + "@types/unist": ^2.0.0 + unist-util-visit: ^4.0.0 + checksum: 989831da913d09a82a99ed9b47b78471b6409bde95942cde47e09da54b7736516f17e3c7e026af468684c1efcec5fb52df363381b2f9dc7fd96ce791c5a2fa4a + languageName: node + linkType: hard + +"unist-util-remove@npm:^3.1.1": + version: 3.1.1 + resolution: "unist-util-remove@npm:3.1.1" + dependencies: + "@types/unist": ^2.0.0 + unist-util-is: ^5.0.0 + unist-util-visit-parents: ^5.0.0 + checksum: ed7c762941e6a9b6db230e9417697c8eb7d36093240b6f6d4ec265c4237d33e332a96a18307c8fb322a1842e3feb2a7564b032b5535fa0634eb1e075a6e344cb + languageName: node + linkType: hard + +"unist-util-stringify-position@npm:^3.0.0": + version: 3.0.3 + resolution: "unist-util-stringify-position@npm:3.0.3" + dependencies: + "@types/unist": ^2.0.0 + checksum: dbd66c15183607ca942a2b1b7a9f6a5996f91c0d30cf8966fb88955a02349d9eefd3974e9010ee67e71175d784c5a9fea915b0aa0b0df99dcb921b95c4c9e124 + languageName: node + linkType: hard + +"unist-util-visit-parents@npm:^4.0.0": + version: 4.1.1 + resolution: "unist-util-visit-parents@npm:4.1.1" + dependencies: + "@types/unist": ^2.0.0 + unist-util-is: ^5.0.0 + checksum: 49d78984a6dd858a989f849d2b4330c8a04d1ee99c0e9920a5e37668cf847dab95db77a3bf0c8aaeb3e66abeae12e2d454949ec401614efef377d8f82d215662 + languageName: node + linkType: hard + +"unist-util-visit-parents@npm:^5.0.0, unist-util-visit-parents@npm:^5.1.1": + version: 5.1.3 + resolution: "unist-util-visit-parents@npm:5.1.3" + dependencies: + "@types/unist": ^2.0.0 + unist-util-is: ^5.0.0 + checksum: 8ecada5978994f846b64658cf13b4092cd78dea39e1ba2f5090a5de842ba4852712c02351a8ae95250c64f864635e7b02aedf3b4a093552bb30cf1bd160efbaa + languageName: node + linkType: hard + +"unist-util-visit@npm:^3.1.0": + version: 3.1.0 + resolution: "unist-util-visit@npm:3.1.0" + dependencies: + "@types/unist": ^2.0.0 + unist-util-is: ^5.0.0 + unist-util-visit-parents: ^4.0.0 + checksum: c37dbc0c5509f85f3abdf46d927b3dd11e6c419159771b1f1a5ce446d36ac993d04b087e28bc6173a172e0fbe9d77e997f120029b2b449766ebe55b6f6e0cc2c + languageName: node + linkType: hard + +"unist-util-visit@npm:^4.0.0, unist-util-visit@npm:^4.1.1": + version: 4.1.2 + resolution: "unist-util-visit@npm:4.1.2" + dependencies: + "@types/unist": ^2.0.0 + unist-util-is: ^5.0.0 + unist-util-visit-parents: ^5.1.1 + checksum: 95a34e3f7b5b2d4b68fd722b6229972099eb97b6df18913eda44a5c11df8b1e27efe7206dd7b88c4ed244a48c474a5b2e2629ab79558ff9eb936840295549cee + languageName: node + linkType: hard + "universalify@npm:^0.1.0, universalify@npm:^0.1.2": version: 0.1.2 resolution: "universalify@npm:0.1.2" @@ -20490,6 +23345,21 @@ next@latest: languageName: node linkType: hard +"use-callback-ref@npm:^1.3.0": + version: 1.3.0 + resolution: "use-callback-ref@npm:1.3.0" + dependencies: + tslib: ^2.0.0 + peerDependencies: + "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 7913df383a5a6fcb399212eedefaac2e0c6f843555202d4e3010bac3848afe38ecaa3d0d6500ad1d936fbeffd637e6c517e68edb024af5e6beca7f27f3ce7b21 + languageName: node + linkType: hard + "use-immer@npm:^0.7.0": version: 0.7.0 resolution: "use-immer@npm:0.7.0" @@ -20532,6 +23402,22 @@ next@latest: languageName: node linkType: hard +"use-sidecar@npm:^1.1.2": + version: 1.1.2 + resolution: "use-sidecar@npm:1.1.2" + dependencies: + detect-node-es: ^1.1.0 + tslib: ^2.0.0 + peerDependencies: + "@types/react": ^16.9.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 925d1922f9853e516eaad526b6fed1be38008073067274f0ecc3f56b17bb8ab63480140dd7c271f94150027c996cea4efe83d3e3525e8f3eda22055f6a39220b + languageName: node + linkType: hard + "use-sound@npm:^4.0.1": version: 4.0.1 resolution: "use-sound@npm:4.0.1" @@ -20604,6 +23490,20 @@ next@latest: languageName: node linkType: hard +"uvu@npm:^0.5.0": + version: 0.5.6 + resolution: "uvu@npm:0.5.6" + dependencies: + dequal: ^2.0.0 + diff: ^5.0.0 + kleur: ^4.0.3 + sade: ^1.7.3 + bin: + uvu: bin.js + checksum: 09460a37975627de9fcad396e5078fb844d01aaf64a6399ebfcfd9e55f1c2037539b47611e8631f89be07656962af0cf48c334993db82b9ae9c3d25ce3862168 + languageName: node + linkType: hard + "v8-compile-cache-lib@npm:^3.0.0": version: 3.0.1 resolution: "v8-compile-cache-lib@npm:3.0.1" @@ -20661,6 +23561,13 @@ next@latest: languageName: node linkType: hard +"validator@npm:^13.7.0": + version: 13.9.0 + resolution: "validator@npm:13.9.0" + checksum: e2c936f041f61faa42bafd17c6faddf939498666cd82e88d733621c286893730b008959f4cb12ab3e236148a4f3805c30b85e3dcf5e0efd8b0cbcd36c02bfc0c + languageName: node + linkType: hard + "vary@npm:~1.1.2": version: 1.1.2 resolution: "vary@npm:1.1.2" @@ -20686,6 +23593,49 @@ next@latest: languageName: node linkType: hard +"vfile-location@npm:^4.0.0": + version: 4.1.0 + resolution: "vfile-location@npm:4.1.0" + dependencies: + "@types/unist": ^2.0.0 + vfile: ^5.0.0 + checksum: c894e8e5224170d1f85288f4a1d1ebcee0780823ea2b49d881648ab360ebf01b37ecb09b1c4439a75f9a51f31a9f9742cd045e987763e367c352a1ef7c50d446 + languageName: node + linkType: hard + +"vfile-matter@npm:^3.0.1": + version: 3.0.1 + resolution: "vfile-matter@npm:3.0.1" + dependencies: + "@types/js-yaml": ^4.0.0 + is-buffer: ^2.0.0 + js-yaml: ^4.0.0 + checksum: ced55ed7d79291b6c9321557d685b3c0072321f3de44790b72005f1e232394dd9ae68311b99286e327ec4f1d168d5bada986eaa1d475757e17b7e24150f503ac + languageName: node + linkType: hard + +"vfile-message@npm:^3.0.0": + version: 3.1.4 + resolution: "vfile-message@npm:3.1.4" + dependencies: + "@types/unist": ^2.0.0 + unist-util-stringify-position: ^3.0.0 + checksum: d0ee7da1973ad76513c274e7912adbed4d08d180eaa34e6bd40bc82459f4b7bc50fcaff41556135e3339995575eac5f6f709aba9332b80f775618ea4880a1367 + languageName: node + linkType: hard + +"vfile@npm:^5.0.0, vfile@npm:^5.3.0": + version: 5.3.7 + resolution: "vfile@npm:5.3.7" + dependencies: + "@types/unist": ^2.0.0 + is-buffer: ^2.0.0 + unist-util-stringify-position: ^3.0.0 + vfile-message: ^3.0.0 + checksum: 642cce703afc186dbe7cabf698dc954c70146e853491086f5da39e1ce850676fc96b169fcf7898aa3ff245e9313aeec40da93acd1e1fcc0c146dc4f6308b4ef9 + languageName: node + linkType: hard + "vite-node@npm:0.26.2": version: 0.26.2 resolution: "vite-node@npm:0.26.2" @@ -20702,6 +23652,26 @@ next@latest: languageName: node linkType: hard +"vite-plugin-dts@npm:^2.1.0": + version: 2.1.0 + resolution: "vite-plugin-dts@npm:2.1.0" + dependencies: + "@babel/parser": ^7.20.15 + "@microsoft/api-extractor": ^7.33.5 + "@rollup/pluginutils": ^5.0.2 + "@rushstack/node-core-library": ^3.53.2 + debug: ^4.3.4 + fast-glob: ^3.2.12 + fs-extra: ^10.1.0 + kolorist: ^1.6.0 + magic-string: ^0.29.0 + ts-morph: 17.0.1 + peerDependencies: + vite: ">=2.9.0" + checksum: 6c51b6de986e622a8ec271b68dcbfac55ab0ba65ca3b74e19e3088ff931913e5bb5c6d15e77525ca0bc11a6c87497f4529132cd0ad6c5970e8794f809cfa9cb8 + languageName: node + linkType: hard + "vite-tsconfig-paths@npm:^4.0.3": version: 4.0.3 resolution: "vite-tsconfig-paths@npm:4.0.3" @@ -20870,6 +23840,20 @@ next@latest: languageName: node linkType: hard +"vscode-oniguruma@npm:^1.7.0": + version: 1.7.0 + resolution: "vscode-oniguruma@npm:1.7.0" + checksum: 53519d91d90593e6fb080260892e87d447e9b200c4964d766772b5053f5699066539d92100f77f1302c91e8fc5d9c772fbe40fe4c90f3d411a96d5a9b1e63f42 + languageName: node + linkType: hard + +"vscode-textmate@npm:^8.0.0": + version: 8.0.0 + resolution: "vscode-textmate@npm:8.0.0" + checksum: 127780dfea89559d70b8326df6ec344cfd701312dd7f3f591a718693812b7852c30b6715e3cfc8b3200a4e2515b4c96f0843c0eacc0a3020969b5de262c2a4bb + languageName: node + linkType: hard + "w3c-hr-time@npm:^1.0.2": version: 1.0.2 resolution: "w3c-hr-time@npm:1.0.2" @@ -20950,6 +23934,13 @@ next@latest: languageName: node linkType: hard +"web-namespaces@npm:^2.0.0": + version: 2.0.1 + resolution: "web-namespaces@npm:2.0.1" + checksum: b6d9f02f1a43d0ef0848a812d89c83801d5bbad57d8bb61f02eb6d7eb794c3736f6cc2e1191664bb26136594c8218ac609f4069722c6f56d9fc2d808fa9271c6 + languageName: node + linkType: hard + "webidl-conversions@npm:^4.0.2": version: 4.0.2 resolution: "webidl-conversions@npm:4.0.2" @@ -21706,9 +24697,40 @@ next@latest: languageName: node linkType: hard +"z-schema@npm:~5.0.2": + version: 5.0.5 + resolution: "z-schema@npm:5.0.5" + dependencies: + commander: ^9.4.1 + lodash.get: ^4.4.2 + lodash.isequal: ^4.5.0 + validator: ^13.7.0 + dependenciesMeta: + commander: + optional: true + bin: + z-schema: bin/z-schema + checksum: 8a1d66817ae4384dc3f63311f0cccaadd95cc9640eaade5fd3fbf91aa80d6bb82fb95d9b9171fa82ac371a0155b32b7f5f77bbe84dabaca611b66f74c628f0b8 + languageName: node + linkType: hard + "zod@npm:^3.19.1": version: 3.19.1 resolution: "zod@npm:3.19.1" checksum: 56e420ea5845912324a8fc61833714a2aec84954e418b52660d76502183c6e62fef9447cbfa64349640c5ce190cf2c24267e006bb80f066183e2f3fa9fe11864 languageName: node linkType: hard + +"zod@npm:^3.20.2": + version: 3.21.4 + resolution: "zod@npm:3.21.4" + checksum: f185ba87342ff16f7a06686767c2b2a7af41110c7edf7c1974095d8db7a73792696bcb4a00853de0d2edeb34a5b2ea6a55871bc864227dace682a0a28de33e1f + languageName: node + linkType: hard + +"zwitch@npm:^2.0.0": + version: 2.0.4 + resolution: "zwitch@npm:2.0.4" + checksum: f22ec5fc2d5f02c423c93d35cdfa83573a3a3bd98c66b927c368ea4d0e7252a500df2a90a6b45522be536a96a73404393c958e945fdba95e6832c200791702b6 + languageName: node + linkType: hard