@clearlaunch/sdk 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/index.ts", "../../clearsdk-core/src/index.ts", "../../clearsdk-versions/src/styles.ts", "../../clearsdk-versions/src/index.ts"],
4
- "sourcesContent": ["/**\n * @clearlaunch/sdk \u2014 the all-in-one bundle for the CDN script tag.\n *\n * This is the batteries-included entry: one <script> gives you\n * window.ClearLaunch.init({ projectKey }) with every widget attached. Under the\n * hood it composes the modular packages (@clearlaunch/core + the widget\n * packages), so apps that prefer smaller bundles can install those directly:\n *\n * import { createCore } from '@clearlaunch/core';\n * import { createVersions } from '@clearlaunch/versions';\n * const core = createCore({ projectKey });\n * const versions = createVersions(core);\n */\nimport { createCore, type ClearCoreConfig } from '@clearlaunch/core';\nimport { createVersions, type VersionsWidget, type PublicRelease } from '@clearlaunch/versions';\n\nexport type { ClearCoreConfig, VersionsWidget, PublicRelease };\n\nexport interface ClearLaunchClient {\n readonly projectKey: string;\n readonly versions: VersionsWidget;\n}\n\nexport function init(config: ClearCoreConfig): ClearLaunchClient {\n const core = createCore(config);\n return {\n projectKey: core.projectKey,\n versions: createVersions(core),\n };\n}\n\nexport const ClearLaunch = { init };\nexport default ClearLaunch;\n", "/**\n * @clearlaunch/core \u2014 the shared foundation every ClearLaunch widget builds on.\n *\n * One public project key (`pk_\u2026`) identifies the project in the browser. Core\n * owns the API base, the fetch client, the remote SDK config (which widgets are\n * enabled + theme), and a small style-injection helper. Widget packages\n * (@clearlaunch/versions, /guides, /feedback) receive a ClearCoreClient and\n * attach themselves to it.\n */\n\nexport interface ClearCoreConfig {\n /** The project's public key (pk_\u2026). Safe to ship in the browser. */\n projectKey: string;\n /** Override the API base. Defaults to the hosted ClearLaunch API. */\n apiUrl?: string;\n}\n\nexport interface SdkTheme {\n primaryColor?: string;\n fontFamily?: string;\n borderRadius?: string;\n logoUrl?: string;\n}\n\nexport interface SdkRemoteConfig {\n projectName: string;\n widgets: { versions?: boolean; guides?: boolean; feedback?: boolean };\n theme?: SdkTheme;\n}\n\nexport interface ClearCoreClient {\n readonly projectKey: string;\n /** Absolute base for this project's public SDK routes:\n * `${apiUrl}/api/v1/public/sdk/${projectKey}` */\n readonly sdkBase: string;\n /** GET a JSON path relative to sdkBase (e.g. 'releases', 'config'). */\n get<T = unknown>(path: string): Promise<T>;\n /** Cached remote config (widgets enabled + theme). Fetched once. */\n config(): Promise<SdkRemoteConfig>;\n}\n\nconst DEFAULT_API_URL = 'https://api.clearlaunch.ai';\n\n/** Escape untrusted text before inserting into innerHTML. Shared by widgets. */\nexport function esc(s: string): string {\n const d = document.createElement('div');\n d.textContent = s;\n return d.innerHTML;\n}\n\n/** Inject a stylesheet once, keyed by id. Shared by widgets. */\nexport function injectStyles(id: string, css: string): void {\n if (document.getElementById(id)) return;\n const style = document.createElement('style');\n style.id = id;\n style.textContent = css;\n document.head.appendChild(style);\n}\n\n/** Resolve a selector-or-element mount target. */\nexport function resolveTarget(target: string | HTMLElement): HTMLElement | null {\n return typeof target === 'string' ? document.querySelector<HTMLElement>(target) : target;\n}\n\nexport function createCore(config: ClearCoreConfig): ClearCoreClient {\n if (!config?.projectKey) throw new Error('ClearLaunch: projectKey is required');\n const apiUrl = (config.apiUrl || DEFAULT_API_URL).replace(/\\/$/, '');\n const sdkBase = `${apiUrl}/api/v1/public/sdk/${encodeURIComponent(config.projectKey)}`;\n\n let configPromise: Promise<SdkRemoteConfig> | null = null;\n\n async function get<T>(path: string): Promise<T> {\n const res = await fetch(`${sdkBase}/${path.replace(/^\\//, '')}`);\n if (!res.ok) throw new Error(`ClearLaunch: request to ${path} failed (${res.status})`);\n return res.json() as Promise<T>;\n }\n\n function configOnce(): Promise<SdkRemoteConfig> {\n if (!configPromise) {\n configPromise = get<SdkRemoteConfig>('config').catch(() => ({\n projectName: '',\n widgets: { versions: true },\n } as SdkRemoteConfig));\n }\n return configPromise;\n }\n\n return {\n projectKey: config.projectKey,\n sdkBase,\n get,\n config: configOnce,\n };\n}\n", "/** Self-contained widget CSS, namespaced under .cl-vw so it never leaks into\n * or inherits from the host page. Theme values are injected via CSS vars. */\nexport const VERSIONS_CSS = `\n.cl-vw { --cl-vw-accent: #6366f1; --cl-vw-radius: 12px;\n font-family: var(--cl-vw-font, inherit); color: #1f2430; }\n.cl-vw * { box-sizing: border-box; }\n.cl-vw__empty { padding: 24px; text-align: center; color: #8891a5; font-size: 14px; }\n.cl-vw__release { padding: 16px 0; border-bottom: 1px solid #eceef3; }\n.cl-vw__release:last-child { border-bottom: 0; }\n.cl-vw__head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 8px; }\n.cl-vw__ver {\n font-weight: 700; font-size: 15px; color: #fff; background: var(--cl-vw-accent);\n padding: 2px 9px; border-radius: 999px; letter-spacing: -0.01em;\n}\n.cl-vw__date { font-size: 12px; color: #8891a5; }\n.cl-vw__group { margin: 10px 0 0; }\n.cl-vw__group-label {\n font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em;\n font-weight: 700; color: #6b7280; margin-bottom: 4px;\n}\n.cl-vw__group--breaking .cl-vw__group-label { color: #dc2626; }\n.cl-vw__list { margin: 0; padding-left: 18px; }\n.cl-vw__list li { font-size: 13px; line-height: 1.55; color: #374151; margin: 2px 0; }\n@media (prefers-color-scheme: dark) {\n .cl-vw { color: #e5e7eb; }\n .cl-vw__release { border-bottom-color: #2a2f3a; }\n .cl-vw__date, .cl-vw__group-label { color: #9aa3b2; }\n .cl-vw__list li { color: #cbd2de; }\n}\n`;\n", "import type { ClearCoreClient } from '@clearlaunch/core';\nimport { esc, injectStyles, resolveTarget } from '@clearlaunch/core';\nimport { VERSIONS_CSS } from './styles';\n\nexport interface ReleaseNotesPublic {\n features: string[];\n enhancements: string[];\n bugFixes: string[];\n breaking: string[];\n}\n\nexport interface PublicRelease {\n version: string;\n publishedAt?: string;\n notes: ReleaseNotesPublic;\n html?: string;\n}\n\nexport interface VersionsWidget {\n /** Render the version-history panel into a container (selector or element). */\n mount(target: string | HTMLElement): Promise<void>;\n /** Fetch releases without rendering (for a custom UI). */\n list(): Promise<PublicRelease[]>;\n}\n\nfunction fmtDate(iso?: string): string {\n if (!iso) return '';\n const d = new Date(iso);\n return isNaN(d.getTime()) ? '' : d.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' });\n}\n\nfunction group(label: string, items: string[], modifier = ''): string {\n if (!items?.length) return '';\n const li = items.map(i => `<li>${esc(i)}</li>`).join('');\n return `<div class=\"cl-vw__group ${modifier}\"><div class=\"cl-vw__group-label\">${label}</div><ul class=\"cl-vw__list\">${li}</ul></div>`;\n}\n\nfunction render(el: HTMLElement, releases: PublicRelease[]): void {\n if (!releases.length) {\n el.innerHTML = `<div class=\"cl-vw\"><div class=\"cl-vw__empty\">No releases yet.</div></div>`;\n return;\n }\n const body = releases.map(r => `\n <div class=\"cl-vw__release\">\n <div class=\"cl-vw__head\">\n <span class=\"cl-vw__ver\">v${esc(r.version)}</span>\n <span class=\"cl-vw__date\">${fmtDate(r.publishedAt)}</span>\n </div>\n ${group('Breaking changes', r.notes.breaking, 'cl-vw__group--breaking')}\n ${group('New features', r.notes.features)}\n ${group('Enhancements', r.notes.enhancements)}\n ${group('Fixes', r.notes.bugFixes)}\n </div>`).join('');\n el.innerHTML = `<div class=\"cl-vw\">${body}</div>`;\n}\n\n/** Attach a versions widget to a core client. */\nexport function createVersions(core: ClearCoreClient): VersionsWidget {\n async function list(): Promise<PublicRelease[]> {\n const data = await core.get<{ releases: PublicRelease[] }>('releases');\n return data.releases || [];\n }\n return {\n list,\n async mount(target) {\n const el = resolveTarget(target);\n if (!el) throw new Error(`ClearLaunch versions: mount target not found: ${String(target)}`);\n injectStyles('cl-vw-styles', VERSIONS_CSS);\n try {\n render(el, await list());\n } catch {\n el.innerHTML = `<div class=\"cl-vw\"><div class=\"cl-vw__empty\">Couldn't load release notes.</div></div>`;\n }\n },\n };\n}\n"],
5
- "mappings": "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",
6
- "names": ["index_exports", "__export", "ClearLaunch", "index_default", "init", "DEFAULT_API_URL", "esc", "s", "d", "injectStyles", "id", "css", "style", "resolveTarget", "target", "createCore", "config", "sdkBase", "configPromise", "get", "path", "res", "configOnce", "VERSIONS_CSS", "fmtDate", "iso", "d", "group", "label", "items", "modifier", "li", "i", "esc", "render", "el", "releases", "body", "r", "createVersions", "core", "list", "target", "resolveTarget", "injectStyles", "VERSIONS_CSS", "init", "config", "core", "createCore", "createVersions", "ClearLaunch", "index_default"]
3
+ "sources": ["../src/bundled.ts", "../../clearsdk-versions/src/index.ts", "../../clearsdk-core/src/index.ts", "../../clearsdk-versions/src/styles.ts", "../../clearsdk-feedback/src/index.ts", "../../clearsdk-feedback/src/styles.ts", "../../sdk/src/index.ts", "../../sdk/src/api-client.ts", "../../sdk/src/renderer.ts", "../../sdk/src/styles.ts", "../src/index.ts"],
4
+ "sourcesContent": ["/**\n * CDN entry \u2014 the batteries-included build.\n *\n * A <script> tag has no package manager, so the optional-peer resolution the\n * modular entry relies on can't work. This entry statically imports every\n * capability and registers it up front, so `window.ClearLaunch.init(\u2026)` has\n * versions, guides and ticketing available with no install step.\n *\n * npm consumers should import '@clearlaunch/sdk' instead and install only the\n * capabilities they use \u2014 that build stays small.\n */\nimport * as versions from '@clearlaunch/versions';\nimport * as feedback from '@clearlaunch/feedback';\nimport * as guideSdk from '@clearlaunch/guide-sdk';\nimport { register } from './index';\n\nregister('@clearlaunch/versions', versions);\nregister('@clearlaunch/feedback', feedback);\nregister('@clearlaunch/guide-sdk', guideSdk);\n\nexport * from './index';\nexport { default } from './index';\n", "import type { ClearCoreClient } from '@clearlaunch/core';\nimport { esc, injectStyles, resolveTarget } from '@clearlaunch/core';\nimport { VERSIONS_CSS } from './styles';\n\nexport interface ReleaseNotesPublic {\n features: string[];\n enhancements: string[];\n bugFixes: string[];\n breaking: string[];\n}\n\nexport interface PublicRelease {\n version: string;\n publishedAt?: string;\n notes: ReleaseNotesPublic;\n html?: string;\n}\n\nexport interface VersionsWidget {\n /** Render the version-history panel into a container (selector or element). */\n mount(target: string | HTMLElement): Promise<void>;\n /** Fetch releases without rendering (for a custom UI). */\n list(): Promise<PublicRelease[]>;\n}\n\nfunction fmtDate(iso?: string): string {\n if (!iso) return '';\n const d = new Date(iso);\n return isNaN(d.getTime()) ? '' : d.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' });\n}\n\nfunction group(label: string, items: string[], modifier = ''): string {\n if (!items?.length) return '';\n const li = items.map(i => `<li>${esc(i)}</li>`).join('');\n return `<div class=\"cl-vw__group ${modifier}\"><div class=\"cl-vw__group-label\">${label}</div><ul class=\"cl-vw__list\">${li}</ul></div>`;\n}\n\nfunction render(el: HTMLElement, releases: PublicRelease[]): void {\n if (!releases.length) {\n el.innerHTML = `<div class=\"cl-vw\"><div class=\"cl-vw__empty\">No releases yet.</div></div>`;\n return;\n }\n const body = releases.map(r => `\n <div class=\"cl-vw__release\">\n <div class=\"cl-vw__head\">\n <span class=\"cl-vw__ver\">v${esc(r.version)}</span>\n <span class=\"cl-vw__date\">${fmtDate(r.publishedAt)}</span>\n </div>\n ${group('Breaking changes', r.notes.breaking, 'cl-vw__group--breaking')}\n ${group('New features', r.notes.features)}\n ${group('Enhancements', r.notes.enhancements)}\n ${group('Fixes', r.notes.bugFixes)}\n </div>`).join('');\n el.innerHTML = `<div class=\"cl-vw\">${body}</div>`;\n}\n\n/** Attach a versions widget to a core client. */\nexport function createVersions(core: ClearCoreClient): VersionsWidget {\n async function list(): Promise<PublicRelease[]> {\n const data = await core.get<{ releases: PublicRelease[] }>('releases');\n return data.releases || [];\n }\n return {\n list,\n async mount(target) {\n const el = resolveTarget(target);\n if (!el) throw new Error(`ClearLaunch versions: mount target not found: ${String(target)}`);\n injectStyles('cl-vw-styles', VERSIONS_CSS);\n try {\n render(el, await list());\n } catch {\n el.innerHTML = `<div class=\"cl-vw\"><div class=\"cl-vw__empty\">Couldn't load release notes.</div></div>`;\n }\n },\n };\n}\n", "/**\r\n * @clearlaunch/core \u2014 the shared foundation every ClearLaunch widget builds on.\r\n *\r\n * One public project key (`pk_\u2026`) identifies the project in the browser. Core\r\n * owns the API base, the fetch client, the remote SDK config (which widgets are\r\n * enabled + theme), and a small style-injection helper. Widget packages\r\n * (@clearlaunch/versions, /guides, /feedback) receive a ClearCoreClient and\r\n * attach themselves to it.\r\n */\r\n\r\nexport interface ClearCoreConfig {\r\n /** The project's public key (pk_\u2026). Safe to ship in the browser. */\r\n projectKey: string;\r\n /** Override the API base. Defaults to the hosted ClearLaunch API. */\r\n apiUrl?: string;\r\n}\r\n\r\nexport interface SdkTheme {\r\n primaryColor?: string;\r\n fontFamily?: string;\r\n borderRadius?: string;\r\n logoUrl?: string;\r\n}\r\n\r\nexport interface SdkRemoteConfig {\r\n projectName: string;\r\n widgets: { versions?: boolean; guides?: boolean; feedback?: boolean };\r\n theme?: SdkTheme;\r\n /**\r\n * Public identifiers the other sub-packages need, resolved from the pk_ key\r\n * so one credential reaches every widget. Both are already public \u2014 the\r\n * feedback token is served by the widget-config route and guide slugs\r\n * appear in /docs/:slug URLs.\r\n *\r\n * Optional because an older API may not return them.\r\n */\r\n guides?: {\r\n /** Published guides only. */\r\n slugs: string[];\r\n /** Which one a bare `.guides` accessor should open. */\r\n default?: string;\r\n };\r\n feedback?:\r\n | { enabled: false }\r\n | {\r\n enabled: true;\r\n token: string;\r\n /** Touchpoints the project has declared (dealership, admin, \u2026). */\r\n services?: string[];\r\n };\r\n}\r\n\r\nexport interface ClearCoreClient {\r\n readonly projectKey: string;\r\n /** Absolute base for this project's public SDK routes:\r\n * `${apiUrl}/api/v1/public/sdk/${projectKey}` */\r\n readonly sdkBase: string;\r\n /** GET a JSON path relative to sdkBase (e.g. 'releases', 'config'). */\r\n get<T = unknown>(path: string): Promise<T>;\r\n /** Cached remote config (widgets enabled + theme). Fetched once. */\r\n config(): Promise<SdkRemoteConfig>;\r\n}\r\n\r\nconst DEFAULT_API_URL = 'https://api.clearlaunch.ai';\r\n\r\n/** Escape untrusted text before inserting into innerHTML. Shared by widgets. */\r\nexport function esc(s: string): string {\r\n const d = document.createElement('div');\r\n d.textContent = s;\r\n return d.innerHTML;\r\n}\r\n\r\n/** Inject a stylesheet once, keyed by id. Shared by widgets. */\r\nexport function injectStyles(id: string, css: string): void {\r\n if (document.getElementById(id)) return;\r\n const style = document.createElement('style');\r\n style.id = id;\r\n style.textContent = css;\r\n document.head.appendChild(style);\r\n}\r\n\r\n/** Resolve a selector-or-element mount target. */\r\nexport function resolveTarget(target: string | HTMLElement): HTMLElement | null {\r\n return typeof target === 'string' ? document.querySelector<HTMLElement>(target) : target;\r\n}\r\n\r\nexport function createCore(config: ClearCoreConfig): ClearCoreClient {\r\n if (!config?.projectKey) throw new Error('ClearLaunch: projectKey is required');\r\n const apiUrl = (config.apiUrl || DEFAULT_API_URL).replace(/\\/$/, '');\r\n const sdkBase = `${apiUrl}/api/v1/public/sdk/${encodeURIComponent(config.projectKey)}`;\r\n\r\n let configPromise: Promise<SdkRemoteConfig> | null = null;\r\n\r\n async function get<T>(path: string): Promise<T> {\r\n const res = await fetch(`${sdkBase}/${path.replace(/^\\//, '')}`);\r\n if (!res.ok) throw new Error(`ClearLaunch: request to ${path} failed (${res.status})`);\r\n return res.json() as Promise<T>;\r\n }\r\n\r\n function configOnce(): Promise<SdkRemoteConfig> {\r\n if (!configPromise) {\r\n configPromise = get<SdkRemoteConfig>('config').catch(() => ({\r\n projectName: '',\r\n widgets: { versions: true },\r\n } as SdkRemoteConfig));\r\n }\r\n return configPromise;\r\n }\r\n\r\n return {\r\n projectKey: config.projectKey,\r\n sdkBase,\r\n get,\r\n config: configOnce,\r\n };\r\n}\r\n", "/** Self-contained widget CSS, namespaced under .cl-vw so it never leaks into\n * or inherits from the host page. Theme values are injected via CSS vars. */\nexport const VERSIONS_CSS = `\n.cl-vw { --cl-vw-accent: #6366f1; --cl-vw-radius: 12px;\n font-family: var(--cl-vw-font, inherit); color: #1f2430; }\n.cl-vw * { box-sizing: border-box; }\n.cl-vw__empty { padding: 24px; text-align: center; color: #8891a5; font-size: 14px; }\n.cl-vw__release { padding: 16px 0; border-bottom: 1px solid #eceef3; }\n.cl-vw__release:last-child { border-bottom: 0; }\n.cl-vw__head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 8px; }\n.cl-vw__ver {\n font-weight: 700; font-size: 15px; color: #fff; background: var(--cl-vw-accent);\n padding: 2px 9px; border-radius: 999px; letter-spacing: -0.01em;\n}\n.cl-vw__date { font-size: 12px; color: #8891a5; }\n.cl-vw__group { margin: 10px 0 0; }\n.cl-vw__group-label {\n font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em;\n font-weight: 700; color: #6b7280; margin-bottom: 4px;\n}\n.cl-vw__group--breaking .cl-vw__group-label { color: #dc2626; }\n.cl-vw__list { margin: 0; padding-left: 18px; }\n.cl-vw__list li { font-size: 13px; line-height: 1.55; color: #374151; margin: 2px 0; }\n@media (prefers-color-scheme: dark) {\n .cl-vw { color: #e5e7eb; }\n .cl-vw__release { border-bottom-color: #2a2f3a; }\n .cl-vw__date, .cl-vw__group-label { color: #9aa3b2; }\n .cl-vw__list li { color: #cbd2de; }\n}\n`;\n", "/**\r\n * ClearLaunch feedback widget \u2014 lets your users raise bugs, feature requests,\r\n * enhancements and general feedback from inside your product. Submissions land\r\n * on your ClearLaunch board as typed cards.\r\n *\r\n * import { mountFeedback } from '@clearlaunch/feedback';\r\n * mountFeedback({ token: 'fb_\u2026', trigger: { kind: 'floating' } });\r\n *\r\n * No dependencies, no framework. The DOM it creates is namespaced under `clf-`\r\n * and every style is set explicitly, so it renders the same inside whatever CSS\r\n * the host page has.\r\n */\r\nimport type {\r\n FeedbackOptions, FeedbackType, FeedbackWidget, FeedbackText, TriggerConfig,\r\n} from './types.js';\r\nimport { CSS } from './styles.js';\r\n\r\nexport type {\r\n FeedbackOptions, FeedbackType, FeedbackWidget, FeedbackText, TriggerConfig,\r\n FeedbackTheme, Edge,\r\n} from './types.js';\r\n\r\nconst DEFAULT_API_URL = 'https://api.clearlaunch.ai';\r\nconst STYLE_ID = 'clearlaunch-feedback-styles';\r\nconst MAX_ATTACHMENTS = 5;\r\nconst MAX_ATTACHMENT_BYTES = 5 * 1024 * 1024; // 5MB \u2014 matches the API's limit\r\n\r\nconst TYPE_LABELS: Record<FeedbackType, string> = {\r\n bug: 'Bug',\r\n feature: 'Feature',\r\n enhancement: 'Improvement',\r\n feedback: 'Feedback',\r\n};\r\n\r\nconst DEFAULT_TEXT: FeedbackText = {\r\n triggerLabel: 'Feedback',\r\n dialogTitle: 'Send feedback',\r\n titleLabel: 'Summary',\r\n titlePlaceholder: 'Briefly, what is it?',\r\n descriptionLabel: 'Details',\r\n descriptionPlaceholder: 'What happened, and what did you expect?',\r\n typeLabel: 'Type',\r\n serviceLabel: 'Which area?',\r\n formattingLabel: 'Formatting',\r\n servicePlaceholder: 'Select\u2026',\r\n emailLabel: 'Your email (optional)',\r\n submit: 'Send',\r\n submitting: 'Sending\u2026',\r\n cancel: 'Cancel',\r\n successTitle: 'Thanks \u2014 we got it',\r\n successBody: 'Your report has been raised with the team.',\r\n attachHint: 'Paste a screenshot, or drop images here.',\r\n required: 'Add a summary and some details.',\r\n failed: 'Could not send that. Please try again.',\r\n};\r\n\r\n/** Escape untrusted text before it goes anywhere near innerHTML. */\r\nfunction esc(s: string): string {\r\n return String(s)\r\n .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')\r\n .replace(/\"/g, '&quot;').replace(/'/g, '&#39;');\r\n}\r\n\r\nfunction injectStyles(): void {\r\n if (typeof document === 'undefined' || document.getElementById(STYLE_ID)) return;\r\n const el = document.createElement('style');\r\n el.id = STYLE_ID;\r\n el.textContent = CSS;\r\n document.head.appendChild(el);\r\n}\r\n\r\n/** Read a File as a base64 payload the API accepts. */\r\nfunction readAsBase64(file: File): Promise<{ data: string; mimeType: string; filename?: string }> {\r\n return new Promise((resolve, reject) => {\r\n const fr = new FileReader();\r\n fr.onerror = () => reject(new Error('Could not read that file'));\r\n fr.onload = () => {\r\n const result = String(fr.result || '');\r\n resolve({ data: result.split(',')[1] || '', mimeType: file.type || 'image/png', filename: file.name });\r\n };\r\n fr.readAsDataURL(file);\r\n });\r\n}\r\n\r\ninterface Attachment { data: string; mimeType: string; filename?: string; preview: string }\r\n\r\n/**\r\n * Markdown formatting the composer offers.\r\n *\r\n * Deliberately a textarea with a toolbar rather than a WYSIWYG editor: card\r\n * descriptions ARE markdown, so this serialises with no conversion step, and a\r\n * contenteditable or a ProseMirror-class dependency would multiply a ~5.4 kB\r\n * gzipped widget that gets dropped into someone else's page.\r\n *\r\n * `wrap` surrounds the selection; `prefix` acts per line, for lists and quotes.\r\n */\r\nconst FORMAT_BUTTONS: Array<{\r\n id: string; title: string; icon: string;\r\n wrap?: string; prefix?: string; placeholder: string;\r\n}> = [\r\n { id: 'bold', title: 'Bold', icon: '<b>B</b>', wrap: '**', placeholder: 'bold text' },\r\n { id: 'italic', title: 'Italic', icon: '<i>I</i>', wrap: '_', placeholder: 'italic text' },\r\n { id: 'code', title: 'Inline code', icon: '<code>&lt;&gt;</code>', wrap: '`', placeholder: 'code' },\r\n { id: 'list', title: 'Bulleted list', icon: '&#8226;&#8212;', prefix: '- ', placeholder: 'list item' },\r\n { id: 'link', title: 'Link', icon: '&#128279;', wrap: '', placeholder: 'text' },\r\n];\r\n\r\n/**\r\n * Apply a markdown transform to a textarea's current selection, preserving\r\n * undo history via execCommand where available so the user's Ctrl+Z still\r\n * works. Falls back to direct value assignment.\r\n */\r\nfunction applyFormat(el: HTMLTextAreaElement, btn: typeof FORMAT_BUTTONS[number]): void {\r\n const start = el.selectionStart;\r\n const end = el.selectionEnd;\r\n const selected = el.value.slice(start, end);\r\n const body = selected || btn.placeholder;\r\n\r\n let replacement: string;\r\n let cursorStart: number;\r\n let cursorEnd: number;\r\n\r\n if (btn.id === 'link') {\r\n replacement = `[${body}](url)`;\r\n // Land on `url` so the next keystroke replaces the placeholder that\r\n // actually needs filling in, not the link text.\r\n cursorStart = start + body.length + 3;\r\n cursorEnd = cursorStart + 3;\r\n } else if (btn.prefix) {\r\n // Per line, so selecting three lines makes three list items.\r\n replacement = body.split('\\n').map(l => (l.startsWith(btn.prefix!) ? l : btn.prefix + l)).join('\\n');\r\n cursorStart = start;\r\n cursorEnd = start + replacement.length;\r\n } else {\r\n const w = btn.wrap!;\r\n replacement = `${w}${body}${w}`;\r\n cursorStart = start + w.length;\r\n cursorEnd = cursorStart + body.length;\r\n }\r\n\r\n el.focus();\r\n el.setSelectionRange(start, end);\r\n // execCommand is deprecated but remains the only way to write into a\r\n // textarea without destroying the native undo stack.\r\n const ok = typeof document.execCommand === 'function' && document.execCommand('insertText', false, replacement);\r\n if (!ok) {\r\n el.value = el.value.slice(0, start) + replacement + el.value.slice(end);\r\n }\r\n el.setSelectionRange(cursorStart, cursorEnd);\r\n}\r\n\r\nexport function mountFeedback(opts: FeedbackOptions): FeedbackWidget {\r\n if (typeof document === 'undefined') {\r\n // SSR \u2014 return an inert widget so importing this module never breaks a\r\n // server render. It becomes real when the client re-mounts.\r\n return { open() {}, close() {}, destroy() {} };\r\n }\r\n if (!opts?.token) throw new Error('[clearlaunch/feedback] `token` is required \u2014 find it in ClearLaunch \u2192 project \u2192 Feedback.');\r\n // Fixing a touchpoint AND asking the submitter to pick one are mutually\r\n // exclusive intents; silently honouring one would tag feedback wrongly.\r\n if (opts.service && opts.services?.length) {\r\n throw new Error('[clearlaunch/feedback] Pass either `service` (fixed touchpoint) or `services` (let the submitter choose) \u2014 not both.');\r\n }\r\n\r\n const text: FeedbackText = { ...DEFAULT_TEXT, ...(opts.text || {}) };\r\n const types: FeedbackType[] = opts.types?.length ? opts.types : ['bug', 'feature', 'enhancement', 'feedback'];\r\n const apiUrl = (opts.apiUrl || DEFAULT_API_URL).replace(/\\/$/, '');\r\n const submitUrl = `${apiUrl}/api/v1/feedback/submit/${encodeURIComponent(opts.token)}`;\r\n const trigger: TriggerConfig = opts.trigger ?? { kind: 'floating' };\r\n\r\n injectStyles();\r\n\r\n const root = document.createElement('div');\r\n root.className = 'clf-root';\r\n const scheme = opts.theme?.colorScheme ?? 'auto';\r\n root.dataset.scheme = scheme === 'auto'\r\n ? (window.matchMedia?.('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')\r\n : scheme;\r\n if (opts.theme?.primaryColor) root.style.setProperty('--clf-primary', opts.theme.primaryColor);\r\n if (opts.theme?.fontFamily) root.style.setProperty('--clf-font', opts.theme.fontFamily);\r\n if (opts.theme?.borderRadius) root.style.setProperty('--clf-radius', opts.theme.borderRadius);\r\n if (opts.theme?.zIndex) root.style.setProperty('--clf-z', String(opts.theme.zIndex));\r\n document.body.appendChild(root);\r\n\r\n let selectedType: FeedbackType = opts.defaultType && types.includes(opts.defaultType) ? opts.defaultType : types[0];\r\n // The picker starts unset so an unanswered picker submits no touchpoint at\r\n // all, rather than silently defaulting everything to the first option.\r\n const pickable: string[] = opts.service ? [] : (opts.services || []);\r\n let selectedService: string | undefined = opts.service;\r\n let attachments: Attachment[] = [];\r\n let overlay: HTMLElement | null = null;\r\n let lastFocused: HTMLElement | null = null;\r\n let destroyed = false;\r\n\r\n // \u2500\u2500 Trigger \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n let triggerEl: HTMLButtonElement | null = null;\r\n if (trigger.kind !== 'none') {\r\n triggerEl = document.createElement('button');\r\n triggerEl.type = 'button';\r\n triggerEl.className = 'clf-trigger';\r\n triggerEl.textContent = trigger.label ?? text.triggerLabel;\r\n triggerEl.setAttribute('aria-haspopup', 'dialog');\r\n\r\n if (trigger.kind === 'sticky') {\r\n const edge = trigger.edge ?? 'right';\r\n triggerEl.classList.add('clf-trigger--sticky', `clf-trigger--${edge}`);\r\n // `offset` positions the tab ALONG the edge. A bare number is px; a\r\n // string passes straight through so callers can use % or calc().\r\n const off = trigger.offset ?? '50%';\r\n const value = typeof off === 'number' ? `${off}px` : off;\r\n if (edge === 'left' || edge === 'right') triggerEl.style.top = value;\r\n else triggerEl.style.left = value;\r\n } else {\r\n const corner = trigger.corner ?? 'bottom-right';\r\n const inset = `${trigger.inset ?? 20}px`;\r\n triggerEl.classList.add('clf-trigger--floating');\r\n if (corner.startsWith('bottom')) triggerEl.style.bottom = inset; else triggerEl.style.top = inset;\r\n if (corner.endsWith('right')) triggerEl.style.right = inset; else triggerEl.style.left = inset;\r\n }\r\n triggerEl.addEventListener('click', () => open());\r\n root.appendChild(triggerEl);\r\n }\r\n\r\n // \u2500\u2500 Dialog \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n function renderAttachments(host: HTMLElement) {\r\n host.innerHTML = attachments.map((a, i) =>\r\n `<div class=\"clf-thumb\"><img src=\"${esc(a.preview)}\" alt=\"\"><button type=\"button\" data-i=\"${i}\" aria-label=\"Remove attachment\">\u00D7</button></div>`,\r\n ).join('');\r\n host.querySelectorAll('button[data-i]').forEach(btn => {\r\n btn.addEventListener('click', () => {\r\n attachments.splice(Number((btn as HTMLElement).dataset.i), 1);\r\n renderAttachments(host);\r\n });\r\n });\r\n }\r\n\r\n async function addFiles(files: FileList | File[], host: HTMLElement, onErr: (m: string) => void) {\r\n for (const file of Array.from(files)) {\r\n if (!file.type.startsWith('image/')) continue;\r\n if (attachments.length >= MAX_ATTACHMENTS) { onErr(`Up to ${MAX_ATTACHMENTS} images.`); break; }\r\n if (file.size > MAX_ATTACHMENT_BYTES) { onErr(`\"${file.name}\" is over 5MB.`); continue; }\r\n try {\r\n const payload = await readAsBase64(file);\r\n attachments.push({ ...payload, preview: URL.createObjectURL(file) });\r\n } catch { onErr('Could not read that image.'); }\r\n }\r\n renderAttachments(host);\r\n }\r\n\r\n function open(type?: FeedbackType) {\r\n if (destroyed || overlay) return;\r\n if (type && types.includes(type)) selectedType = type;\r\n lastFocused = document.activeElement as HTMLElement;\r\n\r\n overlay = document.createElement('div');\r\n overlay.className = 'clf-overlay';\r\n overlay.innerHTML = `\r\n <div class=\"clf-dialog\" role=\"dialog\" aria-modal=\"true\" aria-label=\"${esc(text.dialogTitle)}\">\r\n <div class=\"clf-head\">\r\n <h2>${esc(text.dialogTitle)}</h2>\r\n <button type=\"button\" class=\"clf-x\" aria-label=\"${esc(text.cancel)}\">\u00D7</button>\r\n </div>\r\n <div class=\"clf-body\">\r\n <div class=\"clf-field\">\r\n <span class=\"clf-label\">${esc(text.typeLabel)}</span>\r\n <div class=\"clf-types\">\r\n ${types.map(t => `<button type=\"button\" class=\"clf-type\" data-type=\"${esc(t)}\" aria-pressed=\"${t === selectedType}\">${esc(TYPE_LABELS[t] || t)}</button>`).join('')}\r\n </div>\r\n </div>\r\n ${pickable.length ? `\r\n <div class=\"clf-field\">\r\n <label class=\"clf-label\" for=\"clf-service\">${esc(opts.serviceLabel || text.serviceLabel)}</label>\r\n <select class=\"clf-input\" id=\"clf-service\">\r\n <option value=\"\">${esc(text.servicePlaceholder)}</option>\r\n ${pickable.map(sv => `<option value=\"${esc(sv)}\">${esc(sv)}</option>`).join('')}\r\n </select>\r\n </div>` : ''}\r\n <div class=\"clf-field\">\r\n <label class=\"clf-label\" for=\"clf-title\">${esc(text.titleLabel)}</label>\r\n <input class=\"clf-input\" id=\"clf-title\" type=\"text\" placeholder=\"${esc(text.titlePlaceholder)}\" maxlength=\"200\">\r\n </div>\r\n <div class=\"clf-field\">\r\n <label class=\"clf-label\" for=\"clf-desc\">${esc(text.descriptionLabel)}</label>\r\n ${opts.formatting === false ? '' : `\r\n <div class=\"clf-toolbar\" role=\"group\" aria-label=\"${esc(text.formattingLabel)}\">\r\n ${FORMAT_BUTTONS.map(b =>\r\n `<button type=\"button\" class=\"clf-fmt\" data-fmt=\"${esc(b.id)}\" title=\"${esc(b.title)}\" aria-label=\"${esc(b.title)}\">${b.icon}</button>`,\r\n ).join('')}\r\n </div>`}\r\n <textarea class=\"clf-textarea\" id=\"clf-desc\" placeholder=\"${esc(text.descriptionPlaceholder)}\"></textarea>\r\n <span class=\"clf-attach-hint\">${esc(text.attachHint)}</span>\r\n <div class=\"clf-thumbs\"></div>\r\n </div>\r\n ${opts.user?.email ? '' : `\r\n <div class=\"clf-field\">\r\n <label class=\"clf-label\" for=\"clf-email\">${esc(text.emailLabel)}</label>\r\n <input class=\"clf-input\" id=\"clf-email\" type=\"email\" placeholder=\"you@example.com\">\r\n </div>`}\r\n <div class=\"clf-error\" hidden></div>\r\n </div>\r\n <div class=\"clf-foot\">\r\n <span class=\"clf-spacer\"></span>\r\n <button type=\"button\" class=\"clf-btn clf-btn--ghost\">${esc(text.cancel)}</button>\r\n <button type=\"button\" class=\"clf-btn clf-btn--primary\">${esc(text.submit)}</button>\r\n </div>\r\n </div>`;\r\n root.appendChild(overlay);\r\n\r\n const q = <T extends Element>(sel: string) => overlay!.querySelector(sel) as T;\r\n const titleEl = q<HTMLInputElement>('#clf-title');\r\n const descEl = q<HTMLTextAreaElement>('#clf-desc');\r\n const emailEl = overlay.querySelector('#clf-email') as HTMLInputElement | null;\r\n const errEl = q<HTMLElement>('.clf-error');\r\n overlay.querySelectorAll('.clf-fmt').forEach(b => {\r\n // mousedown, not click: the textarea must keep its selection, and\r\n // clicking a button would blur it first.\r\n b.addEventListener('mousedown', (e: Event) => {\r\n e.preventDefault();\r\n const spec = FORMAT_BUTTONS.find(x => x.id === (b as HTMLElement).dataset.fmt);\r\n if (spec) applyFormat(descEl, spec);\r\n });\r\n });\r\n\r\n const serviceEl = overlay.querySelector('#clf-service') as HTMLSelectElement | null;\r\n serviceEl?.addEventListener('change', () => { selectedService = serviceEl.value || undefined; });\r\n const thumbs = q<HTMLElement>('.clf-thumbs');\r\n const submitBtn = q<HTMLButtonElement>('.clf-btn--primary');\r\n\r\n const showErr = (m: string) => { errEl.textContent = m; errEl.hidden = false; };\r\n const clearErr = () => { errEl.hidden = true; };\r\n\r\n overlay.querySelectorAll('.clf-type').forEach(btn => {\r\n btn.addEventListener('click', () => {\r\n selectedType = (btn as HTMLElement).dataset.type as FeedbackType;\r\n overlay!.querySelectorAll('.clf-type').forEach(b =>\r\n b.setAttribute('aria-pressed', String((b as HTMLElement).dataset.type === selectedType)));\r\n });\r\n });\r\n\r\n // Paste an image straight into the description \u2014 the fastest path from\r\n // \"here's a screenshot\" to a filed ticket.\r\n descEl.addEventListener('paste', (e: ClipboardEvent) => {\r\n const files = Array.from(e.clipboardData?.files || []);\r\n if (files.length) { e.preventDefault(); void addFiles(files, thumbs, showErr); }\r\n });\r\n const stop = (e: Event) => { e.preventDefault(); e.stopPropagation(); };\r\n overlay.addEventListener('dragover', stop);\r\n overlay.addEventListener('drop', (e: DragEvent) => {\r\n stop(e);\r\n if (e.dataTransfer?.files.length) void addFiles(e.dataTransfer.files, thumbs, showErr);\r\n });\r\n\r\n q<HTMLElement>('.clf-x').addEventListener('click', () => close());\r\n q<HTMLElement>('.clf-btn--ghost').addEventListener('click', () => close());\r\n overlay.addEventListener('mousedown', (e) => { if (e.target === overlay) close(); });\r\n\r\n submitBtn.addEventListener('click', async () => {\r\n const title = titleEl.value.trim();\r\n const description = descEl.value.trim();\r\n if (!title || !description) { showErr(text.required); return; }\r\n clearErr();\r\n submitBtn.disabled = true;\r\n submitBtn.textContent = text.submitting;\r\n\r\n try {\r\n const res = await fetch(submitUrl, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({\r\n type: selectedType === 'feedback' ? 'other' : selectedType,\r\n title,\r\n description: opts.metadata && Object.keys(opts.metadata).length\r\n ? `${description}\\n\\n---\\n${Object.entries(opts.metadata).map(([k, v]) => `**${k}:** ${v}`).join('\\n')}`\r\n : description,\r\n submitterEmail: opts.user?.email || emailEl?.value.trim() || undefined,\r\n submitterName: opts.user?.name || undefined,\r\n url: location.href,\r\n userAgent: navigator.userAgent,\r\n source: opts.source || 'feedback',\r\n // Touchpoint tag \u2014 the API copies this onto the created card so\r\n // submissions can be filtered by surface. Omitted when unset so we\r\n // never write an empty string over a value set elsewhere.\r\n ...(selectedService ? { service: selectedService } : {}),\r\n attachments: attachments.map(({ data, mimeType, filename }) => ({ data, mimeType, filename })),\r\n }),\r\n });\r\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\r\n const json = await res.json().catch(() => ({}));\r\n showSuccess();\r\n opts.onSubmit?.({ submissionId: json.submissionId });\r\n } catch (err) {\r\n submitBtn.disabled = false;\r\n submitBtn.textContent = text.submit;\r\n showErr(text.failed);\r\n opts.onError?.(err instanceof Error ? err : new Error(String(err)));\r\n }\r\n });\r\n\r\n document.addEventListener('keydown', onKey);\r\n titleEl.focus();\r\n opts.onOpen?.();\r\n }\r\n\r\n function showSuccess() {\r\n if (!overlay) return;\r\n const dialog = overlay.querySelector('.clf-dialog')!;\r\n dialog.innerHTML = `\r\n <div class=\"clf-success\">\r\n <div class=\"clf-success__tick\">\u2713</div>\r\n <div class=\"clf-success__title\">${esc(text.successTitle)}</div>\r\n <div class=\"clf-success__body\">${esc(text.successBody)}</div>\r\n </div>`;\r\n setTimeout(() => close(), 1800);\r\n }\r\n\r\n function onKey(e: KeyboardEvent) {\r\n if (e.key === 'Escape') close();\r\n }\r\n\r\n function close() {\r\n if (!overlay) return;\r\n document.removeEventListener('keydown', onKey);\r\n attachments.forEach(a => URL.revokeObjectURL(a.preview));\r\n attachments = [];\r\n overlay.remove();\r\n overlay = null;\r\n lastFocused?.focus?.();\r\n opts.onClose?.();\r\n }\r\n\r\n function destroy() {\r\n close();\r\n destroyed = true;\r\n triggerEl?.remove();\r\n root.remove();\r\n }\r\n\r\n return { open, close, destroy };\r\n}\r\n\r\nexport default mountFeedback;\r\n", "/**\n * Widget CSS. Injected once, scoped under `.clf-` so it cannot collide with the\n * host page, and driven by CSS custom properties so themeing is a variable swap\n * rather than a stylesheet fork.\n *\n * Every property is set explicitly \u2014 a host page's `* { box-sizing }`, resets,\n * or inherited font styles must not change how the widget renders.\n */\nexport const CSS = `\n.clf-root {\n --clf-primary: #6366f1;\n --clf-radius: 12px;\n --clf-font: inherit;\n --clf-z: 2147483000;\n\n --clf-bg: #ffffff;\n --clf-fg: #0f172a;\n --clf-muted: #64748b;\n --clf-border: rgba(15, 23, 42, 0.12);\n --clf-input-bg: #ffffff;\n --clf-overlay: rgba(15, 23, 42, 0.45);\n}\n.clf-root[data-scheme=\"dark\"] {\n --clf-bg: #12141f;\n --clf-fg: #f5f6fa;\n --clf-muted: #94a3b8;\n --clf-border: rgba(255, 255, 255, 0.14);\n --clf-input-bg: #191c29;\n --clf-overlay: rgba(0, 0, 0, 0.6);\n}\n\n.clf-root *, .clf-root *::before, .clf-root *::after { box-sizing: border-box; }\n\n/* \u2500\u2500 Trigger \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.clf-trigger {\n position: fixed;\n z-index: var(--clf-z);\n border: none;\n background: var(--clf-primary);\n color: #fff;\n font-family: var(--clf-font);\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n display: inline-flex;\n align-items: center;\n gap: 7px;\n box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);\n transition: filter 0.15s ease, transform 0.15s ease;\n}\n.clf-trigger:hover { filter: brightness(1.08); }\n.clf-trigger:focus-visible { outline: 2px solid var(--clf-primary); outline-offset: 3px; }\n\n/* Sticky edge tabs rotate so the text runs along the edge. */\n.clf-trigger--sticky { padding: 9px 16px; }\n.clf-trigger--left { left: 0; transform-origin: left center; transform: rotate(-90deg) translateX(-50%); border-radius: 0 0 var(--clf-radius) var(--clf-radius); }\n.clf-trigger--right { right: 0; transform-origin: right center; transform: rotate(90deg) translateX(50%); border-radius: 0 0 var(--clf-radius) var(--clf-radius); }\n.clf-trigger--top { top: 0; border-radius: 0 0 var(--clf-radius) var(--clf-radius); }\n.clf-trigger--bottom { bottom: 0; border-radius: var(--clf-radius) var(--clf-radius) 0 0; }\n.clf-trigger--left:hover { transform: rotate(-90deg) translateX(-50%) translateY(-2px); }\n.clf-trigger--right:hover { transform: rotate(90deg) translateX(50%) translateY(-2px); }\n\n.clf-trigger--floating {\n border-radius: 999px;\n padding: 13px 18px;\n}\n.clf-trigger--floating:hover { transform: translateY(-2px); }\n\n/* \u2500\u2500 Dialog \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.clf-overlay {\n position: fixed; inset: 0;\n z-index: calc(var(--clf-z) + 1);\n background: var(--clf-overlay);\n display: flex; align-items: center; justify-content: center;\n padding: 20px;\n animation: clf-fade 0.15s ease-out;\n}\n@keyframes clf-fade { from { opacity: 0 } to { opacity: 1 } }\n\n.clf-dialog {\n width: 100%; max-width: 460px; max-height: 90vh;\n display: flex; flex-direction: column;\n background: var(--clf-bg);\n color: var(--clf-fg);\n font-family: var(--clf-font);\n border-radius: var(--clf-radius);\n border: 1px solid var(--clf-border);\n box-shadow: 0 24px 60px rgba(0, 0, 0, 0.28);\n overflow: hidden;\n}\n.clf-head {\n display: flex; align-items: center; gap: 10px;\n padding: 16px 18px; border-bottom: 1px solid var(--clf-border);\n}\n.clf-head h2 { margin: 0; font-size: 15px; font-weight: 700; flex: 1; color: var(--clf-fg); }\n.clf-x {\n background: none; border: none; cursor: pointer; padding: 4px;\n color: var(--clf-muted); font-size: 20px; line-height: 1; border-radius: 6px;\n}\n.clf-x:hover { color: var(--clf-fg); }\n\n.clf-body { padding: 16px 18px; overflow-y: auto; display: flex; flex-direction: column; gap: 14px; }\n.clf-field { display: flex; flex-direction: column; gap: 6px; }\n.clf-label { font-size: 12px; font-weight: 600; color: var(--clf-muted); }\n.clf-input, .clf-textarea {\n width: 100%; padding: 9px 11px;\n font-family: var(--clf-font); font-size: 14px;\n color: var(--clf-fg); background: var(--clf-input-bg);\n border: 1px solid var(--clf-border); border-radius: 8px;\n outline: none; transition: border-color 0.15s ease;\n}\n.clf-input:focus, .clf-textarea:focus { border-color: var(--clf-primary); }\n.clf-textarea { min-height: 110px; resize: vertical; line-height: 1.5; }\n\n/* Type chooser \u2014 pills, active one carries the brand colour. */\n.clf-types { display: flex; flex-wrap: wrap; gap: 6px; }\n.clf-type {\n padding: 6px 13px; border-radius: 999px; cursor: pointer;\n font-family: var(--clf-font); font-size: 12.5px; font-weight: 600;\n background: transparent; color: var(--clf-muted);\n border: 1px solid var(--clf-border);\n transition: color 0.15s, border-color 0.15s, background 0.15s;\n}\n.clf-type:hover { color: var(--clf-fg); }\n.clf-type[aria-pressed=\"true\"] {\n background: var(--clf-primary); border-color: var(--clf-primary); color: #fff;\n}\n\n/* Attachments */\n.clf-attach-hint { font-size: 11.5px; color: var(--clf-muted); }\n.clf-toolbar {\n display: flex;\n gap: 2px;\n margin-bottom: 4px;\n}\n.clf-fmt {\n min-width: 28px;\n height: 26px;\n padding: 0 6px;\n border: 1px solid transparent;\n border-radius: 4px;\n background: transparent;\n color: var(--clf-muted);\n font: inherit;\n font-size: 12px;\n line-height: 1;\n cursor: pointer;\n}\n.clf-fmt:hover { background: var(--clf-input-bg); color: var(--clf-fg); border-color: var(--clf-border); }\n.clf-fmt:focus-visible { outline: 2px solid var(--clf-primary); outline-offset: 1px; }\n.clf-fmt code { font-size: 11px; }\n.clf-thumbs { display: flex; flex-wrap: wrap; gap: 8px; }\n.clf-thumb { position: relative; width: 56px; height: 56px; border-radius: 8px; overflow: hidden; border: 1px solid var(--clf-border); }\n.clf-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }\n.clf-thumb button {\n position: absolute; top: 2px; right: 2px;\n width: 18px; height: 18px; border-radius: 999px; border: none;\n background: rgba(0,0,0,0.65); color: #fff; cursor: pointer;\n font-size: 12px; line-height: 1; padding: 0;\n}\n\n.clf-foot {\n display: flex; align-items: center; gap: 8px;\n padding: 14px 18px; border-top: 1px solid var(--clf-border);\n}\n.clf-spacer { flex: 1; }\n.clf-btn {\n padding: 9px 18px; border-radius: 8px; cursor: pointer;\n font-family: var(--clf-font); font-size: 13.5px; font-weight: 600;\n border: 1px solid transparent; transition: filter 0.15s ease;\n}\n.clf-btn--primary { background: var(--clf-primary); color: #fff; }\n.clf-btn--primary:hover:not(:disabled) { filter: brightness(1.08); }\n.clf-btn--ghost { background: transparent; color: var(--clf-muted); border-color: var(--clf-border); }\n.clf-btn--ghost:hover { color: var(--clf-fg); }\n.clf-btn:disabled { opacity: 0.55; cursor: not-allowed; }\n\n.clf-error { font-size: 12.5px; color: #ef4444; }\n\n/* Success */\n.clf-success { padding: 30px 20px; text-align: center; }\n.clf-success__tick {\n width: 46px; height: 46px; border-radius: 999px; margin: 0 auto 14px;\n background: var(--clf-primary); color: #fff;\n display: flex; align-items: center; justify-content: center; font-size: 22px;\n}\n.clf-success__title { font-size: 15px; font-weight: 700; margin-bottom: 5px; }\n.clf-success__body { font-size: 13px; color: var(--clf-muted); }\n\n@media (max-width: 520px) {\n .clf-dialog { max-width: none; }\n .clf-trigger--sticky { font-size: 12px; padding: 8px 13px; }\n}\n`;\n", "import type { SdkConfig, GuideClient, Category, ArticleDetail, SearchResult, SdkHero } from './types';\nimport { GuideApiClient } from './api-client';\nimport { KbRenderer } from './renderer';\nimport { BASE_CSS, buildCssVars } from './styles';\n\nexport type { SdkConfig, GuideClient, Category, ArticleDetail, SearchResult, SdkHero };\n\nconst DEFAULT_API_URL = 'https://api.clearlaunch.ai';\n\nfunction injectStyles(vars: string): void {\n const id = 'cl-sdk-styles';\n if (document.getElementById(id)) return;\n const style = document.createElement('style');\n style.id = id;\n style.textContent = BASE_CSS + `:root { ${vars} }`;\n document.head.appendChild(style);\n}\n\nfunction resolveTarget(target: string | HTMLElement | undefined): HTMLElement | null {\n if (!target) return null;\n if (typeof target === 'string') return document.querySelector<HTMLElement>(target);\n return target;\n}\n\nexport async function init(config: SdkConfig): Promise<GuideClient> {\n const apiUrl = config.apiUrl || DEFAULT_API_URL;\n const client = new GuideApiClient(apiUrl, config.guide);\n\n // Fetch remote config for guide title + theme defaults\n let remoteTitle = config.guide;\n let remoteMode: SdkConfig['mode'] = 'inpage';\n let remoteSide: 'left' | 'right' = 'right';\n let remoteLayout: 'two-col' | 'one-col' = 'two-col';\n let remoteTheme: Record<string, string> = {};\n try {\n const res = await fetch(`${apiUrl}/api/v1/public/guides/${config.guide}/config`);\n if (res.ok) {\n const data = await res.json() as {\n guideTitle?: string;\n sdkConfig?: {\n renderMode?: string;\n slideoutSide?: string;\n layout?: string;\n theme?: Record<string, string>;\n };\n };\n remoteTitle = data.guideTitle || remoteTitle;\n remoteMode = (data.sdkConfig?.renderMode as SdkConfig['mode']) || remoteMode;\n remoteSide = (data.sdkConfig?.slideoutSide as 'left' | 'right') || remoteSide;\n remoteLayout = (data.sdkConfig?.layout as 'two-col' | 'one-col') || remoteLayout;\n remoteTheme = data.sdkConfig?.theme || {};\n }\n } catch { /* offline/dev */ }\n\n const mode = config.mode || remoteMode;\n const side = config.side || remoteSide;\n const layout = config.layout || remoteLayout;\n const hero: SdkHero | undefined = config.hero;\n const theme = { ...remoteTheme, ...config.theme };\n const cssVars = buildCssVars(theme);\n const logoUrl = (theme as { logoUrl?: string }).logoUrl;\n\n // Pure API mode \u2014 no DOM\n if (mode === 'api') {\n return {\n getCategories: () => client.getCategories(),\n getArticle: (slug) => client.getArticle(slug),\n search: (query, topK) => client.search(query, topK),\n open: () => {},\n close: () => {},\n destroy: () => {},\n };\n }\n\n injectStyles(cssVars);\n\n if (mode === 'inpage') {\n const target = resolveTarget(config.target);\n if (!target) throw new Error('GuideSDK inpage mode requires a valid `target` element');\n target.style.setProperty('height', target.style.height || '100%');\n const renderer = new KbRenderer(client, remoteTitle, logoUrl, target, layout, hero);\n return {\n getCategories: () => client.getCategories(),\n getArticle: (slug) => client.getArticle(slug),\n search: (query, topK) => client.search(query, topK),\n open: (slug?) => { if (slug) void renderer.showArticle(slug); },\n close: () => {},\n destroy: () => { target.innerHTML = ''; },\n };\n }\n\n // \u2500\u2500 Slideout mode \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n // Resolve margin + radius\n const margin = config.drawerMargin ?? '0';\n const hasMargin = margin !== '0' && margin !== '0px';\n const radius = config.drawerRadius ?? (hasMargin ? '12px' : '0');\n\n const overlay = document.createElement('div');\n overlay.className = 'cl-sdk-overlay';\n\n const drawer = document.createElement('div');\n drawer.className = `cl-sdk-drawer ${side}${hasMargin ? ' has-margin' : ''}`;\n drawer.style.setProperty('--cl-sdk-drawer-margin', margin);\n drawer.style.setProperty('--cl-sdk-drawer-radius', radius);\n // wrap radius so the inner cl-sdk-wrap inherits it\n drawer.style.setProperty('--cl-sdk-wrap-radius', radius);\n\n let renderer: KbRenderer | null = null;\n\n function openDrawer(articleSlug?: string) {\n if (!document.body.contains(overlay)) {\n document.body.appendChild(overlay);\n document.body.appendChild(drawer);\n }\n if (!renderer) {\n renderer = new KbRenderer(client, remoteTitle, logoUrl, drawer, layout, hero, closeDrawer);\n }\n requestAnimationFrame(() => {\n overlay.classList.add('open');\n drawer.classList.add('open');\n });\n if (articleSlug) void renderer.showArticle(articleSlug);\n overlay.addEventListener('click', closeDrawer, { once: true });\n }\n\n function closeDrawer() {\n overlay.classList.remove('open');\n drawer.classList.remove('open');\n }\n\n return {\n getCategories: () => client.getCategories(),\n getArticle: (slug) => client.getArticle(slug),\n search: (query, topK) => client.search(query, topK),\n open: (slug?) => openDrawer(slug),\n close: () => closeDrawer(),\n destroy: () => {\n closeDrawer();\n overlay.remove();\n drawer.remove();\n renderer = null;\n },\n };\n}\n", "import type { Category, ArticleDetail, SearchResult } from './types';\n\nexport class GuideApiClient {\n private base: string;\n private slug: string;\n\n constructor(apiUrl: string, guideSlug: string) {\n this.base = apiUrl.replace(/\\/$/, '');\n this.slug = guideSlug;\n }\n\n private async req<T>(path: string, options?: RequestInit): Promise<T> {\n const res = await fetch(`${this.base}/api/v1/public/guides/${this.slug}${path}`, {\n headers: { 'Content-Type': 'application/json' },\n ...options,\n });\n if (!res.ok) throw new Error(`GuideSDK: ${res.status} ${path}`);\n return res.json() as Promise<T>;\n }\n\n async getCategories(): Promise<Category[]> {\n return this.req<Category[]>('/categories');\n }\n\n async getArticle(articleSlug: string): Promise<ArticleDetail> {\n return this.req<ArticleDetail>(`/articles/${articleSlug}`);\n }\n\n async search(query: string, topK?: number): Promise<SearchResult[]> {\n const data = await this.req<{ results: SearchResult[] }>('/search', {\n method: 'POST',\n body: JSON.stringify({ query, topK }),\n });\n return data.results;\n }\n}\n", "import type { GuideApiClient } from './api-client';\nimport type { Category, ArticleDetail, SearchResult, SdkHero } from './types';\n\nexport class KbRenderer {\n private client: GuideApiClient;\n private guideTitle: string;\n private logoUrl: string | undefined;\n private hero: SdkHero | undefined;\n private container: HTMLElement;\n private layout: 'two-col' | 'one-col';\n private categories: Category[] = [];\n private searchTimer: ReturnType<typeof setTimeout> | null = null;\n\n private bodyEl!: HTMLElement;\n private searchInput!: HTMLInputElement;\n\n constructor(\n client: GuideApiClient,\n guideTitle: string,\n logoUrl: string | undefined,\n container: HTMLElement,\n layout: 'two-col' | 'one-col' = 'two-col',\n hero: SdkHero | undefined,\n onClose?: () => void,\n ) {\n this.client = client;\n this.guideTitle = guideTitle;\n this.logoUrl = logoUrl;\n this.hero = hero;\n this.container = container;\n this.layout = layout;\n this.buildShell(onClose);\n void this.loadCategories();\n }\n\n private buildShell(onClose?: () => void) {\n this.container.innerHTML = `\n <div class=\"cl-sdk-wrap layout-${this.layout}\">\n <div class=\"cl-sdk-header\">\n ${this.logoUrl ? `<img class=\"cl-sdk-logo\" src=\"${this.logoUrl}\" alt=\"logo\" />` : ''}\n <span class=\"cl-sdk-title\">${this.guideTitle}</span>\n ${onClose ? `<button class=\"cl-sdk-close\" aria-label=\"Close\">&#x2715;</button>` : ''}\n </div>\n <div class=\"cl-sdk-search\">\n <input type=\"search\" placeholder=\"Search articles\u2026\" autocomplete=\"off\" spellcheck=\"false\" />\n </div>\n <div class=\"cl-sdk-body\">\n <div class=\"cl-sdk-loading\">Loading\u2026</div>\n </div>\n </div>\n `;\n this.bodyEl = this.container.querySelector('.cl-sdk-body')!;\n this.searchInput = this.container.querySelector('input[type=\"search\"]')!;\n const closeBtn = this.container.querySelector('.cl-sdk-close');\n if (closeBtn && onClose) closeBtn.addEventListener('click', onClose);\n this.searchInput.addEventListener('input', () => this.onSearchInput());\n }\n\n private async loadCategories() {\n this.categories = await this.client.getCategories();\n this.renderIndex();\n }\n\n // \u2500\u2500\u2500 Index view (two-col grid or one-col list) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private renderIndex() {\n if (!this.categories.length) {\n this.bodyEl.innerHTML = '<div class=\"cl-sdk-empty\">No articles published yet.</div>';\n return;\n }\n\n // Flatten all articles into one list with their category as a label\n // then split into two columns for two-col layout\n const allSections = this.categories.map(cat => ({\n label: this.formatCatLabel(cat.category),\n articles: cat.articles,\n }));\n\n const heroHtml = this.buildHeroHtml();\n\n if (this.layout === 'two-col') {\n // Split sections across two columns greedily by article count\n const { left, right } = this.splitColumns(allSections);\n this.bodyEl.innerHTML = `\n ${heroHtml}\n <div class=\"cl-sdk-index\">\n <div class=\"cl-sdk-index-col\">${this.renderIndexCol(left)}</div>\n <div class=\"cl-sdk-index-col\">${this.renderIndexCol(right)}</div>\n </div>\n `;\n } else {\n this.bodyEl.innerHTML = `\n ${heroHtml}\n <div class=\"cl-sdk-index cl-sdk-index--one-col\">\n <div class=\"cl-sdk-index-col\">${this.renderIndexCol(allSections)}</div>\n </div>\n `;\n }\n\n this.bodyEl.querySelectorAll<HTMLElement>('.cl-sdk-index-item').forEach(btn => {\n btn.addEventListener('click', () => void this.openArticle(btn.dataset['slug']!));\n });\n\n // Hero deep-links\n this.bodyEl.querySelectorAll<HTMLElement>('[data-hero-slug]').forEach(btn => {\n btn.addEventListener('click', () => void this.openArticle(btn.dataset['heroSlug']!));\n });\n }\n\n private buildHeroHtml(): string {\n if (!this.hero) return '';\n const linksHtml = this.hero.links?.length\n ? `<div class=\"cl-sdk-hero-links\">` +\n this.hero.links.map(l =>\n `<button class=\"cl-sdk-hero-link\" data-hero-slug=\"${l.slug}\">${l.label}</button>`\n ).join('') +\n `</div>`\n : '';\n return `\n <div class=\"cl-sdk-hero\" style=\"${this.hero.bg ? `background:${this.hero.bg};` : ''}${this.hero.color ? `color:${this.hero.color};` : ''}\">\n ${this.hero.heading ? `<div class=\"cl-sdk-hero-heading\">${this.hero.heading}</div>` : ''}\n ${this.hero.body ? `<div class=\"cl-sdk-hero-body\">${this.hero.body}</div>` : ''}\n ${linksHtml}\n </div>\n `;\n }\n\n private renderIndexCol(sections: { label: string; articles: { slug: string; title: string }[] }[]): string {\n return sections.map(s =>\n `<div class=\"cl-sdk-idx-cat\">${s.label}</div>` +\n s.articles.map(a =>\n `<button class=\"cl-sdk-index-item\" data-slug=\"${a.slug}\">\n <span class=\"cl-sdk-index-item-title\">${a.title}</span>\n <span class=\"cl-sdk-index-item-chevron\">\u203A</span>\n </button>`\n ).join('')\n ).join('');\n }\n\n private splitColumns(sections: { label: string; articles: { slug: string; title: string }[] }[]) {\n // Greedy split \u2014 put sections into left until we pass half total article count\n const total = sections.reduce((n, s) => n + s.articles.length, 0);\n let left: typeof sections = [], right: typeof sections = [], count = 0;\n for (const s of sections) {\n if (count < Math.ceil(total / 2)) { left.push(s); count += s.articles.length; }\n else right.push(s);\n }\n return { left, right };\n }\n\n // \u2500\u2500\u2500 Article view \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async showArticle(slug: string) {\n this.openArticle(slug);\n }\n\n private async openArticle(slug: string) {\n this.bodyEl.innerHTML = `\n <div class=\"cl-sdk-article-view\">\n <button class=\"cl-sdk-back\">\u2039 All articles</button>\n <div class=\"cl-sdk-article-content\"><div class=\"cl-sdk-loading\">Loading\u2026</div></div>\n </div>\n `;\n this.bodyEl.querySelector('.cl-sdk-back')!\n .addEventListener('click', () => this.renderIndex());\n const contentEl = this.bodyEl.querySelector('.cl-sdk-article-content')!;\n try {\n const article: ArticleDetail = await this.client.getArticle(slug);\n contentEl.innerHTML = article.html;\n (contentEl as HTMLElement).scrollTop = 0;\n this.bodyEl.scrollTop = 0;\n } catch {\n contentEl.innerHTML = '<div class=\"cl-sdk-empty\">Article not found.</div>';\n }\n }\n\n // \u2500\u2500\u2500 Search \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private onSearchInput() {\n if (this.searchTimer) clearTimeout(this.searchTimer);\n const q = this.searchInput.value.trim();\n if (!q) { this.renderIndex(); return; }\n this.searchTimer = setTimeout(() => void this.runSearch(q), 300);\n }\n\n private async runSearch(query: string) {\n this.bodyEl.innerHTML = '<div class=\"cl-sdk-loading\">Searching\u2026</div>';\n try {\n const results: SearchResult[] = await this.client.search(query, 8);\n if (!results.length) {\n this.bodyEl.innerHTML = '<div class=\"cl-sdk-empty\">No results found.</div>';\n return;\n }\n this.bodyEl.innerHTML = `<div class=\"cl-sdk-search-results\">` +\n results.map(r =>\n `<div class=\"cl-sdk-sr-item\" data-id=\"${r.articleId}\">\n <div class=\"cl-sdk-sr-title\">${r.articleTitle}</div>\n <div class=\"cl-sdk-sr-snippet\">${r.snippet}</div>\n </div>`\n ).join('') + `</div>`;\n this.bodyEl.querySelectorAll<HTMLElement>('.cl-sdk-sr-item').forEach(el => {\n const found = this.categories.flatMap(c => c.articles).find(a => a.id === el.dataset['id']);\n if (found) el.addEventListener('click', () => void this.openArticle(found.slug));\n });\n } catch {\n this.bodyEl.innerHTML = '<div class=\"cl-sdk-empty\">Search failed.</div>';\n }\n }\n\n private formatCatLabel(cat: string): string {\n return cat.replace(/-/g, ' ');\n }\n}\n", "import type { SdkTheme } from './types';\n\nexport function buildCssVars(theme: SdkTheme = {}): string {\n const primary = theme.primaryColor || '#6366f1';\n const font = theme.fontFamily || 'system-ui, sans-serif';\n const radius = theme.borderRadius || '10px';\n return `\n --cl-sdk-primary: ${primary};\n --cl-sdk-primary-muted: ${primary}22;\n --cl-sdk-font: ${font};\n --cl-sdk-radius: ${radius};\n --cl-sdk-bg: #13141f;\n --cl-sdk-surface: #1c1d2e;\n --cl-sdk-surface-2: #23243a;\n --cl-sdk-border: rgba(255,255,255,0.08);\n --cl-sdk-text: #e8eaf0;\n --cl-sdk-text-muted: #7b809a;\n --cl-sdk-text-secondary: #b0b4c8;\n `;\n}\n\nexport const BASE_CSS = `\n .cl-sdk-wrap *, .cl-sdk-wrap *::before, .cl-sdk-wrap *::after { box-sizing: border-box; margin: 0; padding: 0; }\n\n /* \u2500\u2500 Shell \u2500\u2500 */\n .cl-sdk-wrap {\n font-family: var(--cl-sdk-font);\n font-size: 14px;\n color: var(--cl-sdk-text);\n background: var(--cl-sdk-bg);\n border-radius: var(--cl-sdk-wrap-radius, 0);\n display: flex;\n flex-direction: column;\n height: 100%;\n max-height: 100%;\n overflow: hidden;\n }\n\n /* \u2500\u2500 Header \u2500\u2500 */\n .cl-sdk-header {\n display: flex; align-items: center; gap: 12px;\n padding: 16px 20px;\n border-bottom: 1px solid var(--cl-sdk-border);\n background: var(--cl-sdk-surface);\n flex-shrink: 0; min-height: 56px;\n }\n .cl-sdk-logo { height: 26px; object-fit: contain; }\n .cl-sdk-title { font-size: 15px; font-weight: 700; flex: 1; letter-spacing: -0.2px; }\n .cl-sdk-close {\n background: none; border: none; cursor: pointer;\n color: var(--cl-sdk-text-muted); font-size: 18px; line-height: 1;\n padding: 4px 8px; border-radius: 6px; transition: all 0.15s;\n }\n .cl-sdk-close:hover { background: rgba(255,255,255,0.08); color: var(--cl-sdk-text); }\n\n /* \u2500\u2500 Search \u2500\u2500 */\n .cl-sdk-search {\n padding: 12px 16px;\n border-bottom: 1px solid var(--cl-sdk-border);\n background: var(--cl-sdk-surface);\n flex-shrink: 0;\n }\n .cl-sdk-search input {\n width: 100%; padding: 8px 12px;\n background: var(--cl-sdk-bg); border: 1px solid var(--cl-sdk-border);\n border-radius: 8px; color: var(--cl-sdk-text); font-size: 13px; outline: none;\n transition: border-color 0.15s;\n }\n .cl-sdk-search input::placeholder { color: var(--cl-sdk-text-muted); }\n .cl-sdk-search input:focus { border-color: var(--cl-sdk-primary); }\n\n /* \u2500\u2500 Body \u2014 scrollable container \u2500\u2500 */\n .cl-sdk-body { flex: 1; overflow-y: auto; min-height: 0; overscroll-behavior: contain; }\n .cl-sdk-body::-webkit-scrollbar { width: 4px; }\n .cl-sdk-body::-webkit-scrollbar-thumb { background: var(--cl-sdk-border); border-radius: 4px; }\n\n /* \u2500\u2500 Hero banner \u2500\u2500 */\n .cl-sdk-hero {\n padding: 20px 20px 18px;\n background: var(--cl-sdk-surface-2);\n border-bottom: 1px solid var(--cl-sdk-border);\n }\n .cl-sdk-hero-heading {\n font-size: 16px; font-weight: 700; margin-bottom: 6px;\n color: inherit; letter-spacing: -0.2px;\n }\n .cl-sdk-hero-body {\n font-size: 13px; line-height: 1.55; color: inherit; opacity: 0.8; margin-bottom: 14px;\n }\n .cl-sdk-hero-links { display: flex; flex-wrap: wrap; gap: 8px; }\n .cl-sdk-hero-link {\n padding: 6px 14px;\n background: rgba(255,255,255,0.12); border: 1px solid rgba(255,255,255,0.2);\n border-radius: 20px; color: inherit; font-size: 12px; font-weight: 600;\n cursor: pointer; transition: background 0.15s;\n white-space: nowrap;\n }\n .cl-sdk-hero-link:hover { background: rgba(255,255,255,0.2); }\n\n /* \u2500\u2500 Article index grid \u2500\u2500 */\n .cl-sdk-index {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 0;\n padding: 8px 0 24px;\n }\n .cl-sdk-index--one-col { grid-template-columns: 1fr; }\n .cl-sdk-index-col { padding: 0; }\n\n /* Category label */\n .cl-sdk-idx-cat {\n padding: 20px 20px 6px;\n font-size: 10px; font-weight: 800;\n text-transform: uppercase; letter-spacing: 0.1em;\n color: var(--cl-sdk-text-muted);\n }\n\n /* Article row */\n .cl-sdk-index-item {\n display: flex; align-items: center; justify-content: space-between; gap: 8px;\n width: 100%; text-align: left; background: none;\n border: none; border-bottom: 1px solid var(--cl-sdk-border);\n padding: 11px 20px;\n cursor: pointer; transition: background 0.12s;\n }\n .cl-sdk-index-item:hover { background: var(--cl-sdk-surface-2); }\n .cl-sdk-index-item-title {\n font-size: 13px; color: var(--cl-sdk-text-secondary);\n line-height: 1.4; flex: 1;\n transition: color 0.12s;\n }\n .cl-sdk-index-item:hover .cl-sdk-index-item-title { color: var(--cl-sdk-text); }\n .cl-sdk-index-item-chevron {\n color: var(--cl-sdk-text-muted); font-size: 16px; flex-shrink: 0; opacity: 0.5;\n }\n\n /* \u2500\u2500 Article view \u2500\u2500 */\n .cl-sdk-article-view { display: flex; flex-direction: column; min-height: min-content; }\n .cl-sdk-back {\n display: flex; align-items: center; gap: 6px;\n padding: 12px 20px;\n border: none; border-bottom: 1px solid var(--cl-sdk-border);\n background: var(--cl-sdk-surface);\n color: var(--cl-sdk-primary); font-size: 13px; font-weight: 600;\n cursor: pointer; width: 100%; text-align: left;\n transition: opacity 0.15s; flex-shrink: 0;\n }\n .cl-sdk-back:hover { opacity: 0.75; }\n .cl-sdk-article-content { padding: 24px 24px 40px; flex: 1; }\n\n /* \u2500\u2500 Article typography \u2500\u2500 */\n .cl-sdk-article-content h1 {\n font-size: 20px; font-weight: 700; margin-bottom: 16px;\n color: var(--cl-sdk-text); letter-spacing: -0.3px; line-height: 1.3;\n }\n .cl-sdk-article-content h2 {\n font-size: 15px; font-weight: 700; margin: 24px 0 8px;\n color: var(--cl-sdk-text);\n }\n .cl-sdk-article-content h3 {\n font-size: 12px; font-weight: 800; margin: 18px 0 6px;\n color: var(--cl-sdk-text-muted); text-transform: uppercase; letter-spacing: 0.07em;\n }\n .cl-sdk-article-content p {\n line-height: 1.7; margin-bottom: 14px; color: var(--cl-sdk-text-secondary);\n }\n .cl-sdk-article-content ul, .cl-sdk-article-content ol {\n padding-left: 22px; margin-bottom: 14px;\n }\n .cl-sdk-article-content li {\n line-height: 1.65; color: var(--cl-sdk-text-secondary); margin-bottom: 4px;\n }\n .cl-sdk-article-content strong { color: var(--cl-sdk-text); }\n .cl-sdk-article-content code {\n background: rgba(255,255,255,0.07); padding: 2px 6px;\n border-radius: 4px; font-size: 12px; font-family: monospace; color: var(--cl-sdk-text);\n }\n .cl-sdk-article-content pre {\n background: rgba(0,0,0,0.4); border-radius: 8px;\n padding: 14px 16px; overflow-x: auto; margin-bottom: 14px;\n border: 1px solid var(--cl-sdk-border);\n }\n .cl-sdk-article-content pre code { background: none; padding: 0; }\n .cl-sdk-article-content a { color: var(--cl-sdk-primary); }\n .cl-sdk-article-content hr { border: none; border-top: 1px solid var(--cl-sdk-border); margin: 20px 0; }\n .cl-sdk-article-content table {\n width: 100%; border-collapse: collapse; margin-bottom: 16px; font-size: 13px;\n }\n .cl-sdk-article-content th {\n text-align: left; padding: 8px 12px;\n background: var(--cl-sdk-surface-2); color: var(--cl-sdk-text);\n font-weight: 700; font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em;\n border-bottom: 2px solid var(--cl-sdk-border);\n }\n .cl-sdk-article-content td {\n padding: 8px 12px; color: var(--cl-sdk-text-secondary);\n border-bottom: 1px solid var(--cl-sdk-border); vertical-align: top;\n line-height: 1.5;\n }\n .cl-sdk-article-content tr:last-child td { border-bottom: none; }\n .cl-sdk-article-content tr:hover td { background: var(--cl-sdk-surface-2); }\n\n /* \u2500\u2500 Search results \u2500\u2500 */\n .cl-sdk-search-results { padding: 8px 0; }\n .cl-sdk-sr-item {\n padding: 12px 20px; cursor: pointer;\n border-bottom: 1px solid var(--cl-sdk-border); transition: background 0.12s;\n }\n .cl-sdk-sr-item:hover { background: var(--cl-sdk-surface-2); }\n .cl-sdk-sr-title { font-size: 13px; font-weight: 600; color: var(--cl-sdk-text); }\n .cl-sdk-sr-snippet { font-size: 12px; color: var(--cl-sdk-text-muted); margin-top: 3px; line-height: 1.45; }\n\n /* \u2500\u2500 States \u2500\u2500 */\n .cl-sdk-empty { padding: 48px 20px; text-align: center; color: var(--cl-sdk-text-muted); font-size: 13px; }\n .cl-sdk-loading { padding: 48px 20px; text-align: center; color: var(--cl-sdk-text-muted); font-size: 13px; }\n\n /* \u2500\u2500 Overlay \u2500\u2500 */\n .cl-sdk-overlay {\n position: fixed; inset: 0; background: rgba(0,0,0,0.5);\n z-index: 99998; opacity: 0; transition: opacity 0.25s; pointer-events: none;\n }\n .cl-sdk-overlay.open { opacity: 1; pointer-events: auto; }\n\n /* \u2500\u2500 Drawer \u2500\u2500 */\n .cl-sdk-drawer {\n position: fixed;\n width: min(560px, 96vw);\n z-index: 99999;\n background: var(--cl-sdk-bg);\n box-shadow: -4px 0 40px rgba(0,0,0,0.5);\n transition: transform 0.3s cubic-bezier(.4,0,.2,1);\n display: flex; flex-direction: column;\n overflow: hidden;\n /* Explicit height so child flex items can scroll */\n height: 100dvh;\n }\n .cl-sdk-drawer.right {\n right: var(--cl-sdk-drawer-margin, 0);\n top: var(--cl-sdk-drawer-margin, 0);\n left: auto;\n height: calc(100dvh - var(--cl-sdk-drawer-margin, 0px) - var(--cl-sdk-drawer-margin, 0px));\n transform: translateX(calc(100% + var(--cl-sdk-drawer-margin, 0px)));\n border-radius: var(--cl-sdk-drawer-radius, 0) 0 0 var(--cl-sdk-drawer-radius, 0);\n }\n .cl-sdk-drawer.right.has-margin {\n border-radius: var(--cl-sdk-drawer-radius, 12px);\n box-shadow: 0 8px 48px rgba(0,0,0,0.6);\n }\n .cl-sdk-drawer.left {\n left: var(--cl-sdk-drawer-margin, 0);\n top: var(--cl-sdk-drawer-margin, 0);\n right: auto;\n height: calc(100dvh - var(--cl-sdk-drawer-margin, 0px) - var(--cl-sdk-drawer-margin, 0px));\n transform: translateX(calc(-100% - var(--cl-sdk-drawer-margin, 0px)));\n border-radius: 0 var(--cl-sdk-drawer-radius, 0) var(--cl-sdk-drawer-radius, 0) 0;\n }\n .cl-sdk-drawer.left.has-margin {\n border-radius: var(--cl-sdk-drawer-radius, 12px);\n box-shadow: 0 8px 48px rgba(0,0,0,0.6);\n }\n .cl-sdk-drawer.open { transform: translateX(0); }\n`;\n", "/**\r\n * @clearlaunch/sdk \u2014 one project key, every ClearLaunch surface.\r\n *\r\n * import { clearSDK } from '@clearlaunch/sdk';\r\n * const cl = clearSDK({ projectKey: 'pk_\u2026' });\r\n *\r\n * cl.versions.mount('#whats-new'); // release notes\r\n * cl.guides.mount({ target: '#docs' }); // published user guides\r\n * cl.ticketing.mount({ service: 'dealership' }); // feedback / bug reports\r\n *\r\n * ## How composition works\r\n *\r\n * Each capability lives in its own package so a docs site isn't forced to ship\r\n * a ticketing inbox. They are declared as OPTIONAL peer dependencies: install\r\n * only what you use, and the accessor for anything missing throws a message\r\n * naming the exact package to install rather than a bare `undefined`.\r\n *\r\n * Accessors are lazy \u2014 touching `cl.ticketing` is what resolves and mounts it,\r\n * so an unused capability costs nothing at runtime. The bundled CDN build\r\n * inlines all of them (see build.mjs), so the script tag keeps working with no\r\n * install step.\r\n *\r\n * ## One credential\r\n *\r\n * Sub-packages historically needed their own identifiers: a guide slug, a `fb_`\r\n * feedback token. Both are already public \u2014 guide slugs appear in /docs/:slug\r\n * URLs and the feedback token is served by the widget-config route \u2014 so the\r\n * server resolves them from the `pk_` key and returns them in the SDK config\r\n * (ADR feeb30c5). Callers pass one key and never see the others.\r\n */\r\nimport { createCore, type ClearCoreClient, type ClearCoreConfig, type SdkRemoteConfig } from '@clearlaunch/core';\r\nimport type { VersionsWidget, PublicRelease } from '@clearlaunch/versions';\r\nimport type { FeedbackType, FeedbackTheme, TriggerConfig } from '@clearlaunch/feedback';\r\n\r\nexport type {\r\n ClearCoreConfig, SdkRemoteConfig, VersionsWidget, PublicRelease,\r\n FeedbackType, FeedbackTheme, TriggerConfig,\r\n};\r\n\r\n/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n * Optional peer resolution\r\n * \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\r\n\r\n/**\r\n * Modules the host app supplied by hand.\r\n *\r\n * Bundlers resolve `import` statically, so a truly optional dependency can't be\r\n * `await import`ed without the bundler either erroring or emitting a chunk for\r\n * a package that may not exist. Callers in that situation register the module\r\n * themselves; the CDN build registers all three at startup.\r\n */\r\nconst registry = new Map<string, unknown>();\r\n\r\n/** Supply a capability module explicitly, bypassing package resolution. */\r\nexport function register(pkg: string, mod: unknown): void {\r\n registry.set(pkg, mod);\r\n}\r\n\r\nfunction missing(pkg: string, capability: string): never {\r\n throw new Error(\r\n `[clearlaunch] The \"${capability}\" capability needs ${pkg}, which isn't installed.\\n` +\r\n ` Install it: npm install ${pkg}\\n` +\r\n ` Or, if your bundler can't resolve optional peers, register it yourself:\\n` +\r\n ` import * as mod from '${pkg}';\\n` +\r\n ` import { register } from '@clearlaunch/sdk';\\n` +\r\n ` register('${pkg}', mod);`,\r\n );\r\n}\r\n\r\n/**\r\n * Resolve an optional peer, preferring an explicit registration.\r\n *\r\n * The `require` path covers CJS/Node; the registry covers bundlers. We never\r\n * throw from resolution itself \u2014 the caller decides, so the error names the\r\n * capability the user actually asked for.\r\n */\r\nfunction resolve<T>(pkg: string): T | null {\r\n const registered = registry.get(pkg);\r\n if (registered) return registered as T;\r\n try {\r\n // Hidden from bundlers on purpose: a literal require() of an optional peer\r\n // makes webpack/vite fail the build when it isn't installed.\r\n const req: ((id: string) => unknown) | undefined =\r\n typeof module !== 'undefined' && typeof require === 'function' ? require : undefined;\r\n if (req) return req(pkg) as T;\r\n } catch {\r\n /* not installed, or not a CJS context \u2014 fall through to null */\r\n }\r\n return null;\r\n}\r\n\r\n/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n * Ticketing\r\n * \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\r\n\r\n/**\r\n * Options for the ticketing widget.\r\n *\r\n * `token` is deliberately absent \u2014 it is resolved from the project key. Every\r\n * other option is passed through to @clearlaunch/feedback.\r\n */\r\nexport interface TicketingMountOptions {\r\n /**\r\n * Touchpoint this widget is embedded on (\"dealership\", \"admin\", \u2026). Stored on\r\n * the submission and the resulting card, so feedback can be filtered by\r\n * surface. Must be one of the services declared on the project.\r\n */\r\n service?: string;\r\n trigger?: TriggerConfig;\r\n theme?: FeedbackTheme;\r\n types?: FeedbackType[];\r\n defaultType?: FeedbackType;\r\n source?: string;\r\n user?: { email?: string; name?: string };\r\n metadata?: Record<string, string>;\r\n text?: Record<string, string>;\r\n onSubmit?: (result: { submissionId: string }) => void;\r\n onError?: (err: Error) => void;\r\n onOpen?: () => void;\r\n onClose?: () => void;\r\n}\r\n\r\nexport interface TicketingWidget {\r\n open(type?: FeedbackType): void;\r\n close(): void;\r\n destroy(): void;\r\n}\r\n\r\nexport interface Ticketing {\r\n /**\r\n * Mount the feedback trigger + dialog. Resolves once the project config has\r\n * supplied the feedback token.\r\n */\r\n mount(options?: TicketingMountOptions): Promise<TicketingWidget>;\r\n /** Touchpoints declared on the project, for building your own picker. */\r\n services(): Promise<string[]>;\r\n /** Whether feedback is switched on for this project. */\r\n enabled(): Promise<boolean>;\r\n}\r\n\r\nfunction createTicketing(core: ClearCoreClient, apiUrl?: string): Ticketing {\r\n async function feedbackConfig() {\r\n const cfg = await core.config();\r\n return cfg.feedback && cfg.feedback.enabled ? cfg.feedback : null;\r\n }\r\n\r\n return {\r\n async enabled() {\r\n return !!(await feedbackConfig());\r\n },\r\n\r\n async services() {\r\n const fb = await feedbackConfig();\r\n return fb?.services ?? [];\r\n },\r\n\r\n async mount(options: TicketingMountOptions = {}) {\r\n const fb = await feedbackConfig();\r\n if (!fb) {\r\n throw new Error(\r\n '[clearlaunch] Feedback is not enabled for this project. ' +\r\n 'Turn it on in ClearLaunch \u2192 project \u2192 Feedback.',\r\n );\r\n }\r\n\r\n const mod = resolve<{ mountFeedback: (o: Record<string, unknown>) => TicketingWidget }>(\r\n '@clearlaunch/feedback',\r\n );\r\n if (!mod?.mountFeedback) missing('@clearlaunch/feedback', 'ticketing');\r\n\r\n // A service must be one the project declared, or filtering downstream\r\n // silently accumulates typos that never match anything in the UI.\r\n if (options.service && fb.services?.length && !fb.services.includes(options.service)) {\r\n throw new Error(\r\n `[clearlaunch] service \"${options.service}\" is not declared on this project. ` +\r\n `Declared: ${fb.services.join(', ')}. Add it in ClearLaunch \u2192 project \u2192 Feedback.`,\r\n );\r\n }\r\n\r\n const { service, ...rest } = options;\r\n return mod.mountFeedback({\r\n ...rest,\r\n // Fixed touchpoint wins. Otherwise hand the widget the project's\r\n // declared list so it can offer a picker without the caller having to\r\n // restate what ClearLaunch already knows. Passing both is refused by\r\n // the widget, so they are deliberately exclusive here too.\r\n ...(service\r\n ? { service }\r\n : fb.services?.length ? { services: fb.services } : {}),\r\n token: fb.token,\r\n ...(apiUrl ? { apiUrl } : {}),\r\n });\r\n },\r\n };\r\n}\r\n\r\n/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n * Guides\r\n * \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\r\n\r\nexport interface GuidesMountOptions {\r\n /** Which published guide. Defaults to the project's first published guide. */\r\n slug?: string;\r\n /** Container for the embedded reader. */\r\n target?: string | HTMLElement;\r\n}\r\n\r\nexport interface Guides {\r\n mount(options?: GuidesMountOptions): Promise<unknown>;\r\n /** Slugs of every published guide on this project. */\r\n slugs(): Promise<string[]>;\r\n /** The slug a bare `mount()` would open, if any. */\r\n defaultSlug(): Promise<string | undefined>;\r\n}\r\n\r\nfunction createGuides(core: ClearCoreClient, apiUrl?: string): Guides {\r\n async function guidesConfig() {\r\n const cfg = await core.config();\r\n return cfg.guides ?? { slugs: [] as string[], default: undefined as string | undefined };\r\n }\r\n\r\n return {\r\n async slugs() {\r\n return (await guidesConfig()).slugs ?? [];\r\n },\r\n\r\n async defaultSlug() {\r\n return (await guidesConfig()).default;\r\n },\r\n\r\n async mount(options: GuidesMountOptions = {}) {\r\n const cfg = await guidesConfig();\r\n const slug = options.slug ?? cfg.default;\r\n if (!slug) {\r\n throw new Error(\r\n '[clearlaunch] This project has no published guide. ' +\r\n 'Publish one in ClearLaunch \u2192 project \u2192 Guides, or pass an explicit `slug`.',\r\n );\r\n }\r\n if (options.slug && cfg.slugs?.length && !cfg.slugs.includes(options.slug)) {\r\n throw new Error(\r\n `[clearlaunch] guide \"${options.slug}\" is not published on this project. ` +\r\n `Published: ${cfg.slugs.join(', ')}.`,\r\n );\r\n }\r\n\r\n const mod = resolve<{ init: (c: Record<string, unknown>) => Promise<unknown> }>(\r\n '@clearlaunch/guide-sdk',\r\n );\r\n if (!mod?.init) missing('@clearlaunch/guide-sdk', 'guides');\r\n\r\n return mod.init({\r\n slug,\r\n ...(options.target ? { target: options.target } : {}),\r\n ...(apiUrl ? { apiUrl } : {}),\r\n });\r\n },\r\n };\r\n}\r\n\r\n/* \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n * Client\r\n * \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\r\n\r\nexport interface ClearLaunchClient {\r\n readonly projectKey: string;\r\n /** Raw core client, for direct calls against the public SDK routes. */\r\n readonly core: ClearCoreClient;\r\n /** Resolved project config (name, widgets, theme, guides, feedback). */\r\n config(): Promise<SdkRemoteConfig>;\r\n readonly versions: VersionsWidget;\r\n readonly guides: Guides;\r\n /** Feedback / bug reports. Also available as `.feedback`. */\r\n readonly ticketing: Ticketing;\r\n /** Alias of `.ticketing`, matching the @clearlaunch/feedback package name. */\r\n readonly feedback: Ticketing;\r\n}\r\n\r\n/**\r\n * Create a ClearLaunch client.\r\n *\r\n * Capabilities are lazily constructed on first access and then cached, so an\r\n * app that only ever touches `.versions` never resolves the ticketing or guide\r\n * packages at all.\r\n */\r\nexport function clearSDK(config: ClearCoreConfig): ClearLaunchClient {\r\n const core = createCore(config);\r\n\r\n let versions: VersionsWidget | null = null;\r\n let guides: Guides | null = null;\r\n let ticketing: Ticketing | null = null;\r\n\r\n const client: ClearLaunchClient = {\r\n projectKey: core.projectKey,\r\n core,\r\n config: () => core.config(),\r\n\r\n get versions() {\r\n if (!versions) {\r\n const mod = resolve<{ createVersions: (c: ClearCoreClient) => VersionsWidget }>(\r\n '@clearlaunch/versions',\r\n );\r\n if (!mod?.createVersions) missing('@clearlaunch/versions', 'versions');\r\n versions = mod.createVersions(core);\r\n }\r\n return versions;\r\n },\r\n\r\n get guides() {\r\n if (!guides) guides = createGuides(core, config.apiUrl);\r\n return guides;\r\n },\r\n\r\n get ticketing() {\r\n if (!ticketing) ticketing = createTicketing(core, config.apiUrl);\r\n return ticketing;\r\n },\r\n\r\n get feedback() {\r\n return this.ticketing;\r\n },\r\n };\r\n\r\n return client;\r\n}\r\n\r\n/**\r\n * Back-compat entry for the CDN script tag: `ClearLaunch.init({ projectKey })`.\r\n * Identical to clearSDK().\r\n */\r\nexport const init = clearSDK;\r\n\r\nexport const ClearLaunch = { init, clearSDK, register };\r\nexport default ClearLaunch;\r\n"],
5
+ "mappings": "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A;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;EHdxB,IAAMC,G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",
6
+ "names": ["bundled_exports", "__export", "ClearLaunch", "clearSDK", "index_default", "init", "register", "src_exports", "__export", "createVersions", "DEFAULT_API_URL", "esc", "s", "d", "injectStyles", "id", "css", "style", "resolveTarget", "target", "createCore", "config", "sdkBase", "configPromise", "get", "path", "res", "configOnce", "VERSIONS_CSS", "fmtDate", "iso", "d", "group", "label", "items", "modifier", "li", "i", "esc", "render", "el", "releases", "body", "createVersions", "core", "list", "target", "resolveTarget", "injectStyles", "VERSIONS_CSS", "src_exports", "__export", "src_default", "mountFeedback", "CSS", "DEFAULT_API_URL", "STYLE_ID", "MAX_ATTACHMENTS", "MAX_ATTACHMENT_BYTES", "TYPE_LABELS", "DEFAULT_TEXT", "esc", "s", "injectStyles", "el", "CSS", "readAsBase64", "file", "resolve", "reject", "fr", "result", "FORMAT_BUTTONS", "applyFormat", "btn", "start", "end", "body", "replacement", "cursorStart", "cursorEnd", "l", "w", "mountFeedback", "opts", "_a", "_b", "_c", "_d", "_e", "_f", "_g", "_h", "_i", "_j", "_k", "_l", "_m", "_n", "_o", "text", "types", "submitUrl", "trigger", "root", "scheme", "selectedType", "pickable", "selectedService", "attachments", "overlay", "lastFocused", "destroyed", "triggerEl", "edge", "off", "value", "corner", "inset", "open", "renderAttachments", "host", "a", "i", "addFiles", "files", "onErr", "payload", "type", "t", "sv", "b", "q", "sel", "titleEl", "descEl", "emailEl", "errEl", "e", "spec", "x", "serviceEl", "thumbs", "submitBtn", "showErr", "m", "clearErr", "stop", "close", "title", "description", "res", "k", "v", "data", "mimeType", "filename", "json", "showSuccess", "err", "onKey", "dialog", "destroy", "src_default", "src_exports", "__export", "init", "GuideApiClient", "apiUrl", "guideSlug", "path", "options", "res", "articleSlug", "query", "topK", "KbRenderer", "client", "guideTitle", "logoUrl", "container", "layout", "hero", "onClose", "closeBtn", "allSections", "cat", "heroHtml", "left", "right", "btn", "_a", "linksHtml", "l", "sections", "s", "a", "total", "n", "count", "slug", "contentEl", "article", "q", "query", "results", "el", "found", "c", "buildCssVars", "theme", "primary", "font", "radius", "BASE_CSS", "DEFAULT_API_URL", "injectStyles", "vars", "id", "style", "BASE_CSS", "resolveTarget", "target", "init", "config", "_a", "_b", "_c", "_d", "_e", "_f", "apiUrl", "client", "GuideApiClient", "remoteTitle", "remoteMode", "remoteSide", "remoteLayout", "remoteTheme", "res", "data", "mode", "side", "layout", "hero", "theme", "cssVars", "buildCssVars", "logoUrl", "slug", "query", "topK", "renderer", "KbRenderer", "margin", "hasMargin", "radius", "overlay", "drawer", "openDrawer", "articleSlug", "closeDrawer", "registry", "register", "pkg", "mod", "missing", "capability", "resolve", "registered", "req", "__require", "createTicketing", "core", "apiUrl", "feedbackConfig", "cfg", "_a", "fb", "options", "_b", "service", "rest", "createGuides", "guidesConfig", "slug", "clearSDK", "config", "createCore", "versions", "guides", "ticketing", "init", "ClearLaunch", "index_default", "register", "src_exports"]
7
7
  }
