kopia lustrzana https://github.com/zhengkyl/qrframe
mobile friendly scroll thumbnails + hide code
rodzic
d6249d1ab3
commit
dadb2cb282
|
@ -10,10 +10,11 @@ type Props = {
|
|||
export function Collapsible(props: Props) {
|
||||
return (
|
||||
<KCollapsible class="" defaultOpen={props.defaultOpen}>
|
||||
<KCollapsible.Trigger class="group font-bold w-full inline-flex justify-between leading-tight focus-visible:(outline-none ring-2 ring-fore-base ring-offset-2 ring-offset-back-base) disabled:(pointer-events-none opacity-50) px-2 py-4 mb-2 border-b">
|
||||
<KCollapsible.Trigger class="group font-bold w-full inline-flex justify-between leading-tight focus-visible:(outline-none ring-2 ring-fore-base ring-offset-2 ring-offset-back-base) disabled:(pointer-events-none opacity-50) px-2 py-3 border-b">
|
||||
{props.trigger}
|
||||
<ChevronDown class="w-5 h-5 group-data-[expanded]:rotate-180 transition-transform duration-300" />
|
||||
</KCollapsible.Trigger>
|
||||
{/* Content cannot have y padding b/c it breaks animation */}
|
||||
<KCollapsible.Content class="overflow-hidden animate-collapsible-exit data-[expanded]:animate-collapsible-enter px-2">
|
||||
{props.children}
|
||||
</KCollapsible.Content>
|
||||
|
|
|
@ -101,24 +101,37 @@ export function CodeEditor(props: Props) {
|
|||
}
|
||||
});
|
||||
|
||||
const [showCode, setShowCode] = createSignal(false);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div class="flex justify-between pb-2">
|
||||
<Switch label="Vim mode" value={vimMode()} setValue={setVimMode} />
|
||||
<Button
|
||||
disabled={!dirty()}
|
||||
onMouseDown={() => props.onSave(view.state.doc.toString())}
|
||||
class="bg-green-700 border rounded-md hover:bg-green-700/90 focus-visible:(outline-none ring-2 ring-fore-base ring-offset-2 ring-offset-back-base) disabled:(bg-transparent text-fore-base pointer-events-none opacity-50) transition-colors px-3 py-1 min-w-150px"
|
||||
>
|
||||
{dirty() ? "Save" : "No changes"}
|
||||
</Button>
|
||||
<div class="flex justify-between pb-2 h-11">
|
||||
<Switch label="Show code" value={showCode()} setValue={setShowCode} />
|
||||
<Show when={showCode()}>
|
||||
<label class="flex items-center gap-1 text-sm">
|
||||
Vim mode
|
||||
<input
|
||||
class="h-4 w-4"
|
||||
type="checkbox"
|
||||
checked={vimMode()}
|
||||
onChange={(e) => setVimMode(e.target.checked)}
|
||||
/>
|
||||
</label>
|
||||
<Button
|
||||
disabled={!dirty()}
|
||||
onMouseDown={() => props.onSave(view.state.doc.toString())}
|
||||
class="bg-green-700 border rounded-md hover:bg-green-700/90 focus-visible:(outline-none ring-2 ring-fore-base ring-offset-2 ring-offset-back-base) disabled:(bg-transparent text-fore-base pointer-events-none opacity-50) transition-colors px-3 min-w-150px"
|
||||
>
|
||||
{dirty() ? "Save" : "No changes"}
|
||||
</Button>
|
||||
</Show>
|
||||
</div>
|
||||
<Show when={props.error}>
|
||||
<div class="text-red-100 bg-red-950 px-2 py-1 rounded-md mb-1">
|
||||
{props.error}
|
||||
</div>
|
||||
</Show>
|
||||
<div ref={parent!}></div>
|
||||
<div ref={parent!} classList={{ hidden: !showCode() }}></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Pencil from "lucide-solid/icons/pencil";
|
||||
import Trash2 from "lucide-solid/icons/trash-2";
|
||||
import { For, Show, batch, createSignal, onMount } from "solid-js";
|
||||
import { For, Show, batch, createSignal, onMount, type JSX } from "solid-js";
|
||||
import { createStore } from "solid-js/store";
|
||||
import { Dynamic } from "solid-js/web";
|
||||
import {
|
||||
|
@ -70,15 +70,15 @@ export function Editor(props: Props) {
|
|||
|
||||
onMount(async () => {
|
||||
const storedFuncKeys = localStorage.getItem(USER_FUNC_KEYS_KEY);
|
||||
if (storedFuncKeys == null) return;
|
||||
if (storedFuncKeys != null) {
|
||||
const keys = storedFuncKeys.split(",");
|
||||
for (const key of keys) {
|
||||
const funcCode = localStorage.getItem(key);
|
||||
if (funcCode == null) continue;
|
||||
|
||||
const keys = storedFuncKeys.split(",");
|
||||
for (const key of keys) {
|
||||
const funcCode = localStorage.getItem(key);
|
||||
if (funcCode == null) continue;
|
||||
|
||||
const thumb = localStorage.getItem(`${key}_thumb`) ?? FALLBACK_THUMB;
|
||||
setUserFuncs(userFuncs.length, { key, thumb });
|
||||
const thumb = localStorage.getItem(`${key}_thumb`) ?? FALLBACK_THUMB;
|
||||
setUserFuncs(userFuncs.length, { key, thumb });
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-expect-error adding keys below
|
||||
|
@ -96,7 +96,6 @@ export function Editor(props: Props) {
|
|||
);
|
||||
}
|
||||
const thumb = localStorage.getItem(`${key}_thumb`) ?? FALLBACK_THUMB;
|
||||
console.log(key, thumb.length);
|
||||
thumbs[key as keyof typeof PRESET_MODULES] = thumb;
|
||||
}
|
||||
setPresetThumbs(thumbs);
|
||||
|
@ -239,234 +238,257 @@ export function Editor(props: Props) {
|
|||
setUserFuncs(userFuncs.length, { key, thumb: FALLBACK_THUMB });
|
||||
localStorage.setItem(USER_FUNC_KEYS_KEY, keys.join(","));
|
||||
setRenderFuncKey(key);
|
||||
userSetCode(code, false);
|
||||
userSetCode(code, true);
|
||||
};
|
||||
|
||||
return (
|
||||
<div class={props.class}>
|
||||
<TextareaInput
|
||||
placeholder="https://qrcode.kylezhe.ng"
|
||||
placeholder="https://qrframe.kylezhe.ng"
|
||||
setValue={(s) => setInputQr("text", s)}
|
||||
/>
|
||||
<Collapsible trigger="QR Code">
|
||||
<Collapsible trigger="Data">
|
||||
<Settings />
|
||||
</Collapsible>
|
||||
<Collapsible trigger="Rendering" defaultOpen>
|
||||
<div class="mb-4">
|
||||
<div class="text-sm py-2">Render function</div>
|
||||
<div class="flex sm:flex-wrap gap-2">
|
||||
<For each={Object.entries(PRESET_MODULES)}>
|
||||
{([key, preset]) => (
|
||||
<div
|
||||
onMouseDown={() => {
|
||||
setRenderFuncKey(key);
|
||||
// @ts-expect-error assigning narrow to wider is ok b/c params validated
|
||||
internalSetCode(preset);
|
||||
}}
|
||||
>
|
||||
<div class="h-24 w-24 checkboard">
|
||||
<Collapsible trigger="Render" defaultOpen>
|
||||
<div class="py-4">
|
||||
<div class="mb-4 h-[180px] md:(h-unset)">
|
||||
<div class="flex justify-between">
|
||||
<div class="text-sm py-2 border border-transparent">Render function</div>
|
||||
<Show
|
||||
when={!Object.keys(PRESET_MODULES).includes(renderFuncKey())}
|
||||
>
|
||||
<div class="flex gap-1">
|
||||
<IconButtonDialog
|
||||
title={`Rename ${renderFuncKey()}`}
|
||||
triggerTitle="Rename"
|
||||
triggerChildren={<Pencil class="w-5 h-5" />}
|
||||
onOpenAutoFocus={(e) => e.preventDefault()}
|
||||
>
|
||||
{(close) => {
|
||||
const [rename, setRename] = createSignal(renderFuncKey());
|
||||
const [duplicate, setDuplicate] = createSignal(false);
|
||||
|
||||
let ref: HTMLInputElement;
|
||||
onMount(() => ref.focus());
|
||||
return (
|
||||
<>
|
||||
<TextInput
|
||||
class="mt-2"
|
||||
ref={ref!}
|
||||
defaultValue={rename()}
|
||||
onChange={setRename}
|
||||
onInput={() => duplicate() && setDuplicate(false)}
|
||||
placeholder={renderFuncKey()}
|
||||
/>
|
||||
<div class="absolute p-1 text-sm text-red-600">
|
||||
<Show when={duplicate()}>
|
||||
{rename()} already exists.
|
||||
</Show>
|
||||
</div>
|
||||
<FillButton
|
||||
class="px-3 py-2 float-right mt-4"
|
||||
// input onChange runs after focus lost, so onMouseDown is too early
|
||||
onClick={() => {
|
||||
if (rename() === renderFuncKey()) return close();
|
||||
|
||||
const userFuncKeys = Object.values(userFuncs).map(
|
||||
(func) => func.key
|
||||
);
|
||||
|
||||
if (
|
||||
Object.keys(PRESET_MODULES).includes(
|
||||
rename()
|
||||
) ||
|
||||
userFuncKeys.includes(rename())
|
||||
) {
|
||||
setDuplicate(true);
|
||||
} else {
|
||||
localStorage.removeItem(renderFuncKey());
|
||||
localStorage.setItem(rename(), code());
|
||||
setUserFuncs(
|
||||
userFuncKeys.indexOf(renderFuncKey()),
|
||||
"key",
|
||||
rename()
|
||||
);
|
||||
localStorage.setItem(
|
||||
USER_FUNC_KEYS_KEY,
|
||||
userFuncKeys.join(",")
|
||||
);
|
||||
|
||||
setRenderFuncKey(rename());
|
||||
close();
|
||||
}
|
||||
}}
|
||||
>
|
||||
Confirm
|
||||
</FillButton>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</IconButtonDialog>
|
||||
<IconButtonDialog
|
||||
title={`Delete ${renderFuncKey()}`}
|
||||
triggerTitle="Delete"
|
||||
triggerChildren={<Trash2 class="w-5 h-5" />}
|
||||
>
|
||||
{(close) => (
|
||||
<>
|
||||
<p class="mb-4 text-sm">
|
||||
Are you sure you want to delete this function?
|
||||
</p>
|
||||
<div class="flex justify-end gap-2">
|
||||
<FillButton
|
||||
onMouseDown={() => {
|
||||
const userFuncKeys = Object.values(userFuncs).map(
|
||||
(func) => func.key
|
||||
);
|
||||
|
||||
setUserFuncs((funcs) =>
|
||||
funcs.filter(
|
||||
(func) => func.key !== renderFuncKey()
|
||||
)
|
||||
);
|
||||
localStorage.removeItem(renderFuncKey());
|
||||
|
||||
localStorage.setItem(
|
||||
USER_FUNC_KEYS_KEY,
|
||||
userFuncKeys.join(",")
|
||||
);
|
||||
|
||||
setRenderFuncKey("Square");
|
||||
// @ts-expect-error renderSVG narrow to wider is fine b/c valid params
|
||||
internalSetCode(PRESET_MODULES.Square);
|
||||
|
||||
close();
|
||||
}}
|
||||
>
|
||||
Confirm
|
||||
</FillButton>
|
||||
<FlatButton onMouseDown={close}>Cancel</FlatButton>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</IconButtonDialog>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
<div class="flex gap-3 pt-2 pb-4 md:(flex-wrap static ml-0 px-0 overflow-x-visible) absolute max-w-full overflow-x-auto -ml-6 px-6">
|
||||
<For each={Object.entries(PRESET_MODULES)}>
|
||||
{([key, preset]) => (
|
||||
<Preview
|
||||
onClick={() => {
|
||||
setRenderFuncKey(key);
|
||||
// @ts-expect-error assigning narrow to wider is ok b/c params validated
|
||||
internalSetCode(preset);
|
||||
}}
|
||||
label={key}
|
||||
active={renderFuncKey() === key}
|
||||
>
|
||||
<img
|
||||
class="w-full"
|
||||
class="rounded-sm"
|
||||
src={presetThumbs()[key as keyof typeof PRESET_MODULES]}
|
||||
/>
|
||||
</div>
|
||||
<div class="text-center text-sm">{key}</div>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
<For each={userFuncs}>
|
||||
{(func) => (
|
||||
<div
|
||||
onMouseDown={() => {
|
||||
let storedCode = localStorage.getItem(func.key);
|
||||
if (storedCode == null) {
|
||||
storedCode = `Failed to load ${func.key}`;
|
||||
}
|
||||
setRenderFuncKey(func.key);
|
||||
userSetCode(storedCode, false);
|
||||
}}
|
||||
>
|
||||
<div class="h-24 w-24 checkboard">
|
||||
<img src={func.thumb} />
|
||||
</div>
|
||||
<div class="text-center text-sm">{func.key}</div>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
{/* <GroupedSelect
|
||||
options={[
|
||||
{
|
||||
label: "Presets",
|
||||
options: Object.keys(PRESET_FUNCS),
|
||||
},
|
||||
{
|
||||
label: "Custom",
|
||||
options: [...userFuncKeys, ADD_NEW_FUNC_KEY],
|
||||
},
|
||||
]}
|
||||
value={renderFuncKey()}
|
||||
setValue={(key) => {
|
||||
if (key === ADD_NEW_FUNC_KEY) {
|
||||
createAndSelectFunc("render function", PRESET_FUNCS.Square);
|
||||
} else {
|
||||
let storedCode;
|
||||
if (PRESET_FUNCS.hasOwnProperty(key)) {
|
||||
storedCode = PRESET_FUNCS[key as keyof typeof PRESET_FUNCS];
|
||||
} else {
|
||||
storedCode = localStorage.getItem(key);
|
||||
if (storedCode == null) {
|
||||
storedCode = `Failed to load ${key}`;
|
||||
}
|
||||
}
|
||||
setRenderFuncKey(key);
|
||||
trySetCode(storedCode);
|
||||
</Preview>
|
||||
)}
|
||||
</For>
|
||||
<For each={userFuncs}>
|
||||
{(func) => (
|
||||
<Preview
|
||||
onClick={() => {
|
||||
let storedCode = localStorage.getItem(func.key);
|
||||
if (storedCode == null) {
|
||||
storedCode = `Failed to load ${func.key}`;
|
||||
}
|
||||
setRenderFuncKey(func.key);
|
||||
userSetCode(storedCode, false);
|
||||
}}
|
||||
label={func.key}
|
||||
active={renderFuncKey() === func.key}
|
||||
>
|
||||
<img class="rounded-sm" src={func.thumb} />
|
||||
</Preview>
|
||||
)}
|
||||
</For>
|
||||
<Preview
|
||||
onClick={() =>
|
||||
createAndSelectFunc("custom", PRESET_MODULES.Square.code)
|
||||
}
|
||||
}}
|
||||
/> */}
|
||||
<Show when={!Object.keys(PRESET_MODULES).includes(renderFuncKey())}>
|
||||
<IconButtonDialog
|
||||
title={`Rename ${renderFuncKey()}`}
|
||||
triggerTitle="Rename"
|
||||
triggerChildren={<Pencil class="w-5 h-5" />}
|
||||
onOpenAutoFocus={(e) => e.preventDefault()}
|
||||
label="Create new"
|
||||
active={false}
|
||||
>
|
||||
{(close) => {
|
||||
const [rename, setRename] = createSignal(renderFuncKey());
|
||||
const [duplicate, setDuplicate] = createSignal(false);
|
||||
|
||||
let ref: HTMLInputElement;
|
||||
onMount(() => ref.focus());
|
||||
return (
|
||||
<>
|
||||
<TextInput
|
||||
class="mt-2"
|
||||
ref={ref!}
|
||||
defaultValue={rename()}
|
||||
onChange={setRename}
|
||||
onInput={() => duplicate() && setDuplicate(false)}
|
||||
placeholder={renderFuncKey()}
|
||||
/>
|
||||
<div class="absolute p-1 text-sm text-red-600">
|
||||
<Show when={duplicate()}>
|
||||
{rename()} already exists.
|
||||
</Show>
|
||||
</div>
|
||||
<FillButton
|
||||
class="px-3 py-2 float-right mt-4"
|
||||
// input onChange runs after focus lost, so onMouseDown is too early
|
||||
onClick={() => {
|
||||
if (rename() === renderFuncKey()) return close();
|
||||
|
||||
const userFuncKeys = Object.values(userFuncs).map(
|
||||
(func) => func.key
|
||||
);
|
||||
|
||||
if (
|
||||
Object.keys(PRESET_MODULES).includes(rename()) ||
|
||||
userFuncKeys.includes(rename())
|
||||
) {
|
||||
setDuplicate(true);
|
||||
} else {
|
||||
localStorage.removeItem(renderFuncKey());
|
||||
localStorage.setItem(rename(), code());
|
||||
setUserFuncs(
|
||||
userFuncKeys.indexOf(renderFuncKey()),
|
||||
"key",
|
||||
rename()
|
||||
);
|
||||
localStorage.setItem(
|
||||
USER_FUNC_KEYS_KEY,
|
||||
userFuncKeys.join(",")
|
||||
);
|
||||
|
||||
setRenderFuncKey(rename());
|
||||
close();
|
||||
}
|
||||
}}
|
||||
>
|
||||
Confirm
|
||||
</FillButton>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</IconButtonDialog>
|
||||
<IconButtonDialog
|
||||
title={`Delete ${renderFuncKey()}`}
|
||||
triggerTitle="Delete"
|
||||
triggerChildren={<Trash2 class="w-5 h-5" />}
|
||||
>
|
||||
{(close) => (
|
||||
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
|
||||
<path style="fill:#222" d="M0 0h100v100H0z" />
|
||||
<path
|
||||
style="fill:#fff"
|
||||
d="m55 25-10 1v17H26v13l1 1h19l1 18v1l10-1h1l-1-18h23V43H56V26l-1-1z"
|
||||
/>
|
||||
</svg>
|
||||
</Preview>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2 mb-4">
|
||||
<For each={Object.entries(paramsSchema())}>
|
||||
{([label, { type, ...props }]) => {
|
||||
return (
|
||||
<>
|
||||
<p class="mb-4 text-sm">
|
||||
Are you sure you want to delete this function?
|
||||
</p>
|
||||
<div class="flex justify-end gap-2">
|
||||
<FillButton
|
||||
onMouseDown={() => {
|
||||
const userFuncKeys = Object.values(userFuncs).map(
|
||||
(func) => func.key
|
||||
);
|
||||
|
||||
setUserFuncs((funcs) =>
|
||||
funcs.filter((func) => func.key !== renderFuncKey())
|
||||
);
|
||||
localStorage.removeItem(renderFuncKey());
|
||||
|
||||
localStorage.setItem(
|
||||
USER_FUNC_KEYS_KEY,
|
||||
userFuncKeys.join(",")
|
||||
);
|
||||
|
||||
setRenderFuncKey("Square");
|
||||
// @ts-expect-error renderSVG narrow to wider is fine b/c valid params
|
||||
internalSetCode(PRESET_MODULES.Square);
|
||||
|
||||
close();
|
||||
}}
|
||||
>
|
||||
Confirm
|
||||
</FillButton>
|
||||
<FlatButton onMouseDown={close}>Cancel</FlatButton>
|
||||
<div class="flex justify-between">
|
||||
<div class="text-sm py-2 w-48">{label}</div>
|
||||
{/* @ts-expect-error lose type b/c type and props destructured */}
|
||||
<Dynamic
|
||||
component={PARAM_COMPONENTS[type]}
|
||||
{...props}
|
||||
value={params[label]}
|
||||
setValue={(v: any) => setParams(label, v)}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</IconButtonDialog>
|
||||
</Show>
|
||||
);
|
||||
}}
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2 mb-4">
|
||||
<For each={Object.entries(paramsSchema())}>
|
||||
{([label, { type, ...props }]) => {
|
||||
return (
|
||||
<>
|
||||
<div class="flex justify-between">
|
||||
<div class="text-sm py-2 w-48">{label}</div>
|
||||
{/* @ts-expect-error lose type b/c type and props destructured */}
|
||||
<Dynamic
|
||||
component={PARAM_COMPONENTS[type]}
|
||||
{...props}
|
||||
value={params[label]}
|
||||
setValue={(v: any) => setParams(label, v)}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
<CodeEditor
|
||||
initialValue={code()}
|
||||
onSave={(code) => {
|
||||
if (Object.keys(PRESET_MODULES).includes(renderFuncKey())) {
|
||||
createAndSelectFunc(renderFuncKey(), code);
|
||||
} else {
|
||||
userSetCode(code, true);
|
||||
}
|
||||
}}
|
||||
</For>
|
||||
error={compileError()}
|
||||
clearError={() => setCompileError(null)}
|
||||
/>
|
||||
</div>
|
||||
<CodeEditor
|
||||
initialValue={code()}
|
||||
onSave={(code) => {
|
||||
if (Object.keys(PRESET_MODULES).includes(renderFuncKey())) {
|
||||
createAndSelectFunc(renderFuncKey(), code);
|
||||
} else {
|
||||
userSetCode(code, true);
|
||||
}
|
||||
}}
|
||||
error={compileError()}
|
||||
clearError={() => setCompileError(null)}
|
||||
/>
|
||||
</Collapsible>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type PreviewProps = {
|
||||
label: string;
|
||||
children: JSX.Element;
|
||||
onClick: () => void;
|
||||
active: boolean;
|
||||
};
|
||||
function Preview(props: PreviewProps) {
|
||||
return (
|
||||
<button
|
||||
class="rounded-sm focus-visible:(outline-none ring-2 ring-fore-base ring-offset-2 ring-offset-back-base)"
|
||||
onClick={props.onClick}
|
||||
>
|
||||
<div
|
||||
classList={{
|
||||
"h-24 w-24 rounded-sm checkboard": true,
|
||||
"ring-2 ring-fore-base ring-offset-4 ring-offset-back-base":
|
||||
props.active,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
<div class="pt-1 text-center text-sm w-24 whitespace-pre overflow-hidden text-ellipsis">
|
||||
{props.label}
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ export function Settings() {
|
|||
const { inputQr, setInputQr } = useQrContext();
|
||||
|
||||
return (
|
||||
<div class="flex flex-col gap-2 py-2">
|
||||
<div class="flex flex-col gap-2 py-4">
|
||||
<div class="flex justify-between">
|
||||
<div class="text-sm py-2">Encoding mode</div>
|
||||
<Select
|
||||
|
|
|
@ -126,6 +126,8 @@ function RenderedQrCode() {
|
|||
// race condition is unrealistic (maybe with http requests)
|
||||
// and can't be solved without double buffering
|
||||
await fallbackRender!(outputQr(), paramsCopy, ctx);
|
||||
|
||||
setCanvasDims({ width: ctx.canvas.width, height: ctx.canvas.height });
|
||||
}
|
||||
|
||||
setRuntimeError(null);
|
||||
|
@ -153,9 +155,11 @@ function RenderedQrCode() {
|
|||
{runtimeError()}
|
||||
</div>
|
||||
</Show>
|
||||
<div class="text-center">
|
||||
{canvasDims().width}x{canvasDims().height} px
|
||||
</div>
|
||||
<Show when={getRenderCanvas() != null}>
|
||||
<div class="text-center">
|
||||
{canvasDims().width}x{canvasDims().height} px
|
||||
</div>
|
||||
</Show>
|
||||
<div class="px-2 grid grid-cols-2 gap-y-2 text-sm">
|
||||
<div class="">
|
||||
Version
|
||||
|
|
|
@ -14,9 +14,9 @@ export default function Home() {
|
|||
return (
|
||||
<QrContextProvider>
|
||||
<main class="max-w-screen-2xl mx-auto">
|
||||
<div class="flex flex-wrap">
|
||||
<Editor class="flex-1 flex-grow-3 flex flex-col gap-2 px-4 py-8" />
|
||||
<QrPreview class="flex-1 flex-grow-2 min-w-300px sticky top-0 self-start px-4 py-8 flex flex-col gap-4" />
|
||||
<div class="md:flex py-8">
|
||||
<Editor class="flex-1 flex-grow-3 flex flex-col gap-2 px-4" />
|
||||
<QrPreview class="flex-1 flex-grow-2 min-w-300px sticky top-0 self-start px-4 flex flex-col gap-4" />
|
||||
</div>
|
||||
</main>
|
||||
</QrContextProvider>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { defineConfig } from "unocss";
|
||||
import transformerVariantGroup from "@unocss/transformer-variant-group";
|
||||
export default defineConfig({
|
||||
blocklist: ["m55"],
|
||||
transformers: [transformerVariantGroup()],
|
||||
theme: {
|
||||
colors: {
|
||||
|
|
Ładowanie…
Reference in New Issue