kopia lustrzana https://github.com/zhengkyl/qrframe
run user code yippie
rodzic
7f7873b26f
commit
f7f5b094be
|
@ -3,11 +3,22 @@ import { defineConfig } from "@solidjs/start/config";
|
||||||
import UnoCSS from "unocss/vite";
|
import UnoCSS from "unocss/vite";
|
||||||
import wasmpack from "vite-plugin-wasm-pack";
|
import wasmpack from "vite-plugin-wasm-pack";
|
||||||
|
|
||||||
|
// b/c I'm using vite() instead of static config object
|
||||||
|
// getting a new plugin each time vite() runs messes up unocss styles on the `body`
|
||||||
|
const SharedUnoCss = UnoCSS();
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
server: { preset: "vercel" },
|
server: { preset: "vercel" },
|
||||||
ssr: true,
|
ssr: true,
|
||||||
vite: {
|
vite({ router }) {
|
||||||
plugins: [UnoCSS(), wasmpack(["./fuqr"])],
|
|
||||||
|
return {
|
||||||
|
plugins:
|
||||||
|
// https://github.com/nksaraf/vinxi/issues/262
|
||||||
|
// wasmpack copies pkg to node_modules asynchronously so triggering buildStart 3 times causes errors
|
||||||
|
router === "client"
|
||||||
|
? [SharedUnoCss, wasmpack(["./fuqr"])]
|
||||||
|
: [SharedUnoCss],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
// https://christopher.engineering/en/blog/lucide-icons-with-vite-dev-server/
|
// https://christopher.engineering/en/blog/lucide-icons-with-vite-dev-server/
|
||||||
|
@ -19,5 +30,6 @@ export default defineConfig({
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,27 +15,11 @@ export function get_matrix(input: string, qr_options: QrOptions): Matrix;
|
||||||
export function get_svg(input: string, qr_options: QrOptions, svg_options: SvgOptions): SvgResult;
|
export function get_svg(input: string, qr_options: QrOptions, svg_options: SvgOptions): SvgResult;
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
export enum Module {
|
export enum ECL {
|
||||||
DataOFF = 0,
|
Low = 0,
|
||||||
DataON = 1,
|
Medium = 1,
|
||||||
FinderOFF = 2,
|
Quartile = 2,
|
||||||
FinderON = 3,
|
High = 3,
|
||||||
AlignmentOFF = 4,
|
|
||||||
AlignmentON = 5,
|
|
||||||
TimingOFF = 6,
|
|
||||||
TimingON = 7,
|
|
||||||
FormatOFF = 8,
|
|
||||||
FormatON = 9,
|
|
||||||
VersionOFF = 10,
|
|
||||||
VersionON = 11,
|
|
||||||
Unset = 12,
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
export enum Toggle {
|
|
||||||
Background = 0,
|
|
||||||
BackgroundPixels = 1,
|
|
||||||
ForegroundPixels = 2,
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
|
@ -51,14 +35,6 @@ export enum Mask {
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
export enum ECL {
|
|
||||||
Low = 0,
|
|
||||||
Medium = 1,
|
|
||||||
Quartile = 2,
|
|
||||||
High = 3,
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
export enum Mode {
|
export enum Mode {
|
||||||
Numeric = 0,
|
Numeric = 0,
|
||||||
Alphanumeric = 1,
|
Alphanumeric = 1,
|
||||||
|
@ -66,12 +42,36 @@ export enum Mode {
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
|
export enum Toggle {
|
||||||
|
Background = 0,
|
||||||
|
BackgroundPixels = 1,
|
||||||
|
ForegroundPixels = 2,
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*/
|
||||||
export enum QrError {
|
export enum QrError {
|
||||||
InvalidEncoding = 0,
|
InvalidEncoding = 0,
|
||||||
ExceedsMaxCapacity = 1,
|
ExceedsMaxCapacity = 1,
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
|
export enum Module {
|
||||||
|
DataOFF = 0,
|
||||||
|
DataON = 1,
|
||||||
|
FinderOFF = 2,
|
||||||
|
FinderON = 3,
|
||||||
|
AlignmentOFF = 4,
|
||||||
|
AlignmentON = 5,
|
||||||
|
TimingOFF = 6,
|
||||||
|
TimingON = 7,
|
||||||
|
FormatOFF = 8,
|
||||||
|
FormatON = 9,
|
||||||
|
VersionOFF = 10,
|
||||||
|
VersionON = 11,
|
||||||
|
Unset = 12,
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*/
|
||||||
export class Margin {
|
export class Margin {
|
||||||
free(): void;
|
free(): void;
|
||||||
/**
|
/**
|
||||||
|
@ -193,11 +193,6 @@ export class SvgOptions {
|
||||||
*/
|
*/
|
||||||
constructor();
|
constructor();
|
||||||
/**
|
/**
|
||||||
* @param {number} margin
|
|
||||||
* @returns {SvgOptions}
|
|
||||||
*/
|
|
||||||
margin(margin: number): SvgOptions;
|
|
||||||
/**
|
|
||||||
* @param {number} unit
|
* @param {number} unit
|
||||||
* @returns {SvgOptions}
|
* @returns {SvgOptions}
|
||||||
*/
|
*/
|
||||||
|
@ -213,20 +208,20 @@ export class SvgOptions {
|
||||||
*/
|
*/
|
||||||
background(background: string): SvgOptions;
|
background(background: string): SvgOptions;
|
||||||
/**
|
/**
|
||||||
* @param {Uint8Array} scale_matrix
|
* @param {Uint8Array | undefined} [scale_x_matrix]
|
||||||
* @returns {SvgOptions}
|
* @returns {SvgOptions}
|
||||||
*/
|
*/
|
||||||
scale_x_matrix(scale_matrix: Uint8Array): SvgOptions;
|
scale_x_matrix(scale_x_matrix?: Uint8Array): SvgOptions;
|
||||||
/**
|
/**
|
||||||
* @param {Uint8Array} scale_matrix
|
* @param {Uint8Array | undefined} [scale_y_matrix]
|
||||||
* @returns {SvgOptions}
|
* @returns {SvgOptions}
|
||||||
*/
|
*/
|
||||||
scale_y_matrix(scale_matrix: Uint8Array): SvgOptions;
|
scale_y_matrix(scale_y_matrix?: Uint8Array): SvgOptions;
|
||||||
/**
|
/**
|
||||||
* @param {Uint8Array} scale_matrix
|
* @param {Uint8Array | undefined} [scale_matrix]
|
||||||
* @returns {SvgOptions}
|
* @returns {SvgOptions}
|
||||||
*/
|
*/
|
||||||
scale_matrix(scale_matrix: Uint8Array): SvgOptions;
|
scale_matrix(scale_matrix?: Uint8Array): SvgOptions;
|
||||||
/**
|
/**
|
||||||
* @param {Toggle} toggle
|
* @param {Toggle} toggle
|
||||||
* @returns {SvgOptions}
|
* @returns {SvgOptions}
|
||||||
|
@ -301,6 +296,10 @@ export interface InitOutput {
|
||||||
readonly __wbg_set_matrix_mask: (a: number, b: number) => void;
|
readonly __wbg_set_matrix_mask: (a: number, b: number) => void;
|
||||||
readonly matrix_width: (a: number) => number;
|
readonly matrix_width: (a: number) => number;
|
||||||
readonly matrix_height: (a: number) => number;
|
readonly matrix_height: (a: number) => number;
|
||||||
|
readonly __wbg_version_free: (a: number) => void;
|
||||||
|
readonly __wbg_get_version_0: (a: number) => number;
|
||||||
|
readonly __wbg_set_version_0: (a: number, b: number) => void;
|
||||||
|
readonly version_new: (a: number) => number;
|
||||||
readonly __wbg_qroptions_free: (a: number) => void;
|
readonly __wbg_qroptions_free: (a: number) => void;
|
||||||
readonly qroptions_new: () => number;
|
readonly qroptions_new: () => number;
|
||||||
readonly qroptions_min_version: (a: number, b: number) => number;
|
readonly qroptions_min_version: (a: number, b: number) => number;
|
||||||
|
@ -310,7 +309,6 @@ export interface InitOutput {
|
||||||
readonly qroptions_margin: (a: number, b: number) => number;
|
readonly qroptions_margin: (a: number, b: number) => number;
|
||||||
readonly __wbg_svgoptions_free: (a: number) => void;
|
readonly __wbg_svgoptions_free: (a: number) => void;
|
||||||
readonly svgoptions_new: () => number;
|
readonly svgoptions_new: () => number;
|
||||||
readonly svgoptions_margin: (a: number, b: number) => number;
|
|
||||||
readonly svgoptions_unit: (a: number, b: number) => number;
|
readonly svgoptions_unit: (a: number, b: number) => number;
|
||||||
readonly svgoptions_foreground: (a: number, b: number, c: number) => number;
|
readonly svgoptions_foreground: (a: number, b: number, c: number) => number;
|
||||||
readonly svgoptions_background: (a: number, b: number, c: number) => number;
|
readonly svgoptions_background: (a: number, b: number, c: number) => number;
|
||||||
|
@ -331,10 +329,6 @@ export interface InitOutput {
|
||||||
readonly __wbg_set_svgresult_mask: (a: number, b: number) => void;
|
readonly __wbg_set_svgresult_mask: (a: number, b: number) => void;
|
||||||
readonly get_matrix: (a: number, b: number, c: number, d: number) => void;
|
readonly get_matrix: (a: number, b: number, c: number, d: number) => void;
|
||||||
readonly get_svg: (a: number, b: number, c: number, d: number, e: number) => void;
|
readonly get_svg: (a: number, b: number, c: number, d: number, e: number) => void;
|
||||||
readonly __wbg_version_free: (a: number) => void;
|
|
||||||
readonly __wbg_get_version_0: (a: number) => number;
|
|
||||||
readonly __wbg_set_version_0: (a: number, b: number) => void;
|
|
||||||
readonly version_new: (a: number) => number;
|
|
||||||
readonly __wbindgen_add_to_stack_pointer: (a: number) => number;
|
readonly __wbindgen_add_to_stack_pointer: (a: number) => number;
|
||||||
readonly __wbindgen_free: (a: number, b: number, c: number) => void;
|
readonly __wbindgen_free: (a: number, b: number, c: number) => void;
|
||||||
readonly __wbindgen_malloc: (a: number, b: number) => number;
|
readonly __wbindgen_malloc: (a: number, b: number) => number;
|
||||||
|
|
|
@ -223,22 +223,22 @@ export function get_svg(input, qr_options, svg_options) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
export const Module = Object.freeze({ DataOFF:0,"0":"DataOFF",DataON:1,"1":"DataON",FinderOFF:2,"2":"FinderOFF",FinderON:3,"3":"FinderON",AlignmentOFF:4,"4":"AlignmentOFF",AlignmentON:5,"5":"AlignmentON",TimingOFF:6,"6":"TimingOFF",TimingON:7,"7":"TimingON",FormatOFF:8,"8":"FormatOFF",FormatON:9,"9":"FormatON",VersionOFF:10,"10":"VersionOFF",VersionON:11,"11":"VersionON",Unset:12,"12":"Unset", });
|
export const ECL = Object.freeze({ Low:0,"0":"Low",Medium:1,"1":"Medium",Quartile:2,"2":"Quartile",High:3,"3":"High", });
|
||||||
/**
|
|
||||||
*/
|
|
||||||
export const Toggle = Object.freeze({ Background:0,"0":"Background",BackgroundPixels:1,"1":"BackgroundPixels",ForegroundPixels:2,"2":"ForegroundPixels", });
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
export const Mask = Object.freeze({ M0:0,"0":"M0",M1:1,"1":"M1",M2:2,"2":"M2",M3:3,"3":"M3",M4:4,"4":"M4",M5:5,"5":"M5",M6:6,"6":"M6",M7:7,"7":"M7", });
|
export const Mask = Object.freeze({ M0:0,"0":"M0",M1:1,"1":"M1",M2:2,"2":"M2",M3:3,"3":"M3",M4:4,"4":"M4",M5:5,"5":"M5",M6:6,"6":"M6",M7:7,"7":"M7", });
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
export const ECL = Object.freeze({ Low:0,"0":"Low",Medium:1,"1":"Medium",Quartile:2,"2":"Quartile",High:3,"3":"High", });
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
export const Mode = Object.freeze({ Numeric:0,"0":"Numeric",Alphanumeric:1,"1":"Alphanumeric",Byte:2,"2":"Byte", });
|
export const Mode = Object.freeze({ Numeric:0,"0":"Numeric",Alphanumeric:1,"1":"Alphanumeric",Byte:2,"2":"Byte", });
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
|
export const Toggle = Object.freeze({ Background:0,"0":"Background",BackgroundPixels:1,"1":"BackgroundPixels",ForegroundPixels:2,"2":"ForegroundPixels", });
|
||||||
|
/**
|
||||||
|
*/
|
||||||
export const QrError = Object.freeze({ InvalidEncoding:0,"0":"InvalidEncoding",ExceedsMaxCapacity:1,"1":"ExceedsMaxCapacity", });
|
export const QrError = Object.freeze({ InvalidEncoding:0,"0":"InvalidEncoding",ExceedsMaxCapacity:1,"1":"ExceedsMaxCapacity", });
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
export const Module = Object.freeze({ DataOFF:0,"0":"DataOFF",DataON:1,"1":"DataON",FinderOFF:2,"2":"FinderOFF",FinderON:3,"3":"FinderON",AlignmentOFF:4,"4":"AlignmentOFF",AlignmentON:5,"5":"AlignmentON",TimingOFF:6,"6":"TimingOFF",TimingON:7,"7":"TimingON",FormatOFF:8,"8":"FormatOFF",FormatON:9,"9":"FormatON",VersionOFF:10,"10":"VersionOFF",VersionON:11,"11":"VersionON",Unset:12,"12":"Unset", });
|
||||||
|
|
||||||
const MarginFinalization = (typeof FinalizationRegistry === 'undefined')
|
const MarginFinalization = (typeof FinalizationRegistry === 'undefined')
|
||||||
? { register: () => {}, unregister: () => {} }
|
? { register: () => {}, unregister: () => {} }
|
||||||
|
@ -635,15 +635,6 @@ export class SvgOptions {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @param {number} margin
|
|
||||||
* @returns {SvgOptions}
|
|
||||||
*/
|
|
||||||
margin(margin) {
|
|
||||||
const ptr = this.__destroy_into_raw();
|
|
||||||
const ret = wasm.svgoptions_margin(ptr, margin);
|
|
||||||
return SvgOptions.__wrap(ret);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* @param {number} unit
|
* @param {number} unit
|
||||||
* @returns {SvgOptions}
|
* @returns {SvgOptions}
|
||||||
*/
|
*/
|
||||||
|
@ -675,35 +666,35 @@ export class SvgOptions {
|
||||||
return SvgOptions.__wrap(ret);
|
return SvgOptions.__wrap(ret);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @param {Uint8Array} scale_matrix
|
* @param {Uint8Array | undefined} [scale_x_matrix]
|
||||||
* @returns {SvgOptions}
|
* @returns {SvgOptions}
|
||||||
*/
|
*/
|
||||||
scale_x_matrix(scale_matrix) {
|
scale_x_matrix(scale_x_matrix) {
|
||||||
const ptr = this.__destroy_into_raw();
|
const ptr = this.__destroy_into_raw();
|
||||||
const ptr0 = passArray8ToWasm0(scale_matrix, wasm.__wbindgen_malloc);
|
var ptr0 = isLikeNone(scale_x_matrix) ? 0 : passArray8ToWasm0(scale_x_matrix, wasm.__wbindgen_malloc);
|
||||||
const len0 = WASM_VECTOR_LEN;
|
var len0 = WASM_VECTOR_LEN;
|
||||||
const ret = wasm.svgoptions_scale_x_matrix(ptr, ptr0, len0);
|
const ret = wasm.svgoptions_scale_x_matrix(ptr, ptr0, len0);
|
||||||
return SvgOptions.__wrap(ret);
|
return SvgOptions.__wrap(ret);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @param {Uint8Array} scale_matrix
|
* @param {Uint8Array | undefined} [scale_y_matrix]
|
||||||
* @returns {SvgOptions}
|
* @returns {SvgOptions}
|
||||||
*/
|
*/
|
||||||
scale_y_matrix(scale_matrix) {
|
scale_y_matrix(scale_y_matrix) {
|
||||||
const ptr = this.__destroy_into_raw();
|
const ptr = this.__destroy_into_raw();
|
||||||
const ptr0 = passArray8ToWasm0(scale_matrix, wasm.__wbindgen_malloc);
|
var ptr0 = isLikeNone(scale_y_matrix) ? 0 : passArray8ToWasm0(scale_y_matrix, wasm.__wbindgen_malloc);
|
||||||
const len0 = WASM_VECTOR_LEN;
|
var len0 = WASM_VECTOR_LEN;
|
||||||
const ret = wasm.svgoptions_scale_y_matrix(ptr, ptr0, len0);
|
const ret = wasm.svgoptions_scale_y_matrix(ptr, ptr0, len0);
|
||||||
return SvgOptions.__wrap(ret);
|
return SvgOptions.__wrap(ret);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @param {Uint8Array} scale_matrix
|
* @param {Uint8Array | undefined} [scale_matrix]
|
||||||
* @returns {SvgOptions}
|
* @returns {SvgOptions}
|
||||||
*/
|
*/
|
||||||
scale_matrix(scale_matrix) {
|
scale_matrix(scale_matrix) {
|
||||||
const ptr = this.__destroy_into_raw();
|
const ptr = this.__destroy_into_raw();
|
||||||
const ptr0 = passArray8ToWasm0(scale_matrix, wasm.__wbindgen_malloc);
|
var ptr0 = isLikeNone(scale_matrix) ? 0 : passArray8ToWasm0(scale_matrix, wasm.__wbindgen_malloc);
|
||||||
const len0 = WASM_VECTOR_LEN;
|
var len0 = WASM_VECTOR_LEN;
|
||||||
const ret = wasm.svgoptions_scale_matrix(ptr, ptr0, len0);
|
const ret = wasm.svgoptions_scale_matrix(ptr, ptr0, len0);
|
||||||
return SvgOptions.__wrap(ret);
|
return SvgOptions.__wrap(ret);
|
||||||
}
|
}
|
||||||
|
|
Plik binarny nie jest wyświetlany.
|
@ -32,6 +32,10 @@ export function __wbg_get_matrix_mask(a: number): number;
|
||||||
export function __wbg_set_matrix_mask(a: number, b: number): void;
|
export function __wbg_set_matrix_mask(a: number, b: number): void;
|
||||||
export function matrix_width(a: number): number;
|
export function matrix_width(a: number): number;
|
||||||
export function matrix_height(a: number): number;
|
export function matrix_height(a: number): number;
|
||||||
|
export function __wbg_version_free(a: number): void;
|
||||||
|
export function __wbg_get_version_0(a: number): number;
|
||||||
|
export function __wbg_set_version_0(a: number, b: number): void;
|
||||||
|
export function version_new(a: number): number;
|
||||||
export function __wbg_qroptions_free(a: number): void;
|
export function __wbg_qroptions_free(a: number): void;
|
||||||
export function qroptions_new(): number;
|
export function qroptions_new(): number;
|
||||||
export function qroptions_min_version(a: number, b: number): number;
|
export function qroptions_min_version(a: number, b: number): number;
|
||||||
|
@ -41,7 +45,6 @@ export function qroptions_mask(a: number, b: number): number;
|
||||||
export function qroptions_margin(a: number, b: number): number;
|
export function qroptions_margin(a: number, b: number): number;
|
||||||
export function __wbg_svgoptions_free(a: number): void;
|
export function __wbg_svgoptions_free(a: number): void;
|
||||||
export function svgoptions_new(): number;
|
export function svgoptions_new(): number;
|
||||||
export function svgoptions_margin(a: number, b: number): number;
|
|
||||||
export function svgoptions_unit(a: number, b: number): number;
|
export function svgoptions_unit(a: number, b: number): number;
|
||||||
export function svgoptions_foreground(a: number, b: number, c: number): number;
|
export function svgoptions_foreground(a: number, b: number, c: number): number;
|
||||||
export function svgoptions_background(a: number, b: number, c: number): number;
|
export function svgoptions_background(a: number, b: number, c: number): number;
|
||||||
|
@ -62,10 +65,6 @@ export function __wbg_get_svgresult_mask(a: number): number;
|
||||||
export function __wbg_set_svgresult_mask(a: number, b: number): void;
|
export function __wbg_set_svgresult_mask(a: number, b: number): void;
|
||||||
export function get_matrix(a: number, b: number, c: number, d: number): void;
|
export function get_matrix(a: number, b: number, c: number, d: number): void;
|
||||||
export function get_svg(a: number, b: number, c: number, d: number, e: number): void;
|
export function get_svg(a: number, b: number, c: number, d: number, e: number): void;
|
||||||
export function __wbg_version_free(a: number): void;
|
|
||||||
export function __wbg_get_version_0(a: number): number;
|
|
||||||
export function __wbg_set_version_0(a: number, b: number): void;
|
|
||||||
export function version_new(a: number): number;
|
|
||||||
export function __wbindgen_add_to_stack_pointer(a: number): number;
|
export function __wbindgen_add_to_stack_pointer(a: number): number;
|
||||||
export function __wbindgen_free(a: number, b: number, c: number): void;
|
export function __wbindgen_free(a: number, b: number, c: number): void;
|
||||||
export function __wbindgen_malloc(a: number, b: number): number;
|
export function __wbindgen_malloc(a: number, b: number): number;
|
||||||
|
|
|
@ -10,8 +10,11 @@
|
||||||
"@kobalte/core": "^0.13.1",
|
"@kobalte/core": "^0.13.1",
|
||||||
"@solidjs/router": "^0.13.3",
|
"@solidjs/router": "^0.13.3",
|
||||||
"@solidjs/start": "^1.0.0",
|
"@solidjs/start": "^1.0.0",
|
||||||
|
"@srsholmes/solid-code-input": "^0.0.18",
|
||||||
"@unocss/reset": "^0.59.4",
|
"@unocss/reset": "^0.59.4",
|
||||||
|
"highlight.js": "^11.9.0",
|
||||||
"lucide-solid": "^0.378.0",
|
"lucide-solid": "^0.378.0",
|
||||||
|
"qr-scanner-wechat": "^0.1.3",
|
||||||
"solid-js": "^1.8.17",
|
"solid-js": "^1.8.17",
|
||||||
"unocss": "^0.59.4",
|
"unocss": "^0.59.4",
|
||||||
"vinxi": "^0.3.11"
|
"vinxi": "^0.3.11"
|
||||||
|
|
6525
pnpm-lock.yaml
6525
pnpm-lock.yaml
Plik diff jest za duży
Load Diff
|
@ -6,15 +6,21 @@ type Props = {
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
onMouseDown?: () => void;
|
onMouseDown?: () => void;
|
||||||
children: JSX.Element;
|
children: JSX.Element;
|
||||||
|
tooltip?: string;
|
||||||
|
disabled?: boolean;
|
||||||
};
|
};
|
||||||
export function FlatButton(props: Props) {
|
export function FlatButton(props: Props) {
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
class={`inline-flex justify-center items-center gap-1 border rounded-md hover:bg-fore-base/5 focus-visible:(outline-none ring-2 ring-fore-base ring-offset-2 ring-offset-back-base) ${
|
title={props.tooltip}
|
||||||
props.class || "px-3 py-2"
|
classList={{
|
||||||
}`}
|
"inline-flex justify-center items-center gap-1 border rounded-md hover:bg-fore-base/5 focus-visible:(outline-none ring-2 ring-fore-base ring-offset-2 ring-offset-back-base) disabled:(pointer-events-none opacity-50)":
|
||||||
|
true,
|
||||||
|
[props.class ?? "px-3 py-2"]: true,
|
||||||
|
}}
|
||||||
onMouseDown={props.onMouseDown}
|
onMouseDown={props.onMouseDown}
|
||||||
onClick={props.onClick}
|
onClick={props.onClick}
|
||||||
|
disabled={props.disabled}
|
||||||
>
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
@ -29,7 +29,7 @@ type ItemProps = {
|
||||||
export function ButtonGroupItem(props: ItemProps) {
|
export function ButtonGroupItem(props: ItemProps) {
|
||||||
return (
|
return (
|
||||||
<ToggleGroup.Item
|
<ToggleGroup.Item
|
||||||
class="px-3 py-2 min-w-8 leading-tight border-l first:(border-none rounded-l-md) last:rounded-r-md text-fore-subtle aria-pressed:(bg-fore-base/10 text-fore-base) hover:bg-fore-base/10 focus-visible:(outline-none ring-2 ring-fore-base ring-offset-2 ring-offset-back-base)"
|
class="flex-grow-1 px-3 py-2 min-w-8 leading-tight border-l first:(border-none rounded-l-md) last:rounded-r-md text-fore-subtle aria-pressed:(bg-fore-base/10 text-fore-base) hover:bg-fore-base/10 focus-visible:(outline-none ring-2 ring-fore-base ring-offset-2 ring-offset-back-base)"
|
||||||
value={props.value}
|
value={props.value}
|
||||||
aria-label={props.value ?? props.ariaLabel}
|
aria-label={props.value ?? props.ariaLabel}
|
||||||
title={props.title ? props.value ?? props.ariaLabel : undefined}
|
title={props.title ? props.value ?? props.ariaLabel : undefined}
|
||||||
|
|
|
@ -4,7 +4,7 @@ type Props = {
|
||||||
};
|
};
|
||||||
export function ColorInput(props: Props) {
|
export function ColorInput(props: Props) {
|
||||||
return (
|
return (
|
||||||
<label class="border rounded-md font-mono inline-flex items-center p-1.5 gap-2 cursor-pointer hover:bg-fore-base/5">
|
<label class="border rounded-md font-mono inline-flex items-center py-1.5 px-2 gap-2 cursor-pointer hover:bg-fore-base/5">
|
||||||
{props.color}
|
{props.color}
|
||||||
<input
|
<input
|
||||||
class="rounded-sm border-none w-6 h-6 focus-visible:(outline-none ring-2 ring-fore-base ring-offset-2 ring-offset-back-base)"
|
class="rounded-sm border-none w-6 h-6 focus-visible:(outline-none ring-2 ring-fore-base ring-offset-2 ring-offset-back-base)"
|
||||||
|
|
|
@ -1,249 +0,0 @@
|
||||||
import { For, Show, batch, type JSX } from "solid-js";
|
|
||||||
import { useQrContext } from "~/lib/QrContext";
|
|
||||||
import {
|
|
||||||
ECL_NAMES,
|
|
||||||
ECL_VALUE,
|
|
||||||
MASK_KEY,
|
|
||||||
MASK_NAMES,
|
|
||||||
MASK_VALUE,
|
|
||||||
MODE_KEY,
|
|
||||||
MODE_NAMES,
|
|
||||||
MODE_VALUE,
|
|
||||||
} from "~/lib/options";
|
|
||||||
import { FlatButton } from "./Button";
|
|
||||||
import { ButtonGroup, ButtonGroupItem } from "./ButtonGroup";
|
|
||||||
import { ColorInput } from "./ColorInput";
|
|
||||||
import { ImageInput } from "./ImageInput";
|
|
||||||
import { ModeTextInput } from "./ModeTextInput";
|
|
||||||
import { NumberInput } from "./NumberInput";
|
|
||||||
import { Select } from "./Select";
|
|
||||||
import { Switch } from "./Switch";
|
|
||||||
import { useSvgContext } from "~/lib/SvgContext";
|
|
||||||
|
|
||||||
export function Editor(props: any) {
|
|
||||||
const { inputQr, setInputQr } = useQrContext();
|
|
||||||
const {
|
|
||||||
svgOptions,
|
|
||||||
setSvgOptions,
|
|
||||||
selections,
|
|
||||||
scaleX,
|
|
||||||
scaleY,
|
|
||||||
setScaleXInPlace,
|
|
||||||
setScaleYInPlace,
|
|
||||||
} = useSvgContext();
|
|
||||||
|
|
||||||
// const [logoSize, setLogoSize] = createSignal(25);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div class="flex flex-col gap-2 flex-1 p-4">
|
|
||||||
<ModeTextInput setValue={(s) => setInputQr("text", s)} />
|
|
||||||
<Row title="Mode">
|
|
||||||
<Select
|
|
||||||
values={MODE_NAMES}
|
|
||||||
value={MODE_KEY[inputQr.mode!]}
|
|
||||||
setValue={(name) => setInputQr("mode", MODE_VALUE[name])}
|
|
||||||
/>
|
|
||||||
</Row>
|
|
||||||
<Row title="Min version">
|
|
||||||
<NumberInput
|
|
||||||
min={1}
|
|
||||||
max={40}
|
|
||||||
value={inputQr.minVersion}
|
|
||||||
setValue={(v) => setInputQr("minVersion", v)}
|
|
||||||
/>
|
|
||||||
</Row>
|
|
||||||
<Row title="Min error tolerance">
|
|
||||||
<ButtonGroup
|
|
||||||
value={ECL_NAMES[inputQr.minEcl]}
|
|
||||||
setValue={(v) => setInputQr("minEcl", ECL_VALUE[v])}
|
|
||||||
>
|
|
||||||
<For each={ECL_NAMES}>
|
|
||||||
{(name) => <ButtonGroupItem value={name}>{name}</ButtonGroupItem>}
|
|
||||||
</For>
|
|
||||||
</ButtonGroup>
|
|
||||||
</Row>
|
|
||||||
<Row title="Mask pattern">
|
|
||||||
<ButtonGroup
|
|
||||||
value={MASK_KEY[inputQr.mask!]}
|
|
||||||
setValue={(name) => setInputQr("mask", MASK_VALUE[name])}
|
|
||||||
>
|
|
||||||
<For each={MASK_NAMES}>
|
|
||||||
{(value) => (
|
|
||||||
<ButtonGroupItem value={value}>{value}</ButtonGroupItem>
|
|
||||||
)}
|
|
||||||
</For>
|
|
||||||
</ButtonGroup>
|
|
||||||
</Row>
|
|
||||||
{/* <Row title="Margin">
|
|
||||||
<NumberInput
|
|
||||||
min={0}
|
|
||||||
max={10}
|
|
||||||
step={1}
|
|
||||||
value={inputQr.margin.top}
|
|
||||||
setValue={(v) => setInputQr("margin", (prev) => prev.setTop(v))}
|
|
||||||
/>
|
|
||||||
</Row> */}
|
|
||||||
<Row title="Foreground">
|
|
||||||
<ColorInput
|
|
||||||
color={svgOptions.fgColor}
|
|
||||||
setColor={(v) => setSvgOptions("fgColor", v)}
|
|
||||||
/>
|
|
||||||
<Switch
|
|
||||||
value={svgOptions.pixelateFgImg}
|
|
||||||
setValue={(v) => setSvgOptions("pixelateFgImg", v)}
|
|
||||||
label="Disable smoothing"
|
|
||||||
/>
|
|
||||||
<FlatButton
|
|
||||||
class="text-sm px-2 py-2"
|
|
||||||
onMouseDown={() => {
|
|
||||||
batch(() => {
|
|
||||||
let tmp = svgOptions.fgColor;
|
|
||||||
setSvgOptions("fgColor", svgOptions.bgColor);
|
|
||||||
setSvgOptions("bgColor", tmp);
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Swap
|
|
||||||
</FlatButton>
|
|
||||||
</Row>
|
|
||||||
<Row title="Background">
|
|
||||||
<div class="flex flex-col items-start gap-1">
|
|
||||||
<ColorInput
|
|
||||||
color={svgOptions.bgColor}
|
|
||||||
setColor={(v) => setSvgOptions("bgColor", v)}
|
|
||||||
/>
|
|
||||||
<ImageInput
|
|
||||||
value={svgOptions.bgImgFile}
|
|
||||||
setValue={(v) => setSvgOptions("bgImgFile", v)}
|
|
||||||
/>
|
|
||||||
<Switch
|
|
||||||
value={svgOptions.pixelateBgImg}
|
|
||||||
setValue={(v) => setSvgOptions("pixelateBgImg", v)}
|
|
||||||
label="Disable smoothing"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Row>
|
|
||||||
<Row title="Logo">
|
|
||||||
<div class="flex flex-col gap-1 w-full">
|
|
||||||
<ImageInput
|
|
||||||
value={svgOptions.fgImgFile}
|
|
||||||
setValue={(v) => setSvgOptions("fgImgFile", v)}
|
|
||||||
/>
|
|
||||||
{/* TODO need better img management than just size */}
|
|
||||||
{/* <NumberInput
|
|
||||||
min={0}
|
|
||||||
max={100}
|
|
||||||
step={0.1}
|
|
||||||
value={logoSize()}
|
|
||||||
setValue={setLogoSize}
|
|
||||||
/> */}
|
|
||||||
</div>
|
|
||||||
</Row>
|
|
||||||
<Row title="Scale X">
|
|
||||||
<NumberInput
|
|
||||||
min={0}
|
|
||||||
max={200}
|
|
||||||
step={1}
|
|
||||||
value={
|
|
||||||
selections().length
|
|
||||||
? scaleX()[
|
|
||||||
selections()[0].top * Math.sqrt(scaleX().length) +
|
|
||||||
selections()[0].left
|
|
||||||
]
|
|
||||||
: 100
|
|
||||||
}
|
|
||||||
setValue={(v) => {
|
|
||||||
if (!selections().length) return;
|
|
||||||
setScaleXInPlace((prev) => {
|
|
||||||
let width = Math.sqrt(prev.length);
|
|
||||||
selections().forEach((sel) => {
|
|
||||||
for (let i = sel.top; i < sel.bot; i++) {
|
|
||||||
for (let j = sel.left; j < sel.right; j++) {
|
|
||||||
prev[i * width + j] = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return prev;
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Row>
|
|
||||||
<Row title="Scale Y">
|
|
||||||
<NumberInput
|
|
||||||
min={0}
|
|
||||||
max={200}
|
|
||||||
step={1}
|
|
||||||
value={
|
|
||||||
selections().length
|
|
||||||
? scaleY()[
|
|
||||||
selections()[0].top * Math.sqrt(scaleY().length) +
|
|
||||||
selections()[0].left
|
|
||||||
]
|
|
||||||
: 100
|
|
||||||
}
|
|
||||||
setValue={(v) => {
|
|
||||||
if (!selections().length) return;
|
|
||||||
setScaleYInPlace((prev) => {
|
|
||||||
let width = Math.sqrt(prev.length);
|
|
||||||
selections().forEach((sel) => {
|
|
||||||
for (let i = sel.top; i < sel.bot; i++) {
|
|
||||||
for (let j = sel.left; j < sel.right; j++) {
|
|
||||||
prev[i * width + j] = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return prev;
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Row>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function Row(props: {
|
|
||||||
title: string;
|
|
||||||
children: JSX.Element;
|
|
||||||
sparkle?: boolean;
|
|
||||||
}) {
|
|
||||||
// This should be <label/> but clicking selects first button in buttongroup
|
|
||||||
return (
|
|
||||||
<div class="flex gap-2">
|
|
||||||
<Show when={props.sparkle}>
|
|
||||||
<AnimatedSparkle />
|
|
||||||
</Show>
|
|
||||||
<span class="w-30 py-2 text-left text-sm flex-shrink-0">
|
|
||||||
{props.title}
|
|
||||||
</span>
|
|
||||||
{props.children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function AnimatedSparkle() {
|
|
||||||
return (
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 29 29"
|
|
||||||
class="w-5 h-5 absolute -translate-x-full mt-2"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M14.6 5.8c.5 0 .8 7.4 1.2 7.8.4.4 7.9.5 7.9 1 0 .6-7.5.9-7.8 1.3-.4.4-.5 7.8-1 7.9-.6 0-1-7.5-1.3-7.9-.4-.4-7.9-.5-7.9-1 0-.6 7.5-.9 7.9-1.3.3-.4.4-7.8 1-7.8z"
|
|
||||||
style="fill:#fca4a4"
|
|
||||||
class="animate-pulse"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M25.5 7.5c.1.3-2.3.6-2.4.8-.2.2-.3 2.6-.5 2.7-.3 0-.5-2.3-.7-2.5-.3-.2-2.7-.2-2.8-.5 0-.2 2.3-.5 2.5-.7.2-.2.2-2.7.5-2.7.2 0 .5 2.3.7 2.4.2.2 2.7.3 2.7.5z"
|
|
||||||
style="fill:#fca4a4"
|
|
||||||
class="animate-pulse"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M11.3 21.9c0 .3-3 .1-3.2.3-.2.3 0 3.2-.3 3.2s-.1-3-.4-3.2c-.2-.2-3 0-3-.3s2.8-.1 3-.3c.3-.3 0-3.2.4-3.2.3 0 .1 3 .3 3.2.3.2 3.2 0 3.2.3z"
|
|
||||||
style="fill:#fca4a4"
|
|
||||||
class="animate-pulse"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -3,7 +3,7 @@ type Props = {
|
||||||
};
|
};
|
||||||
|
|
||||||
/** No `value` prop b/c textarea cannot be controlled */
|
/** No `value` prop b/c textarea cannot be controlled */
|
||||||
export function ModeTextInput(props: Props) {
|
export function TextInput(props: Props) {
|
||||||
const onInput = debounce(props.setValue, 300);
|
const onInput = debounce(props.setValue, 300);
|
||||||
return (
|
return (
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
|
|
|
@ -59,7 +59,7 @@ export function RenderGrid(props: Props) {
|
||||||
|
|
||||||
let selection: BoxSelection | null;
|
let selection: BoxSelection | null;
|
||||||
|
|
||||||
function onMouseMove(e: MouseEvent) {
|
function onPointerMove(e: MouseEvent) {
|
||||||
const { x, y } = getPos(e);
|
const { x, y } = getPos(e);
|
||||||
|
|
||||||
if (mode() === Mode.Select) {
|
if (mode() === Mode.Select) {
|
||||||
|
@ -142,7 +142,7 @@ export function RenderGrid(props: Props) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onMouseUp = () => {
|
const onPointerUp = () => {
|
||||||
if (selection != null) {
|
if (selection != null) {
|
||||||
setSelectionsInPlace((prev) => {
|
setSelectionsInPlace((prev) => {
|
||||||
// @ts-expect-error i'm right unless somehow this isn't synchronous
|
// @ts-expect-error i'm right unless somehow this isn't synchronous
|
||||||
|
@ -150,13 +150,13 @@ export function RenderGrid(props: Props) {
|
||||||
return prev;
|
return prev;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
document.removeEventListener("mousemove", onMouseMove);
|
document.removeEventListener("pointermove", onPointerMove);
|
||||||
document.removeEventListener("mouseup", onMouseUp);
|
document.removeEventListener("pointerup", onPointerUp);
|
||||||
};
|
};
|
||||||
|
|
||||||
onCleanup(() => {
|
onCleanup(() => {
|
||||||
document.removeEventListener("mousemove", onMouseMove);
|
document.removeEventListener("pointermove", onPointerMove);
|
||||||
document.removeEventListener("mouseup", onMouseUp);
|
document.removeEventListener("pointerup", onPointerUp);
|
||||||
});
|
});
|
||||||
|
|
||||||
const [mode, setMode] = createSignal(Mode.Select);
|
const [mode, setMode] = createSignal(Mode.Select);
|
||||||
|
@ -229,8 +229,8 @@ export function RenderGrid(props: Props) {
|
||||||
prevX = x;
|
prevX = x;
|
||||||
prevY = y;
|
prevY = y;
|
||||||
|
|
||||||
document.addEventListener("mouseup", onMouseUp);
|
document.addEventListener("pointerup", onPointerUp);
|
||||||
document.addEventListener("mousemove", onMouseMove);
|
document.addEventListener("pointermove", onPointerMove);
|
||||||
}}
|
}}
|
||||||
></canvas>
|
></canvas>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -0,0 +1,152 @@
|
||||||
|
import { For, createSignal, type JSX } from "solid-js";
|
||||||
|
import { useQrContext, type RenderFunc } from "~/lib/QrContext";
|
||||||
|
import {
|
||||||
|
ECL_NAMES,
|
||||||
|
ECL_VALUE,
|
||||||
|
MASK_KEY,
|
||||||
|
MASK_NAMES,
|
||||||
|
MASK_VALUE,
|
||||||
|
MODE_KEY,
|
||||||
|
MODE_NAMES,
|
||||||
|
MODE_VALUE,
|
||||||
|
} from "~/lib/options";
|
||||||
|
import { FlatButton } from "../Button";
|
||||||
|
import { ButtonGroup, ButtonGroupItem } from "../ButtonGroup";
|
||||||
|
import { TextInput } from "../ModeTextInput";
|
||||||
|
import { NumberInput } from "../NumberInput";
|
||||||
|
import { Select } from "../Select";
|
||||||
|
|
||||||
|
import hljs from "highlight.js/lib/core";
|
||||||
|
import javascript from "highlight.js/lib/languages/javascript";
|
||||||
|
import { CodeInput } from "@srsholmes/solid-code-input";
|
||||||
|
|
||||||
|
import "../../styles/atom-one-dark.css";
|
||||||
|
|
||||||
|
hljs.registerLanguage("javascript", javascript);
|
||||||
|
|
||||||
|
export function Editor(props: any) {
|
||||||
|
const { inputQr, setInputQr, setRenderFunc } = useQrContext();
|
||||||
|
|
||||||
|
const [code, setCode] = createSignal(` // qr, ctx are args
|
||||||
|
const pixelSize = 10;
|
||||||
|
ctx.canvas.width = qr.matrixWidth * pixelSize;
|
||||||
|
ctx.canvas.height = qr.matrixHeight * pixelSize;
|
||||||
|
|
||||||
|
ctx.fillStyle = "rgb(255, 255, 255)";
|
||||||
|
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||||
|
|
||||||
|
ctx.fillStyle = "rgb(0, 0, 0)";
|
||||||
|
|
||||||
|
for (let y = 0; y < qr.matrixHeight; y++) {
|
||||||
|
for (let x = 0; x < qr.matrixWidth; x++) {
|
||||||
|
const module = qr.matrix[y * qr.matrixWidth + x];
|
||||||
|
|
||||||
|
if (module & 1) {
|
||||||
|
ctx.fillRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
const [prevCode, setPrevCode] = createSignal(code());
|
||||||
|
|
||||||
|
const saveCode = () => {
|
||||||
|
setRenderFunc(() => new Function("qr", "ctx", code()) as RenderFunc);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class="flex flex-col gap-2 flex-1 p-4">
|
||||||
|
<TextInput setValue={(s) => setInputQr("text", s)} />
|
||||||
|
<Row title="Encoding" tooltip="Also known as Mode">
|
||||||
|
<Select
|
||||||
|
values={MODE_NAMES}
|
||||||
|
value={MODE_KEY[inputQr.mode!]}
|
||||||
|
setValue={(name) => setInputQr("mode", MODE_VALUE[name])}
|
||||||
|
/>
|
||||||
|
</Row>
|
||||||
|
<Row title="Min symbol size" tooltip="Also known as Version">
|
||||||
|
<NumberInput
|
||||||
|
min={1}
|
||||||
|
max={40}
|
||||||
|
value={inputQr.minVersion}
|
||||||
|
setValue={(v) => setInputQr("minVersion", v)}
|
||||||
|
/>
|
||||||
|
</Row>
|
||||||
|
<Row
|
||||||
|
title="Min error tolerance"
|
||||||
|
tooltip="Also known as Error Correction Level"
|
||||||
|
>
|
||||||
|
<ButtonGroup
|
||||||
|
value={ECL_NAMES[inputQr.minEcl]}
|
||||||
|
setValue={(v) => setInputQr("minEcl", ECL_VALUE[v])}
|
||||||
|
>
|
||||||
|
<For each={ECL_NAMES}>
|
||||||
|
{(name) => <ButtonGroupItem value={name}>{name}</ButtonGroupItem>}
|
||||||
|
</For>
|
||||||
|
</ButtonGroup>
|
||||||
|
</Row>
|
||||||
|
<Row title="Mask pattern">
|
||||||
|
<ButtonGroup
|
||||||
|
value={MASK_KEY[inputQr.mask!]}
|
||||||
|
setValue={(name) => setInputQr("mask", MASK_VALUE[name])}
|
||||||
|
>
|
||||||
|
<For each={MASK_NAMES}>
|
||||||
|
{(value) => (
|
||||||
|
<ButtonGroupItem value={value}>{value}</ButtonGroupItem>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</ButtonGroup>
|
||||||
|
</Row>
|
||||||
|
<Row title="Margin">
|
||||||
|
<NumberInput
|
||||||
|
min={0}
|
||||||
|
max={10}
|
||||||
|
step={1}
|
||||||
|
value={inputQr.margin.top}
|
||||||
|
setValue={(v) =>
|
||||||
|
setInputQr("margin", { top: v, bottom: v, left: v, right: v })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Row>
|
||||||
|
<div>
|
||||||
|
<div class="flex justify-between my-2">
|
||||||
|
<div class="text-sm py-2">Render function</div>
|
||||||
|
<FlatButton class="px-3 py-1" disabled={prevCode() === code()} onMouseDown={saveCode}>
|
||||||
|
{prevCode() === code() ? "Saved" : "Save"}
|
||||||
|
</FlatButton>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="hljs-wrapper"
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.ctrlKey && e.key === "s") {
|
||||||
|
e.preventDefault();
|
||||||
|
saveCode();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CodeInput
|
||||||
|
autoHeight={true}
|
||||||
|
value={code()}
|
||||||
|
onChange={setCode}
|
||||||
|
highlightjs={hljs}
|
||||||
|
language="javascript"
|
||||||
|
resize="both"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Row(props: {
|
||||||
|
tooltip?: string;
|
||||||
|
title: string;
|
||||||
|
children: JSX.Element;
|
||||||
|
}) {
|
||||||
|
// This should be <label/> but clicking selects first button in buttongroup
|
||||||
|
return (
|
||||||
|
<div title={props.tooltip}>
|
||||||
|
<div class="text-sm py-2">{props.title}</div>
|
||||||
|
{props.children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
import { QrError } from "fuqr";
|
||||||
|
|
||||||
|
import { Match, Show, Switch, createEffect } from "solid-js";
|
||||||
|
import { useQrContext, type OutputQr } from "~/lib/QrContext";
|
||||||
|
import {
|
||||||
|
ECL_LABELS,
|
||||||
|
ECL_NAMES,
|
||||||
|
MASK_KEY,
|
||||||
|
MODE_KEY,
|
||||||
|
MODE_NAMES,
|
||||||
|
} from "~/lib/options";
|
||||||
|
|
||||||
|
export default function QrPreview() {
|
||||||
|
const { inputQr, outputQr } = useQrContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Show
|
||||||
|
when={typeof outputQr() !== "number"}
|
||||||
|
fallback={
|
||||||
|
<div class="aspect-[1/1] border rounded-md flex justify-center items-center">
|
||||||
|
<Switch>
|
||||||
|
<Match when={outputQr() === QrError.ExceedsMaxCapacity}>
|
||||||
|
Data exceeds max capacity
|
||||||
|
</Match>
|
||||||
|
<Match when={outputQr() === QrError.InvalidEncoding}>
|
||||||
|
{`Input cannot be encoded in ${
|
||||||
|
// @ts-expect-error props.mode not null b/c InvalidEncoding implies mode
|
||||||
|
MODE_NAMES[inputQr.mode + 1]
|
||||||
|
} mode`}
|
||||||
|
</Match>
|
||||||
|
</Switch>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<RenderedQrCode />
|
||||||
|
</Show>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This component assumes outputQr() is not QrError, this simplifies effects and types
|
||||||
|
*
|
||||||
|
* Original problem:
|
||||||
|
* When using Show, effects tracking the when signal run before refs inside Show become valid.
|
||||||
|
* The result was no effect running after initial mount, so no render.
|
||||||
|
* Running the effect in the ref function caused double rendering for future mounts.
|
||||||
|
*/
|
||||||
|
function RenderedQrCode() {
|
||||||
|
const { outputQr: _outputQr, renderFunc } = useQrContext();
|
||||||
|
const outputQr = _outputQr as () => OutputQr
|
||||||
|
|
||||||
|
const fullWidth = () => {
|
||||||
|
const output = outputQr();
|
||||||
|
return output.version * 4 + 17 + output.margin.left + output.margin.right;
|
||||||
|
};
|
||||||
|
const fullHeight = () => {
|
||||||
|
const output = outputQr();
|
||||||
|
return output.version * 4 + 17 + output.margin.top + output.margin.bottom;
|
||||||
|
};
|
||||||
|
|
||||||
|
let qrCanvas: HTMLCanvasElement;
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
const ctx = qrCanvas.getContext("2d")!;
|
||||||
|
ctx.clearRect(0, 0, qrCanvas.width, qrCanvas.height);
|
||||||
|
renderFunc()(outputQr(), ctx);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
class="aspect-[1/1] border rounded-md relative overflow-hidden"
|
||||||
|
style={{
|
||||||
|
"background-image":
|
||||||
|
"repeating-conic-gradient(#ddd 0% 25%, #aaa 25% 50%)",
|
||||||
|
"background-position": "50%",
|
||||||
|
"background-size": `${(1 / fullWidth()) * 100}% ${
|
||||||
|
(1 / fullHeight()) * 100
|
||||||
|
}%`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<canvas
|
||||||
|
class="w-full h-full"
|
||||||
|
ref={qrCanvas!}
|
||||||
|
></canvas>
|
||||||
|
</div>
|
||||||
|
<div class="p-4 grid grid-cols-2 gap-y-2 text-sm text-left">
|
||||||
|
<div class="">
|
||||||
|
Symbol size{" "}
|
||||||
|
<div class="font-bold text-base whitespace-pre">
|
||||||
|
{outputQr().version} ({outputQr().version * 4 + 17}x
|
||||||
|
{outputQr().version * 4 + 17} pixels)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="">
|
||||||
|
Error tolerance{" "}
|
||||||
|
<div class="font-bold text-base whitespace-pre">
|
||||||
|
{ECL_NAMES[outputQr().ecl]} ({ECL_LABELS[outputQr().ecl]})
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="">
|
||||||
|
Encoding{" "}
|
||||||
|
<span class="font-bold text-base">{MODE_KEY[outputQr().mode]}</span>
|
||||||
|
</div>
|
||||||
|
<div class="">
|
||||||
|
Mask{" "}
|
||||||
|
<span class="font-bold text-base">{MASK_KEY[outputQr().mask]}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -112,17 +112,17 @@ export default function SvgPreview() {
|
||||||
</div>
|
</div>
|
||||||
<div class="p-4 grid grid-cols-2 gap-y-2 text-sm text-left">
|
<div class="p-4 grid grid-cols-2 gap-y-2 text-sm text-left">
|
||||||
<div class="">
|
<div class="">
|
||||||
Version{" "}
|
Symbol size{" "}
|
||||||
<span class="font-bold text-base whitespace-pre">
|
<div class="font-bold text-base whitespace-pre">
|
||||||
{svgResult()!.version["0"]} ({svgResult()!.version["0"] * 4 + 17}x
|
{svgResult()!.version["0"]} ({svgResult()!.version["0"] * 4 + 17}x
|
||||||
{svgResult()!.version["0"] * 4 + 17} pixels)
|
{svgResult()!.version["0"] * 4 + 17} pixels)
|
||||||
</span>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="">
|
<div class="">
|
||||||
Error tolerance{" "}
|
Error tolerance{" "}
|
||||||
<span class="font-bold text-base whitespace-pre">
|
<div class="font-bold text-base whitespace-pre">
|
||||||
{ECL_NAMES[svgResult()!.ecl]} ({ECL_LABELS[svgResult()!.ecl]})
|
{ECL_NAMES[svgResult()!.ecl]} ({ECL_LABELS[svgResult()!.ecl]})
|
||||||
</span>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="">
|
<div class="">
|
||||||
Encoding{" "}
|
Encoding{" "}
|
|
@ -11,7 +11,7 @@ export default createHandler(() => (
|
||||||
<link rel="icon" href="/favicon.svg" />
|
<link rel="icon" href="/favicon.svg" />
|
||||||
{assets}
|
{assets}
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-back-base text-fore-base my-8 [--un-default-border-color:fg-subtle] ">
|
<body class="bg-back-base text-fore-base my-8 [--un-default-border-color:fg-subtle]">
|
||||||
<div id="app">{children}</div>
|
<div id="app">{children}</div>
|
||||||
{scripts}
|
{scripts}
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -1,12 +1,23 @@
|
||||||
import {
|
import {
|
||||||
createContext,
|
createContext,
|
||||||
|
createEffect,
|
||||||
createSignal,
|
createSignal,
|
||||||
useContext,
|
useContext,
|
||||||
type Accessor,
|
type Accessor,
|
||||||
type JSX,
|
type JSX,
|
||||||
type Setter,
|
type Setter,
|
||||||
} from "solid-js";
|
} from "solid-js";
|
||||||
import { ECL, Mode, Mask, QrError } from "fuqr";
|
import {
|
||||||
|
ECL,
|
||||||
|
Mode,
|
||||||
|
Mask,
|
||||||
|
QrError,
|
||||||
|
Module,
|
||||||
|
QrOptions,
|
||||||
|
Version,
|
||||||
|
Margin,
|
||||||
|
get_matrix,
|
||||||
|
} from "fuqr";
|
||||||
import { createStore, type SetStoreFunction } from "solid-js/store";
|
import { createStore, type SetStoreFunction } from "solid-js/store";
|
||||||
|
|
||||||
type InputQr = {
|
type InputQr = {
|
||||||
|
@ -37,6 +48,10 @@ export type OutputQr = {
|
||||||
bottom: number;
|
bottom: number;
|
||||||
left: number;
|
left: number;
|
||||||
};
|
};
|
||||||
|
/** Stored as value b/c Matrix is a ptr which becomes null after use */
|
||||||
|
matrix: Module[];
|
||||||
|
matrixWidth: number;
|
||||||
|
matrixHeight: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const QrContext = createContext<{
|
export const QrContext = createContext<{
|
||||||
|
@ -44,8 +59,12 @@ export const QrContext = createContext<{
|
||||||
setInputQr: SetStoreFunction<InputQr>;
|
setInputQr: SetStoreFunction<InputQr>;
|
||||||
outputQr: Accessor<OutputQr | QrError>;
|
outputQr: Accessor<OutputQr | QrError>;
|
||||||
setOutputQr: Setter<OutputQr | QrError>;
|
setOutputQr: Setter<OutputQr | QrError>;
|
||||||
|
renderFunc: Accessor<RenderFunc>;
|
||||||
|
setRenderFunc: Setter<RenderFunc>;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
export type RenderFunc = (qr: OutputQr, ctx: CanvasRenderingContext2D) => void;
|
||||||
|
|
||||||
export function QrContextProvider(props: { children: JSX.Element }) {
|
export function QrContextProvider(props: { children: JSX.Element }) {
|
||||||
const [inputQr, setInputQr] = createStore<InputQr>({
|
const [inputQr, setInputQr] = createStore<InputQr>({
|
||||||
text: "Greetings traveler",
|
text: "Greetings traveler",
|
||||||
|
@ -65,8 +84,59 @@ export function QrContextProvider(props: { children: JSX.Element }) {
|
||||||
QrError.InvalidEncoding
|
QrError.InvalidEncoding
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [renderFunc, setRenderFunc] = createSignal<RenderFunc>(defaultRender);
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
try {
|
||||||
|
// NOTE: Version and Margin cannot be reused, so must be created each time
|
||||||
|
let qrOptions = new QrOptions()
|
||||||
|
.min_version(new Version(inputQr.minVersion))
|
||||||
|
.min_ecl(inputQr.minEcl)
|
||||||
|
.mask(inputQr.mask!) // null makes more sense than undefined
|
||||||
|
.mode(inputQr.mode!) // null makes more sense than undefined
|
||||||
|
.margin(new Margin(10))
|
||||||
|
.margin(
|
||||||
|
new Margin(0)
|
||||||
|
.setTop(inputQr.margin.top)
|
||||||
|
.setRight(inputQr.margin.right)
|
||||||
|
.setBottom(inputQr.margin.bottom)
|
||||||
|
.setLeft(inputQr.margin.left)
|
||||||
|
);
|
||||||
|
|
||||||
|
let m = get_matrix(inputQr.text, qrOptions);
|
||||||
|
|
||||||
|
setOutputQr({
|
||||||
|
text: inputQr.text,
|
||||||
|
matrix: m.value,
|
||||||
|
matrixWidth: m.version["0"] * 4 + 17 + m.margin.left + m.margin.right,
|
||||||
|
matrixHeight: m.version["0"] * 4 + 17 + m.margin.top + m.margin.bottom,
|
||||||
|
version: m.version["0"],
|
||||||
|
ecl: m.ecl,
|
||||||
|
mode: m.mode,
|
||||||
|
mask: m.mask,
|
||||||
|
margin: {
|
||||||
|
top: m.margin.top,
|
||||||
|
right: m.margin.right,
|
||||||
|
bottom: m.margin.bottom,
|
||||||
|
left: m.margin.left,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
setOutputQr(e as QrError);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<QrContext.Provider value={{ inputQr, setInputQr, outputQr, setOutputQr }}>
|
<QrContext.Provider
|
||||||
|
value={{
|
||||||
|
inputQr,
|
||||||
|
setInputQr,
|
||||||
|
outputQr,
|
||||||
|
setOutputQr,
|
||||||
|
renderFunc,
|
||||||
|
setRenderFunc,
|
||||||
|
}}
|
||||||
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
</QrContext.Provider>
|
</QrContext.Provider>
|
||||||
);
|
);
|
||||||
|
@ -79,3 +149,24 @@ export function useQrContext() {
|
||||||
}
|
}
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function defaultRender(qr: OutputQr, ctx: CanvasRenderingContext2D) {
|
||||||
|
const pixelSize = 10;
|
||||||
|
ctx.canvas.width = qr.matrixWidth * pixelSize;
|
||||||
|
ctx.canvas.height = qr.matrixHeight * pixelSize;
|
||||||
|
|
||||||
|
ctx.fillStyle = "rgb(255, 255, 255)";
|
||||||
|
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||||
|
|
||||||
|
ctx.fillStyle = "rgb(0, 0, 0)";
|
||||||
|
|
||||||
|
for (let y = 0; y < qr.matrixHeight; y++) {
|
||||||
|
for (let x = 0; x < qr.matrixWidth; x++) {
|
||||||
|
const module = qr.matrix[y * qr.matrixWidth + x];
|
||||||
|
|
||||||
|
if (module & 1) {
|
||||||
|
ctx.fillRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,162 +0,0 @@
|
||||||
import {
|
|
||||||
createContext,
|
|
||||||
createEffect,
|
|
||||||
createSignal,
|
|
||||||
untrack,
|
|
||||||
useContext,
|
|
||||||
type Accessor,
|
|
||||||
type JSX,
|
|
||||||
type Setter,
|
|
||||||
} from "solid-js";
|
|
||||||
import { createStore, type SetStoreFunction } from "solid-js/store";
|
|
||||||
import {
|
|
||||||
QrOptions,
|
|
||||||
Version,
|
|
||||||
Margin,
|
|
||||||
get_matrix,
|
|
||||||
SvgOptions,
|
|
||||||
get_svg,
|
|
||||||
type QrError,
|
|
||||||
type SvgResult,
|
|
||||||
} from "fuqr";
|
|
||||||
import { useQrContext } from "./QrContext";
|
|
||||||
|
|
||||||
type RenderOptions = {
|
|
||||||
bgColor: string;
|
|
||||||
fgColor: string;
|
|
||||||
bgImgFile: File | null;
|
|
||||||
fgImgFile: File | null;
|
|
||||||
pixelateBgImg: boolean;
|
|
||||||
pixelateFgImg: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type BoxSelection = {
|
|
||||||
top: number;
|
|
||||||
bot: number;
|
|
||||||
left: number;
|
|
||||||
right: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const SvgContext = createContext<{
|
|
||||||
svgOptions: RenderOptions;
|
|
||||||
setSvgOptions: SetStoreFunction<RenderOptions>;
|
|
||||||
selections: Accessor<BoxSelection[]>;
|
|
||||||
setSelectionsInPlace: Setter<BoxSelection[]>;
|
|
||||||
scaleX: Accessor<number[]>;
|
|
||||||
setScaleXInPlace: Setter<number[]>;
|
|
||||||
scaleY: Accessor<number[]>;
|
|
||||||
setScaleYInPlace: Setter<number[]>;
|
|
||||||
svgResult: Accessor<SvgResult | null>;
|
|
||||||
setSvgResult: Setter<SvgResult | null>;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
export function SvgContextProvider(props: { children: JSX.Element }) {
|
|
||||||
const [svgOptions, setSvgOptions] = createStore<RenderOptions>({
|
|
||||||
bgColor: "#ffffff",
|
|
||||||
fgColor: "#000000",
|
|
||||||
bgImgFile: null,
|
|
||||||
fgImgFile: null,
|
|
||||||
pixelateFgImg: false,
|
|
||||||
pixelateBgImg: false,
|
|
||||||
});
|
|
||||||
const { inputQr, outputQr, setOutputQr } = useQrContext();
|
|
||||||
|
|
||||||
const [selections, setSelectionsInPlace] = createSignal<BoxSelection[]>([], {
|
|
||||||
equals: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const [scaleX, setScaleXInPlace] = createSignal<number[]>([], {
|
|
||||||
equals: false,
|
|
||||||
});
|
|
||||||
const [scaleY, setScaleYInPlace] = createSignal<number[]>([], {
|
|
||||||
equals: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const [svgResult, setSvgResult] = createSignal<SvgResult | null>(null);
|
|
||||||
|
|
||||||
createEffect(() => {
|
|
||||||
try {
|
|
||||||
// NOTE: Version and Margin cannot be reused, so must be created each time
|
|
||||||
let qrOptions = new QrOptions()
|
|
||||||
.min_version(new Version(inputQr.minVersion))
|
|
||||||
.min_ecl(inputQr.minEcl)
|
|
||||||
.mask(inputQr.mask!) // null makes more sense than undefined
|
|
||||||
.mode(inputQr.mode!) // null makes more sense than undefined
|
|
||||||
.margin(new Margin(10))
|
|
||||||
.margin(
|
|
||||||
new Margin(0)
|
|
||||||
.setTop(inputQr.margin.top)
|
|
||||||
.setRight(inputQr.margin.right)
|
|
||||||
.setBottom(inputQr.margin.bottom)
|
|
||||||
.setLeft(inputQr.margin.left)
|
|
||||||
);
|
|
||||||
|
|
||||||
let m = get_matrix(inputQr.text, qrOptions);
|
|
||||||
setOutputQr({
|
|
||||||
text: inputQr.text,
|
|
||||||
version: m.version["0"],
|
|
||||||
ecl: m.ecl,
|
|
||||||
mode: m.mode,
|
|
||||||
mask: m.mask,
|
|
||||||
margin: {
|
|
||||||
top: m.margin.top,
|
|
||||||
right: m.margin.right,
|
|
||||||
bottom: m.margin.bottom,
|
|
||||||
left: m.margin.left,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const matrixLength = m.width() * m.height();
|
|
||||||
if (matrixLength !== untrack(scaleX).length) {
|
|
||||||
console.log("setScale");
|
|
||||||
setSelectionsInPlace([]);
|
|
||||||
setScaleXInPlace(Array(matrixLength).fill(100));
|
|
||||||
setScaleYInPlace(Array(matrixLength).fill(100));
|
|
||||||
}
|
|
||||||
|
|
||||||
qrOptions = new QrOptions()
|
|
||||||
.min_version(new Version(m.version[0]))
|
|
||||||
.min_ecl(m.ecl)
|
|
||||||
.mask(m.mask)
|
|
||||||
.mode(m.mode);
|
|
||||||
|
|
||||||
let svgOpts = new SvgOptions()
|
|
||||||
.foreground(svgOptions.fgColor)
|
|
||||||
.background(svgOptions.bgColor)
|
|
||||||
.scale_x_matrix(new Uint8Array(scaleX()))
|
|
||||||
.scale_y_matrix(new Uint8Array(scaleY()));
|
|
||||||
|
|
||||||
// infallible b/c outputQr contains successful options
|
|
||||||
setSvgResult(get_svg(inputQr.text, qrOptions, svgOpts));
|
|
||||||
} catch (e) {
|
|
||||||
setOutputQr(e as QrError);
|
|
||||||
setSvgResult(null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SvgContext.Provider
|
|
||||||
value={{
|
|
||||||
svgOptions,
|
|
||||||
setSvgOptions,
|
|
||||||
svgResult,
|
|
||||||
setSvgResult,
|
|
||||||
selections,
|
|
||||||
setSelectionsInPlace,
|
|
||||||
scaleX,
|
|
||||||
setScaleXInPlace,
|
|
||||||
scaleY,
|
|
||||||
setScaleYInPlace,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{props.children}
|
|
||||||
</SvgContext.Provider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useSvgContext() {
|
|
||||||
const context = useContext(SvgContext);
|
|
||||||
if (!context) {
|
|
||||||
throw new Error("useSvgContext: used outside SvgContextProvider");
|
|
||||||
}
|
|
||||||
return context;
|
|
||||||
}
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { Switch, Match, createSignal } from "solid-js";
|
import { createSignal } from "solid-js";
|
||||||
import { clientOnly } from "@solidjs/start";
|
import { clientOnly } from "@solidjs/start";
|
||||||
|
import { Editor } from "~/components/editor/QrEditor";
|
||||||
|
import { FlatButton } from "~/components/Button";
|
||||||
|
import QrPreview from "~/components/preview/QrPreview";
|
||||||
import init from "fuqr";
|
import init from "fuqr";
|
||||||
import { Editor } from "~/components/Editor";
|
|
||||||
import SvgPreview from "~/components/qr/SvgPreview";
|
|
||||||
import { SvgContextProvider } from "~/lib/SvgContext";
|
|
||||||
|
|
||||||
const QrContextProvider = clientOnly(async () => {
|
const QrContextProvider = clientOnly(async () => {
|
||||||
await init();
|
await init();
|
||||||
|
@ -12,38 +12,17 @@ const QrContextProvider = clientOnly(async () => {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// const MODULE_NAMES = [
|
|
||||||
// "Data",
|
|
||||||
// "Finder",
|
|
||||||
// "Alignment",
|
|
||||||
// "Timing",
|
|
||||||
// "Format",
|
|
||||||
// "Version",
|
|
||||||
// ] as const;
|
|
||||||
|
|
||||||
enum Stage {
|
|
||||||
Create,
|
|
||||||
Customize,
|
|
||||||
}
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const [stage, setStage] = createSignal(Stage.Create);
|
|
||||||
return (
|
return (
|
||||||
<QrContextProvider>
|
<QrContextProvider>
|
||||||
<SvgContextProvider>
|
<main class="max-w-screen-2xl mx-auto p-4">
|
||||||
<main class="max-w-screen-lg mx-auto">
|
|
||||||
<Switch>
|
|
||||||
<Match when={stage() == Stage.Create}>
|
|
||||||
<div class="flex gap-4 flex-wrap">
|
<div class="flex gap-4 flex-wrap">
|
||||||
<Editor />
|
<Editor />
|
||||||
<div class="flex-1 min-w-200px sticky top-0 self-start p-4">
|
<div class="flex-grow-1 min-w-200px sticky top-0 self-start p-4">
|
||||||
<SvgPreview />
|
<QrPreview />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Match>
|
|
||||||
{/* <Match when={stage() == Stage.Customize}></Match> */}
|
|
||||||
</Switch>
|
|
||||||
</main>
|
</main>
|
||||||
</SvgContextProvider>
|
|
||||||
</QrContextProvider>
|
</QrContextProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
Atom One Dark by Daniel Gamage
|
||||||
|
Original One Dark Syntax theme from https://github.com/atom/one-dark-syntax
|
||||||
|
|
||||||
|
base: #282c34
|
||||||
|
mono-1: #abb2bf
|
||||||
|
mono-2: #818896
|
||||||
|
mono-3: #5c6370
|
||||||
|
hue-1: #56b6c2
|
||||||
|
hue-2: #61aeee
|
||||||
|
hue-3: #c678dd
|
||||||
|
hue-4: #98c379
|
||||||
|
hue-5: #e06c75
|
||||||
|
hue-5-2: #be5046
|
||||||
|
hue-6: #d19a66
|
||||||
|
hue-6-2: #e6c07b
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
.hljs-wrapper > * {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.hljs-wrapper > * > * {
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre[class*='language-'] {
|
||||||
|
color: #abb2bf;
|
||||||
|
background: #282c34;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-comment,
|
||||||
|
.hljs-quote {
|
||||||
|
color: #5c6370;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-doctag,
|
||||||
|
.hljs-keyword,
|
||||||
|
.hljs-formula {
|
||||||
|
color: #c678dd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-section,
|
||||||
|
.hljs-name,
|
||||||
|
.hljs-selector-tag,
|
||||||
|
.hljs-deletion,
|
||||||
|
.hljs-subst {
|
||||||
|
color: #e06c75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-literal {
|
||||||
|
color: #56b6c2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-string,
|
||||||
|
.hljs-regexp,
|
||||||
|
.hljs-addition,
|
||||||
|
.hljs-attribute,
|
||||||
|
.hljs-meta .hljs-string {
|
||||||
|
color: #98c379;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-attr,
|
||||||
|
.hljs-variable,
|
||||||
|
.hljs-template-variable,
|
||||||
|
.hljs-type,
|
||||||
|
.hljs-selector-class,
|
||||||
|
.hljs-selector-attr,
|
||||||
|
.hljs-selector-pseudo,
|
||||||
|
.hljs-number {
|
||||||
|
color: #d19a66;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-symbol,
|
||||||
|
.hljs-bullet,
|
||||||
|
.hljs-link,
|
||||||
|
.hljs-meta,
|
||||||
|
.hljs-selector-id,
|
||||||
|
.hljs-title {
|
||||||
|
color: #61aeee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-built_in,
|
||||||
|
.hljs-title.class_,
|
||||||
|
.hljs-class .hljs-title {
|
||||||
|
color: #e6c07b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-emphasis {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-strong {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-link {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
|
@ -15,6 +15,26 @@ export default defineConfig({
|
||||||
subtle: "#1a1b1c",
|
subtle: "#1a1b1c",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
animation: {
|
||||||
|
"content-show": {
|
||||||
|
from: {
|
||||||
|
opacity: 0,
|
||||||
|
transform: "scale(0.96);",
|
||||||
|
},
|
||||||
|
to: {
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"content-hide": {
|
||||||
|
from: {
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
to: {
|
||||||
|
opacity: 0,
|
||||||
|
transform: "scale(0.96);",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
preflights: [
|
preflights: [
|
||||||
{
|
{
|
||||||
|
|
Ładowanie…
Reference in New Issue