@@ -0,0 +1,128 @@
1
+ /**
2
+ * @clearlaunch/sdk — one project key, every ClearLaunch surface.
3
+ *
4
+ * import { clearSDK } from '@clearlaunch/sdk';
5
+ * const cl = clearSDK({ projectKey: 'pk_…' });
6
+ *
7
+ * cl.versions.mount('#whats-new'); // release notes
8
+ * cl.guides.mount({ target: '#docs' }); // published user guides
9
+ * cl.ticketing.mount({ service: 'dealership' }); // feedback / bug reports
10
+ *
11
+ * ## How composition works
12
+ *
13
+ * Each capability lives in its own package so a docs site isn't forced to ship
14
+ * a ticketing inbox. They are declared as OPTIONAL peer dependencies: install
15
+ * only what you use, and the accessor for anything missing throws a message
16
+ * naming the exact package to install rather than a bare `undefined`.
17
+ *
18
+ * Accessors are lazy — touching `cl.ticketing` is what resolves and mounts it,
19
+ * so an unused capability costs nothing at runtime. The bundled CDN build
20
+ * inlines all of them (see build.mjs), so the script tag keeps working with no
21
+ * install step.
22
+ *
23
+ * ## One credential
24
+ *
25
+ * Sub-packages historically needed their own identifiers: a guide slug, a `fb_`
26
+ * feedback token. Both are already public — guide slugs appear in /docs/:slug
27
+ * URLs and the feedback token is served by the widget-config route — so the
28
+ * server resolves them from the `pk_` key and returns them in the SDK config
29
+ * (ADR feeb30c5). Callers pass one key and never see the others.
30
+ */
31
+ import { type ClearCoreClient, type ClearCoreConfig, type SdkRemoteConfig } from '@clearlaunch/core';
32
+ import type { VersionsWidget, PublicRelease } from '@clearlaunch/versions';
33
+ import type { FeedbackType, FeedbackTheme, TriggerConfig } from '@clearlaunch/feedback';
34
+ export type { ClearCoreConfig, SdkRemoteConfig, VersionsWidget, PublicRelease, FeedbackType, FeedbackTheme, TriggerConfig, };
35
+ /** Supply a capability module explicitly, bypassing package resolution. */
36
+ export declare function register(pkg: string, mod: unknown): void;
37
+ /**
38
+ * Options for the ticketing widget.
39
+ *
40
+ * `token` is deliberately absent — it is resolved from the project key. Every
41
+ * other option is passed through to @clearlaunch/feedback.
42
+ */
43
+ export interface TicketingMountOptions {
44
+ /**
45
+ * Touchpoint this widget is embedded on ("dealership", "admin", …). Stored on
46
+ * the submission and the resulting card, so feedback can be filtered by
47
+ * surface. Must be one of the services declared on the project.
48
+ */
49
+ service?: string;
50
+ trigger?: TriggerConfig;
51
+ theme?: FeedbackTheme;
52
+ types?: FeedbackType[];
53
+ defaultType?: FeedbackType;
54
+ source?: string;
55
+ user?: {
56
+ email?: string;
57
+ name?: string;
58
+ };
59
+ metadata?: Record<string, string>;
60
+ text?: Record<string, string>;
61
+ onSubmit?: (result: {
62
+ submissionId: string;
63
+ }) => void;
64
+ onError?: (err: Error) => void;
65
+ onOpen?: () => void;
66
+ onClose?: () => void;
67
+ }
68
+ export interface TicketingWidget {
69
+ open(type?: FeedbackType): void;
70
+ close(): void;
71
+ destroy(): void;
72
+ }
73
+ export interface Ticketing {
74
+ /**
75
+ * Mount the feedback trigger + dialog. Resolves once the project config has
76
+ * supplied the feedback token.
77
+ */
78
+ mount(options?: TicketingMountOptions): Promise<TicketingWidget>;
79
+ /** Touchpoints declared on the project, for building your own picker. */
80
+ services(): Promise<string[]>;
81
+ /** Whether feedback is switched on for this project. */
82
+ enabled(): Promise<boolean>;
83
+ }
84
+ export interface GuidesMountOptions {
85
+ /** Which published guide. Defaults to the project's first published guide. */
86
+ slug?: string;
87
+ /** Container for the embedded reader. */
88
+ target?: string | HTMLElement;
89
+ }
90
+ export interface Guides {
91
+ mount(options?: GuidesMountOptions): Promise<unknown>;
92
+ /** Slugs of every published guide on this project. */
93
+ slugs(): Promise<string[]>;
94
+ /** The slug a bare `mount()` would open, if any. */
95
+ defaultSlug(): Promise<string | undefined>;
96
+ }
97
+ export interface ClearLaunchClient {
98
+ readonly projectKey: string;
99
+ /** Raw core client, for direct calls against the public SDK routes. */
100
+ readonly core: ClearCoreClient;
101
+ /** Resolved project config (name, widgets, theme, guides, feedback). */
102
+ config(): Promise<SdkRemoteConfig>;
103
+ readonly versions: VersionsWidget;
104
+ readonly guides: Guides;
105
+ /** Feedback / bug reports. Also available as `.feedback`. */
106
+ readonly ticketing: Ticketing;
107
+ /** Alias of `.ticketing`, matching the @clearlaunch/feedback package name. */
108
+ readonly feedback: Ticketing;
109
+ }
110
+ /**
111
+ * Create a ClearLaunch client.
112
+ *
113
+ * Capabilities are lazily constructed on first access and then cached, so an
114
+ * app that only ever touches `.versions` never resolves the ticketing or guide
115
+ * packages at all.
116
+ */
117
+ export declare function clearSDK(config: ClearCoreConfig): ClearLaunchClient;
118
+ /**
119
+ * Back-compat entry for the CDN script tag: `ClearLaunch.init({ projectKey })`.
120
+ * Identical to clearSDK().
121
+ */
122
+ export declare const init: typeof clearSDK;
123
+ export declare const ClearLaunch: {
124
+ init: typeof clearSDK;
125
+ clearSDK: typeof clearSDK;
126
+ register: typeof register;
127
+ };
128
+ export default ClearLaunch;
package/package.json CHANGED
@@ -1,17 +1,53 @@
1
1
  {
2
2
  "name": "@clearlaunch/sdk",
3
- "version": "0.2.1",
4
- "description": "ClearLaunch all-in-one SDK bundle version history, guides, feedback from one project key. Composes @clearlaunch/core + widgets.",
3
+ "version": "0.3.0",
4
+ "description": "ClearLaunch SDK — one project key for release notes, user guides and ticketing. Composes @clearlaunch/core + the capability packages.",
5
5
  "main": "dist/clearlaunch.cjs.js",
6
6
  "module": "dist/clearlaunch.esm.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/clearlaunch.esm.js",
12
+ "require": "./dist/clearlaunch.cjs.js"
13
+ }
14
+ },
7
15
  "files": [
8
- "dist"
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "sideEffects": false,
20
+ "keywords": [
21
+ "clearlaunch",
22
+ "sdk",
23
+ "release-notes",
24
+ "changelog",
25
+ "docs",
26
+ "feedback",
27
+ "ticketing"
9
28
  ],
10
29
  "scripts": {
11
- "build": "node build.mjs"
30
+ "build": "node build.mjs && tsc --emitDeclarationOnly --declaration --outDir dist",
31
+ "test": "npm run build && node test/compose.test.mjs"
32
+ },
33
+ "peerDependencies": {
34
+ "@clearlaunch/core": "^0.1.0",
35
+ "@clearlaunch/feedback": "^0.1.0",
36
+ "@clearlaunch/guide-sdk": "^0.1.0",
37
+ "@clearlaunch/versions": "^0.1.0"
38
+ },
39
+ "peerDependenciesMeta": {
40
+ "@clearlaunch/versions": { "optional": true },
41
+ "@clearlaunch/feedback": { "optional": true },
42
+ "@clearlaunch/guide-sdk": { "optional": true }
12
43
  },
13
44
  "devDependencies": {
14
- "esbuild": "^0.21.0"
45
+ "@clearlaunch/core": "file:../clearsdk-core",
46
+ "@clearlaunch/feedback": "file:../clearsdk-feedback",
47
+ "@clearlaunch/guide-sdk": "file:../sdk",
48
+ "@clearlaunch/versions": "file:../clearsdk-versions",
49
+ "esbuild": "^0.21.0",
50
+ "typescript": "^5.4.0"
15
51
  },
16
52
  "publishConfig": {
17
53
  "access": "public"