Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
240 changes: 191 additions & 49 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
"@sveltejs/adapter-cloudflare": "^7.2.9",
"@sveltejs/kit": "^2.63.0",
"@sveltejs/vite-plugin-svelte": "^7.1.2",
"@types/node": "^26.1.1",
"@vitest/browser-playwright": "^4.1.8",
"mdsvex": "^0.12.7",
"playwright": "^1.60.0",
"prettier": "^3.8.3",
"prettier-plugin-svelte": "^4.1.0",
Expand All @@ -34,7 +36,7 @@
"wrangler": "^4.27.0"
},
"dependencies": {
"@collagejs/core": "0.x || 1.x",
"@collagejs/core": "^0.10.1",
"@lucide/svelte": "^1.24.0",
"bootstrap": "^5.3.8",
"sass": "^1.101.0"
Expand Down
8 changes: 8 additions & 0 deletions src/app.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,12 @@ declare global {
}
}

declare module '*.md' {
import type { SvelteComponent } from 'svelte'

export default class Comp extends SvelteComponent{}

export const metadata: Record<string, unknown>
}

export {};
7 changes: 7 additions & 0 deletions src/lib/contexts.svelte.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { createContext } from "svelte";

export class PaletteContext {
value = $state<string>('');
}

export const [palette, setPalette] = createContext<PaletteContext>();
21 changes: 21 additions & 0 deletions src/lib/md-layouts/Blockquote.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<script lang="ts">
import type { HTMLBlockquoteAttributes } from "svelte/elements";

let {
children,
...restProps
}: HTMLBlockquoteAttributes = $props();
</script>

<blockquote {...restProps}>
{@render children?.()}
</blockquote>

<style>
blockquote {
padding: 1rem;
margin: 1rem;
background: rgba(var(--cjs-white-rgb), 0.15);
color: var(--color-text-secondary);
}
</style>
10 changes: 10 additions & 0 deletions src/lib/md-layouts/Hr.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<div role="separator" class="hr">
</div>

<style>
.hr {
width: 100%;
height: 0.12rem;
background: var(--cjs-primary-gradient);
}
</style>
23 changes: 23 additions & 0 deletions src/lib/md-layouts/Li.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<script lang="ts">
import { Check } from '@lucide/svelte';
import type { HTMLLiAttributes } from 'svelte/elements';
import { ListContext, listContext } from './list-context.svelte.js';

let {
children,
...restProps
}: HTMLLiAttributes = $props();

let ctx: ListContext | undefined;
try {
ctx = listContext();
} catch { }
const isOrdered = !!ctx?.value;
</script>

