@baack-software/baack-ts-editor 0.1.4 → 0.1.6

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/loader.js CHANGED
@@ -1,2 +1,2 @@
1
- import{a as v,b as s,d as C,g as f,l as o,m as L}from"./chunks/chunk-V4BSLQ2M.js";var l=class{start;firstEnded;down(t,e){let n=t.x<e*.15||t.x>e*.85;this.firstEnded!==void 0&&t.time-this.firstEnded>800&&(this.firstEnded=void 0),this.start=n?void 0:t}cancel(){this.start=void 0,this.firstEnded=void 0}up(t,e){let n=this.start;if(this.start=void 0,!n)return!1;let i=t.x-n.x,d=t.y-n.y;return-i>=e*.3&&Math.abs(d)<=Math.abs(i)/2&&t.time-n.time<=600?this.firstEnded!==void 0?(this.firstEnded=void 0,!0):(this.firstEnded=t.time,!1):(this.firstEnded=void 0,!1)}};var A=500,R=10,m=class{client;constructor(t){this.client=new C({baseUrl:t,credentials:"include"})}clientContext(){return this.client.read(s.CLIENT_CONTEXT,"")}entityView(t,e={}){return this.client.read(s.ENTITY_VIEW,t,{version:e.version,language:e.language,variant:e.variant})}createEntity(t){return this.client.create(s.ENTITY,t)}async companies(){return(await this.client.read(s.COMPANIES,"")).companies??[]}async searchEntities(t){let e=await this.client.search({owner:{urn:t},includeScopes:["ENTITY"],order:"NAME"});for(let d=0;e.status!=="FULL_RESULT"&&e.urn&&d<R;d+=1)await new Promise(c=>setTimeout(c,A)),e=await this.client.searchResult(e.urn);let n=e.status==="FULL_RESULT",i=x(e);for(;n&&e.urn&&e.pagination?.isMorePages&&e.pagination.after;)e=await this.client.searchResult(e.urn,e.pagination.after),i.push(...x(e));return{urns:[...new Set(i)],complete:n}}create(t,e){return this.client.create(t.endpoint,e)}read(t,e){return this.client.read(t.endpoint,e)}update(t,e,n){return this.client.update(t.endpoint,e,n)}async remove(t,e){await this.client.delete(t.endpoint,e)}history(t,e,n){return this.client.read(t.historyEndpoint,e,{after:n})}publishVersion(t,e){return this.client.update(t.historyEndpoint,e.urn,e)}readEntity(t){return this.client.read(s.ENTITY,t)}updateEntity(t,e){return this.client.update(s.ENTITY,t,e)}createTask(t){return this.client.create(s.TASK,t)}uploadImageFile(t,e,n){let i=new FormData;return i.append("imageItemUrn",t),i.append("imageItemSize",e),i.append("src",n,n.name),this.client.upload(s.IMAGE_FILE,i)}};function x(r){return(r.items??[]).filter(t=>t.type==="ENTITY"&&t.urn).map(t=>t.urn)}var k="baack-editor:editor",b=2e3,I=new m(v().baseUrl),g=!1,E=!1;async function S(){if(window.baackEditorLoaded)return;window.baackEditorLoaded=!0;let r=new URLSearchParams(location.search),t=r.get("version"),e=t==="LATEST"?"LATEST":t&&/^\d+$/.test(t)?Number(t):void 0,n=r.has("edit")||e!==void 0;document.addEventListener("keydown",i=>{i.altKey&&i.shiftKey&&i.code==="KeyE"&&(i.preventDefault(),h(!0,e))}),N(()=>{h(!0,e)}),(n||D())&&await h(n,e)}async function h(r,t){if(g||E)return;E=!0;let e;try{e=await I.clientContext()}catch(n){console.warn("Baack editor: could not read the client context; editing is unavailable.",n);return}finally{E=!1}e.hasSession===!0?M(e,t):r&&_(e.loginIframeSrc,t)}function M(r,t){if(g)return;g=!0,F();let e=f(),n=L(e),i=async()=>{n.disabled=!0,(await import("./editor.js")).start({transport:I,host:e,context:r,...t!==void 0?{mode:t}:{}})};t!==void 0?i():n.addEventListener("click",()=>{i()},{once:!0})}function _(r,t){let e=f();if(e.root.querySelector(".login-modal"))return;let n=o("div","login-modal"),i=o("div","panel modal");i.setAttribute("role","dialog"),i.setAttribute("aria-modal","true"),i.setAttribute("aria-label","Log in to edit");let d=o("div","title",t!==void 0?"Log in to see draft content":"Log in to edit this page"),c=o("p","message info",t!==void 0?"This link shows draft content. Log in to Baack to see it; until then this is the live page.":"Log in with your Baack account. The editor opens when you have logged in."),u=o("button","","Close");if(u.type="button",i.append(d,c),r){let a=o("iframe");a.title="Log in to Baack",a.src=r;let P=o("div","login");P.append(a),i.append(P)}else i.append(o("p","message","The login page is not available. Log in at Baack, then reload this page."));let T=o("div","actions");T.append(u),i.append(T),n.append(i),e.root.append(n);let p,y=()=>{clearTimeout(p),n.remove()};u.addEventListener("click",y),n.addEventListener("keydown",a=>{a.key==="Escape"&&y()}),u.focus();let w=async()=>{try{let a=await I.clientContext();if(a.hasSession===!0){y(),M(a,t);return}}catch{}n.isConnected&&(p=setTimeout(()=>{w()},b))};p=setTimeout(()=>{w()},b)}function N(r){let t=new l,e=(n,i)=>({x:n.clientX,y:n.clientY,time:i.timeStamp});document.addEventListener("touchstart",n=>{let i=n.touches[0];n.touches.length!==1||!i||!U(n.target)?t.cancel():t.down(e(i,n),window.innerWidth)},{passive:!0}),document.addEventListener("touchend",n=>{let i=n.changedTouches[0];n.touches.length===0&&i&&t.up(e(i,n),window.innerWidth)&&r()},{passive:!0}),document.addEventListener("touchcancel",()=>t.cancel(),{passive:!0})}function U(r){for(let t=r instanceof Element?r:null;t;t=t.parentElement){if(t.matches('input, textarea, select, [contenteditable]:not([contenteditable="false"])'))return!1;let e=getComputedStyle(t).overflowX;if((e==="auto"||e==="scroll")&&t.scrollWidth>t.clientWidth)return!1}return!0}function D(){try{return window.localStorage.getItem(k)==="1"}catch{return!1}}function F(){try{window.localStorage.setItem(k,"1")}catch{}}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>{S()},{once:!0}):S();
1
+ import{a as b,b as S,e as g,j as a,k as w,l as x,m as M}from"./chunks/chunk-MSWF55NC.js";import{a as o}from"./chunks/chunk-5RFN34CS.js";var f=class{start;firstEnded;down(t,e){let n=t.x<e*.15||t.x>e*.85;this.firstEnded!==void 0&&t.time-this.firstEnded>800&&(this.firstEnded=void 0),this.start=n?void 0:t}cancel(){this.start=void 0,this.firstEnded=void 0}up(t,e){let n=this.start;if(this.start=void 0,!n)return!1;let i=t.x-n.x,s=t.y-n.y;return-i>=e*.3&&Math.abs(s)<=Math.abs(i)/2&&t.time-n.time<=600?this.firstEnded!==void 0?(this.firstEnded=void 0,!0):(this.firstEnded=t.time,!1):(this.firstEnded=void 0,!1)}};var G=500,B=10,y=class{client;tasks;constructor(t){this.client=new S({baseUrl:t,credentials:"include",failureGuard:!1})}taskClient(){return this.tasks??=import("./chunks/dist-CF3GEGCB.js").then(({TaskClient:t})=>new t(this.client)),this.tasks}clientContext(){return this.client.read(o.CLIENT_CONTEXT,"")}entityView(t,e={}){return this.client.read(o.ENTITY_VIEW,t,{version:e.version,language:e.language,variant:e.variant})}createEntity(t){return this.client.create(o.ENTITY,t)}async companies(){return(await this.client.read(o.COMPANIES,"")).companies??[]}async searchEntities(t){let e=await this.client.search({owner:{urn:t},includeScopes:["ENTITY"],order:"NAME"});for(let s=0;e.status!=="FULL_RESULT"&&e.urn&&s<B;s+=1)await new Promise(d=>setTimeout(d,G)),e=await this.client.searchResult(e.urn);let n=e.status==="FULL_RESULT",i=A(e);for(;n&&e.urn&&e.pagination?.isMorePages&&e.pagination.after;)e=await this.client.searchResult(e.urn,e.pagination.after),i.push(...A(e));return{urns:[...new Set(i)],complete:n}}create(t,e){return this.client.create(t.endpoint,e)}read(t,e){return this.client.read(t.endpoint,e)}update(t,e,n){return this.client.update(t.endpoint,e,n)}async remove(t,e){await this.client.delete(t.endpoint,e)}history(t,e,n){return this.client.read(t.historyEndpoint,e,{after:n})}publishVersion(t,e){return this.client.update(t.historyEndpoint,e.urn,e)}readEntity(t){return this.client.read(o.ENTITY,t)}updateEntity(t,e){return this.client.update(o.ENTITY,t,e)}createTask(t){return this.client.create(o.TASK,t)}tasksAbout(t,e){return this.taskClient().then(n=>n.about(t,{owner:e,concurrency:4}))}setTaskStatus(t,e,n){return this.taskClient().then(i=>i.setStatus(t,e,n?{actor:n}:{}))}commentOnTask(t,e,n){return this.taskClient().then(i=>i.comment(t,e,n?{actor:n}:{}))}taskLogs(t,e){return this.taskClient().then(n=>n.logs(t,e?{after:e}:{}))}uploadImageFile(t,e,n){let i=new FormData;return i.append("imageItemUrn",t),i.append("imageItemSize",e),i.append("src",n,n.name),this.client.upload(o.IMAGE_FILE,i)}};function A(r){return(r.items??[]).filter(t=>t.type==="ENTITY"&&t.urn).map(t=>t.urn)}var N="baack-editor:editor",U=2e3,E=new y(b().baseUrl),C=!1,P=!1,D=()=>window.baackEditorConfig?.launcher==="page",L,u;async function R(){if(window.baackEditorLoaded)return;window.baackEditorLoaded=!0;let r=new URLSearchParams(location.search),t=r.get("version"),e=t==="LATEST"?"LATEST":t&&/^\d+$/.test(t)?Number(t):void 0,n=r.has("edit")||e!==void 0;document.addEventListener("keydown",i=>{i.altKey&&i.shiftKey&&i.code==="KeyE"&&(i.preventDefault(),h(!0,e))}),V(()=>{h(!0,e)}),document.addEventListener("click",i=>{!D()||!(i.target instanceof Element)||!i.target.closest("[data-baack-editor-launch]")||(i.preventDefault(),u?u.then(s=>s.setEditMode(!s.isEditing)):L?F(L,e):h(!0,e))}),(n||Y())&&await h(n,e)}async function h(r,t){if(C||u||P)return;P=!0;let e;try{e=await E.clientContext()}catch(n){console.warn("Baack editor: could not read the client context; editing is unavailable.",n);return}finally{P=!1}e.hasSession===!0?H(e,t,r):r&&W(e.loginIframeSrc,t)}function H(r,t,e=!1){if(D()){_(),L=r,(e||t!==void 0)&&F(r,t);return}if(C)return;C=!0,_();let n=g(),i=w(n),s=async()=>{i.disabled=!0,(await import("./editor.js")).start({transport:E,host:n,context:r,...t!==void 0?{mode:t}:{}})};t!==void 0?s():i.addEventListener("click",()=>{s()},{once:!0})}function F(r,t){if(u)return;let e=g();w(e).closest(".toolbar").hidden=!0,u=import("./editor.js").then(n=>n.start({transport:E,host:e,context:r,...t!==void 0?{mode:t}:{}}))}function W(r,t){let e=g();if(e.root.querySelector(".login-modal"))return;let n=a("div","login-modal"),i=a("div","panel modal");i.setAttribute("role","dialog"),i.setAttribute("aria-modal","true"),i.setAttribute("aria-label","Log in to edit");let s=a("div","title",t!==void 0?"Log in to see draft content":"Log in to edit this page"),d=a("p","message info",t!==void 0?"This link shows draft content. Log in to Baack to see it; until then this is the live page.":"Log in with your Baack account, in the window that opens. The editor starts when you have logged in."),l=a("button","","Close");l.type="button",i.append(s,d);let c=r?x(r):void 0;c||i.append(a("p","message","The login page is not available. Log in at Baack, then reload this page."));let v=a("div","actions");v.append(l,...c?[c.element]:[]),i.append(v),n.append(i),e.root.append(n);let m,O=M(()=>{k()}),I=()=>{clearTimeout(m),O(),n.remove()};l.addEventListener("click",I),n.addEventListener("keydown",p=>{p.key==="Escape"&&I()}),(c?.element??l).focus();let T=!1;async function k(){if(!(T||!n.isConnected)){T=!0,clearTimeout(m);try{let p=await E.clientContext();if(p.hasSession===!0){c?.close(),I(),H(p,t,!0);return}}catch{}finally{T=!1}n.isConnected&&(m=setTimeout(()=>{k()},U))}}m=setTimeout(()=>{k()},U)}function V(r){let t=new f,e=(n,i)=>({x:n.clientX,y:n.clientY,time:i.timeStamp});document.addEventListener("touchstart",n=>{let i=n.touches[0];n.touches.length!==1||!i||!X(n.target)?t.cancel():t.down(e(i,n),window.innerWidth)},{passive:!0}),document.addEventListener("touchend",n=>{let i=n.changedTouches[0];n.touches.length===0&&i&&t.up(e(i,n),window.innerWidth)&&r()},{passive:!0}),document.addEventListener("touchcancel",()=>t.cancel(),{passive:!0})}function X(r){for(let t=r instanceof Element?r:null;t;t=t.parentElement){if(t.matches('input, textarea, select, [contenteditable]:not([contenteditable="false"])'))return!1;let e=getComputedStyle(t).overflowX;if((e==="auto"||e==="scroll")&&t.scrollWidth>t.clientWidth)return!1}return!0}function Y(){try{return window.localStorage.getItem(N)==="1"}catch{return!1}}function _(){try{window.localStorage.setItem(N,"1")}catch{}}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>{R()},{once:!0}):R();
2
2
  //# sourceMappingURL=loader.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/core/swipe.ts", "../src/core/transport.ts", "../src/loader.ts"],
