Update 3d chart mode, add US heatmap, move kjol-web -> kjol-website
This commit is contained in:
114
go/cmd/kjol-website/frontend/css/style.css
Normal file
114
go/cmd/kjol-website/frontend/css/style.css
Normal file
@@ -0,0 +1,114 @@
|
||||
/* ---------------------------------------------------------------------------
|
||||
kjol-website — brand stylesheet for the Kjol JS Web section (/js/*).
|
||||
---------------------------------------------------------------------------
|
||||
There is deliberately no `@import "tailwindcss"` here. The bundler PREPENDS
|
||||
kjol's shared scaffold (go/jsruntime/styles/theme.css) to this file, and that
|
||||
scaffold does the import — an @import has to come first, and this file no
|
||||
longer is. See jsbundler/css.go and the header of theme.css.
|
||||
|
||||
What is left is only what is genuinely this app's: the brand.
|
||||
|
||||
The values below match the Go/WASM section's css/app.css on purpose — same
|
||||
Open Sans, same navy accent — so that crossing between /wasm and /js reads as
|
||||
two parts of ONE site rather than two demos that happen to share a domain. The
|
||||
two sections are built by completely different pipelines; they should not look
|
||||
like it.
|
||||
--------------------------------------------------------------------------- */
|
||||
|
||||
/* Open Sans — self-hosted (files in wwwroot/fonts). One variable file per subset
|
||||
carries weights 400–700 upright and italic. unicode-range keeps the browser to the
|
||||
one subset a glyph needs; font-display: swap paints the fallback first. The same four
|
||||
blocks live in the /wasm side's css/app.css so both front-ends share one typeface. */
|
||||
@font-face {
|
||||
font-family: "Open Sans";
|
||||
font-style: normal;
|
||||
font-weight: 400 700;
|
||||
font-display: swap;
|
||||
src: url("/fonts/opensans-latin-normal.woff2") format("woff2");
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Open Sans";
|
||||
font-style: normal;
|
||||
font-weight: 400 700;
|
||||
font-display: swap;
|
||||
src: url("/fonts/opensans-latin-ext-normal.woff2") format("woff2");
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Open Sans";
|
||||
font-style: italic;
|
||||
font-weight: 400 700;
|
||||
font-display: swap;
|
||||
src: url("/fonts/opensans-latin-italic.woff2") format("woff2");
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Open Sans";
|
||||
font-style: italic;
|
||||
font-weight: 400 700;
|
||||
font-display: swap;
|
||||
src: url("/fonts/opensans-latin-ext-italic.woff2") format("woff2");
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
@theme {
|
||||
/* The UI typeface — see the @font-face blocks above. This one line points the kit's
|
||||
font-sans utilities and the html rule below onto Open Sans. */
|
||||
--font-sans: "Open Sans", ui-sans-serif, system-ui, sans-serif;
|
||||
|
||||
/* The brand: navy throughout, a muted flag-navy. These are the SAME values the Go/WASM
|
||||
section's css/app.css sets, and under the same names — so the Layers menu, the
|
||||
sidebar highlight and the callouts are the same navy on both sides of the site rather
|
||||
than two navies that happen to be close.
|
||||
|
||||
primary FILLS (white text sits on it); accent is TEXT and icons (it has to be
|
||||
readable on the surface) and is a slightly deeper navy so a link reads as one;
|
||||
primary-subtle/-border are the tinted panel. Only the flag tile keeps a red. */
|
||||
--color-primary: #1e3a63; /* muted navy — fills; they carry white text */
|
||||
--color-primary-hover: #16294a;
|
||||
--color-primary-subtle: #eef2f8; /* a navy wash — tinted panels, callouts */
|
||||
--color-primary-border: #c5d1e2;
|
||||
--color-accent: #1c3a66; /* navy — accent TEXT (links, eyebrow, active rows) */
|
||||
|
||||
}
|
||||
|
||||
/* Only the font. The page's background and text colour come from the shared
|
||||
scaffold's `html` rule, which paints them from --color-surface / --color-ink —
|
||||
the tokens the .dark block re-points. Setting them here would pin the page to
|
||||
white and leave a dark app sitting in a white window. */
|
||||
html {
|
||||
font-family: var(--font-sans);
|
||||
}
|
||||
|
||||
/* The dark values for the brand.
|
||||
---------------------------------------------------------------------------
|
||||
Navy is too dark to read on a near-black page, so both brand tokens climb to a lighter
|
||||
blue. The accent (TEXT) climbs furthest, to a soft sky a link stays legible in; the fill
|
||||
climbs less, to a steel blue that still looks like a button and still carries white text.
|
||||
And the tinted panel inverts outright, because a pale wash on #101013 is not a tint, it
|
||||
is a white box.
|
||||
|
||||
Same values, same names, as the Go/WASM section's css/app.css. */
|
||||
.dark {
|
||||
--color-accent: #9fc1ec;
|
||||
--color-primary: #2b4f80;
|
||||
--color-primary-hover: #37619b;
|
||||
--color-primary-subtle: #182234;
|
||||
--color-primary-border: #2c3e5c;
|
||||
}
|
||||
|
||||
/* The wordmark's logo tile: a muted Norwegian flag. Identical to the Go/WASM section's
|
||||
css/app.css — the two front-ends share one mark. See there for the full note; in short
|
||||
it is the flag's Scandinavian cross (navy cross, off-white outline, red field, offset
|
||||
left) drawn in layered gradients over a red base, constant across themes, with a flat
|
||||
dark scrim on top so the plain white sailboat reads on it without an outline. */
|
||||
.flag-no {
|
||||
background-color: #a83f4c;
|
||||
background-image:
|
||||
linear-gradient(rgba(0, 0, 0, 0.42), rgba(0, 0, 0, 0.42)),
|
||||
linear-gradient(180deg, transparent 44.5%, #294c76 44.5%, #294c76 55.5%, transparent 55.5%),
|
||||
linear-gradient(90deg, transparent 32.5%, #294c76 32.5%, #294c76 43.5%, transparent 43.5%),
|
||||
linear-gradient(180deg, transparent 39%, #ece6da 39%, #ece6da 61%, transparent 61%),
|
||||
linear-gradient(90deg, transparent 27%, #ece6da 27%, #ece6da 49%, transparent 49%);
|
||||
}
|
||||
59
go/cmd/kjol-website/frontend/src/app.ts
Normal file
59
go/cmd/kjol-website/frontend/src/app.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
// SPA entry for the Kjøl JS Web section (/js/*).
|
||||
//
|
||||
// This file is .ts and NOT .tsx on purpose — it is not a style choice. The bundler
|
||||
// resolves the SPA entry as src/app.ts (falling back to src/app.js) and nothing
|
||||
// else, so the entry cannot contain JSX. Hence createComponent() here, and JSX in
|
||||
// the pages it points at.
|
||||
//
|
||||
// The section is mounted under a base path rather than at the root: the front page
|
||||
// and the whole /wasm section are served by Kjøl Wasm Web, a different binary, which
|
||||
// this bundle knows nothing about. `base: "/js"` keeps every route in here relative
|
||||
// to that, so a link to "/components" resolves to /js/components and the two SPAs
|
||||
// never fight over a URL.
|
||||
//
|
||||
// Crossing OUT of /js (to the front page, or into /wasm) is a plain <a href> and a
|
||||
// real page load — the rest of the site is a different binary. That is the
|
||||
// honest cost of running two front-ends behind one server, and it is one navigation.
|
||||
|
||||
import { render, createComponent } from "solid-js/web";
|
||||
import { Router } from "@solidjs/router";
|
||||
import type { RouteDefinition } from "@solidjs/router";
|
||||
|
||||
import { Shell } from "./layout/Shell.tsx";
|
||||
import { Overview } from "./pages/Overview.tsx";
|
||||
import { Components } from "./pages/Components.tsx";
|
||||
import { NotFound } from "./pages/NotFound.tsx";
|
||||
|
||||
// Routes as plain data: solid-router accepts RouteDefinition[] as `children`, which
|
||||
// is what lets a JSX-free entry declare a full route tree.
|
||||
//
|
||||
// There are only two. The kit used to be spread across /kit, /forms, /table and
|
||||
// /theming — a split along the lines of the SOURCE FILES rather than along anything a
|
||||
// reader wants: a person looking for a date picker does not know, and should not have
|
||||
// to guess, whether it was filed under forms or under overlays. It is one page now,
|
||||
// and the sidebar jumps you down it.
|
||||
const routes: RouteDefinition[] = [
|
||||
{ path: "/", component: Overview },
|
||||
{ path: "/components", component: Components },
|
||||
|
||||
// The catch-all, and it is not optional. The SERVER answers every /js/* URL with this
|
||||
// shell — it has no idea which paths the router knows about — so without a fallback an
|
||||
// unknown one renders the chrome around an empty <main>: a blank page, with a 200, and
|
||||
// nothing to tell you why. Kjøl Wasm Web has the same catch-all for the same reason.
|
||||
{ path: "*", component: NotFound },
|
||||
];
|
||||
|
||||
const root = document.getElementById("app");
|
||||
if (root) {
|
||||
render(
|
||||
() =>
|
||||
createComponent(Router, {
|
||||
base: "/js",
|
||||
root: Shell,
|
||||
get children() {
|
||||
return routes;
|
||||
},
|
||||
}),
|
||||
root,
|
||||
);
|
||||
}
|
||||
36
go/cmd/kjol-website/frontend/src/componentGroups.ts
Normal file
36
go/cmd/kjol-website/frontend/src/componentGroups.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
// The component groups: one entry per section of /js/components, AND one line in the
|
||||
// sidebar that jumps to it.
|
||||
//
|
||||
// Declaring them once, as data, is what keeps those two in step — the sidebar cannot
|
||||
// offer a jump to a section that does not exist, and a section cannot go missing from
|
||||
// the sidebar.
|
||||
//
|
||||
// This is the JS mirror of app/components.go's componentGroups(). The ids match, so the
|
||||
// two sections of the site have the same shape and a reader crossing between them lands
|
||||
// in the same place. The ICONS differ, and have to: this side names FontAwesome, the Go
|
||||
// side names webui's own hand-drawn registry, and where the two have no glyph in common
|
||||
// the names diverge.
|
||||
|
||||
export interface ComponentGroup {
|
||||
id: string;
|
||||
label: string;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
export const COMPONENT_GROUPS: ComponentGroup[] = [
|
||||
{ id: "buttons", label: "Buttons", icon: "check" },
|
||||
{ id: "badges", label: "Badges & alerts", icon: "circle-info" },
|
||||
{ id: "cards", label: "Cards & layout", icon: "table-columns" },
|
||||
{ id: "icons", label: "Icons", icon: "star" },
|
||||
{ id: "forms", label: "Forms & inputs", icon: "pen-to-square" },
|
||||
{ id: "selects", label: "Selects & comboboxes", icon: "sliders" },
|
||||
{ id: "toggles", label: "Toggles & signature", icon: "check" },
|
||||
{ id: "dates", label: "Dates", icon: "calendar" },
|
||||
{ id: "tables", label: "Tables", icon: "table" },
|
||||
{ id: "overlays", label: "Overlays", icon: "copy" },
|
||||
{ id: "feedback", label: "Toasts & tours", icon: "bell" },
|
||||
{ id: "navigation", label: "Tabs & navigation", icon: "bars" },
|
||||
{ id: "search", label: "Fuzzy search", icon: "magnifying-glass" },
|
||||
{ id: "charts", label: "Charts", icon: "chart-column" },
|
||||
{ id: "theming", label: "Theming", icon: "palette" },
|
||||
];
|
||||
94
go/cmd/kjol-website/frontend/src/layers.ts
Normal file
94
go/cmd/kjol-website/frontend/src/layers.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
// What kjøl is made of, as data.
|
||||
//
|
||||
// There are two kinds of thing here, and conflating them was the mistake this file used
|
||||
// to make — one flat list called "the layers", holding both.
|
||||
//
|
||||
// LAYERS are LANGUAGES. What kjøl is written in, and what it gives you in each:
|
||||
// the Go base, the TypeScript kit, the C base, the Jai modules. A layer is
|
||||
// a directory of code you can use on its own.
|
||||
//
|
||||
// COMPOSITIONS are FRAMEWORKS. What you get when the layers are assembled into
|
||||
// something that does a job — the two web engines. A composition is not
|
||||
// another language; it is a use of them.
|
||||
//
|
||||
// Kjøl Wasm Web is Go, all the way down. Kjøl JS Web is TypeScript compiled by a Go
|
||||
// toolchain — two layers, one framework. Listing that beside "C" as though they were the
|
||||
// same kind of noun told the reader nothing about either.
|
||||
//
|
||||
// This is the JS mirror of app/layers.go. The ids, the order and the taglines match; only
|
||||
// the ICONS diverge, and they have to — see below.
|
||||
|
||||
export interface Layer {
|
||||
name: string;
|
||||
href: string;
|
||||
tagline: string;
|
||||
/** Live = you can click into worked examples. Reference = documented, no demo. */
|
||||
live: boolean;
|
||||
/**
|
||||
* The ONE field that does not match app/layers.go, and cannot: the two kits have
|
||||
* different icon sets. This side names FontAwesome; the Go side names webui's own
|
||||
* hand-drawn registry, which has no FontAwesome in it at all. Where the two have no
|
||||
* glyph in common the names diverge.
|
||||
*
|
||||
* Both sides fail loudly rather than quietly — a name neither registry knows renders
|
||||
* an empty box, and app/icons_test.go fails the build over it.
|
||||
*/
|
||||
icon: string;
|
||||
}
|
||||
|
||||
/** Languages: what kjøl is written in. */
|
||||
export const LANGUAGES: Layer[] = [
|
||||
{
|
||||
name: "Go",
|
||||
href: "/go",
|
||||
tagline: "The base: config, database, logging, HTTP, mail, validation — and both web engines.",
|
||||
live: false,
|
||||
icon: "server",
|
||||
},
|
||||
{
|
||||
name: "TypeScript",
|
||||
href: "/ts",
|
||||
tagline: "The Solid component kit, the vendored runtime, and the generic scaffolding apps import as @kjol/*.",
|
||||
live: false,
|
||||
icon: "code",
|
||||
},
|
||||
{
|
||||
name: "C",
|
||||
href: "/c",
|
||||
tagline: "Arena allocator, counted strings, math, a lexer, a platform layer — and a build system that is a C file.",
|
||||
live: true,
|
||||
icon: "bolt",
|
||||
},
|
||||
{
|
||||
name: "Jai",
|
||||
href: "/jai",
|
||||
tagline: "Console rendering. Early.",
|
||||
live: false,
|
||||
icon: "cube",
|
||||
},
|
||||
];
|
||||
|
||||
/** Compositions: what the languages are assembled into. */
|
||||
export const COMPOSITIONS: Layer[] = [
|
||||
{
|
||||
name: "Kjøl Wasm Web",
|
||||
href: "/wasm",
|
||||
tagline: "Web interfaces written in Go, compiled to WebAssembly. SSR + hydration, and no JavaScript build at all.",
|
||||
live: true,
|
||||
icon: "code",
|
||||
},
|
||||
{
|
||||
name: "Kjøl JS Web",
|
||||
href: "/js",
|
||||
tagline: "The Solid component kit, bundled by a Go toolchain: TSX → Solid → esbuild, Tailwind in Go.",
|
||||
live: true,
|
||||
icon: "table-columns",
|
||||
},
|
||||
];
|
||||
|
||||
/** The layer or composition the current path belongs to, or undefined on the front page. */
|
||||
export function currentLayer(path: string): Layer | undefined {
|
||||
return [...COMPOSITIONS, ...LANGUAGES].find(
|
||||
(l) => path === l.href || path.startsWith(l.href + "/"),
|
||||
);
|
||||
}
|
||||
33
go/cmd/kjol-website/frontend/src/layout/Demo.tsx
Normal file
33
go/cmd/kjol-website/frontend/src/layout/Demo.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
// A worked example: the code on one side, that same code RUNNING on the other.
|
||||
//
|
||||
// The code string is written by hand rather than extracted from the source, and that
|
||||
// is a known compromise — a hand-copied snippet can drift from the component beside
|
||||
// it. The alternative (a build step that slices the real source) buys accuracy at the
|
||||
// cost of a second thing to maintain, and the snippets here are short enough to read
|
||||
// against the live demo in one glance. If they start getting long, that trade flips.
|
||||
|
||||
import { JSXElement } from "solid-js";
|
||||
import { CodeBox } from "@ui/General";
|
||||
|
||||
export function Demo(props: { title: string; code: string; children?: JSXElement }) {
|
||||
return (
|
||||
<section class="mt-10">
|
||||
<h2 class="text-lg font-semibold text-ink">{props.title}</h2>
|
||||
|
||||
<div class="mt-3 overflow-hidden rounded-default border border-line">
|
||||
{/* The live half. It sits on the plain surface, not in a tinted "preview"
|
||||
box, because a component that only looks right against a special
|
||||
background is a component that will look wrong in the app. */}
|
||||
<div class="border-b border-line px-4 py-2">
|
||||
<span class="font-mono text-[11px] uppercase tracking-widest text-ink-faint">running</span>
|
||||
</div>
|
||||
<div class="px-4 py-6">{props.children}</div>
|
||||
|
||||
<div class="border-t border-line bg-surface-muted px-4 py-2">
|
||||
<span class="font-mono text-[11px] uppercase tracking-widest text-ink-faint">source</span>
|
||||
</div>
|
||||
<CodeBox code={props.code} class="rounded-none border-0" />
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
245
go/cmd/kjol-website/frontend/src/layout/Shell.tsx
Normal file
245
go/cmd/kjol-website/frontend/src/layout/Shell.tsx
Normal file
@@ -0,0 +1,245 @@
|
||||
// The Kjøl JS Web shell: top bar (wordmark + Layers menu), sidebar, content.
|
||||
//
|
||||
// It is deliberately a near-copy of the Go/WASM section's AppLayout. Two front-ends,
|
||||
// one site: if the chrome drifted, crossing from /wasm to /js would feel like leaving
|
||||
// for somebody else's website. The components underneath are completely different —
|
||||
// these are Solid components from the kit, those are Go functions returning a VNode —
|
||||
// and the page should not betray that.
|
||||
|
||||
import { For, Show } from "solid-js";
|
||||
import { A, useLocation, useNavigate } from "@solidjs/router";
|
||||
import { Icon } from "@ui/Icons";
|
||||
import { Menu, MenuTrigger, MenuContent } from "@ui/Menu";
|
||||
import { ThemeToggle, initTheme } from "@ui/Theme";
|
||||
import { LANGUAGES, COMPOSITIONS, currentLayer, Layer } from "../layers.ts";
|
||||
import { COMPONENT_GROUPS } from "../componentGroups.ts";
|
||||
|
||||
interface NavItem {
|
||||
path: string;
|
||||
label: string;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
// The section's own pages. Paths are relative to the router base (/js).
|
||||
const NAV: NavItem[] = [
|
||||
{ path: "/", label: "Overview", icon: "circle-info" },
|
||||
{ path: "/components", label: "Components", icon: "table-columns" },
|
||||
];
|
||||
|
||||
// jumpTo scrolls a section into view, routing there first if we are somewhere else.
|
||||
//
|
||||
// A plain <a href="#forms"> would work if the reader were already on the components
|
||||
// page, and would do nothing useful from anywhere else. The router's <A> is no good
|
||||
// either — it would try to navigate to a route called "#forms".
|
||||
//
|
||||
// The queueMicrotask is not superstition: after navigate() the target section does not
|
||||
// exist yet, because the page it lives on has not rendered. Scrolling on the next tick
|
||||
// is the earliest moment the element is actually there to scroll to.
|
||||
function jumpTo(navigate: (to: string) => void, onComponentsPage: boolean, id: string) {
|
||||
const scroll = () => document.getElementById(id)?.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
|
||||
if (onComponentsPage) {
|
||||
scroll();
|
||||
return;
|
||||
}
|
||||
navigate("/components");
|
||||
queueMicrotask(scroll);
|
||||
}
|
||||
|
||||
// The site's primary navigation, as TWO dropdowns: Layers (the languages) and
|
||||
// Compositions (the frameworks assembled out of them — see layers.ts).
|
||||
//
|
||||
// Two menus, not one with two headings inside it. They answer different questions —
|
||||
// "what is this written in" and "what can I read" — and a reader who wants the second
|
||||
// should not have to scroll past the first to find it. The kit's single-open manager
|
||||
// means opening one closes the other, so they behave like one control with two halves.
|
||||
//
|
||||
// Anything not `live` still appears, greyed, with the reason. A menu that silently omits
|
||||
// half the library teaches the reader that the library is half the size it is.
|
||||
//
|
||||
// Same shape as the Go side (app/layers.go: layersMenu / compositionsMenu).
|
||||
function Dropdown(props: { label: string; rows: Layer[] }) {
|
||||
const location = useLocation();
|
||||
const current = () => currentLayer(location.pathname);
|
||||
|
||||
return (
|
||||
// bottom-end, because the triggers sit at the right-hand end of the bar and a 24rem
|
||||
// panel hanging off the left edge of one would run past the window.
|
||||
<Menu placement="bottom-end">
|
||||
<MenuTrigger>
|
||||
<span class="inline-flex items-center gap-1.5 rounded-default px-3 py-1.5 text-sm font-medium text-ink-soft hover:bg-surface-raised hover:text-ink">
|
||||
{props.label}
|
||||
<Icon icon="chevron-down" size={11} class="text-ink-faint" />
|
||||
</span>
|
||||
</MenuTrigger>
|
||||
|
||||
<MenuContent class="w-96">
|
||||
<For each={props.rows}>{(layer) => <LayerItem layer={layer} current={current()} />}</For>
|
||||
</MenuContent>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
// LayerItem is one row of the menu.
|
||||
//
|
||||
// The markup is deliberately the same shape and the same classes as app/layers.go's
|
||||
// layerItem. Two front-ends, one menu: if they drifted, this is where it would show,
|
||||
// because it is the one component a reader sees on both sides within seconds of each
|
||||
// other.
|
||||
function LayerItem(props: { layer: Layer; current?: Layer }) {
|
||||
return (
|
||||
<Show
|
||||
when={props.layer.live}
|
||||
fallback={
|
||||
<div class="flex cursor-default flex-col gap-0.5 px-3 py-2 opacity-55">
|
||||
<span class="flex items-center gap-2 text-sm font-medium text-ink-muted">
|
||||
{props.layer.name}
|
||||
<span class="rounded-full bg-surface-raised px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wider text-ink-muted">
|
||||
reference
|
||||
</span>
|
||||
</span>
|
||||
<span class="text-xs text-ink-muted">{props.layer.tagline}</span>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{/* A plain <a href>, not the router's <A>: the other side is served by a
|
||||
different binary, so crossing to it has to be a real navigation, not a
|
||||
client-side route the router would try to handle itself. */}
|
||||
<a
|
||||
href={props.layer.href}
|
||||
class={
|
||||
"flex flex-col gap-0.5 px-3 py-2 no-underline hover:bg-surface-raised " +
|
||||
(props.current?.href === props.layer.href ? "bg-primary-subtle" : "")
|
||||
}
|
||||
>
|
||||
<span class="text-sm font-medium text-ink">{props.layer.name}</span>
|
||||
<span class="text-xs text-ink-muted">{props.layer.tagline}</span>
|
||||
</a>
|
||||
</Show>
|
||||
);
|
||||
}
|
||||
|
||||
function Wordmark() {
|
||||
// A plain <a href>, not a router <A>: "/" is the front page, which belongs to the
|
||||
// Go/WASM binary. Routing to it inside this SPA would resolve to /js and land you
|
||||
// back where you started.
|
||||
return (
|
||||
<a href="/" class="flex items-center gap-2.5 no-underline">
|
||||
{/* text-white, not a theme token: the flag tile is the same in both themes, so
|
||||
the boat on top of it has to be too. The flag carries a dark scrim (.flag-no)
|
||||
so the plain white boat reads without a shadow of its own. */}
|
||||
<span class="inline-flex h-8 w-8 items-center justify-center rounded-default flag-no text-white">
|
||||
<Icon icon="sailboat" size={17} />
|
||||
</span>
|
||||
<span class="flex items-baseline gap-1.5">
|
||||
<span class="text-lg font-semibold tracking-tight text-ink">Kjøl JS Web</span>
|
||||
<span class="text-sm text-ink-faint">Solid + Go toolchain</span>
|
||||
</span>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
function Sidebar() {
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
// The router's pathname is absolute (/js/components); NAV paths are base-relative.
|
||||
const active = (path: string) => location.pathname === "/js" + (path === "/" ? "" : path);
|
||||
const onComponents = () => location.pathname === "/js/components";
|
||||
|
||||
// The same active treatment the Go sidebar uses (app/pages.go: sidebarLink) — a
|
||||
// tinted panel and accent text, not a grey fill. Now that the Solid theme carries the
|
||||
// primary-subtle / accent tokens, the two sidebars are the same sidebar.
|
||||
//
|
||||
// No icons, also matching the Go sidebar: the sidebar is a list of words, and a glyph on
|
||||
// every row is noise to read past. So `block`, not `flex items-center gap-2`.
|
||||
const linkCls = (on: boolean) =>
|
||||
on
|
||||
? "block rounded-default bg-primary-subtle px-2 py-1.5 text-sm font-medium text-accent no-underline"
|
||||
: "block rounded-default px-2 py-1.5 text-sm text-ink-soft no-underline hover:bg-surface-raised hover:text-ink";
|
||||
|
||||
return (
|
||||
<aside class="sticky top-[3.75rem] hidden h-[calc(100vh-3.75rem)] w-56 shrink-0 overflow-y-auto py-10 lg:block">
|
||||
<p class="px-2 text-[11px] font-semibold uppercase tracking-widest text-ink-faint">Kjøl JS Web</p>
|
||||
<ul class="mt-2 space-y-0.5">
|
||||
<For each={NAV}>
|
||||
{(item) => (
|
||||
<li>
|
||||
<A href={item.path} end={item.path === "/"} class={linkCls(active(item.path))}>
|
||||
{item.label}
|
||||
</A>
|
||||
</li>
|
||||
)}
|
||||
</For>
|
||||
</ul>
|
||||
|
||||
{/* The component groups are not pages — they are anchors into the one components
|
||||
page, and clicking one scrolls you there.
|
||||
|
||||
They are not highlighted by which section you have scrolled to. Finding that
|
||||
out means measuring all fifteen of them on every scroll frame, and the only
|
||||
way to act on the answer is a state write, which re-renders. Sixty times a
|
||||
second, to move a highlight. The highlight is not worth the page. */}
|
||||
<p class="mt-8 px-2 text-[11px] font-semibold uppercase tracking-widest text-ink-faint">
|
||||
Components
|
||||
</p>
|
||||
<ul class="mt-2 space-y-0.5">
|
||||
<For each={COMPONENT_GROUPS}>
|
||||
{(g) => (
|
||||
<li>
|
||||
<a
|
||||
href={"/js/components#" + g.id}
|
||||
class={linkCls(false)}
|
||||
onclick={(e) => {
|
||||
e.preventDefault();
|
||||
jumpTo(navigate, onComponents(), g.id);
|
||||
}}
|
||||
>
|
||||
{g.label}
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
</For>
|
||||
</ul>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
|
||||
export function Shell(props: { children?: any }) {
|
||||
// Once, at the root. The boot script in the document head has ALREADY put the right
|
||||
// class on <html> — this only syncs the toggle's signals with it and starts
|
||||
// following the OS while the mode is "system". Calling it late is harmless; not
|
||||
// calling it just leaves the button showing the wrong icon.
|
||||
initTheme();
|
||||
|
||||
return (
|
||||
<div class="min-h-screen bg-surface">
|
||||
{/* bg-surface/90, not bg-white/90: the translucent sticky bar has to be
|
||||
translucent over whatever the surface currently IS. */}
|
||||
<nav class="sticky top-0 z-20 border-b border-line bg-surface/90 backdrop-blur">
|
||||
<div class="mx-auto flex max-w-[110rem] items-center gap-3 px-6 py-3">
|
||||
<Wordmark />
|
||||
<span class="rounded-full border border-line px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wider text-ink-faint">
|
||||
Docs
|
||||
</span>
|
||||
<div class="ml-auto flex items-center gap-2">
|
||||
<Dropdown label="Layers" rows={LANGUAGES} />
|
||||
<Dropdown label="Compositions" rows={COMPOSITIONS} />
|
||||
<a
|
||||
href="/"
|
||||
class="rounded-default px-3 py-1.5 text-sm font-medium text-ink-soft no-underline hover:bg-surface-raised hover:text-ink"
|
||||
>
|
||||
Home
|
||||
</a>
|
||||
<ThemeToggle small />
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="mx-auto flex max-w-[110rem] gap-8 px-6">
|
||||
<Sidebar />
|
||||
<main class="min-w-0 flex-1 py-10">{props.children}</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
1585
go/cmd/kjol-website/frontend/src/pages/Components.tsx
Normal file
1585
go/cmd/kjol-website/frontend/src/pages/Components.tsx
Normal file
File diff suppressed because it is too large
Load Diff
41
go/cmd/kjol-website/frontend/src/pages/NotFound.tsx
Normal file
41
go/cmd/kjol-website/frontend/src/pages/NotFound.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
// The fallback for any /js/* URL the router does not know.
|
||||
//
|
||||
// The server cannot 404 these: it answers every /js/* path with the same SPA shell,
|
||||
// because it has no idea which routes the bundle contains. So the router has to be the
|
||||
// one to say so — and if it does not, an unknown URL renders the chrome around an empty
|
||||
// <main>, which is a blank page with a 200 and no explanation.
|
||||
//
|
||||
// It names the paths that MOVED, because that is what a stale bookmark most likely wants:
|
||||
// /js/kit, /js/forms, /js/table and /js/theming were four pages, and are now four
|
||||
// sections of one.
|
||||
|
||||
import { useLocation, useNavigate } from "@solidjs/router";
|
||||
import { ButtonUI, BUTTON_COLOR_PRIMARY, BUTTON_COLOR_LIGHT_NEUTRAL } from "@ui/Buttons";
|
||||
|
||||
export function NotFound() {
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<div class="max-w-2xl py-10">
|
||||
<h1 class="text-2xl font-semibold tracking-tight text-ink">Page not found</h1>
|
||||
<p class="mt-2 leading-relaxed text-ink-soft">
|
||||
No route matches <code class="rounded bg-surface-raised px-1 py-0.5 font-mono text-[13px]">{location.pathname}</code>.
|
||||
</p>
|
||||
<p class="mt-3 leading-relaxed text-ink-soft">
|
||||
The kit used to be spread across several pages — <code class="font-mono">/js/kit</code>,{" "}
|
||||
<code class="font-mono">/js/forms</code>, <code class="font-mono">/js/table</code>,{" "}
|
||||
<code class="font-mono">/js/theming</code>. It is one page now, and they are sections of it.
|
||||
</p>
|
||||
|
||||
<div class="mt-6 flex flex-wrap items-center gap-2">
|
||||
<ButtonUI color={BUTTON_COLOR_PRIMARY} onclick={() => navigate("/components")}>
|
||||
Go to Components
|
||||
</ButtonUI>
|
||||
<ButtonUI color={BUTTON_COLOR_LIGHT_NEUTRAL} onclick={() => navigate("/")}>
|
||||
Overview
|
||||
</ButtonUI>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
115
go/cmd/kjol-website/frontend/src/pages/Overview.tsx
Normal file
115
go/cmd/kjol-website/frontend/src/pages/Overview.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
// /js — what the JS layer is, and how it is built.
|
||||
|
||||
import { For } from "solid-js";
|
||||
import { useNavigate } from "@solidjs/router";
|
||||
import { Card, CardHeader } from "@ui/Cards";
|
||||
import { AlertBlue } from "@ui/Alerts";
|
||||
import { CodeBox } from "@ui/General";
|
||||
import { Icon } from "@ui/Icons";
|
||||
import { COMPONENT_GROUPS } from "../componentGroups.ts";
|
||||
|
||||
export function Overview() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p class="text-xs font-semibold uppercase tracking-widest text-primary">Kjøl JS Web</p>
|
||||
<h1 class="mt-2 text-3xl font-semibold tracking-tight text-ink">
|
||||
A Solid kit, built by a Go toolchain
|
||||
</h1>
|
||||
|
||||
<p class="mt-4 leading-relaxed text-ink-soft">
|
||||
This layer is the original one: a Solid.js component kit — forms, tables, modals, menus,
|
||||
tooltips, charts — that the applications shared before any of it was rewritten in Go. It is
|
||||
still what those applications run.
|
||||
</p>
|
||||
<p class="mt-3 leading-relaxed text-ink-soft">
|
||||
What is unusual is the build. There is no Node, no Vite, no Babel, and no{" "}
|
||||
<code class="rounded bg-surface-raised px-1 py-0.5 font-mono text-[13px]">node_modules</code>.
|
||||
The TSX is compiled to Solid's runtime calls by a Go program, the CSS by a Go implementation
|
||||
of Tailwind v4, and the whole thing is bundled by esbuild's Go API. The toolchain is a Go
|
||||
package you import.
|
||||
</p>
|
||||
|
||||
<h2 class="mt-10 text-lg font-semibold text-ink">The pipeline</h2>
|
||||
<p class="mt-2 leading-relaxed text-ink-soft">
|
||||
One command builds this section. Every stage of it is Go:
|
||||
</p>
|
||||
|
||||
<div class="mt-4 space-y-3">
|
||||
<Stage
|
||||
n="1"
|
||||
title="TSX → Solid"
|
||||
body="kjol/jsbundler compiles each .tsx into dom-expressions calls — the same output Babel's Solid preset produces. It is checked against Babel by a render-equivalence test: both are compiled, both are rendered, and the HTML must match."
|
||||
/>
|
||||
<Stage
|
||||
n="2"
|
||||
title="Solid → bundle"
|
||||
body="esbuild's Go API bundles it. Vendored packages resolve out of a pinned manifest rather than their own exports maps, because solid-js's bare entry mis-resolves to its SSR build — where every effect is a silent no-op."
|
||||
/>
|
||||
<Stage
|
||||
n="3"
|
||||
title="Tailwind"
|
||||
body="kjol/tw scans the sources for candidate class names and compiles the stylesheet. It is a Go implementation, so it can just as happily scan .go files — which is exactly what the Wasm Web layer needs it to do."
|
||||
/>
|
||||
</div>
|
||||
|
||||
<CodeBox class="mt-5" code={"$ go run ./server -build\nGenerating FA icon subset...\n FA icons: 94 defs for 47 names\nGenerating public routes...\n /js/ssr Ssr (rendered, 2498 bytes)\nBundling JS + CSS...\n\nBundle Files Size Time\n-------------------------------------------------------\nbundle.min.js 1 1.4 MB 198ms\nbundle.min.css 2500 74.6 KB 30ms"} />
|
||||
|
||||
<AlertBlue header="One reactive instance, always" class="mt-8">
|
||||
The single hardest invariant in this build is that there is exactly one copy of solid-js. Two
|
||||
copies do not error — they render fine and then silently stop flushing effects, so onMount
|
||||
never fires and nothing updates. kjol's vendor manifest is searched before the app's for
|
||||
precisely this reason.
|
||||
</AlertBlue>
|
||||
|
||||
<h2 class="mt-10 text-lg font-semibold text-ink">The kit</h2>
|
||||
<p class="mt-2 leading-relaxed text-ink-soft">
|
||||
All of it is on one page. It used to be three — Components, Forms, AutoTable — which is a
|
||||
split along the lines of the source files rather than along anything a reader wants: a person
|
||||
looking for a date picker does not know, and should not have to guess, whether it was filed
|
||||
under forms or under overlays.
|
||||
</p>
|
||||
|
||||
<div class="mt-5 grid gap-3 sm:grid-cols-3">
|
||||
<For each={COMPONENT_GROUPS}>
|
||||
{(g) => (
|
||||
<a
|
||||
href={"/js/components#" + g.id}
|
||||
class="group flex items-center gap-2 rounded-default border border-line bg-surface px-3 py-2.5 no-underline shadow-xs transition hover:border-primary hover:shadow-sm"
|
||||
onclick={(e) => {
|
||||
e.preventDefault();
|
||||
navigate("/components");
|
||||
queueMicrotask(() =>
|
||||
document.getElementById(g.id)?.scrollIntoView({ behavior: "smooth", block: "start" }),
|
||||
);
|
||||
}}
|
||||
>
|
||||
<Icon icon={g.icon} size={14} class="shrink-0 text-primary" />
|
||||
<span class="text-sm font-medium text-ink">{g.label}</span>
|
||||
<Icon
|
||||
icon="arrow-right"
|
||||
size={11}
|
||||
class="ml-auto text-ink-faint transition group-hover:text-primary"
|
||||
/>
|
||||
</a>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Stage(props: { n: string; title: string; body: string }) {
|
||||
return (
|
||||
<div class="flex gap-4 border-l-2 border-line pl-4">
|
||||
<span class="mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-surface-raised text-xs font-semibold text-ink-soft">
|
||||
{props.n}
|
||||
</span>
|
||||
<div>
|
||||
<h3 class="font-semibold text-ink">{props.title}</h3>
|
||||
<p class="mt-1 text-sm leading-relaxed text-ink-soft">{props.body}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// The chrome around every server-rendered public page.
|
||||
//
|
||||
// The bundler's SSR entry is hardcoded to import { PublicLayout } from this exact
|
||||
// path and to call it with { currentPath, children } — it is a contract, not a
|
||||
// convention. The client takeover (public.tsx) wraps the same body in the same
|
||||
// layout with the same currentPath, which is what makes the server markup and the
|
||||
// post-takeover markup identical. If they diverged, the page would visibly rebuild
|
||||
// itself the moment the bundle landed.
|
||||
//
|
||||
// Deliberately plain. This renders inside goja against a DOM shim at BUILD time,
|
||||
// where there is no layout, no getBoundingClientRect and no window — so nothing in
|
||||
// here may measure the page. That rules out the kit's floating components (Menu,
|
||||
// Tooltip, Popover), which is why the Layers menu is a row of links here and a real
|
||||
// menu everywhere else.
|
||||
|
||||
import { JSXElement } from "solid-js";
|
||||
|
||||
export function PublicLayout(props: { currentPath: string; children?: JSXElement }) {
|
||||
return (
|
||||
<div class="min-h-screen bg-surface">
|
||||
<nav class="border-b border-line">
|
||||
<div class="mx-auto flex max-w-2xl items-center gap-2 px-4 py-4">
|
||||
<a href="/" class="flex items-center gap-2.5 no-underline">
|
||||
<span class="inline-flex h-8 w-8 items-center justify-center rounded-default bg-fill-neutral text-on-fill-neutral">
|
||||
{/* The boat is the point of the name: kjol is Norwegian for KEEL. Inlined
|
||||
rather than pulled from the icon kit, because the kit's <Icon> reads a
|
||||
CSS custom property at runtime to pick its style — and under SSR there
|
||||
is no computed style to read. */}
|
||||
<svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<path d="M11.25 3.75v12M11.25 15.75H4.5l6.75-12M14.25 15.75h4.5l-4.5-7.5zM2.25 18.75h19.5l-2.4 3H4.65z" />
|
||||
</svg>
|
||||
</span>
|
||||
<span class="flex items-baseline gap-1.5">
|
||||
<span class="text-lg font-semibold tracking-tight text-ink">Kjøl JS Web</span>
|
||||
<span class="text-sm text-ink-faint">Solid + Go toolchain</span>
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<ul class="ml-auto flex items-center gap-1">
|
||||
<li>
|
||||
<a
|
||||
href="/js"
|
||||
class={
|
||||
props.currentPath === "/js"
|
||||
? "rounded-default bg-surface-raised px-3 py-1.5 text-sm font-medium text-ink no-underline"
|
||||
: "rounded-default px-3 py-1.5 text-sm font-medium text-ink-soft no-underline hover:bg-surface-raised hover:text-ink"
|
||||
}
|
||||
>
|
||||
Docs
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="/wasm"
|
||||
class="rounded-default px-3 py-1.5 text-sm font-medium text-ink-soft no-underline hover:bg-surface-raised hover:text-ink"
|
||||
>
|
||||
Wasm Web
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main>{props.children}</main>
|
||||
|
||||
<footer class="mx-auto max-w-2xl px-4 pb-14">
|
||||
<p class="text-sm text-ink-faint">
|
||||
Kjøl JS Web is one layer of kjol — a shared base layer. kjol is Norwegian for keel.
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
77
go/cmd/kjol-website/frontend/src/pages/public/Ssr.tsx
Normal file
77
go/cmd/kjol-website/frontend/src/pages/public/Ssr.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
// A server-rendered public page.
|
||||
//
|
||||
// The SPA under /js/* is client-only: the browser gets an empty #app and Solid fills
|
||||
// it. That is fine for a docs section behind a click, and wrong for anything a search
|
||||
// engine or a slow phone has to read.
|
||||
//
|
||||
// This page takes the other route. The bundler renders it at BUILD time — the real
|
||||
// component, executed in goja against a DOM shim — and bakes the resulting HTML into
|
||||
// a Go registry (internal/handlers/public_pages.gen.go). The server ships that HTML
|
||||
// directly, so the page is complete before any JavaScript loads. The client bundle
|
||||
// then re-renders the same component over the top and it becomes interactive.
|
||||
//
|
||||
// serverData() is what makes it more than a static file: the handler can inject data
|
||||
// for a request, and the SAME component renders it — on the server at request time,
|
||||
// and again in the browser after takeover, from the same inlined JSON. No refetch, no
|
||||
// flash of a skeleton.
|
||||
|
||||
import { serverData } from "@kjol/ssr/serverData.ts";
|
||||
|
||||
interface BuildInfo {
|
||||
renderedAt: string;
|
||||
stage: string;
|
||||
}
|
||||
|
||||
export function Ssr() {
|
||||
// Read inside the reactive body, never captured at module load — the value has to
|
||||
// be observed at render time, and there are three different render times.
|
||||
const info = () => serverData<BuildInfo>();
|
||||
|
||||
return (
|
||||
<div class="page-ssr mx-auto max-w-2xl px-4 py-14">
|
||||
<p class="text-xs font-semibold uppercase tracking-widest text-primary">Kjøl JS Web</p>
|
||||
<h1 class="mt-2 text-3xl font-semibold tracking-tight text-ink">
|
||||
This page was rendered by Go
|
||||
</h1>
|
||||
|
||||
<p class="mt-4 leading-relaxed text-ink-soft">
|
||||
Not by a Node renderer, and not in your browser. A Go program executed this Solid component
|
||||
in an embedded JavaScript engine, serialized the DOM it produced, and compiled the result
|
||||
into the server binary. View source: the markup arrived complete.
|
||||
</p>
|
||||
|
||||
{/* No data → the skeleton. This is exactly what the build-time bake sees, because
|
||||
the bake injects nothing; it is also what a crawler sees. With data injected at
|
||||
request time, the same three lines render the real values instead. */}
|
||||
{!info() ? (
|
||||
<div class="mt-8 animate-pulse rounded-default border border-line p-5">
|
||||
<div class="h-3 w-40 rounded bg-surface-strong" />
|
||||
<div class="mt-3 h-3 w-64 rounded bg-surface-raised" />
|
||||
</div>
|
||||
) : (
|
||||
<dl class="mt-8 rounded-default border border-line p-5">
|
||||
<div class="flex justify-between text-sm">
|
||||
<dt class="text-ink-muted">rendered at</dt>
|
||||
<dd class="font-mono text-ink">{info()!.renderedAt}</dd>
|
||||
</div>
|
||||
<div class="mt-2 flex justify-between text-sm">
|
||||
<dt class="text-ink-muted">stage</dt>
|
||||
<dd class="font-mono text-ink">{info()!.stage}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
)}
|
||||
|
||||
<p class="mt-8 leading-relaxed text-ink-soft">
|
||||
The skeleton above is the honest default. The bake runs with no data, so a component that
|
||||
cannot render without data cannot be baked — which is a useful constraint to discover at build
|
||||
time rather than in production.
|
||||
</p>
|
||||
|
||||
<p class="mt-8 text-sm text-ink-muted">
|
||||
<a href="/js" class="text-primary underline underline-offset-4">
|
||||
Back to Kjøl JS Web
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
30
go/cmd/kjol-website/frontend/src/pages/public/pages.ts
Normal file
30
go/cmd/kjol-website/frontend/src/pages/public/pages.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
// SINGLE SOURCE OF TRUTH for server-rendered public pages.
|
||||
//
|
||||
// Add an entry here, then write the component it points at, then run the bundler.
|
||||
// It regenerates:
|
||||
// - internal/handlers/public_pages.gen.go Go registry: route → <title> + baked HTML
|
||||
// - frontend/src/pages/public/routes.gen.ts client takeover map: route → component
|
||||
//
|
||||
// Both generated files are read back by code that is committed, so neither is
|
||||
// optional — but neither is hand-edited either.
|
||||
|
||||
export interface PublicPageDef {
|
||||
path: string; // URL pathname
|
||||
module: string; // component file, relative to frontend/src
|
||||
component: string; // exported component name
|
||||
title: string; // <title> text
|
||||
dynamic?: boolean; // ISR: also bake the render bundle so the server can render
|
||||
// this page with live data at request time
|
||||
}
|
||||
|
||||
export const publicPages: PublicPageDef[] = [
|
||||
{
|
||||
path: "/js/ssr",
|
||||
module: "pages/public/Ssr.tsx",
|
||||
component: "Ssr",
|
||||
title: "Server-rendered — Kjøl JS Web",
|
||||
// dynamic: the server may inject data for this route at request time, so bake
|
||||
// the render bundle too, not just the static skeleton.
|
||||
dynamic: true,
|
||||
},
|
||||
];
|
||||
17
go/cmd/kjol-website/frontend/src/pages/public/routes.gen.ts
Normal file
17
go/cmd/kjol-website/frontend/src/pages/public/routes.gen.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
// Code generated by cmd/bundle; DO NOT EDIT.
|
||||
// Source: frontend/src/pages/public/pages.ts
|
||||
|
||||
import { JSXElement } from "solid-js";
|
||||
import { Ssr } from "./Ssr.tsx";
|
||||
|
||||
// Body component for each public route, keyed by URL pathname. The client
|
||||
// router (public.ts) renders these when navigating without a full reload.
|
||||
export const publicRoutes: Record<string, () => JSXElement> = {
|
||||
"/js/ssr": Ssr,
|
||||
};
|
||||
|
||||
// <title> for each public route, applied by the client router on navigation
|
||||
// (the first load gets its title from the server-rendered shell).
|
||||
export const publicTitles: Record<string, string> = {
|
||||
"/js/ssr": "Server-rendered — Kjøl JS Web",
|
||||
};
|
||||
35
go/cmd/kjol-website/frontend/src/public.tsx
Normal file
35
go/cmd/kjol-website/frontend/src/public.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
// Client takeover for the server-rendered public pages.
|
||||
//
|
||||
// The server ships each page's HTML inside #page-root — fast first paint, readable by
|
||||
// a crawler, works with JavaScript off. This boots the same component and swaps it in,
|
||||
// making the page interactive.
|
||||
//
|
||||
// It wraps the body in the SAME PublicLayout with the SAME currentPath the build-time
|
||||
// bake used (see jsbundler/genssr.go: ssrEntrySolid). That is not tidiness — if the two
|
||||
// trees differed, the page would visibly rebuild itself the instant this bundle landed.
|
||||
//
|
||||
// It is a re-render takeover, not attach-hydration: Solid renders the client tree into
|
||||
// a detached node FIRST, then replaces #page-root's children in one step. The server
|
||||
// markup stays on screen until identical client markup is ready to replace it, so there
|
||||
// is no window in which the page is half-built.
|
||||
|
||||
import { render } from "solid-js/web";
|
||||
import { PublicLayout } from "./pages/public/PublicLayout.tsx";
|
||||
import { publicRoutes } from "./pages/public/routes.gen.ts";
|
||||
|
||||
const root = document.getElementById("page-root");
|
||||
const path = window.location.pathname;
|
||||
const Body = root ? publicRoutes[path] : undefined;
|
||||
|
||||
if (root && Body) {
|
||||
const staging = document.createElement(root.tagName);
|
||||
render(
|
||||
() => (
|
||||
<PublicLayout currentPath={path}>
|
||||
<Body />
|
||||
</PublicLayout>
|
||||
),
|
||||
staging,
|
||||
);
|
||||
root.replaceChildren(...staging.childNodes);
|
||||
}
|
||||
8
go/cmd/kjol-website/frontend/src/vendor.d.ts
vendored
Normal file
8
go/cmd/kjol-website/frontend/src/vendor.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
// Ambient shims for tsserver only. The vendored pdf-lib / pdfjs-dist here are trimmed to
|
||||
// the runtime files the bundler pins (frontend/vendor/vendor.json), so their `.d.ts` type
|
||||
// trees are absent and each package.json `types` field points at a file that was not
|
||||
// vendored. @ui/AutoTable imports both; the bundler resolves them at build time, but the
|
||||
// editor needs a declaration or it reports "cannot find module". These make them `any`,
|
||||
// which is all this example needs — it does not exercise the PDF export path itself.
|
||||
declare module "pdf-lib";
|
||||
declare module "pdfjs-dist";
|
||||
589
go/cmd/kjol-website/frontend/vendor/@kurkle/color/dist/color.esm.js
vendored
Normal file
589
go/cmd/kjol-website/frontend/vendor/@kurkle/color/dist/color.esm.js
vendored
Normal file
@@ -0,0 +1,589 @@
|
||||
/*!
|
||||
* @kurkle/color v0.3.4
|
||||
* https://github.com/kurkle/color#readme
|
||||
* (c) 2024 Jukka Kurkela
|
||||
* Released under the MIT License
|
||||
*/
|
||||
function round(v) {
|
||||
return v + 0.5 | 0;
|
||||
}
|
||||
const lim = (v, l, h) => Math.max(Math.min(v, h), l);
|
||||
function p2b(v) {
|
||||
return lim(round(v * 2.55), 0, 255);
|
||||
}
|
||||
function b2p(v) {
|
||||
return lim(round(v / 2.55), 0, 100);
|
||||
}
|
||||
function n2b(v) {
|
||||
return lim(round(v * 255), 0, 255);
|
||||
}
|
||||
function b2n(v) {
|
||||
return lim(round(v / 2.55) / 100, 0, 1);
|
||||
}
|
||||
function n2p(v) {
|
||||
return lim(round(v * 100), 0, 100);
|
||||
}
|
||||
|
||||
const map$1 = {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, A: 10, B: 11, C: 12, D: 13, E: 14, F: 15, a: 10, b: 11, c: 12, d: 13, e: 14, f: 15};
|
||||
const hex = [...'0123456789ABCDEF'];
|
||||
const h1 = b => hex[b & 0xF];
|
||||
const h2 = b => hex[(b & 0xF0) >> 4] + hex[b & 0xF];
|
||||
const eq = b => ((b & 0xF0) >> 4) === (b & 0xF);
|
||||
const isShort = v => eq(v.r) && eq(v.g) && eq(v.b) && eq(v.a);
|
||||
function hexParse(str) {
|
||||
var len = str.length;
|
||||
var ret;
|
||||
if (str[0] === '#') {
|
||||
if (len === 4 || len === 5) {
|
||||
ret = {
|
||||
r: 255 & map$1[str[1]] * 17,
|
||||
g: 255 & map$1[str[2]] * 17,
|
||||
b: 255 & map$1[str[3]] * 17,
|
||||
a: len === 5 ? map$1[str[4]] * 17 : 255
|
||||
};
|
||||
} else if (len === 7 || len === 9) {
|
||||
ret = {
|
||||
r: map$1[str[1]] << 4 | map$1[str[2]],
|
||||
g: map$1[str[3]] << 4 | map$1[str[4]],
|
||||
b: map$1[str[5]] << 4 | map$1[str[6]],
|
||||
a: len === 9 ? (map$1[str[7]] << 4 | map$1[str[8]]) : 255
|
||||
};
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
const alpha = (a, f) => a < 255 ? f(a) : '';
|
||||
function hexString(v) {
|
||||
var f = isShort(v) ? h1 : h2;
|
||||
return v
|
||||
? '#' + f(v.r) + f(v.g) + f(v.b) + alpha(v.a, f)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
const HUE_RE = /^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;
|
||||
function hsl2rgbn(h, s, l) {
|
||||
const a = s * Math.min(l, 1 - l);
|
||||
const f = (n, k = (n + h / 30) % 12) => l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
|
||||
return [f(0), f(8), f(4)];
|
||||
}
|
||||
function hsv2rgbn(h, s, v) {
|
||||
const f = (n, k = (n + h / 60) % 6) => v - v * s * Math.max(Math.min(k, 4 - k, 1), 0);
|
||||
return [f(5), f(3), f(1)];
|
||||
}
|
||||
function hwb2rgbn(h, w, b) {
|
||||
const rgb = hsl2rgbn(h, 1, 0.5);
|
||||
let i;
|
||||
if (w + b > 1) {
|
||||
i = 1 / (w + b);
|
||||
w *= i;
|
||||
b *= i;
|
||||
}
|
||||
for (i = 0; i < 3; i++) {
|
||||
rgb[i] *= 1 - w - b;
|
||||
rgb[i] += w;
|
||||
}
|
||||
return rgb;
|
||||
}
|
||||
function hueValue(r, g, b, d, max) {
|
||||
if (r === max) {
|
||||
return ((g - b) / d) + (g < b ? 6 : 0);
|
||||
}
|
||||
if (g === max) {
|
||||
return (b - r) / d + 2;
|
||||
}
|
||||
return (r - g) / d + 4;
|
||||
}
|
||||
function rgb2hsl(v) {
|
||||
const range = 255;
|
||||
const r = v.r / range;
|
||||
const g = v.g / range;
|
||||
const b = v.b / range;
|
||||
const max = Math.max(r, g, b);
|
||||
const min = Math.min(r, g, b);
|
||||
const l = (max + min) / 2;
|
||||
let h, s, d;
|
||||
if (max !== min) {
|
||||
d = max - min;
|
||||
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
||||
h = hueValue(r, g, b, d, max);
|
||||
h = h * 60 + 0.5;
|
||||
}
|
||||
return [h | 0, s || 0, l];
|
||||
}
|
||||
function calln(f, a, b, c) {
|
||||
return (
|
||||
Array.isArray(a)
|
||||
? f(a[0], a[1], a[2])
|
||||
: f(a, b, c)
|
||||
).map(n2b);
|
||||
}
|
||||
function hsl2rgb(h, s, l) {
|
||||
return calln(hsl2rgbn, h, s, l);
|
||||
}
|
||||
function hwb2rgb(h, w, b) {
|
||||
return calln(hwb2rgbn, h, w, b);
|
||||
}
|
||||
function hsv2rgb(h, s, v) {
|
||||
return calln(hsv2rgbn, h, s, v);
|
||||
}
|
||||
function hue(h) {
|
||||
return (h % 360 + 360) % 360;
|
||||
}
|
||||
function hueParse(str) {
|
||||
const m = HUE_RE.exec(str);
|
||||
let a = 255;
|
||||
let v;
|
||||
if (!m) {
|
||||
return;
|
||||
}
|
||||
if (m[5] !== v) {
|
||||
a = m[6] ? p2b(+m[5]) : n2b(+m[5]);
|
||||
}
|
||||
const h = hue(+m[2]);
|
||||
const p1 = +m[3] / 100;
|
||||
const p2 = +m[4] / 100;
|
||||
if (m[1] === 'hwb') {
|
||||
v = hwb2rgb(h, p1, p2);
|
||||
} else if (m[1] === 'hsv') {
|
||||
v = hsv2rgb(h, p1, p2);
|
||||
} else {
|
||||
v = hsl2rgb(h, p1, p2);
|
||||
}
|
||||
return {
|
||||
r: v[0],
|
||||
g: v[1],
|
||||
b: v[2],
|
||||
a: a
|
||||
};
|
||||
}
|
||||
function rotate(v, deg) {
|
||||
var h = rgb2hsl(v);
|
||||
h[0] = hue(h[0] + deg);
|
||||
h = hsl2rgb(h);
|
||||
v.r = h[0];
|
||||
v.g = h[1];
|
||||
v.b = h[2];
|
||||
}
|
||||
function hslString(v) {
|
||||
if (!v) {
|
||||
return;
|
||||
}
|
||||
const a = rgb2hsl(v);
|
||||
const h = a[0];
|
||||
const s = n2p(a[1]);
|
||||
const l = n2p(a[2]);
|
||||
return v.a < 255
|
||||
? `hsla(${h}, ${s}%, ${l}%, ${b2n(v.a)})`
|
||||
: `hsl(${h}, ${s}%, ${l}%)`;
|
||||
}
|
||||
|
||||
const map = {
|
||||
x: 'dark',
|
||||
Z: 'light',
|
||||
Y: 're',
|
||||
X: 'blu',
|
||||
W: 'gr',
|
||||
V: 'medium',
|
||||
U: 'slate',
|
||||
A: 'ee',
|
||||
T: 'ol',
|
||||
S: 'or',
|
||||
B: 'ra',
|
||||
C: 'lateg',
|
||||
D: 'ights',
|
||||
R: 'in',
|
||||
Q: 'turquois',
|
||||
E: 'hi',
|
||||
P: 'ro',
|
||||
O: 'al',
|
||||
N: 'le',
|
||||
M: 'de',
|
||||
L: 'yello',
|
||||
F: 'en',
|
||||
K: 'ch',
|
||||
G: 'arks',
|
||||
H: 'ea',
|
||||
I: 'ightg',
|
||||
J: 'wh'
|
||||
};
|
||||
const names$1 = {
|
||||
OiceXe: 'f0f8ff',
|
||||
antiquewEte: 'faebd7',
|
||||
aqua: 'ffff',
|
||||
aquamarRe: '7fffd4',
|
||||
azuY: 'f0ffff',
|
||||
beige: 'f5f5dc',
|
||||
bisque: 'ffe4c4',
|
||||
black: '0',
|
||||
blanKedOmond: 'ffebcd',
|
||||
Xe: 'ff',
|
||||
XeviTet: '8a2be2',
|
||||
bPwn: 'a52a2a',
|
||||
burlywood: 'deb887',
|
||||
caMtXe: '5f9ea0',
|
||||
KartYuse: '7fff00',
|
||||
KocTate: 'd2691e',
|
||||
cSO: 'ff7f50',
|
||||
cSnflowerXe: '6495ed',
|
||||
cSnsilk: 'fff8dc',
|
||||
crimson: 'dc143c',
|
||||
cyan: 'ffff',
|
||||
xXe: '8b',
|
||||
xcyan: '8b8b',
|
||||
xgTMnPd: 'b8860b',
|
||||
xWay: 'a9a9a9',
|
||||
xgYF: '6400',
|
||||
xgYy: 'a9a9a9',
|
||||
xkhaki: 'bdb76b',
|
||||
xmagFta: '8b008b',
|
||||
xTivegYF: '556b2f',
|
||||
xSange: 'ff8c00',
|
||||
xScEd: '9932cc',
|
||||
xYd: '8b0000',
|
||||
xsOmon: 'e9967a',
|
||||
xsHgYF: '8fbc8f',
|
||||
xUXe: '483d8b',
|
||||
xUWay: '2f4f4f',
|
||||
xUgYy: '2f4f4f',
|
||||
xQe: 'ced1',
|
||||
xviTet: '9400d3',
|
||||
dAppRk: 'ff1493',
|
||||
dApskyXe: 'bfff',
|
||||
dimWay: '696969',
|
||||
dimgYy: '696969',
|
||||
dodgerXe: '1e90ff',
|
||||
fiYbrick: 'b22222',
|
||||
flSOwEte: 'fffaf0',
|
||||
foYstWAn: '228b22',
|
||||
fuKsia: 'ff00ff',
|
||||
gaRsbSo: 'dcdcdc',
|
||||
ghostwEte: 'f8f8ff',
|
||||
gTd: 'ffd700',
|
||||
gTMnPd: 'daa520',
|
||||
Way: '808080',
|
||||
gYF: '8000',
|
||||
gYFLw: 'adff2f',
|
||||
gYy: '808080',
|
||||
honeyMw: 'f0fff0',
|
||||
hotpRk: 'ff69b4',
|
||||
RdianYd: 'cd5c5c',
|
||||
Rdigo: '4b0082',
|
||||
ivSy: 'fffff0',
|
||||
khaki: 'f0e68c',
|
||||
lavFMr: 'e6e6fa',
|
||||
lavFMrXsh: 'fff0f5',
|
||||
lawngYF: '7cfc00',
|
||||
NmoncEffon: 'fffacd',
|
||||
ZXe: 'add8e6',
|
||||
ZcSO: 'f08080',
|
||||
Zcyan: 'e0ffff',
|
||||
ZgTMnPdLw: 'fafad2',
|
||||
ZWay: 'd3d3d3',
|
||||
ZgYF: '90ee90',
|
||||
ZgYy: 'd3d3d3',
|
||||
ZpRk: 'ffb6c1',
|
||||
ZsOmon: 'ffa07a',
|
||||
ZsHgYF: '20b2aa',
|
||||
ZskyXe: '87cefa',
|
||||
ZUWay: '778899',
|
||||
ZUgYy: '778899',
|
||||
ZstAlXe: 'b0c4de',
|
||||
ZLw: 'ffffe0',
|
||||
lime: 'ff00',
|
||||
limegYF: '32cd32',
|
||||
lRF: 'faf0e6',
|
||||
magFta: 'ff00ff',
|
||||
maPon: '800000',
|
||||
VaquamarRe: '66cdaa',
|
||||
VXe: 'cd',
|
||||
VScEd: 'ba55d3',
|
||||
VpurpN: '9370db',
|
||||
VsHgYF: '3cb371',
|
||||
VUXe: '7b68ee',
|
||||
VsprRggYF: 'fa9a',
|
||||
VQe: '48d1cc',
|
||||
VviTetYd: 'c71585',
|
||||
midnightXe: '191970',
|
||||
mRtcYam: 'f5fffa',
|
||||
mistyPse: 'ffe4e1',
|
||||
moccasR: 'ffe4b5',
|
||||
navajowEte: 'ffdead',
|
||||
navy: '80',
|
||||
Tdlace: 'fdf5e6',
|
||||
Tive: '808000',
|
||||
TivedBb: '6b8e23',
|
||||
Sange: 'ffa500',
|
||||
SangeYd: 'ff4500',
|
||||
ScEd: 'da70d6',
|
||||
pOegTMnPd: 'eee8aa',
|
||||
pOegYF: '98fb98',
|
||||
pOeQe: 'afeeee',
|
||||
pOeviTetYd: 'db7093',
|
||||
papayawEp: 'ffefd5',
|
||||
pHKpuff: 'ffdab9',
|
||||
peru: 'cd853f',
|
||||
pRk: 'ffc0cb',
|
||||
plum: 'dda0dd',
|
||||
powMrXe: 'b0e0e6',
|
||||
purpN: '800080',
|
||||
YbeccapurpN: '663399',
|
||||
Yd: 'ff0000',
|
||||
Psybrown: 'bc8f8f',
|
||||
PyOXe: '4169e1',
|
||||
saddNbPwn: '8b4513',
|
||||
sOmon: 'fa8072',
|
||||
sandybPwn: 'f4a460',
|
||||
sHgYF: '2e8b57',
|
||||
sHshell: 'fff5ee',
|
||||
siFna: 'a0522d',
|
||||
silver: 'c0c0c0',
|
||||
skyXe: '87ceeb',
|
||||
UXe: '6a5acd',
|
||||
UWay: '708090',
|
||||
UgYy: '708090',
|
||||
snow: 'fffafa',
|
||||
sprRggYF: 'ff7f',
|
||||
stAlXe: '4682b4',
|
||||
tan: 'd2b48c',
|
||||
teO: '8080',
|
||||
tEstN: 'd8bfd8',
|
||||
tomato: 'ff6347',
|
||||
Qe: '40e0d0',
|
||||
viTet: 'ee82ee',
|
||||
JHt: 'f5deb3',
|
||||
wEte: 'ffffff',
|
||||
wEtesmoke: 'f5f5f5',
|
||||
Lw: 'ffff00',
|
||||
LwgYF: '9acd32'
|
||||
};
|
||||
function unpack() {
|
||||
const unpacked = {};
|
||||
const keys = Object.keys(names$1);
|
||||
const tkeys = Object.keys(map);
|
||||
let i, j, k, ok, nk;
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
ok = nk = keys[i];
|
||||
for (j = 0; j < tkeys.length; j++) {
|
||||
k = tkeys[j];
|
||||
nk = nk.replace(k, map[k]);
|
||||
}
|
||||
k = parseInt(names$1[ok], 16);
|
||||
unpacked[nk] = [k >> 16 & 0xFF, k >> 8 & 0xFF, k & 0xFF];
|
||||
}
|
||||
return unpacked;
|
||||
}
|
||||
|
||||
let names;
|
||||
function nameParse(str) {
|
||||
if (!names) {
|
||||
names = unpack();
|
||||
names.transparent = [0, 0, 0, 0];
|
||||
}
|
||||
const a = names[str.toLowerCase()];
|
||||
return a && {
|
||||
r: a[0],
|
||||
g: a[1],
|
||||
b: a[2],
|
||||
a: a.length === 4 ? a[3] : 255
|
||||
};
|
||||
}
|
||||
|
||||
const RGB_RE = /^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/;
|
||||
function rgbParse(str) {
|
||||
const m = RGB_RE.exec(str);
|
||||
let a = 255;
|
||||
let r, g, b;
|
||||
if (!m) {
|
||||
return;
|
||||
}
|
||||
if (m[7] !== r) {
|
||||
const v = +m[7];
|
||||
a = m[8] ? p2b(v) : lim(v * 255, 0, 255);
|
||||
}
|
||||
r = +m[1];
|
||||
g = +m[3];
|
||||
b = +m[5];
|
||||
r = 255 & (m[2] ? p2b(r) : lim(r, 0, 255));
|
||||
g = 255 & (m[4] ? p2b(g) : lim(g, 0, 255));
|
||||
b = 255 & (m[6] ? p2b(b) : lim(b, 0, 255));
|
||||
return {
|
||||
r: r,
|
||||
g: g,
|
||||
b: b,
|
||||
a: a
|
||||
};
|
||||
}
|
||||
function rgbString(v) {
|
||||
return v && (
|
||||
v.a < 255
|
||||
? `rgba(${v.r}, ${v.g}, ${v.b}, ${b2n(v.a)})`
|
||||
: `rgb(${v.r}, ${v.g}, ${v.b})`
|
||||
);
|
||||
}
|
||||
|
||||
const to = v => v <= 0.0031308 ? v * 12.92 : Math.pow(v, 1.0 / 2.4) * 1.055 - 0.055;
|
||||
const from = v => v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
|
||||
function interpolate(rgb1, rgb2, t) {
|
||||
const r = from(b2n(rgb1.r));
|
||||
const g = from(b2n(rgb1.g));
|
||||
const b = from(b2n(rgb1.b));
|
||||
return {
|
||||
r: n2b(to(r + t * (from(b2n(rgb2.r)) - r))),
|
||||
g: n2b(to(g + t * (from(b2n(rgb2.g)) - g))),
|
||||
b: n2b(to(b + t * (from(b2n(rgb2.b)) - b))),
|
||||
a: rgb1.a + t * (rgb2.a - rgb1.a)
|
||||
};
|
||||
}
|
||||
|
||||
function modHSL(v, i, ratio) {
|
||||
if (v) {
|
||||
let tmp = rgb2hsl(v);
|
||||
tmp[i] = Math.max(0, Math.min(tmp[i] + tmp[i] * ratio, i === 0 ? 360 : 1));
|
||||
tmp = hsl2rgb(tmp);
|
||||
v.r = tmp[0];
|
||||
v.g = tmp[1];
|
||||
v.b = tmp[2];
|
||||
}
|
||||
}
|
||||
function clone(v, proto) {
|
||||
return v ? Object.assign(proto || {}, v) : v;
|
||||
}
|
||||
function fromObject(input) {
|
||||
var v = {r: 0, g: 0, b: 0, a: 255};
|
||||
if (Array.isArray(input)) {
|
||||
if (input.length >= 3) {
|
||||
v = {r: input[0], g: input[1], b: input[2], a: 255};
|
||||
if (input.length > 3) {
|
||||
v.a = n2b(input[3]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
v = clone(input, {r: 0, g: 0, b: 0, a: 1});
|
||||
v.a = n2b(v.a);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
function functionParse(str) {
|
||||
if (str.charAt(0) === 'r') {
|
||||
return rgbParse(str);
|
||||
}
|
||||
return hueParse(str);
|
||||
}
|
||||
class Color {
|
||||
constructor(input) {
|
||||
if (input instanceof Color) {
|
||||
return input;
|
||||
}
|
||||
const type = typeof input;
|
||||
let v;
|
||||
if (type === 'object') {
|
||||
v = fromObject(input);
|
||||
} else if (type === 'string') {
|
||||
v = hexParse(input) || nameParse(input) || functionParse(input);
|
||||
}
|
||||
this._rgb = v;
|
||||
this._valid = !!v;
|
||||
}
|
||||
get valid() {
|
||||
return this._valid;
|
||||
}
|
||||
get rgb() {
|
||||
var v = clone(this._rgb);
|
||||
if (v) {
|
||||
v.a = b2n(v.a);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
set rgb(obj) {
|
||||
this._rgb = fromObject(obj);
|
||||
}
|
||||
rgbString() {
|
||||
return this._valid ? rgbString(this._rgb) : undefined;
|
||||
}
|
||||
hexString() {
|
||||
return this._valid ? hexString(this._rgb) : undefined;
|
||||
}
|
||||
hslString() {
|
||||
return this._valid ? hslString(this._rgb) : undefined;
|
||||
}
|
||||
mix(color, weight) {
|
||||
if (color) {
|
||||
const c1 = this.rgb;
|
||||
const c2 = color.rgb;
|
||||
let w2;
|
||||
const p = weight === w2 ? 0.5 : weight;
|
||||
const w = 2 * p - 1;
|
||||
const a = c1.a - c2.a;
|
||||
const w1 = ((w * a === -1 ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
|
||||
w2 = 1 - w1;
|
||||
c1.r = 0xFF & w1 * c1.r + w2 * c2.r + 0.5;
|
||||
c1.g = 0xFF & w1 * c1.g + w2 * c2.g + 0.5;
|
||||
c1.b = 0xFF & w1 * c1.b + w2 * c2.b + 0.5;
|
||||
c1.a = p * c1.a + (1 - p) * c2.a;
|
||||
this.rgb = c1;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
interpolate(color, t) {
|
||||
if (color) {
|
||||
this._rgb = interpolate(this._rgb, color._rgb, t);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
clone() {
|
||||
return new Color(this.rgb);
|
||||
}
|
||||
alpha(a) {
|
||||
this._rgb.a = n2b(a);
|
||||
return this;
|
||||
}
|
||||
clearer(ratio) {
|
||||
const rgb = this._rgb;
|
||||
rgb.a *= 1 - ratio;
|
||||
return this;
|
||||
}
|
||||
greyscale() {
|
||||
const rgb = this._rgb;
|
||||
const val = round(rgb.r * 0.3 + rgb.g * 0.59 + rgb.b * 0.11);
|
||||
rgb.r = rgb.g = rgb.b = val;
|
||||
return this;
|
||||
}
|
||||
opaquer(ratio) {
|
||||
const rgb = this._rgb;
|
||||
rgb.a *= 1 + ratio;
|
||||
return this;
|
||||
}
|
||||
negate() {
|
||||
const v = this._rgb;
|
||||
v.r = 255 - v.r;
|
||||
v.g = 255 - v.g;
|
||||
v.b = 255 - v.b;
|
||||
return this;
|
||||
}
|
||||
lighten(ratio) {
|
||||
modHSL(this._rgb, 2, ratio);
|
||||
return this;
|
||||
}
|
||||
darken(ratio) {
|
||||
modHSL(this._rgb, 2, -ratio);
|
||||
return this;
|
||||
}
|
||||
saturate(ratio) {
|
||||
modHSL(this._rgb, 1, ratio);
|
||||
return this;
|
||||
}
|
||||
desaturate(ratio) {
|
||||
modHSL(this._rgb, 1, -ratio);
|
||||
return this;
|
||||
}
|
||||
rotate(deg) {
|
||||
rotate(this._rgb, deg);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
function index_esm(input) {
|
||||
return new Color(input);
|
||||
}
|
||||
|
||||
export { Color, b2n, b2p, index_esm as default, hexParse, hexString, hsl2rgb, hslString, hsv2rgb, hueParse, hwb2rgb, lim, n2b, n2p, nameParse, p2b, rgb2hsl, rgbParse, rgbString, rotate, round };
|
||||
77
go/cmd/kjol-website/frontend/vendor/@kurkle/color/package.json
vendored
Normal file
77
go/cmd/kjol-website/frontend/vendor/@kurkle/color/package.json
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"name": "@kurkle/color",
|
||||
"type": "module",
|
||||
"version": "0.3.4",
|
||||
"description": "css color parsing, manupulation and conversion",
|
||||
"sideEffects": false,
|
||||
"main": "dist/color.cjs",
|
||||
"module": "dist/color.esm.js",
|
||||
"types": "dist/color.d.ts",
|
||||
"exports": {
|
||||
"types": "./dist/color.d.ts",
|
||||
"import": "./dist/color.esm.js",
|
||||
"require": "./dist/color.cjs"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "node util/copy_dist.js && rollup -c",
|
||||
"lint": "eslint src/*.js test/*.js util/*.js",
|
||||
"test": "node test/index.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/kurkle/color.git"
|
||||
},
|
||||
"files": [
|
||||
"dist/*",
|
||||
"dist/color.d.ts"
|
||||
],
|
||||
"keywords": [
|
||||
"color",
|
||||
"colour",
|
||||
"css",
|
||||
"hsl",
|
||||
"hex",
|
||||
"rgb",
|
||||
"rgba",
|
||||
"hwb",
|
||||
"hsv",
|
||||
"cmyk"
|
||||
],
|
||||
"author": "Jukka Kurkela",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/kurkle/color/issues"
|
||||
},
|
||||
"homepage": "https://github.com/kurkle/color#readme",
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-terser": "^0.4.0",
|
||||
"assert": "^2.0.0",
|
||||
"benchmark": "^2.1.4",
|
||||
"chartjs-color": "^2.4.1",
|
||||
"chartjs-color-string": "^0.6.0",
|
||||
"child_process": "^1.0.2",
|
||||
"chroma-js": "^3.1.1",
|
||||
"color-name": "^2.0.0",
|
||||
"color-names": "^2.0.0",
|
||||
"color-parse": "^2.0.2",
|
||||
"color-parser": "^0.1.0",
|
||||
"color-string": "^1.5.5",
|
||||
"csscolorparser": "^1.0.3",
|
||||
"eslint": "^9.15.0",
|
||||
"eslint-config-chartjs": "^0.3.0",
|
||||
"eslint-config-defaults": "^9.0.0",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"eslint-plugin-react": "^7.22.0",
|
||||
"fs": "0.0.1-security",
|
||||
"perf_hooks": "0.0.1",
|
||||
"rollup": "^4.25.0",
|
||||
"rollup-plugin-analyzer": "^4.0.0",
|
||||
"rollup-plugin-cleanup": "^3.2.1",
|
||||
"rollup-plugin-istanbul": "^5.0.0",
|
||||
"rollup-plugin-visualizer": "^5.8.3",
|
||||
"tinycolor2": "^1.4.2",
|
||||
"typedoc": "^0.26.7",
|
||||
"typescript": "^5.6.2",
|
||||
"util": "^0.12.3"
|
||||
}
|
||||
}
|
||||
11599
go/cmd/kjol-website/frontend/vendor/chart.js/dist/chart.js
vendored
Normal file
11599
go/cmd/kjol-website/frontend/vendor/chart.js/dist/chart.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2915
go/cmd/kjol-website/frontend/vendor/chart.js/dist/chunks/helpers.dataset.cjs
vendored
Normal file
2915
go/cmd/kjol-website/frontend/vendor/chart.js/dist/chunks/helpers.dataset.cjs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
go/cmd/kjol-website/frontend/vendor/chart.js/dist/chunks/helpers.dataset.cjs.map
vendored
Normal file
1
go/cmd/kjol-website/frontend/vendor/chart.js/dist/chunks/helpers.dataset.cjs.map
vendored
Normal file
File diff suppressed because one or more lines are too long
2788
go/cmd/kjol-website/frontend/vendor/chart.js/dist/chunks/helpers.dataset.js
vendored
Normal file
2788
go/cmd/kjol-website/frontend/vendor/chart.js/dist/chunks/helpers.dataset.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
go/cmd/kjol-website/frontend/vendor/chart.js/dist/chunks/helpers.dataset.js.map
vendored
Normal file
1
go/cmd/kjol-website/frontend/vendor/chart.js/dist/chunks/helpers.dataset.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
139
go/cmd/kjol-website/frontend/vendor/chart.js/package.json
vendored
Normal file
139
go/cmd/kjol-website/frontend/vendor/chart.js/package.json
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
{
|
||||
"name": "chart.js",
|
||||
"homepage": "https://www.chartjs.org",
|
||||
"description": "Simple HTML5 charts using the canvas element.",
|
||||
"version": "4.5.1",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"sideEffects": [
|
||||
"./auto/auto.js",
|
||||
"./auto/auto.cjs",
|
||||
"./dist/chart.umd.min.js",
|
||||
"./dist/chart.umd.js"
|
||||
],
|
||||
"jsdelivr": "./dist/chart.umd.min.js",
|
||||
"unpkg": "./dist/chart.umd.min.js",
|
||||
"main": "./dist/chart.cjs",
|
||||
"module": "./dist/chart.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/types.d.ts",
|
||||
"import": "./dist/chart.js",
|
||||
"require": "./dist/chart.cjs"
|
||||
},
|
||||
"./auto": {
|
||||
"types": "./auto/auto.d.ts",
|
||||
"import": "./auto/auto.js",
|
||||
"require": "./auto/auto.cjs"
|
||||
},
|
||||
"./helpers": {
|
||||
"types": "./helpers/helpers.d.ts",
|
||||
"import": "./helpers/helpers.js",
|
||||
"require": "./helpers/helpers.cjs"
|
||||
}
|
||||
},
|
||||
"types": "./dist/types.d.ts",
|
||||
"keywords": [
|
||||
"canvas",
|
||||
"charts",
|
||||
"data",
|
||||
"graphs",
|
||||
"html5",
|
||||
"responsive"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/chartjs/Chart.js.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/chartjs/Chart.js/issues"
|
||||
},
|
||||
"files": [
|
||||
"auto/**",
|
||||
"dist/**",
|
||||
"!dist/docs/**",
|
||||
"helpers/**"
|
||||
],
|
||||
"scripts": {
|
||||
"autobuild": "rollup -c -w",
|
||||
"copyDeclarations": "node -e \"fs.cpSync('./src/types/', './dist/types/', {recursive:true})\"",
|
||||
"emitDeclarations": "tsc --emitDeclarationOnly && pnpm copyDeclarations",
|
||||
"build": "rollup -c && pnpm emitDeclarations",
|
||||
"dev": "karma start ./karma.conf.cjs --auto-watch --no-single-run --browsers chrome --grep",
|
||||
"dev:ff": "karma start ./karma.conf.cjs --auto-watch --no-single-run --browsers firefox --grep",
|
||||
"docs": "pnpm run build && pnpm --filter \"./docs/**\" build",
|
||||
"docs:dev": "pnpm run build && pnpm --filter \"./docs/**\" dev",
|
||||
"lint-js": "eslint \"src/**/*.{js,ts}\" \"test/**/*.js\" \"docs/**/*.js\" --cache",
|
||||
"lint-md": "eslint \"**/*.md\" --cache",
|
||||
"lint-types": "pnpm build && node test/types/autogen.js && tsc -p test/types",
|
||||
"lint": "concurrently \"pnpm:lint-*\"",
|
||||
"test": "pnpm lint && pnpm test-ci",
|
||||
"test-ci": "concurrently \"pnpm:test-ci-*\"",
|
||||
"test-ci-karma": "cross-env NODE_ENV=test karma start ./karma.conf.cjs --auto-watch --single-run --coverage --grep",
|
||||
"test-ci-integration": "pnpm --filter \"./test/integration/**\" test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@kurkle/color": "^0.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^23.0.2",
|
||||
"@rollup/plugin-inject": "^5.0.2",
|
||||
"@rollup/plugin-json": "^5.0.1",
|
||||
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||
"@swc/core": "^1.3.18",
|
||||
"@types/estree": "^1.0.0",
|
||||
"@types/offscreencanvas": "^2019.7.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.32.0",
|
||||
"@typescript-eslint/parser": "^5.32.0",
|
||||
"chartjs-adapter-luxon": "^1.2.0",
|
||||
"chartjs-adapter-moment": "^1.0.0",
|
||||
"chartjs-test-utils": "^0.4.0",
|
||||
"concurrently": "^7.3.0",
|
||||
"coveralls": "^3.1.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.21.0",
|
||||
"eslint-config-chartjs": "^0.3.0",
|
||||
"eslint-plugin-es": "^4.1.0",
|
||||
"eslint-plugin-html": "^7.1.0",
|
||||
"eslint-plugin-markdown": "^3.0.0",
|
||||
"esm": "^3.2.25",
|
||||
"glob": "^8.0.3",
|
||||
"jasmine": "^3.7.0",
|
||||
"jasmine-core": "^3.7.1",
|
||||
"karma": "^6.3.2",
|
||||
"karma-chrome-launcher": "^3.1.0",
|
||||
"karma-coverage": "^2.0.3",
|
||||
"karma-edge-launcher": "^0.4.2",
|
||||
"karma-firefox-launcher": "^2.1.0",
|
||||
"karma-jasmine": "^4.0.1",
|
||||
"karma-jasmine-html-reporter": "^1.5.4",
|
||||
"karma-rollup-preprocessor": "7.0.7",
|
||||
"karma-safari-private-launcher": "^1.0.0",
|
||||
"karma-spec-reporter": "0.0.32",
|
||||
"luxon": "^3.0.1",
|
||||
"moment": "^2.29.4",
|
||||
"moment-timezone": "^0.5.34",
|
||||
"pixelmatch": "^5.3.0",
|
||||
"rollup": "^3.3.0",
|
||||
"rollup-plugin-cleanup": "^3.2.1",
|
||||
"rollup-plugin-istanbul": "^4.0.0",
|
||||
"rollup-plugin-swc3": "^0.7.0",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"typescript": "^4.7.4",
|
||||
"yargs": "^17.5.1"
|
||||
},
|
||||
"engines": {
|
||||
"pnpm": ">=8"
|
||||
},
|
||||
"packageManager": "pnpm@8.13.0",
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"html-entities": "1.4.0"
|
||||
},
|
||||
"peerDependencyRules": {
|
||||
"ignoreMissing": [
|
||||
"chart.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
39404
go/cmd/kjol-website/frontend/vendor/pdf-lib/dist/pdf-lib.esm.js
vendored
Normal file
39404
go/cmd/kjol-website/frontend/vendor/pdf-lib/dist/pdf-lib.esm.js
vendored
Normal file
File diff suppressed because one or more lines are too long
141
go/cmd/kjol-website/frontend/vendor/pdf-lib/package.json
vendored
Normal file
141
go/cmd/kjol-website/frontend/vendor/pdf-lib/package.json
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
{
|
||||
"name": "pdf-lib",
|
||||
"version": "1.17.1",
|
||||
"description": "Create and modify PDF files with JavaScript",
|
||||
"author": "Andrew Dillon <andrew.dillon.j@gmail.com>",
|
||||
"contributors": [
|
||||
"jerp (https://github.com/jerp)",
|
||||
"Greg Bacchus (https://github.com/gregbacchus)",
|
||||
"Mickael Lecoq (https://github.com/mlecoq)",
|
||||
"Philip Murphy (https://github.com/philipjmurphy)",
|
||||
"Dmitry Kozliuk (https://github.com/PlushBeaver)",
|
||||
"Said Amezyane (https://github.com/samezyane)",
|
||||
"Georges Gabereau (https://github.com/multiplegeorges)",
|
||||
"Gerard Smit (https://github.com/GerardSmit)",
|
||||
"jlmessenger (https://github.com/jlmessenger)",
|
||||
"thebenlamm (https://github.com/thebenlamm)",
|
||||
"cshenks (https://github.com/cshenks)",
|
||||
"James Woodrow (https://github.com/jwoodrow)",
|
||||
"Guillaume Grossetie (https://github.com/Mogztter)",
|
||||
"Philipp Tessenow (https://github.com/tessi)",
|
||||
"Tim Kräuter (https://github.com/timKraeuter)",
|
||||
"Richard Bateman (https://github.com/taxilian)",
|
||||
"Sebastian Martinez (https://github.com/sebastinez)",
|
||||
"soadzoor (https://github.com/soadzoor)",
|
||||
"Slobodan Babic (https://github.com/bockoblur)",
|
||||
"Zach Toben (https://github.com/ztoben)",
|
||||
"Zack Sheppard (https://github.com/zackdotcomputer)",
|
||||
"DkDavid (https://github.com/DkDavid)",
|
||||
"Bj Tecu (https://github.com/btecu)",
|
||||
"Brent McSharry (https://github.com/mcshaz)",
|
||||
"Tim Knapp (https://github.com/duffyd)",
|
||||
"Ching Chang (https://github.com/ChingChang9)"
|
||||
],
|
||||
"scripts": {
|
||||
"release:latest": "yarn publish --tag latest && yarn pack && yarn release:tag",
|
||||
"release:next": "yarn publish --tag next",
|
||||
"release:prep": "yarn clean && yarn lint && yarn typecheck && yarn test && yarn build",
|
||||
"release:tag": "TAG=\"v$(yarn --silent get:version)\" && git tag $TAG && git push origin $TAG",
|
||||
"get:version": "node --eval 'console.log(require(`./package.json`).version)'",
|
||||
"clean": "rimraf ts3.4 build cjs dist es scratchpad/build coverage tsBuildInfo.json apps/node-build apps/node/tsBuildInfo.json isolate*.log flamegraph.html out.pdf",
|
||||
"typecheck": "tsc --noEmit --incremental false --tsBuildInfoFile null",
|
||||
"test": "jest --config jest.json --runInBand",
|
||||
"testw": "jest --config jest.json --watch",
|
||||
"testc": "jest --config jest.json --coverage && open coverage/index.html",
|
||||
"lint": "yarn lint:prettier && yarn lint:tslint:src && yarn lint:tslint:tests",
|
||||
"lint:tslint:src": "tslint --project tsconfig.json --fix",
|
||||
"lint:tslint:tests": "tslint --project tests/tsconfig.json --fix",
|
||||
"lint:prettier": "prettier --write \"./{src,tests,apps}/**/*.{ts,js,json,html,css}\" --loglevel error",
|
||||
"build": "yarn build:cjs && yarn build:es && yarn build:esm && yarn build:esm:min && yarn build:umd && yarn build:umd:min && yarn build:downlevel-dts",
|
||||
"build:cjs": "ttsc --module commonjs --outDir cjs",
|
||||
"build:es": "ttsc --module ES2015 --outDir es",
|
||||
"build:esm": "rollup --config rollup.config.js --file dist/pdf-lib.esm.js --environment MODULE_TYPE:es",
|
||||
"build:esm:min": "rollup --config rollup.config.js --file dist/pdf-lib.esm.min.js --environment MINIFY,MODULE_TYPE:es",
|
||||
"build:umd": "rollup --config rollup.config.js --file dist/pdf-lib.js --environment MODULE_TYPE:umd",
|
||||
"build:umd:min": "rollup --config rollup.config.js --file dist/pdf-lib.min.js --environment MINIFY,MODULE_TYPE:umd",
|
||||
"build:downlevel-dts": "rimraf ts3.4 && yarn downlevel-dts . ts3.4 && rimraf ts3.4/scratchpad",
|
||||
"scratchpad:start": "ttsc --build scratchpad/tsconfig.json --watch",
|
||||
"scratchpad:run": "node scratchpad/build/scratchpad/index.js",
|
||||
"scratchpad:flame": "rimraf isolate*.log && node --prof scratchpad/build/scratchpad/index.js && node --prof-process --preprocess -j isolate*.log | flamebearer",
|
||||
"apps:node": "ttsc --build apps/node/tsconfig.json && node apps/node-build/index.js",
|
||||
"apps:deno": "deno run --allow-read --allow-write --allow-run apps/deno/index.ts",
|
||||
"apps:web": "http-server -c-1 .",
|
||||
"apps:web:mac": "bash -c 'sleep 1 && open http://localhost:8080/apps/web/test1.html' & yarn apps:web",
|
||||
"apps:rn:ios": "cd apps/rn && yarn add ./../.. --force && react-native run-ios",
|
||||
"apps:rn:android": "yarn apps:rn:emulator & cd apps/rn && yarn add ./../.. --force && react-native run-android",
|
||||
"apps:rn:emulator": "emulator -avd \"$(emulator -list-avds | head -n 1)\" & bash -c 'sleep 5 && adb reverse tcp:8080 tcp:8080 && adb reverse tcp:8081 tcp:8081'"
|
||||
},
|
||||
"main": "cjs/index.js",
|
||||
"module": "es/index.js",
|
||||
"unpkg": "dist/pdf-lib.min.js",
|
||||
"types": "cjs/index.d.ts",
|
||||
"typesVersions": {
|
||||
"<=3.5": {
|
||||
"*": [
|
||||
"ts3.4/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"cjs/",
|
||||
"dist/",
|
||||
"es/",
|
||||
"src/",
|
||||
"ts3.4",
|
||||
"LICENSE.md",
|
||||
"package.json",
|
||||
"README.md",
|
||||
"yarn.lock"
|
||||
],
|
||||
"dependencies": {
|
||||
"@pdf-lib/standard-fonts": "^1.0.0",
|
||||
"@pdf-lib/upng": "^1.0.1",
|
||||
"pako": "^1.0.11",
|
||||
"tslib": "^1.11.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@pdf-lib/fontkit": "^1.1.0",
|
||||
"@rollup/plugin-commonjs": "^13.0.0",
|
||||
"@rollup/plugin-json": "^4.1.0",
|
||||
"@rollup/plugin-node-resolve": "^8.0.1",
|
||||
"@types/jest": "^26.0.0",
|
||||
"@types/node-fetch": "^2.5.7",
|
||||
"@types/pako": "^1.0.1",
|
||||
"@zerollup/ts-transform-paths": "^1.7.18",
|
||||
"downlevel-dts": "^0.5.0",
|
||||
"flamebearer": "^1.1.3",
|
||||
"http-server": "^0.12.3",
|
||||
"jest": "^26.0.1",
|
||||
"node-fetch": "^2.6.0",
|
||||
"prettier": "^2.0.5",
|
||||
"rimraf": "^3.0.2",
|
||||
"rollup": "^2.17.1",
|
||||
"rollup-plugin-terser": "^6.1.0",
|
||||
"ts-jest": "^26.1.0",
|
||||
"tslint": "^6.1.2",
|
||||
"tslint-config-prettier": "^1.18.0",
|
||||
"ttypescript": "^1.5.10",
|
||||
"typescript": "^3.9.5"
|
||||
},
|
||||
"license": "MIT",
|
||||
"private": false,
|
||||
"homepage": "https://pdf-lib.js.org",
|
||||
"repository": "git+https://github.com/Hopding/pdf-lib.git",
|
||||
"bugs": {
|
||||
"url": "https://github.com/Hopding/pdf-lib/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"pdf-lib",
|
||||
"pdf",
|
||||
"document",
|
||||
"create",
|
||||
"modify",
|
||||
"creation",
|
||||
"modification",
|
||||
"edit",
|
||||
"editing",
|
||||
"typescript",
|
||||
"javascript",
|
||||
"library"
|
||||
]
|
||||
}
|
||||
26465
go/cmd/kjol-website/frontend/vendor/pdfjs-dist/build/pdf.mjs
vendored
Normal file
26465
go/cmd/kjol-website/frontend/vendor/pdfjs-dist/build/pdf.mjs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
28
go/cmd/kjol-website/frontend/vendor/pdfjs-dist/build/pdf.worker.min.mjs
vendored
Normal file
28
go/cmd/kjol-website/frontend/vendor/pdfjs-dist/build/pdf.worker.min.mjs
vendored
Normal file
File diff suppressed because one or more lines are too long
34
go/cmd/kjol-website/frontend/vendor/pdfjs-dist/package.json
vendored
Normal file
34
go/cmd/kjol-website/frontend/vendor/pdfjs-dist/package.json
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "pdfjs-dist",
|
||||
"version": "5.5.207",
|
||||
"main": "build/pdf.mjs",
|
||||
"types": "types/src/pdf.d.ts",
|
||||
"description": "Generic build of Mozilla's PDF.js library.",
|
||||
"keywords": [
|
||||
"Mozilla",
|
||||
"pdf",
|
||||
"pdf.js"
|
||||
],
|
||||
"homepage": "https://mozilla.github.io/pdf.js/",
|
||||
"bugs": "https://github.com/mozilla/pdf.js/issues",
|
||||
"license": "Apache-2.0",
|
||||
"optionalDependencies": {
|
||||
"@napi-rs/canvas": "^0.1.95",
|
||||
"node-readable-to-web-readable-stream": "^0.4.2"
|
||||
},
|
||||
"browser": {
|
||||
"canvas": false,
|
||||
"fs": false,
|
||||
"http": false,
|
||||
"https": false,
|
||||
"url": false
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/mozilla/pdf.js.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.19.0 || >=22.13.0 || >=24"
|
||||
},
|
||||
"scripts": {}
|
||||
}
|
||||
12
go/cmd/kjol-website/frontend/vendor/vendor.json
vendored
Normal file
12
go/cmd/kjol-website/frontend/vendor/vendor.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"//": "This app's vendored packages, MERGED on top of kjol's base manifest (go/jsruntime/runtime/vendor.json), which pins solid-js, solid-js/web, solid-js/html, solid-js/store, @solidjs/router and solid-refresh. kjol's manifest is searched FIRST, so its solid-js wins and there is exactly one reactive instance — a split instance does not error, it silently stops flushing effects, so onMount never fires and nothing updates.",
|
||||
|
||||
"//2": "pdf-lib and pdfjs-dist are here because @ui/AutoTable imports them at the TOP LEVEL for PDF export. That makes them a hard dependency of the kit, not an optional extra: leave them out and esbuild emits a bare `import ... from \"pdf-lib\"`, the browser cannot resolve it, and the entire bundle fails to evaluate — you get an empty page and one line in the console. Any app that uses AutoTable must vendor these two.",
|
||||
|
||||
"//3": "Only the files the bundler actually pins are vendored, not the whole npm packages — 3.5 MB rather than 62 MB of type definitions, CJS builds and documentation. If a subpath import is ever added that reaches outside dist/ or build/, this is the first place it will fail.",
|
||||
|
||||
"entrypoints": {
|
||||
"pdf-lib": "pdf-lib/dist/pdf-lib.esm.js",
|
||||
"pdfjs-dist": "pdfjs-dist/build/pdf.mjs"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user