<li {...restProps}>
{#if !isOrdered}
<Check size="0.8em" color="var(--cjs-primary-400)" />&nbsp;
{/if}
{@render children?.()}
</li>
51 changes: 51 additions & 0 deletions src/lib/md-layouts/MdLayout.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<script module>
export { default as h1 } from './headers/H1.svelte';
export { default as h2 } from './headers/H2.svelte';
export { default as h3 } from './headers/H3.svelte';
export { default as h4 } from './headers/H4.svelte';
export { default as h5 } from './headers/H5.svelte';
export { default as h6 } from './headers/H6.svelte';
export { default as hr } from './Hr.svelte';
export { default as li } from './Li.svelte';
export { default as ol } from './Ol.svelte';
export { default as blockquote } from './Blockquote.svelte';
</script>

<script lang="ts">
import type { Snippet } from 'svelte';
import '../../scss/code.css';
import '../../scss/md-content.css';
import H1 from './headers/H1.svelte';

type Props = {
title: string;
seoTitle?: string;
description?: string;
children?: Snippet;
};

let {
title,
seoTitle = title,
description,
children
}: Props = $props();

let topEl: HTMLElement | null = null;

$effect(() => {
topEl?.scrollIntoView({ behavior: 'smooth', block: 'start' });
});
</script>

<svelte:head>
<title>{seoTitle} - CollageJS</title>
{#if description}
<meta name="description" content={description} />
{/if}
</svelte:head>

<article bind:this={topEl} class="widget widget-rounded px-5 py-3 md-content mb-1">
<H1>{title}</H1>
{@render children?.()}
</article>
15 changes: 15 additions & 0 deletions src/lib/md-layouts/Ol.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<script lang="ts">
import type { HTMLOlAttributes } from "svelte/elements";
import { ListContext, setListContext } from "./list-context.svelte";

let {
children,
...restProps
}: HTMLOlAttributes = $props();

setListContext(new ListContext(true));
</script>

<ol {...restProps}>
{@render children?.()}
</ol>
10 changes: 10 additions & 0 deletions src/lib/md-layouts/headers/H1.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<script lang="ts">
import type { ComponentProps } from 'svelte';
import Header from './Header.svelte';

let {
...restProps
}: Omit<ComponentProps<typeof Header>, 'level'> = $props();
</script>

<Header level={1} {...restProps} />
10 changes: 10 additions & 0 deletions src/lib/md-layouts/headers/H2.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<script lang="ts">
import type { ComponentProps } from 'svelte';
import Header from './Header.svelte';

let {
...restProps
}: Omit<ComponentProps<typeof Header>, 'level'> = $props();
</script>

<Header level={2} {...restProps} />
10 changes: 10 additions & 0 deletions src/lib/md-layouts/headers/H3.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<script lang="ts">
import type { ComponentProps } from 'svelte';
import Header from './Header.svelte';

let {
...restProps
}: Omit<ComponentProps<typeof Header>, 'level'> = $props();
</script>

<Header level={3} {...restProps} />
10 changes: 10 additions & 0 deletions src/lib/md-layouts/headers/H4.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<script lang="ts">
import type { ComponentProps } from 'svelte';
import Header from './Header.svelte';

let {
...restProps
}: Omit<ComponentProps<typeof Header>, 'level'> = $props();
</script>

<Header level={4} {...restProps} />
10 changes: 10 additions & 0 deletions src/lib/md-layouts/headers/H5.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<script lang="ts">
import type { ComponentProps } from 'svelte';
import Header from './Header.svelte';

let {
...restProps
}: Omit<ComponentProps<typeof Header>, 'level'> = $props();
</script>

<Header level={5} {...restProps} />
10 changes: 10 additions & 0 deletions src/lib/md-layouts/headers/H6.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<script lang="ts">
import type { ComponentProps } from 'svelte';
import Header from './Header.svelte';

let {
...restProps
}: Omit<ComponentProps<typeof Header>, 'level'> = $props();
</script>

<Header level={6} {...restProps} />
25 changes: 25 additions & 0 deletions src/lib/md-layouts/headers/Header.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<script lang="ts">
import type { HTMLAttributes } from "svelte/elements";

type Props = HTMLAttributes<HTMLHeadingElement> & {
level: 1 | 2 | 3 | 4 | 5 | 6;
};

let {
level,
class: cssClass,
children,
...restProps
}: Props = $props();
</script>

<svelte:element this={`h${level}`} class={['cjs-header', cssClass]} {...restProps}>
{@render children?.()}
</svelte:element>

<style>
.cjs-header {
color: var(--cjs-primary-400);
text-shadow: 0 0 1px rgba(var(--cjs-black-rgb), 0.5);
}
</style>
10 changes: 10 additions & 0 deletions src/lib/md-layouts/list-context.svelte.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { createContext } from "svelte";

export class ListContext {
value = $state(false);
constructor(initial: boolean = false) {
this.value = initial;
}
}

export const [listContext, setListContext] = createContext<ListContext>();
68 changes: 68 additions & 0 deletions src/routes/(docs-like)/+layout.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<script lang="ts">
import { page } from '$app/state';
import type { ArticleDefinition, ArticleNode } from '../../types.js';
import type { LayoutProps } from './$types';
import Footer from './Footer.svelte';
import NavArticleButtons from './NavArticleButtons/NavArticleButtons.svelte';
import PrimarySidebar from './PrimarySidebar/PrimarySidebar.svelte';
import { PrimarySidebarContext, setPrimarySidebar } from './PrimarySidebar/context.svelte.js';
import TopNav from './TopNav.svelte';
import { isArticleGroupDefinition } from './utils.js';

let { data, children }: LayoutProps = $props();

const primarySidebarCtx = setPrimarySidebar(new PrimarySidebarContext());

const flattenedArticleDefinitions = $derived.by(() => {
const extract = (ads: ArticleNode[]) => {
const result: ArticleDefinition[] = [];
for (let ad of ads) {
if (isArticleGroupDefinition(ad)) {
console.debug('extracting articles from group: %o', ad.title);
result.push(...extract(ad.articles));
continue;
}
result.push(ad);
}
return result;
};
return extract(primarySidebarCtx.value);
});
$inspect(flattenedArticleDefinitions).with((t, v) => {
console.log('flattenedArticleDefinitions (%s): %o', t, v);
});
const currentArticleDefinitionIndex = $derived(
flattenedArticleDefinitions.findIndex((ad) => page.url.pathname === ad.href)
);
const nextArticleDefinition = $derived(flattenedArticleDefinitions[currentArticleDefinitionIndex + 1]);
const previousArticleDefinition = $derived(flattenedArticleDefinitions[currentArticleDefinitionIndex - 1]);
</script>

<div class="contents d-flex flex-column gap-3 h-100">
<TopNav />
<div class="flex-grow-1 overflow-auto">
<main class="container">
<div class="row gx-1 gy-1">
{#if primarySidebarCtx.value?.length > 0}
<div class="col-12 col-md-3 order-2 order-md-1">
<PrimarySidebar articleDefinitions={primarySidebarCtx.value} />
</div>
{/if}
<div class={['col-12 order-1 order-md-2', primarySidebarCtx.value?.length > 0 && 'col-md-9']}>
{@render children()}
{#if nextArticleDefinition || previousArticleDefinition}
<NavArticleButtons prev={previousArticleDefinition} next={nextArticleDefinition} />
{/if}
</div>
</div>
</main>
<Footer />
</div>
</div>

<style>
.contents {
--widget-bg: rgba(var(--cjs-black-rgb), 0.8);
--widget-fg: rgba(var(--cjs-white-rgb), 0.9);
}
</style>
16 changes: 16 additions & 0 deletions src/routes/(docs-like)/Footer.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

<footer class="mt-3 pt-3 pb-5">
<div class="container">
<div class="row">
<div class="col d-flex justify-content-center">
Copyright &copy; 2026 CollageJS. All rights reserved.
</div>
</div>
</div>
</footer>

<style>
footer {
background-color: rgba(var(--cjs-black-rgb), 0.15);
}
</style>
Loading
Loading