4
- "sourcesContent": ["// The touch gesture that asks for the editor: two quick swipes to the left\n// across the middle of the screen. It starts away from both edges, where iOS\n// and Android take a swipe as Back, and is mostly horizontal, so scrolling\n// never counts. Pure, so the rules are tested without a browser.\n\n/** How far a swipe must travel, as a share of the screen's width. */\nconst MIN_DISTANCE = 0.3;\n/** The edge bands a swipe may not start in, as a share of the width. */\nconst EDGE = 0.15;\n/** The longest a single swipe may take. */\nconst MAX_SWIPE_MS = 600;\n/** The longest gap between the end of the first swipe and the start of the second. */\nconst MAX_GAP_MS = 800;\n\nexport interface TouchPoint {\n x: number;\n y: number;\n time: number;\n}\n\nexport class DoubleSwipe {\n private start: TouchPoint | undefined;\n private firstEnded: number | undefined;\n\n /** A single finger touches down; `width` is the screen's width. */\n down(point: TouchPoint, width: number): void {\n const fromEdge = point.x < width * EDGE || point.x > width * (1 - EDGE);\n if (this.firstEnded !== undefined && point.time - this.firstEnded > MAX_GAP_MS) this.firstEnded = undefined;\n this.start = fromEdge ? undefined : point;\n }\n\n /** Another finger, or a touch that cannot be a swipe (in a field, a carousel): the gesture starts again. */\n cancel(): void {\n this.start = undefined;\n this.firstEnded = undefined;\n }\n\n /** The finger lifts. True when this completes the second swipe. */\n up(point: TouchPoint, width: number): boolean {\n const start = this.start;\n this.start = undefined;\n if (!start) return false;\n const dx = point.x - start.x;\n const dy = point.y - start.y;\n const swipe = -dx >= width * MIN_DISTANCE && Math.abs(dy) <= Math.abs(dx) / 2 && point.time - start.time <= MAX_SWIPE_MS;\n if (!swipe) {\n this.firstEnded = undefined;\n return false;\n }\n if (this.firstEnded !== undefined) {\n this.firstEnded = undefined;\n return true;\n }\n this.firstEnded = point.time;\n return false;\n }\n}\n", "// The editor's only route to the API. Failures are never swallowed: a non-2xx\n// response rejects with BaackApiError (carrying the HTTP status), and a\n// network failure rejects with fetch's own error.\n\nimport { BaackClient, Endpoint, type ClientContext, type Company, type Entity, type Pagination, type Search, type Task } from '@baack-software/baack-ts-client';\nimport type { ContentItem, ItemRepresentation, TypeEntry } from './registry.ts';\n\nexport interface ItemHistory<I extends ContentItem> {\n item?: I | null;\n history: I[];\n pagination?: Pagination | null;\n url: string;\n}\n\nexport interface EntityViewOptions {\n /** 'LATEST' or an ISO timestamp. Omit for the live view. Needs a session. */\n version?: string;\n /**\n * The language and variant to look up by page path, or, with a URN, to hold\n * to: the view treats a URN as a locator and may otherwise return another\n * language's entity. Read entities in scope through `ScopedViews`.\n */\n language?: string;\n variant?: string;\n}\n\n/** What is sent to create an entity. The server assigns the URN and scopes the name under the owner. */\nexport interface NewEntity {\n owner: { urn: string };\n status: 'DRAFT_UNPUBLISHED';\n /** A page path such as /blog/hello. */\n name: string;\n language: string;\n variant: string;\n}\n\n/** Entity URNs found by a search. `complete` is false when the search was still partial after polling. */\nexport interface EntitySearch {\n urns: string[];\n complete: boolean;\n}\n\n/** How often, and how many times, a partial search is read again (as the legacy client does). */\nconst SEARCH_POLL_MS = 500;\nconst SEARCH_POLLS = 10;\n\nexport interface Transport {\n clientContext(): Promise<ClientContext>;\n /** By entity URN, or by page path (`/blog/hello`) with language and variant. */\n entityView(scope: string, options?: EntityViewOptions): Promise<Entity>;\n createEntity(entity: NewEntity): Promise<Entity>;\n /** The companies the user may administer. */\n companies(): Promise<Company[]>;\n /** Every entity the owner has: the search returns them all, unfiltered, by URN. */\n searchEntities(owner: string): Promise<EntitySearch>;\n create<I extends ContentItem>(entry: TypeEntry<I>, item: ItemRepresentation<I>): Promise<I>;\n read<I extends ContentItem>(entry: TypeEntry<I>, urn: string): Promise<I>;\n update<I extends ContentItem>(entry: TypeEntry<I>, urn: string, item: ItemRepresentation<I>): Promise<I>;\n remove(entry: TypeEntry, urn: string): Promise<void>;\n /** One page of an item's versions, newest first; `after` continues from the previous page. */\n history<I extends ContentItem>(entry: TypeEntry<I>, urn: string, after?: string): Promise<ItemHistory<I>>;\n /** Makes a version live (moves the live pointer). The body is the version's item. */\n publishVersion<I extends ContentItem>(entry: TypeEntry<I>, item: I): Promise<ItemHistory<I>>;\n /** The entity's own fields and status, for the lifecycle. */\n readEntity(urn: string): Promise<Entity>;\n /** A lifecycle verb: the entity's fields with a new status. Item arrays are never sent. */\n updateEntity(urn: string, entity: EntityUpdate): Promise<Entity>;\n createTask(task: Partial<Task>): Promise<Task>;\n /** Stages one file for one slot (DEFAULT, MEDIUM or LARGE) of an image version whose slot is empty. */\n uploadImageFile(imageItemUrn: string, size: 'DEFAULT' | 'MEDIUM' | 'LARGE', file: File): Promise<unknown>;\n}\n\n/** The fields of an entity a lifecycle change sends. */\nexport interface EntityUpdate {\n owner: { urn: string };\n name: string;\n language: string;\n variant: string;\n status: 'PUBLISH' | 'DRAFT_UNPUBLISHED';\n publishTimestamp?: string;\n}\n\n/** Talks to Baack directly with the user's session cookie. */\nexport class DirectTransport implements Transport {\n private readonly client: BaackClient;\n\n constructor(baseUrl: string) {\n this.client = new BaackClient({ baseUrl, credentials: 'include' });\n }\n\n clientContext(): Promise<ClientContext> {\n return this.client.read<ClientContext>(Endpoint.CLIENT_CONTEXT, '');\n }\n\n entityView(scope: string, options: EntityViewOptions = {}): Promise<Entity> {\n return this.client.read<Entity>(Endpoint.ENTITY_VIEW, scope, {\n version: options.version,\n language: options.language,\n variant: options.variant,\n });\n }\n\n createEntity(entity: NewEntity): Promise<Entity> {\n return this.client.create<Entity>(Endpoint.ENTITY, entity);\n }\n\n async companies(): Promise<Company[]> {\n return (await this.client.read<{ companies?: Company[] }>(Endpoint.COMPANIES, '')).companies ?? [];\n }\n\n async searchEntities(owner: string): Promise<EntitySearch> {\n // `order: NAME` may not be implemented yet; it is harmless to ask.\n // A link is sent by URN only (writes are permissive).\n let result: Search = await this.client.search({ owner: { urn: owner } as Company, includeScopes: ['ENTITY'], order: 'NAME' });\n for (let poll = 0; result.status !== 'FULL_RESULT' && result.urn && poll < SEARCH_POLLS; poll += 1) {\n await new Promise((resolve) => setTimeout(resolve, SEARCH_POLL_MS));\n result = await this.client.searchResult(result.urn);\n }\n const complete = result.status === 'FULL_RESULT';\n const urns = entityUrns(result);\n // Later pages of a full result.\n while (complete && result.urn && result.pagination?.isMorePages && result.pagination.after) {\n result = await this.client.searchResult(result.urn, result.pagination.after);\n urns.push(...entityUrns(result));\n }\n return { urns: [...new Set(urns)], complete };\n }\n\n create<I extends ContentItem>(entry: TypeEntry<I>, item: ItemRepresentation<I>): Promise<I> {\n return this.client.create<I>(entry.endpoint, item);\n }\n\n read<I extends ContentItem>(entry: TypeEntry<I>, urn: string): Promise<I> {\n return this.client.read<I>(entry.endpoint, urn);\n }\n\n update<I extends ContentItem>(entry: TypeEntry<I>, urn: string, item: ItemRepresentation<I>): Promise<I> {\n return this.client.update<I>(entry.endpoint, urn, item);\n }\n\n async remove(entry: TypeEntry, urn: string): Promise<void> {\n await this.client.delete(entry.endpoint, urn);\n }\n\n history<I extends ContentItem>(entry: TypeEntry<I>, urn: string, after?: string): Promise<ItemHistory<I>> {\n return this.client.read<ItemHistory<I>>(entry.historyEndpoint, urn, { after });\n }\n\n publishVersion<I extends ContentItem>(entry: TypeEntry<I>, item: I): Promise<ItemHistory<I>> {\n return this.client.update<ItemHistory<I>>(entry.historyEndpoint, item.urn!, item);\n }\n\n readEntity(urn: string): Promise<Entity> {\n return this.client.read<Entity>(Endpoint.ENTITY, urn);\n }\n\n updateEntity(urn: string, entity: EntityUpdate): Promise<Entity> {\n return this.client.update<Entity>(Endpoint.ENTITY, urn, entity);\n }\n\n createTask(task: Partial<Task>): Promise<Task> {\n return this.client.create<Task>(Endpoint.TASK, task);\n }\n\n uploadImageFile(imageItemUrn: string, size: 'DEFAULT' | 'MEDIUM' | 'LARGE', file: File): Promise<unknown> {\n const form = new FormData();\n form.append('imageItemUrn', imageItemUrn);\n form.append('imageItemSize', size);\n form.append('src', file, file.name);\n return this.client.upload(Endpoint.IMAGE_FILE, form);\n }\n}\n\nfunction entityUrns(result: Search): string[] {\n return (result.items ?? []).filter((item) => item.type === 'ENTITY' && item.urn).map((item) => item.urn);\n}\n", "// The script a site includes. Anonymous visitors cost nothing: the loader\n// makes no API call unless this browser has used the editor before, or\n// someone asks for it (`?edit`, `?version=\u2026`, Alt+Shift+E, or two swipes to\n// the left on a touch screen). With a session\n// it offers \"Edit page\", and the full editor loads only when pressed. Without\n// one, when asked, it opens a login modal with Baack's login iframe.\n\nimport type { ClientContext } from '@baack-software/baack-ts-client';\nimport { readConfig } from './config.ts';\nimport { DoubleSwipe } from './core/swipe.ts';\nimport { DirectTransport } from './core/transport.ts';\nimport { createHost, el, type EditorHost } from './ui/host.ts';\nimport { createLauncher } from './ui/launcher.ts';\nimport type { ReadMode } from './view.ts';\n\ndeclare global {\n interface Window {\n baackEditorLoaded?: boolean;\n }\n}\n\n/** Set once this browser has had an editor session, so later visits check for one. */\nconst FLAG = 'baack-editor:editor';\n/** While the login modal is open, how often to check whether the login has completed. */\nconst LOGIN_POLL_MS = 2000;\n\nconst transport = new DirectTransport(readConfig().baseUrl);\nlet started = false;\nlet checking = false;\n\nasync function boot(): Promise<void> {\n if (window.baackEditorLoaded) return;\n window.baackEditorLoaded = true;\n\n // ?version=LATEST or ?version=<epoch ms> asks for drafts; ?edit asks for the editor.\n const params = new URLSearchParams(location.search);\n const version = params.get('version');\n const mode: ReadMode | undefined = version === 'LATEST' ? 'LATEST' : version && /^\\d+$/.test(version) ? Number(version) : undefined;\n const asked = params.has('edit') || mode !== undefined;\n\n document.addEventListener('keydown', (event) => {\n if (event.altKey && event.shiftKey && event.code === 'KeyE') {\n event.preventDefault();\n void check(true, mode);\n }\n });\n watchSwipes(() => void check(true, mode));\n if (asked || readFlag()) await check(asked, mode);\n}\n\n/** Asks the API whether there is a session; offers the editor, or (when asked for) a login. */\nasync function check(explicit: boolean, mode: ReadMode | undefined): Promise<void> {\n if (started || checking) return;\n checking = true;\n let context: ClientContext;\n try {\n context = await transport.clientContext();\n } catch (error) {\n console.warn('Baack editor: could not read the client context; editing is unavailable.', error);\n return;\n } finally {\n checking = false;\n }\n if (context.hasSession === true) {\n offer(context, mode);\n } else if (explicit) {\n // Only a session may see drafts: without one the page stays as built.\n openLogin(context.loginIframeSrc, mode);\n }\n}\n\nfunction offer(context: ClientContext, mode: ReadMode | undefined): void {\n if (started) return;\n started = true;\n writeFlag();\n const host = createHost();\n const button = createLauncher(host);\n const start = async () => {\n button.disabled = true;\n const editor = await import('./editor.ts');\n editor.start({ transport, host, context, ...(mode !== undefined ? { mode } : {}) });\n };\n if (mode !== undefined) {\n void start();\n } else {\n button.addEventListener('click', () => void start(), { once: true });\n }\n}\n\n/** A modal with Baack's login iframe. Checks for the session until the login completes, then offers the editor. */\nfunction openLogin(iframeSrc: string | null | undefined, mode: ReadMode | undefined): void {\n const host: EditorHost = createHost();\n if (host.root.querySelector('.login-modal')) return;\n const backdrop = el('div', 'login-modal');\n const dialog = el('div', 'panel modal');\n dialog.setAttribute('role', 'dialog');\n dialog.setAttribute('aria-modal', 'true');\n dialog.setAttribute('aria-label', 'Log in to edit');\n const title = el('div', 'title', mode !== undefined ? 'Log in to see draft content' : 'Log in to edit this page');\n const text = el(\n 'p',\n 'message info',\n mode !== undefined\n ? 'This link shows draft content. Log in to Baack to see it; until then this is the live page.'\n : 'Log in with your Baack account. The editor opens when you have logged in.',\n );\n const close = el('button', '', 'Close');\n close.type = 'button';\n dialog.append(title, text);\n if (iframeSrc) {\n const frame = el('iframe');\n frame.title = 'Log in to Baack';\n frame.src = iframeSrc;\n const login = el('div', 'login');\n login.append(frame);\n dialog.append(login);\n } else {\n dialog.append(el('p', 'message', 'The login page is not available. Log in at Baack, then reload this page.'));\n }\n const actions = el('div', 'actions');\n actions.append(close);\n dialog.append(actions);\n backdrop.append(dialog);\n host.root.append(backdrop);\n\n let timer: ReturnType<typeof setTimeout> | undefined;\n const dismiss = () => {\n clearTimeout(timer);\n backdrop.remove();\n };\n close.addEventListener('click', dismiss);\n backdrop.addEventListener('keydown', (event) => {\n if (event.key === 'Escape') dismiss();\n });\n close.focus();\n\n const poll = async () => {\n try {\n const context = await transport.clientContext();\n if (context.hasSession === true) {\n dismiss();\n offer(context, mode);\n return;\n }\n } catch {\n // Keep waiting; the modal stays until the login completes or it is closed.\n }\n if (backdrop.isConnected) timer = setTimeout(() => void poll(), LOGIN_POLL_MS);\n };\n timer = setTimeout(() => void poll(), LOGIN_POLL_MS);\n}\n\n/**\n * On touch screens, two quick swipes to the left across the middle of the page ask for the editor, as Alt+Shift+E\n * does on a keyboard. Passive listeners, so the page scrolls as usual and visitors pay nothing.\n */\nfunction watchSwipes(onSwipe: () => void): void {\n const gesture = new DoubleSwipe();\n const point = (touch: Touch, event: TouchEvent) => ({ x: touch.clientX, y: touch.clientY, time: event.timeStamp });\n document.addEventListener(\n 'touchstart',\n (event) => {\n const touch = event.touches[0];\n if (event.touches.length !== 1 || !touch || !swipeable(event.target)) gesture.cancel();\n else gesture.down(point(touch, event), window.innerWidth);\n },\n { passive: true },\n );\n document.addEventListener(\n 'touchend',\n (event) => {\n const touch = event.changedTouches[0];\n if (event.touches.length === 0 && touch && gesture.up(point(touch, event), window.innerWidth)) onSwipe();\n },\n { passive: true },\n );\n document.addEventListener('touchcancel', () => gesture.cancel(), { passive: true });\n}\n\n/** A swipe in a field, or in something that scrolls sideways (a carousel), belongs to it. */\nfunction swipeable(target: EventTarget | null): boolean {\n for (let node = target instanceof Element ? target : null; node; node = node.parentElement) {\n if (node.matches('input, textarea, select, [contenteditable]:not([contenteditable=\"false\"])')) return false;\n const overflow = getComputedStyle(node).overflowX;\n if ((overflow === 'auto' || overflow === 'scroll') && node.scrollWidth > node.clientWidth) return false;\n }\n return true;\n}\n\nfunction readFlag(): boolean {\n try {\n return window.localStorage.getItem(FLAG) === '1';\n } catch {\n return false;\n }\n}\n\nfunction writeFlag(): void {\n try {\n window.localStorage.setItem(FLAG, '1');\n } catch {\n // Without storage, later visits need ?edit or Alt+Shift+E.\n }\n}\n\nif (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => void boot(), { once: true });\n} else {\n void boot();\n}\n"],
5
- "mappings": "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",
6
- "names": ["DoubleSwipe", "point", "width", "fromEdge", "start", "dx", "dy", "SEARCH_POLL_MS", "SEARCH_POLLS", "DirectTransport", "baseUrl", "BaackClient", "Endpoint", "scope", "options", "entity", "owner", "result", "poll", "resolve", "complete", "urns", "entityUrns", "entry", "item", "urn", "after", "task", "imageItemUrn", "size", "file", "form", "FLAG", "LOGIN_POLL_MS", "transport", "DirectTransport", "readConfig", "started", "checking", "boot", "params", "version", "mode", "asked", "event", "check", "watchSwipes", "readFlag", "explicit", "context", "error", "offer", "openLogin", "writeFlag", "host", "createHost", "button", "createLauncher", "start", "iframeSrc", "backdrop", "el", "dialog", "title", "text", "close", "frame", "login", "actions", "timer", "dismiss", "poll", "onSwipe", "gesture", "DoubleSwipe", "point", "touch", "swipeable", "target", "node", "overflow"]
4
+ "sourcesContent": ["// The touch gesture that asks for the editor: two quick swipes to the left\n// across the middle of the screen. It starts away from both edges, where iOS\n// and Android take a swipe as Back, and is mostly horizontal, so scrolling\n// never counts. Pure, so the rules are tested without a browser.\n\n/** How far a swipe must travel, as a share of the screen's width. */\nconst MIN_DISTANCE = 0.3;\n/** The edge bands a swipe may not start in, as a share of the width. */\nconst EDGE = 0.15;\n/** The longest a single swipe may take. */\nconst MAX_SWIPE_MS = 600;\n/** The longest gap between the end of the first swipe and the start of the second. */\nconst MAX_GAP_MS = 800;\n\nexport interface TouchPoint {\n x: number;\n y: number;\n time: number;\n}\n\nexport class DoubleSwipe {\n private start: TouchPoint | undefined;\n private firstEnded: number | undefined;\n\n /** A single finger touches down; `width` is the screen's width. */\n down(point: TouchPoint, width: number): void {\n const fromEdge = point.x < width * EDGE || point.x > width * (1 - EDGE);\n if (this.firstEnded !== undefined && point.time - this.firstEnded > MAX_GAP_MS) this.firstEnded = undefined;\n this.start = fromEdge ? undefined : point;\n }\n\n /** Another finger, or a touch that cannot be a swipe (in a field, a carousel): the gesture starts again. */\n cancel(): void {\n this.start = undefined;\n this.firstEnded = undefined;\n }\n\n /** The finger lifts. True when this completes the second swipe. */\n up(point: TouchPoint, width: number): boolean {\n const start = this.start;\n this.start = undefined;\n if (!start) return false;\n const dx = point.x - start.x;\n const dy = point.y - start.y;\n const swipe = -dx >= width * MIN_DISTANCE && Math.abs(dy) <= Math.abs(dx) / 2 && point.time - start.time <= MAX_SWIPE_MS;\n if (!swipe) {\n this.firstEnded = undefined;\n return false;\n }\n if (this.firstEnded !== undefined) {\n this.firstEnded = undefined;\n return true;\n }\n this.firstEnded = point.time;\n return false;\n }\n}\n", "// The editor's only route to the API. Failures are never swallowed: a non-2xx\n// response rejects with BaackApiError (carrying the HTTP status), and a\n// network failure rejects with fetch's own error.\n\nimport { BaackClient, Endpoint, type ClientContext, type Company, type Entity, type Pagination, type Search, type Task, type TaskLog, type TaskStatus } from '@baack-software/baack-ts-client';\nimport type { TaskClient, TaskListing, TaskLogPage } from '@baack-software/baack-ts-tasks';\nimport type { ContentItem, ItemRepresentation, TypeEntry } from './registry.ts';\n\nexport interface ItemHistory<I extends ContentItem> {\n item?: I | null;\n history: I[];\n pagination?: Pagination | null;\n url: string;\n}\n\nexport interface EntityViewOptions {\n /** 'LATEST' or an ISO timestamp. Omit for the live view. Needs a session. */\n version?: string;\n /**\n * The language and variant to look up by page path, or, with a URN, to hold\n * to: the view treats a URN as a locator and may otherwise return another\n * language's entity. Read entities in scope through `ScopedViews`.\n */\n language?: string;\n variant?: string;\n}\n\n/** What is sent to create an entity. The server assigns the URN and scopes the name under the owner. */\nexport interface NewEntity {\n owner: { urn: string };\n status: 'DRAFT_UNPUBLISHED';\n /** A page path such as /blog/hello. */\n name: string;\n language: string;\n variant: string;\n}\n\n/** Entity URNs found by a search. `complete` is false when the search was still partial after polling. */\nexport interface EntitySearch {\n urns: string[];\n complete: boolean;\n}\n\n/** How often, and how many times, a partial search is read again (as the legacy client does). */\nconst SEARCH_POLL_MS = 500;\nconst SEARCH_POLLS = 10;\n\nexport interface Transport {\n clientContext(): Promise<ClientContext>;\n /** By entity URN, or by page path (`/blog/hello`) with language and variant. */\n entityView(scope: string, options?: EntityViewOptions): Promise<Entity>;\n createEntity(entity: NewEntity): Promise<Entity>;\n /** The companies the user may administer. */\n companies(): Promise<Company[]>;\n /** Every entity the owner has: the search returns them all, unfiltered, by URN. */\n searchEntities(owner: string): Promise<EntitySearch>;\n create<I extends ContentItem>(entry: TypeEntry<I>, item: ItemRepresentation<I>): Promise<I>;\n read<I extends ContentItem>(entry: TypeEntry<I>, urn: string): Promise<I>;\n update<I extends ContentItem>(entry: TypeEntry<I>, urn: string, item: ItemRepresentation<I>): Promise<I>;\n remove(entry: TypeEntry, urn: string): Promise<void>;\n /** One page of an item's versions, newest first; `after` continues from the previous page. */\n history<I extends ContentItem>(entry: TypeEntry<I>, urn: string, after?: string): Promise<ItemHistory<I>>;\n /** Makes a version live (moves the live pointer). The body is the version's item. */\n publishVersion<I extends ContentItem>(entry: TypeEntry<I>, item: I): Promise<ItemHistory<I>>;\n /** The entity's own fields and status, for the lifecycle. */\n readEntity(urn: string): Promise<Entity>;\n /** A lifecycle verb: the entity's fields with a new status. Item arrays are never sent. */\n updateEntity(urn: string, entity: EntityUpdate): Promise<Entity>;\n createTask(task: Partial<Task>): Promise<Task>;\n /** The tasks associated with an entity, every status, and the people related to it (milestone 13). */\n tasksAbout(entityUrn: string, owner: string): Promise<TaskListing>;\n /** Sets a task's status; the task as the API has it after the change. `actor` is credited in its activity. */\n setTaskStatus(taskUrn: string, status: TaskStatus, actor?: string): Promise<Task>;\n commentOnTask(taskUrn: string, comment: string, actor?: string): Promise<TaskLog>;\n /** One page of a task's activity, newest first; `after` continues from the previous page. */\n taskLogs(taskUrn: string, after?: string): Promise<TaskLogPage>;\n /** Stages one file for one slot (DEFAULT, MEDIUM or LARGE) of an image version whose slot is empty. */\n uploadImageFile(imageItemUrn: string, size: 'DEFAULT' | 'MEDIUM' | 'LARGE', file: File): Promise<unknown>;\n}\n\n/** The fields of an entity a lifecycle change sends. */\nexport interface EntityUpdate {\n owner: { urn: string };\n name: string;\n language: string;\n variant: string;\n status: 'PUBLISH' | 'DRAFT_UNPUBLISHED';\n publishTimestamp?: string;\n}\n\n/** Talks to Baack directly with the user's session cookie. */\nexport class DirectTransport implements Transport {\n private readonly client: BaackClient;\n /** The tasks core, imported on first use so it stays out of the loader (it is in editor.js). */\n private tasks: Promise<TaskClient> | undefined;\n\n constructor(baseUrl: string) {\n // No failure guard: the editor sends only when the user asks (rule 4), and a\n // retry the user asks for must reach the API, even after a 403.\n this.client = new BaackClient({ baseUrl, credentials: 'include', failureGuard: false });\n }\n\n private taskClient(): Promise<TaskClient> {\n this.tasks ??= import('@baack-software/baack-ts-tasks').then(({ TaskClient }) => new TaskClient(this.client));\n return this.tasks;\n }\n\n clientContext(): Promise<ClientContext> {\n return this.client.read<ClientContext>(Endpoint.CLIENT_CONTEXT, '');\n }\n\n entityView(scope: string, options: EntityViewOptions = {}): Promise<Entity> {\n return this.client.read<Entity>(Endpoint.ENTITY_VIEW, scope, {\n version: options.version,\n language: options.language,\n variant: options.variant,\n });\n }\n\n createEntity(entity: NewEntity): Promise<Entity> {\n return this.client.create<Entity>(Endpoint.ENTITY, entity);\n }\n\n async companies(): Promise<Company[]> {\n return (await this.client.read<{ companies?: Company[] }>(Endpoint.COMPANIES, '')).companies ?? [];\n }\n\n async searchEntities(owner: string): Promise<EntitySearch> {\n // `order: NAME` may not be implemented yet; it is harmless to ask.\n // A link is sent by URN only (writes are permissive).\n let result: Search = await this.client.search({ owner: { urn: owner } as Company, includeScopes: ['ENTITY'], order: 'NAME' });\n for (let poll = 0; result.status !== 'FULL_RESULT' && result.urn && poll < SEARCH_POLLS; poll += 1) {\n await new Promise((resolve) => setTimeout(resolve, SEARCH_POLL_MS));\n result = await this.client.searchResult(result.urn);\n }\n const complete = result.status === 'FULL_RESULT';\n const urns = entityUrns(result);\n // Later pages of a full result.\n while (complete && result.urn && result.pagination?.isMorePages && result.pagination.after) {\n result = await this.client.searchResult(result.urn, result.pagination.after);\n urns.push(...entityUrns(result));\n }\n return { urns: [...new Set(urns)], complete };\n }\n\n create<I extends ContentItem>(entry: TypeEntry<I>, item: ItemRepresentation<I>): Promise<I> {\n return this.client.create<I>(entry.endpoint, item);\n }\n\n read<I extends ContentItem>(entry: TypeEntry<I>, urn: string): Promise<I> {\n return this.client.read<I>(entry.endpoint, urn);\n }\n\n update<I extends ContentItem>(entry: TypeEntry<I>, urn: string, item: ItemRepresentation<I>): Promise<I> {\n return this.client.update<I>(entry.endpoint, urn, item);\n }\n\n async remove(entry: TypeEntry, urn: string): Promise<void> {\n await this.client.delete(entry.endpoint, urn);\n }\n\n history<I extends ContentItem>(entry: TypeEntry<I>, urn: string, after?: string): Promise<ItemHistory<I>> {\n return this.client.read<ItemHistory<I>>(entry.historyEndpoint, urn, { after });\n }\n\n publishVersion<I extends ContentItem>(entry: TypeEntry<I>, item: I): Promise<ItemHistory<I>> {\n return this.client.update<ItemHistory<I>>(entry.historyEndpoint, item.urn!, item);\n }\n\n readEntity(urn: string): Promise<Entity> {\n return this.client.read<Entity>(Endpoint.ENTITY, urn);\n }\n\n updateEntity(urn: string, entity: EntityUpdate): Promise<Entity> {\n return this.client.update<Entity>(Endpoint.ENTITY, urn, entity);\n }\n\n createTask(task: Partial<Task>): Promise<Task> {\n return this.client.create<Task>(Endpoint.TASK, task);\n }\n\n tasksAbout(entityUrn: string, owner: string): Promise<TaskListing> {\n return this.taskClient().then((tasks) => tasks.about(entityUrn, { owner, concurrency: 4 }));\n }\n\n setTaskStatus(taskUrn: string, status: TaskStatus, actor?: string): Promise<Task> {\n return this.taskClient().then((tasks) => tasks.setStatus(taskUrn, status, actor ? { actor } : {}));\n }\n\n commentOnTask(taskUrn: string, comment: string, actor?: string): Promise<TaskLog> {\n return this.taskClient().then((tasks) => tasks.comment(taskUrn, comment, actor ? { actor } : {}));\n }\n\n taskLogs(taskUrn: string, after?: string): Promise<TaskLogPage> {\n return this.taskClient().then((tasks) => tasks.logs(taskUrn, after ? { after } : {}));\n }\n\n uploadImageFile(imageItemUrn: string, size: 'DEFAULT' | 'MEDIUM' | 'LARGE', file: File): Promise<unknown> {\n const form = new FormData();\n form.append('imageItemUrn', imageItemUrn);\n form.append('imageItemSize', size);\n form.append('src', file, file.name);\n return this.client.upload(Endpoint.IMAGE_FILE, form);\n }\n}\n\nfunction entityUrns(result: Search): string[] {\n return (result.items ?? []).filter((item) => item.type === 'ENTITY' && item.urn).map((item) => item.urn);\n}\n", "// The script a site includes. Anonymous visitors cost nothing: the loader\n// makes no API call unless this browser has used the editor before, or\n// someone asks for it (`?edit`, `?version=\u2026`, Alt+Shift+E, or two swipes to\n// the left on a touch screen). With a session\n// it offers \"Edit page\", and the full editor loads only when pressed. Without\n// one, when asked, it opens a login modal that opens Baack's login in a window.\n\nimport type { ClientContext } from '@baack-software/baack-ts-client';\nimport { readConfig } from './config.ts';\nimport { DoubleSwipe } from './core/swipe.ts';\nimport { DirectTransport } from './core/transport.ts';\nimport { createHost, el, type EditorHost } from './ui/host.ts';\nimport { createLauncher } from './ui/launcher.ts';\nimport { loginLink, whenBack } from './ui/login-window.ts';\nimport type { ReadMode } from './view.ts';\nimport type { Editor } from './controller.ts';\n\ndeclare global {\n interface Window {\n baackEditorLoaded?: boolean;\n }\n}\n\n/** Set once this browser has had an editor session, so later visits check for one. */\nconst FLAG = 'baack-editor:editor';\n/** While the login modal is open, how often to check whether the login has completed. */\nconst LOGIN_POLL_MS = 2000;\n\nconst transport = new DirectTransport(readConfig().baseUrl);\nlet started = false;\nlet checking = false;\n/** The page has its own Edit buttons (`launcher: 'page'`): no floating launcher. */\nconst pageLaunch = (): boolean => window.baackEditorConfig?.launcher === 'page';\n/** With the `page` launcher: the session found before any Edit button was pressed, and the editor once started. */\nlet session: ClientContext | undefined;\nlet running: Promise<Editor> | undefined;\n\nasync function boot(): Promise<void> {\n if (window.baackEditorLoaded) return;\n window.baackEditorLoaded = true;\n\n // ?version=LATEST or ?version=<epoch ms> asks for drafts; ?edit asks for the editor.\n const params = new URLSearchParams(location.search);\n const version = params.get('version');\n const mode: ReadMode | undefined = version === 'LATEST' ? 'LATEST' : version && /^\\d+$/.test(version) ? Number(version) : undefined;\n const asked = params.has('edit') || mode !== undefined;\n\n document.addEventListener('keydown', (event) => {\n if (event.altKey && event.shiftKey && event.code === 'KeyE') {\n event.preventDefault();\n void check(true, mode);\n }\n });\n watchSwipes(() => void check(true, mode));\n // The page's own Edit buttons, wherever and whenever they are rendered: they start editing, or end it.\n document.addEventListener('click', (event) => {\n if (!pageLaunch() || !(event.target instanceof Element) || !event.target.closest('[data-baack-editor-launch]')) return;\n event.preventDefault();\n if (running) void running.then((editor) => editor.setEditMode(!editor.isEditing));\n else if (session) launch(session, mode);\n else void check(true, mode);\n });\n if (asked || readFlag()) await check(asked, mode);\n}\n\n/** Asks the API whether there is a session; offers the editor, or (when asked for) a login. */\nasync function check(explicit: boolean, mode: ReadMode | undefined): Promise<void> {\n if (started || running || checking) return;\n checking = true;\n let context: ClientContext;\n try {\n context = await transport.clientContext();\n } catch (error) {\n console.warn('Baack editor: could not read the client context; editing is unavailable.', error);\n return;\n } finally {\n checking = false;\n }\n if (context.hasSession === true) {\n offer(context, mode, explicit);\n } else if (explicit) {\n // Only a session may see drafts: without one the page stays as built.\n openLogin(context.loginIframeSrc, mode);\n }\n}\n\n/**\n * `now`: someone asked to edit (an Edit button, ?edit, Alt+Shift+E). With the `page` launcher editing then starts\n * at once; unasked, it waits for the page's Edit button.\n */\nfunction offer(context: ClientContext, mode: ReadMode | undefined, now = false): void {\n if (pageLaunch()) {\n writeFlag();\n session = context;\n if (now || mode !== undefined) launch(context, mode);\n return;\n }\n if (started) return;\n started = true;\n writeFlag();\n const host = createHost();\n const button = createLauncher(host);\n const start = async () => {\n button.disabled = true;\n const editor = await import('./editor.ts');\n editor.start({ transport, host, context, ...(mode !== undefined ? { mode } : {}) });\n };\n if (mode !== undefined) {\n void start();\n } else {\n button.addEventListener('click', () => void start(), { once: true });\n }\n}\n\n/** The `page` launcher: starts the editor, with its toolbar shown only while editing (toolbar.ts). */\nfunction launch(context: ClientContext, mode: ReadMode | undefined): void {\n if (running) return;\n const host = createHost();\n createLauncher(host).closest<HTMLElement>('.toolbar')!.hidden = true;\n running = import('./editor.ts').then((editor) =>\n editor.start({ transport, host, context, ...(mode !== undefined ? { mode } : {}) }),\n );\n}\n\n/**\n * A modal offering Baack's login in a window. Checks for the session until the login completes, and at once when the\n * page is back in front; then closes the login window and offers the editor.\n */\nfunction openLogin(iframeSrc: string | null | undefined, mode: ReadMode | undefined): void {\n const host: EditorHost = createHost();\n if (host.root.querySelector('.login-modal')) return;\n const backdrop = el('div', 'login-modal');\n const dialog = el('div', 'panel modal');\n dialog.setAttribute('role', 'dialog');\n dialog.setAttribute('aria-modal', 'true');\n dialog.setAttribute('aria-label', 'Log in to edit');\n const title = el('div', 'title', mode !== undefined ? 'Log in to see draft content' : 'Log in to edit this page');\n const text = el(\n 'p',\n 'message info',\n mode !== undefined\n ? 'This link shows draft content. Log in to Baack to see it; until then this is the live page.'\n : 'Log in with your Baack account, in the window that opens. The editor starts when you have logged in.',\n );\n const close = el('button', '', 'Close');\n close.type = 'button';\n dialog.append(title, text);\n const login = iframeSrc ? loginLink(iframeSrc) : undefined;\n if (!login) {\n dialog.append(el('p', 'message', 'The login page is not available. Log in at Baack, then reload this page.'));\n }\n const actions = el('div', 'actions');\n // shadcn/ui's dialog footer: the main action last.\n actions.append(close, ...(login ? [login.element] : []));\n dialog.append(actions);\n backdrop.append(dialog);\n host.root.append(backdrop);\n\n let timer: ReturnType<typeof setTimeout> | undefined;\n const stopWatching = whenBack(() => void poll());\n const dismiss = () => {\n clearTimeout(timer);\n stopWatching();\n backdrop.remove();\n };\n close.addEventListener('click', dismiss);\n backdrop.addEventListener('keydown', (event) => {\n if (event.key === 'Escape') dismiss();\n });\n (login?.element ?? close).focus();\n\n let polling = false;\n async function poll(): Promise<void> {\n if (polling || !backdrop.isConnected) return;\n polling = true;\n clearTimeout(timer);\n try {\n const context = await transport.clientContext();\n if (context.hasSession === true) {\n login?.close();\n dismiss();\n offer(context, mode, true);\n return;\n }\n } catch {\n // Keep waiting; the modal stays until the login completes or it is closed.\n } finally {\n polling = false;\n }\n if (backdrop.isConnected) timer = setTimeout(() => void poll(), LOGIN_POLL_MS);\n }\n timer = setTimeout(() => void poll(), LOGIN_POLL_MS);\n}\n\n/**\n * On touch screens, two quick swipes to the left across the middle of the page ask for the editor, as Alt+Shift+E\n * does on a keyboard. Passive listeners, so the page scrolls as usual and visitors pay nothing.\n */\nfunction watchSwipes(onSwipe: () => void): void {\n const gesture = new DoubleSwipe();\n const point = (touch: Touch, event: TouchEvent) => ({ x: touch.clientX, y: touch.clientY, time: event.timeStamp });\n document.addEventListener(\n 'touchstart',\n (event) => {\n const touch = event.touches[0];\n if (event.touches.length !== 1 || !touch || !swipeable(event.target)) gesture.cancel();\n else gesture.down(point(touch, event), window.innerWidth);\n },\n { passive: true },\n );\n document.addEventListener(\n 'touchend',\n (event) => {\n const touch = event.changedTouches[0];\n if (event.touches.length === 0 && touch && gesture.up(point(touch, event), window.innerWidth)) onSwipe();\n },\n { passive: true },\n );\n document.addEventListener('touchcancel', () => gesture.cancel(), { passive: true });\n}\n\n/** A swipe in a field, or in something that scrolls sideways (a carousel), belongs to it. */\nfunction swipeable(target: EventTarget | null): boolean {\n for (let node = target instanceof Element ? target : null; node; node = node.parentElement) {\n if (node.matches('input, textarea, select, [contenteditable]:not([contenteditable=\"false\"])')) return false;\n const overflow = getComputedStyle(node).overflowX;\n if ((overflow === 'auto' || overflow === 'scroll') && node.scrollWidth > node.clientWidth) return false;\n }\n return true;\n}\n\nfunction readFlag(): boolean {\n try {\n return window.localStorage.getItem(FLAG) === '1';\n } catch {\n return false;\n }\n}\n\nfunction writeFlag(): void {\n try {\n window.localStorage.setItem(FLAG, '1');\n } catch {\n // Without storage, later visits need ?edit or Alt+Shift+E.\n }\n}\n\nif (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => void boot(), { once: true });\n} else {\n void boot();\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["DoubleSwipe", "point", "width", "fromEdge", "start", "dx", "dy", "SEARCH_POLL_MS", "SEARCH_POLLS", "DirectTransport", "baseUrl", "BaackClient", "TaskClient", "Endpoint", "scope", "options", "entity", "owner", "result", "poll", "resolve", "complete", "urns", "entityUrns", "entry", "item", "urn", "after", "task", "entityUrn", "tasks", "taskUrn", "status", "actor", "comment", "imageItemUrn", "size", "file", "form", "FLAG", "LOGIN_POLL_MS", "transport", "DirectTransport", "readConfig", "started", "checking", "pageLaunch", "session", "running", "boot", "params", "version", "mode", "asked", "event", "check", "watchSwipes", "editor", "launch", "readFlag", "explicit", "context", "error", "offer", "openLogin", "now", "writeFlag", "host", "createHost", "button", "createLauncher", "start", "iframeSrc", "backdrop", "el", "dialog", "title", "text", "close", "login", "loginLink", "actions", "timer", "stopWatching", "whenBack", "poll", "dismiss", "polling", "onSwipe", "gesture", "DoubleSwipe", "point", "touch", "swipeable", "target", "node", "overflow"]
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@baack-software/baack-ts-editor",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Edit Baack content in place, on your own site's pages",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -29,8 +29,9 @@
29
29
  "prepublishOnly": "node build.mjs --minify"
30
30
  },
