All checks were successful
Build and Deploy Nuxt / build (push) Successful in 24s
99 lines
3.9 KiB
TypeScript
99 lines
3.9 KiB
TypeScript
/* Route-shape helpers for the reader experience.
|
|
|
|
An article route is a page of the open book drawn by ArticleBookStack —
|
|
pages laid out side by side, older ones parked with their spines showing —
|
|
while the article edit page and the new-article page remain plain,
|
|
full-height pages. Every language version of an article has an address of
|
|
its own — /wiki/<lang>/<identity>, English no different from the rest, so
|
|
/wiki/en/bayes-theorem and /wiki/fr/bayes-theorem sit side by side. The one
|
|
short form, /wiki/<slug> (what a wiki link to [[Bayes Theorem]] writes, and
|
|
what an old bookmark holds), names no language: the open-article middleware
|
|
reads which document the slug speaks and turns the address into that
|
|
version's own. */
|
|
|
|
const READER_PATH = /^\/wiki\/([^/]+?)\/?$/
|
|
const VERSION_READER_PATH = /^\/wiki\/([a-z]{2})\/([^/]+?)\/?$/
|
|
const WIKI_SUB_PATH = /^\/wiki\/[^/]+\/[^/]+/
|
|
|
|
/** What a reader address names: the article's identity, and which language
|
|
* version the address asks for (null for the short form, which asks for
|
|
* whichever version the slug itself speaks). */
|
|
export interface ReaderLocation {
|
|
identity: string
|
|
lang: string | null
|
|
}
|
|
|
|
export function parseReaderPath(path: string): ReaderLocation | null {
|
|
const version = VERSION_READER_PATH.exec(path)
|
|
// /wiki/<x>/edit is the editor whatever <x> is — never a version address
|
|
if (version && version[2]!.toLowerCase() !== 'edit') {
|
|
return { identity: decodeURIComponent(version[2]!), lang: version[1]! }
|
|
}
|
|
const plain = READER_PATH.exec(path)
|
|
if (plain) return { identity: decodeURIComponent(plain[1]!), lang: null }
|
|
return null
|
|
}
|
|
|
|
export function isReaderPath(path: string): boolean {
|
|
return parseReaderPath(path) !== null
|
|
}
|
|
|
|
export function wikiSlugFromPath(path: string): string | null {
|
|
return parseReaderPath(path)?.identity ?? null
|
|
}
|
|
|
|
/**
|
|
* The address one language version lives at: /wiki/<lang>/<identity>, the
|
|
* article's identity being the canonical version's slug — so every version of
|
|
* one article shares the last part of its address and differs only by the
|
|
* language in front. Documents carry their own stored slug, which no address
|
|
* needs: the wiki finds a version by (identity, language).
|
|
*/
|
|
export function versionHref(identity: string, language?: string): string {
|
|
const code = language?.trim().toLowerCase() || DEFAULT_LANGUAGE
|
|
return `/wiki/${encodeURIComponent(code)}/${encodeURIComponent(identity)}`
|
|
}
|
|
|
|
/**
|
|
* The "reading surface": the reader itself plus the edit/new-article pages —
|
|
* the pages that belong to the open book. The stack of open pages survives the
|
|
* whole session either way; this marks the pages outside it (the list, the
|
|
* graph, home), which are the places worth returning to once the last page is
|
|
* closed.
|
|
*/
|
|
export function isReadingSurface(path: string): boolean {
|
|
return isReaderPath(path) || WIKI_SUB_PATH.test(path) || path === '/new'
|
|
}
|
|
|
|
/* ---------- the wiki's own pages ----------
|
|
|
|
The link map and the graphing calculator are pages the shelf holds no
|
|
copies of — but the book can read them too. Each has one plain address of
|
|
its own, and useArticleTabs can lay it on the row beside the open articles:
|
|
the page then draws the live surface instead of an article, for as long as
|
|
its page stays open. */
|
|
|
|
export type ToolPage = 'graph' | 'calculator'
|
|
|
|
const TOOL_PAGE_PATHS: Record<ToolPage, string> = {
|
|
graph: '/graph',
|
|
calculator: '/calculator',
|
|
}
|
|
|
|
/** The address a tool page lives at. */
|
|
export function toolHref(page: ToolPage): string {
|
|
return TOOL_PAGE_PATHS[page]
|
|
}
|
|
|
|
/** Which tool page an address names, if any. */
|
|
export function toolFromPath(path: string): ToolPage | null {
|
|
const clean = path.replace(/\/+$/, '')
|
|
for (const page of Object.keys(TOOL_PAGE_PATHS) as ToolPage[])
|
|
if (clean === TOOL_PAGE_PATHS[page]) return page
|
|
return null
|
|
}
|
|
|
|
export function isToolPath(path: string): boolean {
|
|
return toolFromPath(path) !== null
|
|
}
|