@sevenfold/setto-client 0.34.0 → 0.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/blog/SettoBlog.d.ts +20 -0
- package/dist/blog/data.d.ts +36 -0
- package/dist/blog/index.d.ts +2 -0
- package/dist/blog/render.d.ts +16 -0
- package/dist/blog/strings.d.ts +25 -0
- package/dist/blog.js +13 -0
- package/dist/blog.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/lib/media.d.ts +16 -0
- package/dist/lib/optimized-image.d.ts +14 -2
- package/dist/pages/manifest.d.ts +18 -1
- package/dist/setto-client.js +762 -200
- package/dist/setto-client.js.map +1 -1
- package/dist/types.d.ts +8 -0
- package/dist/vite/index.d.ts +6 -0
- package/dist/vite/plugin.d.ts +32 -0
- package/dist/vite/static.d.ts +78 -0
- package/dist/vite.js +550 -0
- package/dist/vite.js.map +1 -0
- package/package.json +10 -2
- package/virtual.d.ts +11 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type BuiltinSectionProps } from '../pages/SettoPage';
|
|
2
|
+
export declare function SettoBlogSection({ sectionId, first }: BuiltinSectionProps): import("react/jsx-runtime").JSX.Element;
|
|
3
|
+
/**
|
|
4
|
+
* The blog for a site that routes by hand instead of through `pages.json`
|
|
5
|
+
* (setto.no): mount it on `<basePath>` and `<basePath>/*`, and pass the same
|
|
6
|
+
* path to `settoBlog({ blogPath })`.
|
|
7
|
+
*
|
|
8
|
+
* <Route path="/blogg/*" element={<SettoBlog basePath="/blogg" />} />
|
|
9
|
+
*/
|
|
10
|
+
export declare function SettoBlog({ basePath, title }: {
|
|
11
|
+
basePath?: string;
|
|
12
|
+
title?: string;
|
|
13
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export declare function SettoBlogLatest({ sectionId, first }: BuiltinSectionProps): import("react/jsx-runtime").JSX.Element | null;
|
|
15
|
+
/**
|
|
16
|
+
* Shape is scoped `.setto-blog :where(…)` (one class: beats Preflight's
|
|
17
|
+
* element resets); colour stays at zero specificity so the site's own wins.
|
|
18
|
+
* Same split, same reason, as the form's sheet.
|
|
19
|
+
*/
|
|
20
|
+
export declare const BLOG_CSS = "\n.setto-blog{padding:clamp(48px,8vw,96px) 16px}\n.setto-blog :where(.setto-blog__inner){max-width:68ch;margin:0 auto}\n.setto-blog :where(.setto-blog__inner--wide){max-width:1120px}\n.setto-blog :where(.setto-blog__title){font-family:var(--setto-display-font,inherit);font-size:clamp(2rem,4vw,2.75rem);line-height:1.15;font-weight:600;margin:0 0 .75em}\n.setto-blog :where(.setto-blog__list){display:grid;gap:clamp(1.5rem,3vw,2.5rem);grid-template-columns:repeat(auto-fill,minmax(min(100%,300px),1fr))}\n.setto-blog :where(.setto-blog__card){display:grid;gap:.9rem;align-content:start}\n.setto-blog :where(.setto-blog__card-cover){display:block;overflow:hidden;border-radius:.9rem;aspect-ratio:16/10}\n.setto-blog :where(.setto-blog__card-cover img,.setto-blog__cover img,.setto-blog__figure img,.setto-blog__gallery-item img){display:block;width:100%;height:100%;object-fit:cover}\n.setto-blog :where(.setto-blog__card-body){display:grid;gap:.45rem}\n.setto-blog :where(.setto-blog__card-title){font-family:var(--setto-display-font,inherit);font-size:1.3rem;line-height:1.25;font-weight:600;margin:0}\n.setto-blog :where(.setto-blog__card-lead){margin:0;line-height:1.55}\n.setto-blog :where(.setto-blog__date){font-size:.875rem;letter-spacing:.01em}\n.setto-blog :where(.setto-blog__more,.setto-blog__back){display:inline-flex;gap:.35em;align-items:center;font-weight:500;width:fit-content}\n.setto-blog :where(.setto-blog__back){margin-bottom:2rem}\n.setto-blog :where(.setto-blog__latest-head){display:flex;flex-wrap:wrap;justify-content:space-between;align-items:baseline;gap:.5rem 1.5rem;margin-bottom:1.75rem}\n.setto-blog :where(.setto-blog__latest-head .setto-blog__title){margin:0}\n.setto-blog :where(.setto-blog__post-header){display:grid;gap:.6rem;margin-bottom:2rem}\n.setto-blog :where(.setto-blog__post-title){font-family:var(--setto-display-font,inherit);font-size:clamp(2rem,5vw,3rem);line-height:1.1;font-weight:600;margin:0}\n.setto-blog :where(.setto-blog__lead){font-size:1.2rem;line-height:1.5;margin:0}\n.setto-blog :where(.setto-blog__cover){margin:0 0 2.5rem;overflow:hidden;border-radius:1rem;aspect-ratio:16/9}\n.setto-blog :where(.setto-blog__prose){font-size:1.075rem;line-height:1.7}\n.setto-blog :where(.setto-blog__prose > * + *){margin-top:1.15em}\n.setto-blog :where(.setto-blog__prose h2,.setto-blog__prose h3,.setto-blog__prose h4){font-family:var(--setto-display-font,inherit);line-height:1.25;font-weight:600;margin:1.8em 0 0}\n.setto-blog :where(.setto-blog__prose h2){font-size:1.6rem}\n.setto-blog :where(.setto-blog__prose h3){font-size:1.3rem}\n.setto-blog :where(.setto-blog__prose h4){font-size:1.1rem}\n.setto-blog :where(.setto-blog__prose ul,.setto-blog__prose ol){padding-left:1.4em}\n.setto-blog :where(.setto-blog__prose ul){list-style:disc}\n.setto-blog :where(.setto-blog__prose ol){list-style:decimal}\n.setto-blog :where(.setto-blog__prose li + li){margin-top:.35em}\n.setto-blog :where(.setto-blog__prose blockquote){margin-left:0;padding-left:1.1em;border-left-width:3px;border-left-style:solid;font-style:italic}\n.setto-blog :where(.setto-blog__prose pre){overflow-x:auto;padding:1em;border-radius:.6rem;font-size:.9em}\n.setto-blog :where(.setto-blog__prose code){font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.92em}\n.setto-blog :where(.setto-blog__prose hr){border:0;border-top-width:1px;border-top-style:solid;margin:2.5em 0}\n.setto-blog :where(.setto-blog__prose a){text-decoration:underline;text-underline-offset:.18em}\n.setto-blog :where(.setto-blog__table){overflow-x:auto}\n.setto-blog :where(.setto-blog__table table){border-collapse:collapse;width:100%;font-size:.95em}\n.setto-blog :where(.setto-blog__table th,.setto-blog__table td){padding:.55em .8em;border-bottom-width:1px;border-bottom-style:solid;text-align:left}\n.setto-blog :where(.setto-blog__figure,.setto-blog__gallery,.setto-blog__video){margin:2em 0}\n.setto-blog :where(.setto-blog__figure img){border-radius:.8rem;height:auto}\n.setto-blog :where(figcaption){font-size:.9rem;margin-top:.6em;opacity:.75}\n.setto-blog :where(.setto-blog__gallery-grid){display:grid;gap:.6rem;grid-template-columns:repeat(auto-fit,minmax(min(100%,150px),1fr))}\n.setto-blog :where(.setto-blog__gallery-item){margin:0}\n.setto-blog :where(.setto-blog__gallery-item img){border-radius:.6rem;aspect-ratio:4/3}\n.setto-blog :where(.setto-blog__video-frame){position:relative;aspect-ratio:16/9;border-radius:.8rem;overflow:hidden}\n.setto-blog :where(.setto-blog__video-frame iframe){position:absolute;inset:0;width:100%;height:100%;border:0}\n.setto-blog :where(.setto-blog__video-play){position:absolute;inset:0;width:100%;height:100%;padding:0;border:0;cursor:pointer;display:grid;place-items:center}\n.setto-blog :where(.setto-blog__video-play img){position:absolute;inset:0;width:100%;height:100%;object-fit:cover}\n.setto-blog :where(.setto-blog__video-icon){position:relative;display:block;line-height:0}\n.setto-blog :where(.setto-blog__callout){padding:1.1em 1.3em;border-radius:.8rem;border-left-width:4px;border-left-style:solid}\n.setto-blog :where(.setto-blog__callout > * + *){margin-top:.7em}\n.setto-blog :where(.setto-blog__callout ul,.setto-blog__callout ol){padding-left:1.3em;list-style:disc}\n.setto-blog :where(.setto-blog__share){display:flex;flex-wrap:wrap;gap:.5rem .9rem;align-items:center;margin-top:3rem;padding-top:1.5rem;border-top-width:1px;border-top-style:solid}\n.setto-blog :where(.setto-blog__share-label){font-weight:600}\n.setto-blog :where(.setto-blog__share a,.setto-blog__share button){font:inherit;padding:.35rem .85rem;border-radius:999px;border-width:1px;border-style:solid;cursor:pointer;text-decoration:none;line-height:1.4}\n.setto-blog :where(.setto-blog__empty,.setto-blog__loading){opacity:.75}\n:where(.setto-blog){color:inherit}\n:where(.setto-blog__date){opacity:.7}\n:where(.setto-blog a){color:inherit}\n:where(.setto-blog__prose blockquote,.setto-blog__prose hr,.setto-blog__table th,.setto-blog__table td,.setto-blog__share){border-color:color-mix(in srgb,currentColor 18%,transparent)}\n:where(.setto-blog__prose pre,.setto-blog__card-cover){background:color-mix(in srgb,currentColor 6%,transparent)}\n:where(.setto-blog__share a,.setto-blog__share button){border-color:color-mix(in srgb,currentColor 30%,transparent);background:transparent;color:inherit}\n:where(.setto-blog__callout){background:color-mix(in srgb,currentColor 5%,transparent);border-left-color:color-mix(in srgb,currentColor 40%,transparent)}\n:where(.setto-blog__callout[data-tone=\"tip\"]){border-left-color:#0f7b4f}\n:where(.setto-blog__callout[data-tone=\"warning\"]){border-left-color:#b54708}\n:where(.setto-blog__video-frame){background:#000}\n";
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The posts a site's build hands to the library, and how a page chooses among
|
|
3
|
+
* them. No parser and no React: the build (`@setto/client/vite`) has already
|
|
4
|
+
* validated every file against the contract and turned its body into a syntax
|
|
5
|
+
* tree, so the browser only walks data.
|
|
6
|
+
*/
|
|
7
|
+
import type { Root } from 'mdast';
|
|
8
|
+
import type { BlogFrontmatter } from './contract';
|
|
9
|
+
/** A post as the list knows it: its frontmatter, and nothing to download. */
|
|
10
|
+
export type BlogPostSummary = BlogFrontmatter;
|
|
11
|
+
/** A post as its page knows it: frontmatter and the body's syntax tree. */
|
|
12
|
+
export interface BlogPostData {
|
|
13
|
+
frontmatter: BlogFrontmatter;
|
|
14
|
+
body: Root;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* What `virtual:setto-blog` exports and `SettoConfig.blog` carries. Bodies are
|
|
18
|
+
* loaded one at a time, so a site with a hundred posts does not ship a
|
|
19
|
+
* hundred articles to read one.
|
|
20
|
+
*/
|
|
21
|
+
export interface SettoBlogData {
|
|
22
|
+
posts: readonly BlogPostSummary[];
|
|
23
|
+
load(slug: string, locale: string): Promise<BlogPostData | null>;
|
|
24
|
+
}
|
|
25
|
+
/** Newest first; a tie breaks on slug so the order never flickers. */
|
|
26
|
+
export declare function byDateDesc(a: BlogPostSummary, b: BlogPostSummary): number;
|
|
27
|
+
/**
|
|
28
|
+
* One entry per post, in the reader's language where it exists and in the
|
|
29
|
+
* site's own otherwise. A post written only in Norwegian still shows to an
|
|
30
|
+
* English reader — untranslated is better than missing.
|
|
31
|
+
*/
|
|
32
|
+
export declare function postsForLanguage(posts: readonly BlogPostSummary[], language: string, fallbackLanguage: string): BlogPostSummary[];
|
|
33
|
+
/** The version of one post to show this reader, or null if there is none. */
|
|
34
|
+
export declare function postForSlug(posts: readonly BlogPostSummary[], slug: string, language: string, fallbackLanguage: string): BlogPostSummary | null;
|
|
35
|
+
/** `2026-10-01` as «1. oktober 2026» or «1 October 2026». */
|
|
36
|
+
export declare function formatPostDate(date: string, language: string): string;
|
package/dist/blog/index.d.ts
CHANGED
|
@@ -6,5 +6,7 @@
|
|
|
6
6
|
* step uses it to read posts.
|
|
7
7
|
*/
|
|
8
8
|
export * from './contract';
|
|
9
|
+
/** The host a post's images must come from: pass it as `imageHosts`. */
|
|
10
|
+
export { SETTO_MEDIA_HOST, isSettoMediaUrl } from '../lib/media';
|
|
9
11
|
export { validateBlogPost, parseBlogPost } from './validate';
|
|
10
12
|
export type { BlogIssue, BlogPost, BlogValidationResult, BlogValidateOptions } from './validate';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { RootContent } from 'mdast';
|
|
2
|
+
export declare function BlogBody({ nodes, language }: {
|
|
3
|
+
nodes: readonly RootContent[];
|
|
4
|
+
language: string;
|
|
5
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
/**
|
|
7
|
+
* An image, resized by the site's optimizer when it has one and the image is
|
|
8
|
+
* one it may read (`isOptimizable`: this site's own, or a blog photograph in
|
|
9
|
+
* Setto's media store). If a variant fails, the original is shown instead.
|
|
10
|
+
*/
|
|
11
|
+
export declare function BlogImg({ src, alt, sizes, className }: {
|
|
12
|
+
src: string;
|
|
13
|
+
alt: string;
|
|
14
|
+
sizes: string;
|
|
15
|
+
className?: string;
|
|
16
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The few words the blog says itself — around the posts, never in them. In
|
|
3
|
+
* Norwegian and English; any other language reads English, which is what a
|
|
4
|
+
* visitor who chose it can most likely read.
|
|
5
|
+
*/
|
|
6
|
+
declare const NO: {
|
|
7
|
+
blog: string;
|
|
8
|
+
latest: string;
|
|
9
|
+
allPosts: string;
|
|
10
|
+
readMore: string;
|
|
11
|
+
back: string;
|
|
12
|
+
empty: string;
|
|
13
|
+
notFound: string;
|
|
14
|
+
loading: string;
|
|
15
|
+
share: string;
|
|
16
|
+
moreShare: string;
|
|
17
|
+
copyLink: string;
|
|
18
|
+
copied: string;
|
|
19
|
+
email: string;
|
|
20
|
+
video: string;
|
|
21
|
+
play: string;
|
|
22
|
+
};
|
|
23
|
+
export type BlogStrings = typeof NO;
|
|
24
|
+
export declare function blogStrings(language: string): BlogStrings;
|
|
25
|
+
export {};
|
package/dist/blog.js
CHANGED
|
@@ -78,6 +78,17 @@ function parseBlogPostPath(path2) {
|
|
|
78
78
|
function blogPostRoute(slug) {
|
|
79
79
|
return `${BLOG_ROUTE}/${slug}`;
|
|
80
80
|
}
|
|
81
|
+
const SETTO_MEDIA_HOST = "3adyw42hy72dtzjo.public.blob.vercel-storage.com";
|
|
82
|
+
const SETTO_MEDIA_BLOG_PREFIX = "/blog/";
|
|
83
|
+
function isSettoMediaUrl(src) {
|
|
84
|
+
if (!src) return false;
|
|
85
|
+
try {
|
|
86
|
+
const url = new URL(src);
|
|
87
|
+
return url.protocol === "https:" && url.hostname === SETTO_MEDIA_HOST && !url.username && !url.password && url.port === "" && url.pathname.startsWith(SETTO_MEDIA_BLOG_PREFIX);
|
|
88
|
+
} catch {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
81
92
|
const emptyOptions$1 = {};
|
|
82
93
|
function toString$1(value2, options) {
|
|
83
94
|
const settings = emptyOptions$1;
|
|
@@ -23937,10 +23948,12 @@ export {
|
|
|
23937
23948
|
BLOG_TAGS_MAX,
|
|
23938
23949
|
BLOG_TAG_MAX,
|
|
23939
23950
|
BLOG_TITLE_MAX,
|
|
23951
|
+
SETTO_MEDIA_HOST,
|
|
23940
23952
|
YOUTUBE_ID_PATTERN,
|
|
23941
23953
|
blogPostPath,
|
|
23942
23954
|
blogPostRoute,
|
|
23943
23955
|
isBlogComponentName,
|
|
23956
|
+
isSettoMediaUrl,
|
|
23944
23957
|
isValidBlogLocale,
|
|
23945
23958
|
isValidBlogSlug,
|
|
23946
23959
|
parseBlogPost,
|