31
31
  "dependencies": {
32
- "@baack-software/baack-ts-client": "^0.0.2",
33
- "@baack-software/baack-ts-components": "^0.0.1"
32
+ "@baack-software/baack-ts-client": "^0.0.12",
33
+ "@baack-software/baack-ts-components": "^0.0.3",
34
+ "@baack-software/baack-ts-tasks": "^0.0.6"
34
35
  },
35
36
  "devDependencies": {
36
37
  "@playwright/test": "~1.63.0",
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/config.ts", "../../src/ui/icons.ts", "../../src/ui/theme.ts", "../../src/ui/host.ts", "../../src/ui/launcher.ts", "../../node_modules/@baack-software/baack-ts-client/src/endpoints.ts", "../../node_modules/@baack-software/baack-ts-client/src/errors.ts", "../../node_modules/@baack-software/baack-ts-client/src/client.ts"],
4
- "sourcesContent": ["import type { AfterPublish } from './after-publish.ts';\nimport type { EntityNameForPage, PagePathForEntity } from './page-entity.ts';\n\n// Site configuration, set before the loader runs:\n// <script>window.baackEditorConfig = { baseUrl: 'https://api.baack.co' }</script>\n\nexport interface EditorConfig {\n /** Where the Baack API is. Requests carry the user's session cookie. */\n baseUrl: string;\n /** How long a publish may stay in progress before the editor says it is taking too long. Default 5 minutes. */\n publishTimeoutMs?: number;\n /** Image slots a version needs before its entity can be published. Default: the default slot only. */\n requiredImageSlots?: ('DEFAULT' | 'MEDIUM' | 'LARGE')[];\n /**\n * Off by default. Widths to create the medium and large files at, in the\n * browser, when only a default file is chosen. Never scales up.\n */\n imageWidths?: { MEDIUM?: number; LARGE?: number };\n /** Largest image file the editor will upload, in bytes. Default 10 MB. */\n maxImageBytes?: number;\n /** Above this size the editor warns that the file is large for the web. Default 2 MB. */\n warnImageBytes?: number;\n /**\n * After every publish (an entity, or a version from History): a message to\n * show, such as \"Live after the next build, in about 2 minutes\", or a\n * function called with what was published, which may return a message.\n * Fixed text also replaces the note about built sites on leaving edit mode.\n */\n afterPublish?: AfterPublish;\n /**\n * The entity name that serves a page whose markup names no entity, from\n * the page's URL path: used to find its content, and offered when creating\n * it. Baack has no index entities, so a directory path needs a name of its\n * own. Default: `/` (and `/index.html`) is `/home`, otherwise the path\n * without a trailing slash.\n */\n entityNameForPage?: EntityNameForPage;\n /**\n * The reverse: the page address an entity serves, from its name. Used for\n * a link's suggestions and the `path` told to `afterPublish`. Default:\n * `/home` is `/`, any other name its own path.\n */\n pagePathForEntity?: PagePathForEntity;\n}\n\ndeclare global {\n interface Window {\n baackEditorConfig?: Partial<EditorConfig>;\n }\n}\n\n/**\n * The configuration, from `window.baackEditorConfig`, or (for sites whose\n * Content-Security-Policy forbids inline scripts) a meta tag:\n * <meta name=\"baack-editor-api\" content=\"https://api.baack.co\">\n */\nexport function readConfig(): EditorConfig {\n const meta = document.querySelector<HTMLMetaElement>('meta[name=\"baack-editor-api\"]')?.content;\n return { ...window.baackEditorConfig, baseUrl: window.baackEditorConfig?.baseUrl ?? meta ?? 'https://api.baack.co' };\n}\n", "// The editor's icons: a few from lucide (https://lucide.dev, ISC licence),\n// copied as shapes and drawn with DOM calls, so there is no package and no\n// markup parsed. 24 by 24, stroked in currentColor, as lucide draws them.\n\ntype Shape = [tag: 'path' | 'circle' | 'rect', attributes: Record<string, string | number>];\n\nconst path = (d: string): Shape => ['path', { d }];\nconst dot = (cx: number, cy: number): Shape => ['circle', { cx, cy, r: 1 }];\n\nconst ICONS = {\n 'arrow-down': [path('M12 5v14'), path('m19 12-7 7-7-7')],\n 'arrow-up': [path('m5 12 7-7 7 7'), path('M12 19V5')],\n check: [path('M20 6 9 17l-5-5')],\n 'chevron-down': [path('m6 9 6 6 6-6')],\n 'chevron-left': [path('m15 18-6-6 6-6')],\n 'chevron-right': [path('m9 18 6-6-6-6')],\n ellipsis: [dot(12, 12), dot(19, 12), dot(5, 12)],\n 'grip-vertical': [dot(9, 12), dot(9, 5), dot(9, 19), dot(15, 12), dot(15, 5), dot(15, 19)],\n history: [path('M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8'), path('M3 3v5h5'), path('M12 7v5l4 2')],\n 'list-ordered': [path('M10 12h11'), path('M10 18h11'), path('M10 6h11'), path('M4 10h2'), path('M4 6h1v4'), path('M6 18H4c0-1 2-2 2-3s-1-1.5-2-1')],\n 'panel-left': [['rect', { width: 18, height: 18, x: 3, y: 3, rx: 2 }], path('M9 3v18')],\n 'panel-right': [['rect', { width: 18, height: 18, x: 3, y: 3, rx: 2 }], path('M15 3v18')],\n 'pencil-line': [path('M13 21h8'), path('m15 5 4 4'), path('M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z')],\n plus: [path('M5 12h14'), path('M12 5v14')],\n search: [['circle', { cx: 11, cy: 11, r: 8 }], path('m21 21-4.3-4.3')],\n x: [path('M18 6 6 18'), path('m6 6 12 12')],\n} satisfies Record<string, Shape[]>;\n\nexport type IconName = keyof typeof ICONS;\n\nconst SVG = 'http://www.w3.org/2000/svg';\n\n/** An icon, hidden from assistive technology: the control it sits in carries the name. */\nexport function icon(name: IconName): SVGSVGElement {\n const svg = document.createElementNS(SVG, 'svg');\n for (const [attribute, value] of Object.entries({\n viewBox: '0 0 24 24',\n width: 16,\n height: 16,\n fill: 'none',\n stroke: 'currentColor',\n 'stroke-width': 2,\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n 'aria-hidden': 'true',\n focusable: 'false',\n class: `icon icon-${name}`,\n })) {\n svg.setAttribute(attribute, String(value));\n }\n for (const [tag, attributes] of ICONS[name] as Shape[]) {\n const shape = document.createElementNS(SVG, tag);\n for (const [attribute, value] of Object.entries(attributes)) shape.setAttribute(attribute, String(value));\n svg.append(shape);\n }\n return svg;\n}\n", "// The Baack look (docs/plans/baack-look.md): www.baack.co's shadcn/ui tokens\n// as CSS custom properties on the editor's shadow host, light and dark. The\n// editor's stylesheet (host.ts) uses only these: no literal colours.\n//\n// Sizes are in px, not rem: rem follows the host page's root font size.\n\nconst LIGHT = {\n background: '#ffffff',\n foreground: '#111827',\n popover: '#ffffff',\n 'popover-foreground': '#111827',\n primary: '#1d4ed8',\n 'primary-foreground': '#ffffff',\n secondary: '#f6f7f9',\n 'secondary-foreground': '#111827',\n muted: '#f6f7f9',\n 'muted-foreground': '#4b5563',\n accent: '#f3f4f6',\n 'accent-foreground': '#111827',\n destructive: '#dc2626',\n // Red text on a red tint (destructive buttons, badges): darker, for 4.5:1 contrast.\n 'destructive-text': '#b91c1c',\n border: '#e5e7eb',\n input: '#d1d5db',\n ring: '#1d4ed8',\n success: '#047857',\n // The site's amber tone: unsaved and pending work.\n warning: '#92400e',\n // The site's violet tone: previews of other versions.\n preview: '#6d28d9',\n overlay: 'rgb(0 0 0 / 0.5)',\n 'shadow-color': 'rgb(17 24 39 / 0.1)',\n // The outline button and fields: shadcn's light and dark treatments differ.\n 'control-background': 'var(--background)',\n 'control-border': 'var(--border)',\n 'control-hover': 'var(--muted)',\n 'field-background': 'transparent',\n 'switch-off': 'var(--input)',\n 'switch-thumb-off': 'var(--background)',\n 'switch-thumb-on': 'var(--background)',\n};\n\nconst DARK: typeof LIGHT = {\n background: '#0d1117',\n foreground: '#f3f4f6',\n popover: '#151b24',\n 'popover-foreground': '#f3f4f6',\n primary: '#6b9bff',\n 'primary-foreground': '#0d1117',\n secondary: '#1c2430',\n 'secondary-foreground': '#f3f4f6',\n muted: '#11161e',\n 'muted-foreground': '#a1a9b6',\n accent: '#1f2733',\n 'accent-foreground': '#f3f4f6',\n destructive: '#f87171',\n 'destructive-text': '#f87171',\n border: '#262e3a',\n input: '#343e4d',\n ring: '#6b9bff',\n success: '#34d399',\n warning: '#fbbf24',\n preview: '#c4b5fd',\n overlay: 'rgb(0 0 0 / 0.65)',\n 'shadow-color': 'rgb(0 0 0 / 0.45)',\n 'control-background': 'color-mix(in oklab, var(--input) 30%, transparent)',\n 'control-border': 'var(--input)',\n 'control-hover': 'color-mix(in oklab, var(--input) 50%, transparent)',\n 'field-background': 'color-mix(in oklab, var(--input) 30%, transparent)',\n 'switch-off': 'color-mix(in oklab, var(--input) 80%, transparent)',\n 'switch-thumb-off': 'var(--foreground)',\n 'switch-thumb-on': 'var(--primary-foreground)',\n};\n\nconst SCALE = `\n /* Installed fonts only, so no site needs to allow a font source in its Content Security Policy. Figtree when the\n page has loaded it (www.baack.co), then the nearest match on Apple devices, then each platform's UI font. */\n --font-sans: Figtree, \"Avenir Next\", \"Segoe UI Variable Text\", \"Segoe UI\", Roboto, system-ui, -apple-system, sans-serif;\n --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n --text-xs: 12px;\n --text-sm: 14px;\n --text-base: 16px;\n --radius: 10px;\n --radius-sm: 6px;\n --radius-md: 8px;\n --radius-lg: 10px;\n --radius-xl: 14px;\n --shadow-sm: 0 1px 2px 0 var(--shadow-color);\n --shadow-md: 0 4px 6px -1px var(--shadow-color), 0 2px 4px -2px var(--shadow-color);\n --shadow-lg: 0 10px 15px -3px var(--shadow-color), 0 4px 6px -4px var(--shadow-color);\n /* shadcn's ring-3 ring-ring/50. */\n --focus-ring: 0 0 0 3px color-mix(in oklab, var(--ring) 50%, transparent);\n`;\n\nfunction tokens(set: typeof LIGHT): string {\n return Object.entries(set)\n .map(([name, value]) => ` --${name}: ${value};`)\n .join('\\n');\n}\n\n/** The tokens, for the start of the shadow root's stylesheet. Dark follows the visitor's system setting. */\nexport const THEME_CSS = `\n:host {\n color-scheme: light;\n${tokens(LIGHT)}\n${SCALE}\n}\n@media (prefers-color-scheme: dark) {\n :host {\n color-scheme: dark;\n${tokens(DARK)}\n }\n}\n/* Baack's login page does not follow the dark setting yet, so the login stays light: the modal around it, and the\n frame wherever it is (an iframe's color-scheme is the one its page sees). */\n.login-modal {\n color-scheme: light;\n${tokens(LIGHT)}\n}\n.login iframe { color-scheme: light; background: ${LIGHT.background}; }\n`;\n\n/**\n * Outline colours for elements on the host page, outside the shadow root: each is light-dark(), so it follows the\n * page's own color-scheme, with the light colour for browsers without light-dark().\n */\nexport const PAGE_COLOURS = {\n primary: [LIGHT.primary, DARK.primary],\n warning: ['#b45309', DARK.warning],\n preview: ['#7c3aed', DARK.preview],\n success: [LIGHT.success, DARK.success],\n muted: ['#6b7280', '#9ca3af'],\n} as const satisfies Record<string, readonly [string, string]>;\n", "// The editor's own corner of the page: one element with a shadow root, so\n// host page CSS cannot reach the editor UI and the editor's CSS cannot leak.\n\nimport { icon } from './icons.ts';\nimport { THEME_CSS } from './theme.ts';\n\nconst HOST_ID = 'baack-editor-host';\n\nconst STYLES = `${THEME_CSS}\n:host { all: initial; }\n* { box-sizing: border-box; }\n.panel {\n position: fixed; z-index: 2147483647;\n font: var(--text-sm)/1.45 var(--font-sans);\n color: var(--popover-foreground); background: var(--popover);\n border: 1px solid var(--border); border-radius: var(--radius-lg);\n box-shadow: var(--shadow-md);\n padding: 10px 12px;\n}\n.toolbar { right: 16px; bottom: 16px; display: flex; flex-direction: column; gap: 6px; max-width: 380px; }\n/* A row wraps whole buttons onto the next line rather than squeezing their labels. */\n.toolbar-row { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; }\n.toolbar-row > button, .toolbar-row > .brand { white-space: nowrap; }\n.toolbar > .message-action { align-self: flex-start; }\n.debug { font-size: var(--text-xs); color: var(--muted-foreground); }\n.debug summary { cursor: pointer; }\n.debug pre { margin: 4px 0 0; padding: 6px; max-height: 12em; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; background: var(--muted); border-radius: var(--radius-sm); font: 11px/1.45 var(--font-mono); }\n.brand { font-weight: 700; letter-spacing: -0.01em; }\n.pending { color: var(--warning); }\n/* Buttons: shadcn/ui's variants and sizes (button() in this module). With no class, the outline variant. */\nbutton {\n display: inline-flex; align-items: center; justify-content: center; gap: 6px; flex-shrink: 0;\n font: 500 var(--text-sm)/1.25 var(--font-sans); color: var(--foreground); cursor: pointer; user-select: none;\n min-height: 32px; padding: 4px 10px; border-radius: var(--radius-lg);\n border: 1px solid var(--control-border); background: var(--control-background); background-clip: padding-box;\n transition: background-color 120ms, border-color 120ms, box-shadow 120ms, color 120ms;\n}\nbutton:enabled:hover { background-color: var(--control-hover); color: var(--foreground); }\nbutton:active:not([aria-haspopup]) { transform: translateY(1px); }\nbutton:focus-visible { outline: none; border-color: var(--ring); box-shadow: var(--focus-ring); }\n/* Not pointer-events: none, so the title saying why it is disabled still shows on hover. */\nbutton:disabled { opacity: 0.5; cursor: default; }\nbutton svg { flex-shrink: 0; width: 16px; height: 16px; pointer-events: none; }\nbutton.primary { background: var(--primary); border-color: transparent; color: var(--primary-foreground); }\nbutton.primary:enabled:hover { background: color-mix(in oklab, var(--primary) 80%, transparent); }\nbutton.secondary { background: var(--secondary); border-color: transparent; color: var(--secondary-foreground); }\nbutton.secondary:enabled:hover { background: color-mix(in oklab, var(--secondary), var(--foreground) 5%); }\nbutton.ghost { background: transparent; border-color: transparent; }\nbutton.ghost:enabled:hover { background: var(--control-hover); }\nbutton.destructive { background: color-mix(in oklab, var(--destructive) 10%, transparent); border-color: transparent; color: var(--destructive-text); }\nbutton.destructive:enabled:hover { background: color-mix(in oklab, var(--destructive) 20%, transparent); }\nbutton.destructive:focus-visible { border-color: color-mix(in oklab, var(--destructive) 40%, transparent); box-shadow: 0 0 0 3px color-mix(in oklab, var(--destructive) 20%, transparent); }\n/* A link inside text: no box, so it sits in the line. */\nbutton.link { display: inline; min-height: 0; padding: 0; border: 0; border-radius: var(--radius-sm); background: none; font: inherit; color: var(--primary); text-decoration: underline; text-underline-offset: 4px; }\nbutton.link:hover { background: none; text-decoration-thickness: 2px; }\nbutton.link:active { transform: none; }\nbutton.xs { min-height: 24px; gap: 4px; padding: 2px 8px; font-size: var(--text-xs); border-radius: var(--radius-md); }\nbutton.xs svg { width: 12px; height: 12px; }\nbutton.sm { min-height: 28px; gap: 4px; padding: 2px 10px; font-size: 12.8px; border-radius: var(--radius-md); }\nbutton.sm svg { width: 14px; height: 14px; }\nbutton.lg { min-height: 36px; }\nbutton.icon, button.icon-sm, button.icon-xs { padding: 0; width: 32px; height: 32px; min-height: 0; }\nbutton.icon-sm { width: 28px; height: 28px; border-radius: var(--radius-md); }\nbutton.icon-xs { width: 24px; height: 24px; border-radius: var(--radius-md); }\nbutton.icon-xs svg { width: 12px; height: 12px; }\n/* A toggle that is on, such as the live-page preview. */\nbutton[aria-pressed=\"true\"] { background: color-mix(in oklab, var(--primary) 10%, var(--control-background)); border-color: color-mix(in oklab, var(--primary) 60%, transparent); color: var(--primary); }\n/* Messages: shadcn/ui's alert. The colour is in the border and tint; the text keeps full contrast. */\n.message:empty, .issues:empty, .error:empty { display: none; }\np.message, p.error, .issues {\n margin: 0; padding: 8px 12px; border-radius: var(--radius-lg); font-size: var(--text-sm); color: var(--foreground);\n border: 1px solid color-mix(in oklab, var(--destructive) 50%, transparent);\n background: color-mix(in oklab, var(--destructive) 5%, var(--popover));\n}\np.message.info { border-color: var(--border); background: var(--popover); }\n.issues { padding-left: 12px; border-color: color-mix(in oklab, var(--warning) 45%, transparent); background: color-mix(in oklab, var(--warning) 6%, var(--popover)); }\nul.issues { padding-left: 28px; }\n.issues li::marker { color: var(--warning); }\n.issues li.error::marker { color: var(--destructive); }\n.issues li { color: var(--foreground); }\n/* A status line beside a field or in a list: coloured text, not a box. */\nspan.error, .hint.error { color: var(--destructive-text); }\n.login iframe { display: block; width: 100%; height: 140px; border: 1px solid var(--border); border-radius: var(--radius-md); }\n.panel:not(.popover) { z-index: 2147483646; }\n/* Below the editor's panels, so a popover or the sidebar covers the tags. */\n.order-tag {\n position: fixed; z-index: 2147483645; min-height: 0;\n font: 500 var(--text-xs)/1.3 var(--font-sans); color: var(--foreground);\n padding: 2px 8px; border-radius: 999px; border: 1px solid var(--border);\n background: color-mix(in oklab, var(--background) 94%, transparent); box-shadow: var(--shadow-sm);\n}\n.popover { width: max-content; min-width: 240px; max-width: 360px; display: flex; flex-direction: column; gap: 10px; padding: 12px 14px; }\n.popover.wide { width: min(560px, calc(100vw - 16px)); max-width: none; }\n/* Never taller than the window: what doesn't fit scrolls inside the panel, on its background. */\n.popover { max-height: calc(100vh - 16px); overflow-y: auto; }\n.form:empty { display: none; }\n.form { display: flex; flex-direction: column; gap: 10px; }\n.field { display: flex; flex-direction: column; gap: 6px; }\n.field.checkbox { flex-direction: row; align-items: center; gap: 8px; cursor: pointer; }\n/* A field's label: shadcn/ui's label. A caption over a value shown read-only stays small and muted. */\n.caption { color: var(--muted-foreground); font-size: var(--text-xs); font-weight: 500; }\n.field > .caption, .field.checkbox .caption { color: var(--foreground); font-size: var(--text-sm); line-height: 1; }\n/* Fields: shadcn/ui's input, textarea and select. */\ninput, textarea, select {\n font: var(--text-sm)/1.4 var(--font-sans); color: var(--foreground); background: var(--field-background);\n min-width: 0; min-height: 32px; border: 1px solid var(--input); border-radius: var(--radius-lg); padding: 4px 10px;\n transition: border-color 120ms, box-shadow 120ms;\n}\nselect { cursor: pointer; }\ninput:focus-visible, textarea:focus-visible, select:focus-visible { outline: none; border-color: var(--ring); box-shadow: var(--focus-ring); }\ninput::placeholder, textarea::placeholder { color: var(--muted-foreground); }\ninput:disabled, textarea:disabled, select:disabled { opacity: 0.5; cursor: not-allowed; background: color-mix(in oklab, var(--input) 50%, transparent); }\n[aria-invalid=\"true\"] { border-color: var(--destructive); box-shadow: 0 0 0 3px color-mix(in oklab, var(--destructive) 20%, transparent); }\ntextarea { resize: vertical; width: 100%; }\ninput[type=\"file\"] { min-height: 0; padding: 4px; }\ninput[type=\"file\"]::file-selector-button {\n font: 500 var(--text-sm) var(--font-sans); color: var(--foreground); cursor: pointer; margin-right: 8px;\n padding: 3px 10px; border: 1px solid var(--control-border); border-radius: var(--radius-md); background: var(--control-background);\n}\ninput[type=\"checkbox\"]:not(.switch) { min-height: 0; accent-color: var(--primary); }\n/* A switch: a checkbox with role=\"switch\", drawn as shadcn/ui's. */\ninput.switch {\n appearance: none; position: relative; flex-shrink: 0; cursor: pointer; margin: 0; padding: 0;\n width: 32px; height: 18.4px; min-height: 0; border-radius: 999px; border: 1px solid transparent; background: var(--switch-off);\n transition: background-color 150ms, box-shadow 120ms;\n}\ninput.switch::before {\n content: \"\"; position: absolute; top: 50%; left: 0; width: 16px; height: 16px; border-radius: 999px;\n background: var(--switch-thumb-off); transform: translate(0, -50%); transition: transform 150ms;\n}\ninput.switch:checked { background: var(--primary); }\ninput.switch:checked::before { background: var(--switch-thumb-on); transform: translate(14px, -50%); }\n/* iOS Safari zooms into a field under 16px as it takes focus. */\n@media (max-width: 767px) { input, textarea, select { font-size: var(--text-base); } }\n/* The entity picker: a combo box with its list below. */\n.entity-picker { display: flex; flex-direction: column; gap: 4px; }\n/* shadcn/ui's combobox (its Command list): a search field, then the options. */\n.combobox-field, .select-box { position: relative; display: flex; flex-direction: column; }\n.combobox-field > svg, .select-box > svg { position: absolute; top: 50%; transform: translateY(-50%); pointer-events: none; color: var(--muted-foreground); }\n.combobox-field > svg:first-child { left: 10px; }\n.combobox-field > svg:last-child, .select-box > svg { right: 10px; }\n.combobox-field > input { padding-left: 32px; padding-right: 32px; }\n.select-box > select { appearance: none; padding-right: 32px; }\n.entity-options { list-style: none; margin: 0; padding: 4px; border: 1px solid var(--border); border-radius: var(--radius-lg); max-height: 220px; overflow: auto; background: var(--popover); box-shadow: var(--shadow-md); }\n.entity-option { display: flex; align-items: center; gap: 6px; padding: 6px 8px; border-radius: var(--radius-sm); cursor: pointer; overflow-wrap: anywhere; }\n.entity-option > .meta { flex: 1 1 auto; }\n.entity-option > svg { flex-shrink: 0; }\n.entity-option:hover, .entity-option.active { background: var(--accent); color: var(--accent-foreground); }\n.entity-option[aria-selected=\"true\"] { font-weight: 500; }\n.entity-option.create { margin-top: 4px; border-top: 1px solid var(--border); border-radius: 0 0 var(--radius-sm) var(--radius-sm); }\n.entity-create:empty { display: none; }\n.entity-create { border-top: 1px solid var(--border); padding-top: 6px; }\n.field.code textarea { font: var(--text-xs)/1.45 var(--font-mono); }\n.hint:empty { display: none; }\n.popover[hidden], .login[hidden], .pending[hidden], [hidden] { display: none !important; }\n.login-modal { position: fixed; inset: 0; z-index: 2147483647; background: var(--overlay); display: grid; place-items: center; }\n/* shadcn/ui's dialog: a larger title, a muted description, then the frame and its button. */\n.panel.modal { position: static; width: min(440px, calc(100vw - 32px)); display: flex; flex-direction: column; gap: 16px; padding: 24px; border-radius: var(--radius-xl); box-shadow: var(--shadow-lg); }\n.modal > .title { font-size: 18px; line-height: 1.2; letter-spacing: -0.01em; }\n.modal > p.message.info { padding: 0; border: 0; background: none; color: var(--muted-foreground); margin-top: -8px; }\n.modal .login iframe { height: 320px; }\n.pending { color: var(--warning); border-color: color-mix(in oklab, var(--warning) 40%, transparent); background: color-mix(in oklab, var(--warning) 10%, var(--background)); }\n.rows { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }\n.row.problem { color: var(--destructive); }\n.simple-form { display: flex; flex-direction: column; gap: 10px; }\n/* Values shown read-only, such as both sides of a conflict: line breaks kept, long lines wrapped, and a long value\n scrolls in its box rather than running past the panel or the window. */\n.snippet { font: var(--text-xs)/1.45 var(--font-mono); background: var(--muted); border-radius: var(--radius-sm); padding: 6px 8px; white-space: pre-wrap; overflow-wrap: anywhere; max-height: 14em; overflow: auto; margin: 0; }\n/* shadcn/ui's dropdown menu (src/ui/menu.ts). */\n.menu {\n position: fixed; z-index: 2147483647; display: flex; flex-direction: column; padding: 4px;\n background: var(--popover); color: var(--popover-foreground);\n border: 1px solid var(--border); border-radius: var(--radius-lg); box-shadow: var(--shadow-md);\n animation: baack-fade-in 100ms ease-out;\n}\n.menu-item {\n justify-content: flex-start; gap: 8px; width: 100%; min-height: 0; padding: 6px 8px;\n border: 0; border-radius: var(--radius-sm); background: none; font-weight: 400; white-space: nowrap;\n}\n.menu-item svg { color: var(--muted-foreground); }\n.menu-item:enabled:hover, .menu-item:focus-visible { background: var(--accent); color: var(--accent-foreground); box-shadow: none; }\n.menu-item:active { transform: none; }\n.menu-item.destructive { color: var(--destructive-text); }\n.menu-item.destructive svg { color: currentColor; }\n/* shadcn/ui's tooltip (src/ui/tooltip.ts). */\n.tooltip {\n position: fixed; z-index: 2147483647; pointer-events: none; max-width: 280px; width: max-content;\n padding: 6px 12px; border-radius: var(--radius-md); background: var(--foreground); color: var(--background);\n font: var(--text-xs)/1.35 var(--font-sans); animation: baack-fade-in 100ms ease-out;\n}\n@keyframes baack-fade-in { from { opacity: 0; transform: scale(0.96); } }\n@media (prefers-reduced-motion: reduce) { .menu, .tooltip { animation: none; } }\n/* The sidebar (src/ui/stack.ts), docked right (or left); a bottom sheet on narrow screens. */\n.side { top: 16px; right: 16px; bottom: 16px; width: min(420px, calc(100vw - 32px)); display: flex; flex-direction: column; gap: 8px; padding: 12px 14px; box-shadow: var(--shadow-lg); }\n.side[data-side=\"left\"] { right: auto; left: 16px; }\n/* shadcn/ui's sheet, floating clear of the page's edge: it slides in from its side, or up from the bottom. */\n.side.stack:not([hidden]) { animation: baack-sheet-right 200ms ease-out; }\n.side.stack[data-side=\"left\"]:not([hidden]) { animation-name: baack-sheet-left; }\n@keyframes baack-sheet-right { from { transform: translateX(calc(100% + 16px)); } }\n@keyframes baack-sheet-left { from { transform: translateX(calc(-100% - 16px)); } }\n@keyframes baack-sheet-up { from { transform: translateY(calc(100% + 16px)); } }\n@media (prefers-reduced-motion: reduce) { .side.stack:not([hidden]) { animation: none !important; } }\n.side-dock { white-space: nowrap; }\n@media (max-width: 640px) {\n .side.stack, .side.stack[data-side=\"left\"] { top: auto; left: 8px; right: 8px; width: auto; max-height: 60vh; border-radius: var(--radius-xl) var(--radius-xl) var(--radius-md) var(--radius-md); }\n .side.stack.item-editing { display: none; }\n /* A sheet has no side to dock on. */\n .side.stack .side-dock { display: none; }\n .side.stack:not([hidden]), .side.stack[data-side=\"left\"]:not([hidden]) { animation-name: baack-sheet-up; }\n /* The sheet's grip, as on a drawer. */\n .side.stack::before { content: \"\"; align-self: center; flex-shrink: 0; width: 40px; height: 4px; margin: -4px 0 2px; border-radius: 999px; background: var(--muted-foreground); opacity: 0.35; }\n}\n/* The sidebar stack (src/ui/stack.ts): Back, breadcrumb, title, then the level on screen. */\n.stack { gap: 4px; }\n/* The toolbar's entity: its title, then its state and unsaved count, as one line of text that wraps. */\n.entity-label { display: block; width: 100%; padding: 6px 10px; text-align: left; font-weight: 400; line-height: 1.4; }\n.level-lifecycle { display: flex; flex-direction: column; gap: 6px; margin-bottom: 6px; }\n.level-entity, .level-slots, .level-page { display: flex; flex-direction: column; gap: 8px; }\n.stack-bar { display: flex; align-items: center; gap: 6px; }\n.stack-spacer { flex: 1 1 auto; }\n.stack-back { max-width: 60%; padding-left: 4px; }\n.stack-back-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.stack-close { white-space: nowrap; }\n.stack-crumbs ol { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 6px; list-style: none; margin: 0; padding: 0; font-size: var(--text-xs); color: var(--muted-foreground); overflow-wrap: anywhere; }\n.stack-crumbs li { display: inline-flex; align-items: center; }\n.stack-crumb-separator svg { width: 14px; height: 14px; }\n.stack-crumb { border: none; background: none; min-height: 0; padding: 0; font-size: inherit; font-weight: 400; color: var(--muted-foreground); }\n.stack-crumb:hover { background: none; color: var(--foreground); }\n.stack-crumb:active { transform: none; }\n.stack-crumbs [aria-current=\"page\"] { color: var(--foreground); }\n.stack-title { margin: 0; font-size: var(--text-base); font-weight: 600; outline: none; }\n.stack-body { position: relative; flex: 1 1 auto; min-height: 0; overflow-y: auto; overflow-x: hidden; border-top: 1px solid var(--border); padding-top: 8px; }\n.stack-level.leaving { position: absolute; left: 0; right: 0; background: var(--popover); z-index: 1; }\n.stack-row { display: flex; justify-content: flex-start; align-items: center; gap: 8px; width: 100%; text-align: left; margin: 0 0 4px; padding: 8px 10px; background: var(--popover); box-shadow: none; }\n.stack-row-text { display: flex; flex-direction: column; flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }\n.stack-row-label { font-weight: 600; }\n.stack-chevron { display: inline-flex; color: var(--muted-foreground); }\n.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; margin: 0; padding: 0; }\n.tab-panel { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; gap: 8px; }\n.tab-panel > .outline-body { flex: 1 1 auto; min-height: 0; }\n.outline-header { display: flex; justify-content: space-between; align-items: center; }\n.outline-body { overflow: auto; display: flex; flex-direction: column; gap: 14px; }\n.section-title { margin: 0; font-size: var(--text-sm); }\n.outline-group { display: flex; flex-direction: column; gap: 4px; margin-top: 8px; }\n.group-heading { display: flex; justify-content: space-between; align-items: center; color: var(--foreground); font-size: var(--text-xs); font-weight: 600; border-bottom: 1px solid var(--border); padding-bottom: 4px; }\n.group-hint { margin: 0; color: var(--muted-foreground); font-size: var(--text-xs); }\n.section-status { margin: 2px 0 0; color: var(--muted-foreground); font-size: var(--text-xs); }\n.status.note { background: var(--secondary); color: var(--secondary-foreground); }\n.outline .row { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 6px; padding: 3px 0; border-bottom: 1px solid var(--border); }\n.row-main { flex: 1 1 auto; display: flex; flex-wrap: wrap; justify-content: flex-start; align-items: baseline; gap: 4px; text-align: left; border: 0; background: none; padding: 2px 6px; font-weight: 400; }\nbutton.row-main { min-height: 0; }\nbutton.row-main:hover { background: var(--accent); }\nbutton.row-main:active, .stack-row:active { transform: none; }\n.row-name { font-weight: 600; }\n.row-name.link { font-weight: 600; }\n.outline .row.current { background: color-mix(in oklab, var(--primary) 8%, var(--popover)); box-shadow: inset 3px 0 0 var(--primary); }\n/* Badges: shadcn/ui's badge, for lifecycle and item states. Secondary unless the state says otherwise. */\n.status, .flag {\n display: inline-flex; align-items: center; gap: 4px; flex-shrink: 0; white-space: nowrap;\n height: 20px; padding: 0 8px; border-radius: 999px; border: 1px solid transparent;\n font: 500 var(--text-xs)/1 var(--font-sans); background: var(--secondary); color: var(--secondary-foreground);\n}\n.status-unpublished-changes, .status-unsaved { background: color-mix(in oklab, var(--warning) 12%, var(--popover)); color: var(--warning); }\n.status-not-in-content, .flag { background: color-mix(in oklab, var(--destructive) 10%, var(--popover)); color: var(--destructive-text); }\n.status-up-to-date { background: transparent; border-color: var(--success); color: var(--success); }\n.history-row { flex-direction: column; align-items: stretch !important; gap: 4px !important; padding: 8px 0 !important; }\n.history-row > .actions { margin-top: -2px; }\n.history-row > .row-main { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; width: 100%; text-align: left; }\n.history-row > .row-main .stack-chevron { margin-left: auto; }\n.version-head { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; margin: 0 0 8px; }\n.version-change { margin-bottom: 8px; }\n.level-confirm .description { margin: 8px 0; }\n.level-confirm > .meta { margin-top: 0; }\n/* A long review keeps Publish in reach: its buttons stay at the bottom of the sidebar. */\n.level-review > .actions { position: sticky; bottom: 0; background: var(--popover); padding: 8px 0 2px; border-top: 1px solid var(--border); }\n.level-schedule { display: flex; flex-direction: column; gap: 8px; }\n.level-schedule .hint { margin: 0; }\n.row > .change { padding-inline: 6px; }\n.change { white-space: pre-wrap; overflow-wrap: anywhere; font-size: var(--text-xs); color: var(--foreground); }\n.change ins { background: color-mix(in oklab, var(--success) 15%, transparent); color: var(--success); text-decoration: underline; }\n.change del { background: color-mix(in oklab, var(--destructive) 12%, transparent); color: var(--destructive); }\n.change .delta { font-weight: 600; padding: 0 3px; border-radius: 3px; }\n/* The arrow glyph sits low in most fonts: centre it on the line. */\n.change .arrow { display: inline-block; vertical-align: middle; line-height: 1; margin: 0 0.35em; }\n.change .delta.up { background: color-mix(in oklab, var(--success) 15%, transparent); color: var(--success); }\n.change .delta.down { background: color-mix(in oklab, var(--destructive) 12%, transparent); color: var(--destructive); }\n.slot-preview { display: flex; align-items: center; gap: 6px; }\n.slot-preview img { width: 56px; height: 40px; object-fit: cover; border-radius: var(--radius-sm); border: 1px solid var(--border); }\n.slot-preview .meta { font-size: 11px; overflow-wrap: anywhere; }\n.extras:empty { display: none; }\n.extras { display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: center; gap: 4px; }\n.extras button { white-space: nowrap; }\n.popover .title { overflow-wrap: anywhere; }\n.reorder-row { align-items: center; }\n.reorder-row.dragging { opacity: 0.5; }\n.handle { display: inline-flex; cursor: grab; color: var(--muted-foreground); padding: 0 2px; }\n.entities:empty { display: none; }\n.entities { display: flex; flex-direction: column; gap: 8px; border-top: 1px solid var(--border); padding-top: 6px; max-height: 50vh; overflow: auto; }\n.entity { display: flex; flex-direction: column; gap: 4px; }\n.entity + .entity { border-top: 1px dashed var(--border); padding-top: 6px; }\n.unsent { color: var(--warning); }\n.lifecycle-status { font-weight: 600; color: var(--foreground); }\n.title { font-weight: 600; }\n.meta { color: var(--muted-foreground); font-weight: 400; }\n/* Buttons wrap whole onto the next line rather than squeezing their labels. */\n.actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 6px; }\n.actions > button { white-space: nowrap; }\n.hint { margin: 0; color: var(--muted-foreground); font-size: var(--text-xs); }\n`;\n\nexport interface EditorHost {\n readonly element: HTMLElement;\n readonly root: ShadowRoot;\n}\n\n/** Creates the host, or returns the existing one if the loader ran twice. */\nexport function createHost(): EditorHost {\n let element = document.getElementById(HOST_ID);\n if (!element) {\n element = document.createElement('div');\n element.id = HOST_ID;\n element.attachShadow({ mode: 'open' });\n document.body.append(element);\n }\n const root = element.shadowRoot!;\n if (!root.querySelector('style')) {\n const style = document.createElement('style');\n style.textContent = STYLES;\n root.append(style);\n }\n return { element, root };\n}\n\n/**\n * A collapsed \"Debug\" section showing an API error body (pretty-printed JSON),\n * inserted as text. Empty when there is nothing to show.\n */\nexport function debugDetails(debug: string | undefined): HTMLElement[] {\n if (!debug) return [];\n const details = el('details', 'debug');\n details.append(el('summary', '', 'Debug'), el('pre', '', debug));\n return [details];\n}\n\n/** shadcn/ui's button variants. Outline is the editor's usual button, so it has no class. */\nexport type ButtonVariant = 'primary' | 'outline' | 'secondary' | 'ghost' | 'destructive' | 'link';\nexport type ButtonSize = 'xs' | 'sm' | 'default' | 'lg' | 'icon' | 'icon-sm' | 'icon-xs';\n\n/** A button (type \"button\", so it never submits a form) with its label as text. */\nexport function button(label: string, variant: ButtonVariant = 'outline', size: ButtonSize = 'default', className = ''): HTMLButtonElement {\n const classes = [className, variant === 'outline' ? '' : variant, size === 'default' ? '' : size].filter(Boolean).join(' ');\n const element = el('button', classes, label);\n element.type = 'button';\n return element;\n}\n\n/** The variant for an action that may be marked primary or destructive. */\nexport function variantOf(action: { primary?: boolean; destructive?: boolean }): ButtonVariant {\n return action.destructive ? 'destructive' : action.primary ? 'primary' : 'outline';\n}\n\n/** A select with shadcn/ui's chevron in place of the browser's arrow. */\nexport function selectBox(select: HTMLSelectElement): HTMLElement {\n const box = el('span', 'select-box');\n box.append(select, icon('chevron-down'));\n return box;\n}\n\n/** Creates an element with a class and optional text, as text. */\nexport function el<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n className = '',\n text = '',\n): HTMLElementTagNameMap[K] {\n const element = document.createElement(tag);\n if (className) element.className = className;\n if (text) element.textContent = text;\n return element;\n}\n", "// The only UI the loader needs: the button that loads the editor. Kept apart\n// from the toolbar so anonymous visitors never download the rest.\n\nimport { el, type EditorHost } from './host.ts';\n\nexport const EDIT_LABEL = 'Edit page';\n\n/** The loader's minimal toolbar: just the button that loads the editor. */\nexport function createLauncher(host: EditorHost): HTMLButtonElement {\n const existing = host.root.querySelector<HTMLButtonElement>('.toolbar button.mode');\n if (existing) return existing;\n const bar = el('div', 'panel toolbar');\n bar.setAttribute('role', 'region');\n bar.setAttribute('aria-label', 'Baack editor');\n const row = el('div', 'toolbar-row');\n const button = el('button', 'mode', EDIT_LABEL);\n button.type = 'button';\n button.setAttribute('aria-pressed', 'false');\n row.append(el('span', 'brand', 'Baack'), button);\n bar.append(row);\n host.root.append(bar);\n return button;\n}\n", "export const Endpoint = Object.freeze({\n ADDRESS: '/n/v1/address/',\n API_CLIENT: '/n/v1/apiclient/',\n BEARER_TOKEN: '/n/v1/bearertoken/',\n BOOLEAN: '/n/v1/booleanitem/',\n BOOLEAN_HISTORY : '/n/v1/booleanitemhistory/',\n CLIENT_CONTEXT : '/n/v1/clientcontext',\n CLIENT_IDENTIFIER: '/n/v1/clientidentifier/',\n COMPANIES: '/n/v1/companies',\n COMPANY: '/n/v1/company/',\n DATE_TIME: '/n/v1/datetimeitem/',\n DATE_TIME_HISTORY: '/n/v1/datetimeitemhistory/',\n DOUBLE: '/n/v1/doubleitem/',\n DOUBLE_HISTORY: '/n/v1/doubleitemhistory/',\n EMAIL: '/n/v1/email/',\n ENTITY: '/n/v1/entity/',\n ENTITY_LIST: '/n/v1/entitylist/',\n ENTITY_VIEW : '/v/v1/entityview/',\n GROUP: '/n/v1/group/',\n GROUP_MEMBERSHIP: '/n/v1/groupmembership/',\n IDENTITY: '/n/v1/identity/',\n IDENTITY_NOTIFICATION_INBOX: '/n/v1/identitynotificationinbox/',\n IMAGE: '/n/v1/imageitem/',\n IMAGE_HISTORY: '/n/v1/imageitemhistory/',\n IMAGE_FILE: '/n/v1/imageitemfile',\n JSON: '/n/v1/jsonitem/',\n JSON_HISTORY: '/n/v1/jsonitemhistory/',\n LAT_LONG: '/n/v1/latlongitem/',\n LAT_LONG_HISTORY: '/n/v1/latlongitemhistory/',\n LINK: '/n/v1/linkitem/',\n LINK_HISTORY: '/n/v1/linkitemhistory/',\n LONG: '/n/v1/longitem/',\n LONG_HISTORY: '/n/v1/longitemhistory/',\n MARKDOWN: '/n/v1/markdownitem/',\n MARKDOWN_HISTORY: '/n/v1/markdownitemhistory/',\n MESSAGE_THREAD: '/n/v1/messagethread/',\n MESSAGE_THREAD_NOTIFICATION: '/n/v1/messagethreadnotification/',\n MONEY: '/n/v1/moneyitem/',\n MONEY_HISTORY: '/n/v1/moneyitemhistory/',\n OAUTH_TOKEN: '/n/v1/oauthtoken/',\n PAYMENT_INVOICE: '/n/v1/paymentinvoice/',\n PERMISSION: '/n/v1/permission/',\n PERSON: '/n/v1/person/',\n PERSON_NAME: '/n/v1/personname/',\n SEARCH : '/n/v1/search/',\n REFERENCE: '/n/v1/referenceitem/',\n /**\n * Not yet released by the API; follows the pattern of the other item\n * history endpoints and may change.\n * @experimental\n */\n REFERENCE_HISTORY: '/n/v1/referenceitemhistory/',\n TASK: '/n/v1/task/',\n TASK_LOG: '/n/v1/tasklog/',\n TASK_LOGS: '/n/v1/tasklogs/',\n TEMPLATE: '/n/v1/templateitem/',\n TEMPLATE_HISTORY: '/n/v1/templateitemhistory/',\n TEXT: '/n/v1/textitem/',\n TEXT_HISTORY: '/n/v1/textitemhistory/',\n THREAD_POST: '/n/v1/threadpost/',\n WEBHOOK_SUBSCRIPTION: '/n/v1/webhooksubscription/',\n WEBHOOK_TRIGGER: '/n/v1/webhooktrigger/',\n WORKFLOW: '/n/v1/workflow/'\n});\n\n\n\n", "import type { ApiError } from './types/index.ts';\n\n/**\n * Thrown for any non-2xx response from the Baack API. Carries the HTTP status\n * so callers can tell an expired session (401) from a missing permission (403),\n * a conflict (409) or a server failure (5xx).\n *\n * Network failures (no response at all) are not wrapped: `fetch` rejects with\n * its own error, typically a `TypeError`.\n */\nexport class BaackApiError extends Error {\n override readonly name = 'BaackApiError';\n\n constructor(\n readonly status: number,\n readonly statusText: string,\n /** The raw response body, which may be a JSON error document or empty. */\n readonly body: string,\n readonly method: string,\n readonly url: string,\n /** The JSON body that was sent, when there was one, for debugging (not multipart uploads). */\n readonly requestBody?: string,\n ) {\n super(`Baack API Error [${status}]: ${body || statusText}`);\n }\n\n get isUnauthorized(): boolean {\n return this.status === 401;\n }\n\n get isForbidden(): boolean {\n return this.status === 403;\n }\n\n get isNotFound(): boolean {\n return this.status === 404;\n }\n\n get isConflict(): boolean {\n return this.status === 409 || this.status === 412;\n }\n\n get isServerError(): boolean {\n return this.status >= 500;\n }\n\n /** The API error object from the body, when there is one. */\n get apiError(): ApiError | undefined {\n const parsed = this.json<ApiError>();\n return parsed !== null && typeof parsed === 'object' ? parsed : undefined;\n }\n\n /** The API's description of the problem, when it gave one. */\n get detail(): string | undefined {\n const detail = this.apiError?.detail;\n return typeof detail === 'string' && detail !== '' ? detail : undefined;\n }\n\n /** The body parsed as JSON, or undefined when it is empty or not JSON. */\n json<T = unknown>(): T | undefined {\n if (!this.body) return undefined;\n try {\n return JSON.parse(this.body) as T;\n } catch {\n return undefined;\n }\n }\n}\n\nexport function isBaackApiError(error: unknown): error is BaackApiError {\n return error instanceof BaackApiError;\n}\n", "import { Endpoint } from './endpoints.ts';\nimport { BaackApiError } from './errors.ts';\nimport type { Search } from './types/index.ts';\n\nexport interface BaackConfig {\n baseUrl: string;\n headers?: Record<string, string>;\n /**\n * Whether the browser sends cookies with each request. Use 'include' for a\n * browser client that relies on the user's Baack session from another\n * origin. Defaults to the fetch default ('same-origin').\n */\n credentials?: 'omit' | 'same-origin' | 'include';\n}\n\n/**\n * The HTTP method behind each of the API's operations, named as the API docs\n * and this client's methods name them: `Method.UPDATE` is `PUT`. Use these\n * rather than HTTP method strings, for example in tests or a proxy allowlist.\n */\nexport const Method = {\n CREATE: 'POST',\n READ: 'GET',\n UPDATE: 'PUT',\n DELETE: 'DELETE',\n} as const;\nexport type Method = (typeof Method)[keyof typeof Method];\n\nexport type QueryParams =Record<string, string | number | boolean | null | undefined>;\n\nexport class BaackClient {\n private baseUrl: string;\n private defaultHeaders: Record<string, string>;\n private credentials: BaackConfig['credentials'];\n\n constructor(config: BaackConfig) {\n this.baseUrl = config.baseUrl.replace(/\\/$/, ''); // Remove trailing slash\n this.defaultHeaders = { ...config.headers };\n this.credentials = config.credentials;\n }\n\n /**\n * Core request handler.\n * Supports both Browser and Node environments via global fetch.\n *\n * Throws BaackApiError for any non-2xx response. Resolves to undefined for\n * an empty response body (for example a 204 from a delete).\n */\n private async request<T>(\n path: string,\n options: RequestInit = {},\n params?: QueryParams\n ): Promise<T> {\n const url = new URL(this.baseUrl + path);\n if (params) {\n Object.entries(params).forEach(([k, v]) => {\n if (v !== undefined && v !== null) {\n url.searchParams.append(k, String(v));\n }\n });\n }\n\n // Only declare a JSON body when there is one: a Content-Type header on a\n // bodiless cross-origin GET forces a CORS preflight.\n const headers: Record<string, string> = typeof options.body === 'string'\n ? { 'Content-Type': 'application/json', ...this.defaultHeaders }\n : { ...this.defaultHeaders };\n\n const init: RequestInit = {\n ...options,\n headers: {\n ...headers,\n ...(options.headers as Record<string, string> | undefined),\n },\n };\n if (this.credentials) {\n init.credentials = this.credentials;\n }\n\n const method = options.method ?? Method.READ;\n const response = await fetch(url.toString(), init);\n const text = await response.text();\n\n if (!response.ok) {\n const sent = typeof init.body === 'string' ? init.body : undefined;\n throw new BaackApiError(response.status, response.statusText, text, method, url.toString(), sent);\n }\n\n return (text ? JSON.parse(text) : undefined) as T;\n }\n\n /**\n * CREATE / POST method for creating resources\n */\n public async create<T>(endpoint: string, body?: unknown): Promise<T> {\n return this.request<T>(endpoint, jsonInit(Method.CREATE, body));\n }\n\n /**\n * READ / GET method for all endpoints. `headers` are added to this request\n * only: a server reading the entity view for a visitor can forward their\n * `Accept-Language`, so the view picks the best available language.\n */\n public async read<T>(endpoint: string, urn: string, params?: QueryParams, headers?: Record<string, string>): Promise<T> {\n return this.request<T>(join(endpoint, urn), { method: Method.READ, ...(headers ? { headers } : {}) }, params);\n }\n\n /**\n * UPDATE / PUT for updating representations\n */\n public async update<T>(endpoint: string, urn: string, body?: unknown): Promise<T> {\n return this.request<T>(join(endpoint, urn), jsonInit(Method.UPDATE, body));\n }\n\n /**\n * DELETE for deleting representations\n */\n public async delete<T = undefined>(endpoint: string, urn: string): Promise<T> {\n return this.request<T>(join(endpoint, urn), {\n method: Method.DELETE,\n });\n }\n\n /**\n * POST a multipart form, for example an image file upload. The browser sets\n * the multipart Content-Type and boundary itself.\n */\n public async upload<T>(endpoint: string, form: FormData): Promise<T> {\n return this.request<T>(endpoint, { method: Method.CREATE, body: form });\n }\n\n /**\n * Starts a search (`owner`, `includeScopes`, optional `order`). The result\n * may be partial (`status` before `FULL_RESULT`): read it again with\n * `searchResult` until it is full, and follow its `pagination`.\n */\n public async search(params: Search): Promise<Search> {\n return this.create<Search>(Endpoint.SEARCH, params);\n }\n\n /** Reads a search's results again by its URN, optionally from a page (`after`). */\n public async searchResult(urn: string, after?: string): Promise<Search> {\n return this.read<Search>(Endpoint.SEARCH, urn, { after });\n }\n}\n\n/**\n * Appends a URN or path to an endpoint. Endpoints end with a slash and paths\n * such as '/home' start with one, so drop the duplicate: the API accepts\n * either, but one slash is the canonical URL.\n */\nfunction join(endpoint: string, urn: string): string {\n return endpoint.endsWith('/') && urn.startsWith('/') ? endpoint + urn.slice(1) : endpoint + urn;\n}\n\nfunction jsonInit(method: Method, body: unknown): RequestInit {\n return body === undefined ? { method } : { method, body: JSON.stringify(body) };\n}\n"],
5
- "mappings": "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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoTpB,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",
6
- "names": ["readConfig", "meta", "path", "d", "dot", "cx", "cy", "ICONS", "SVG", "icon", "name", "svg", "attribute", "value", "tag", "attributes", "shape", "LIGHT", "DARK", "SCALE", "tokens", "set", "name", "value", "THEME_CSS", "PAGE_COLOURS", "HOST_ID", "STYLES", "THEME_CSS", "createHost", "element", "root", "style", "debugDetails", "debug", "details", "el", "button", "label", "variant", "size", "className", "classes", "variantOf", "action", "selectBox", "select", "box", "icon", "tag", "text", "EDIT_LABEL", "createLauncher", "host", "existing", "bar", "el", "row", "button", "Endpoint", "BaackApiError", "status", "statusText", "body", "method", "url", "requestBody", "parsed", "detail", "isBaackApiError", "error", "Method", "BaackClient", "config", "path", "options", "params", "url", "k", "v", "headers", "init", "method", "response", "text", "sent", "BaackApiError", "endpoint", "body", "jsonInit", "urn", "join", "form", "Endpoint", "after"]
7
- }