@heedkit/sdk-js 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +74 -0
- package/dist/heedkit.iife.js +115 -0
- package/dist/heedkit.iife.js.map +1 -0
- package/dist/index.cjs +714 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +116 -0
- package/dist/index.d.ts +116 -0
- package/dist/index.js +686 -0
- package/dist/index.js.map +1 -0
- package/package.json +57 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts","../src/widget.ts"],"sourcesContent":["export { HeedKitClient } from \"./client\";\nexport type {\n Comment,\n EndUser,\n Feature,\n FeatureKind,\n HeedKitConfig,\n GroupMode,\n InitResult,\n Interaction,\n KindInteractions,\n ShowCounts,\n Theme,\n Visibility,\n} from \"./client\";\n\nexport { mount, type MountOptions, type Widget } from \"./widget\";\n","export type Visibility = \"public\" | \"private\";\n\nexport type Interaction = \"upvote\" | \"downvote\" | \"plus_one\" | \"like\";\n\nexport type KindInteractions = Partial<Record<Interaction, boolean>>;\n\nexport type ShowCounts = Partial<Record<FeatureKind, boolean>>;\n\nexport type GroupMode = \"tabs\" | \"list\";\n\nexport type Theme = {\n primary?: string;\n primaryDark?: string;\n radius?: number;\n /// `\"system\"` follows the OS color scheme at render time.\n mode?: \"light\" | \"dark\" | \"system\";\n font_family?: string;\n font_size?: \"sm\" | \"md\" | \"lg\";\n group_mode?: GroupMode;\n show_counts?: ShowCounts;\n // Older deployments may still send camelCase keys for these — kept for backcompat.\n fontFamily?: string;\n};\n\nexport type EndUser = {\n externalId?: string;\n email?: string;\n name?: string;\n avatarUrl?: string;\n platform?: string;\n};\n\nexport type FeatureKind =\n | \"feature_request\"\n | \"bug_report\"\n | \"improvement\"\n | \"appreciation\"\n | \"other\";\n\nexport type Feature = {\n id: string;\n title: string;\n description: string;\n status: \"open\" | \"planned\" | \"in_progress\" | \"shipped\" | \"declined\";\n kind: FeatureKind;\n /// Whether the item is visible beyond its author + the project team.\n visibility: Visibility;\n /// Whether the item appears on the project's roadmap (public if visibility=public).\n on_roadmap: boolean;\n tag: string | null;\n vote_count: number;\n voted: boolean;\n platform: string | null;\n author_name: string | null;\n created_at: string;\n};\n\nexport type Comment = {\n id: string;\n body: string;\n author_name: string | null;\n is_internal: boolean;\n created_at: string;\n};\n\nexport type HeedKitConfig = {\n projectKey: string;\n apiUrl?: string;\n user?: EndUser;\n};\n\n/// Project configuration returned by /sdk/init (nested under `project`).\nexport type ProjectConfig = {\n name: string;\n theme: Theme;\n enabled_kinds: FeatureKind[];\n /// Default visibility applied to new submissions of each kind.\n kind_visibility: Record<FeatureKind, Visibility>;\n /// Which interactions admin has enabled per kind. The widget should only render\n /// the affordances listed here.\n kind_interactions: Record<FeatureKind, KindInteractions>;\n is_public_roadmap?: boolean;\n};\n\nexport type InitResult = {\n end_user_id: string;\n project: ProjectConfig;\n};\n\nconst DEFAULT_API = \"https://api.heedkit.com\";\nconst DEVICE_ID_KEY = \"heedkit.device_id\";\n\n/**\n * Stable per-browser identifier persisted in localStorage. When the customer\n * doesn't pass `externalId`, we still want votes/submissions to stick to the\n * same EndUser across page loads — otherwise every refresh would create a new\n * anonymous account.\n *\n * Returns null on the server (SSR), so callers should fall back to a fresh id.\n */\nexport function getOrCreateDeviceId(): string | null {\n try {\n if (typeof window === \"undefined\" || !window.localStorage) return null;\n const existing = window.localStorage.getItem(DEVICE_ID_KEY);\n if (existing) return existing;\n const next = \"dev_\" + (crypto?.randomUUID?.() ?? Math.random().toString(36).slice(2));\n window.localStorage.setItem(DEVICE_ID_KEY, next);\n return next;\n } catch {\n // Privacy mode / disabled storage — caller falls back to anonymous.\n return null;\n }\n}\n\n/// Map a raw API feature onto the SDK shape (the backend compacts null fields and\n/// exposes the author display name as `author`).\nfunction normalizeFeature(f: any): Feature {\n return {\n id: String(f.id),\n title: f.title,\n description: f.description ?? \"\",\n status: f.status,\n kind: f.kind,\n visibility: f.visibility,\n on_roadmap: f.on_roadmap ?? false,\n tag: f.tag ?? null,\n vote_count: f.vote_count ?? 0,\n voted: f.voted ?? false,\n platform: f.platform ?? null,\n author_name: f.author_name ?? f.author ?? null,\n created_at: f.created_at,\n };\n}\n\nfunction normalizeComment(c: any): Comment {\n return {\n id: String(c.id),\n body: c.body,\n author_name: c.author_name ?? c.author ?? null,\n // The SDK endpoint only ever returns public comments.\n is_internal: c.is_internal ?? false,\n created_at: c.created_at,\n };\n}\n\nexport class HeedKitClient {\n private apiUrl: string;\n private projectKey: string;\n private endUserId: string | null = null;\n private theme: Theme = {};\n private projectName = \"\";\n private enabledKinds: FeatureKind[] = [];\n private kindVisibility: Partial<Record<FeatureKind, Visibility>> = {};\n private kindInteractions: Partial<Record<FeatureKind, KindInteractions>> = {};\n\n constructor(config: HeedKitConfig) {\n this.apiUrl = config.apiUrl || DEFAULT_API;\n this.projectKey = config.projectKey;\n }\n\n async init(user: EndUser = {}): Promise<InitResult> {\n // If the caller didn't pass an external_id, fall back to a stable\n // per-browser device id so refreshes keep the same EndUser.\n const externalId = user.externalId ?? getOrCreateDeviceId() ?? undefined;\n const body = {\n external_id: externalId,\n email: user.email,\n name: user.name,\n avatar_url: user.avatarUrl,\n platform: user.platform || \"web\",\n };\n const res = await this.request<InitResult>(\"/sdk/init\", \"POST\", body);\n this.endUserId = res.end_user_id;\n // The Rails backend nests project config under `project`; tolerate a flat\n // response from older deployments too.\n const p: any = (res as any).project ?? res;\n this.theme = p.theme || {};\n this.projectName = p.name ?? p.project_name ?? \"\";\n this.enabledKinds = p.enabled_kinds || [];\n this.kindVisibility = p.kind_visibility || {};\n this.kindInteractions = p.kind_interactions || {};\n return res;\n }\n\n getTheme() { return this.theme; }\n getEnabledKinds(): FeatureKind[] { return this.enabledKinds; }\n getKindVisibility() { return this.kindVisibility; }\n getKindInteractions() { return this.kindInteractions; }\n getProjectName() { return this.projectName; }\n getEndUserId() { return this.endUserId; }\n\n /// Convenience: which interactions are enabled for a given kind, in stable order.\n getInteractionsFor(kind: FeatureKind): Interaction[] {\n const row = this.kindInteractions[kind] || {};\n return ([\"upvote\", \"downvote\", \"plus_one\", \"like\"] as Interaction[]).filter(\n (i) => row[i]\n );\n }\n\n async list(\n opts: { status?: string; kind?: FeatureKind; sort?: \"top\" | \"new\" } = {}\n ): Promise<Feature[]> {\n this.ensureInit();\n const params = new URLSearchParams({ end_user_id: this.endUserId! });\n if (opts.status) params.set(\"status\", opts.status);\n if (opts.kind) params.set(\"kind\", opts.kind);\n if (opts.sort) params.set(\"sort\", opts.sort);\n // Rails returns { features, next_cursor }; tolerate a bare array too.\n const res = await this.request<any>(`/sdk/features?${params}`, \"GET\");\n const features = Array.isArray(res) ? res : (res.features ?? []);\n return features.map((f: any) => normalizeFeature(f));\n }\n\n async submit(input: {\n title: string;\n description?: string;\n tag?: string;\n kind?: FeatureKind;\n }): Promise<Feature> {\n this.ensureInit();\n const res = await this.request<any>(\"/sdk/features\", \"POST\", {\n end_user_id: this.endUserId,\n title: input.title,\n description: input.description || \"\",\n tag: input.tag || null,\n kind: input.kind || \"feature_request\",\n });\n return normalizeFeature(res);\n }\n\n async vote(featureId: string): Promise<{ voted: boolean; vote_count: number }> {\n this.ensureInit();\n return this.request(`/sdk/features/${featureId}/vote`, \"POST\", {\n end_user_id: this.endUserId,\n });\n }\n\n async listComments(featureId: string): Promise<Comment[]> {\n const res = await this.request<any>(`/sdk/features/${featureId}/comments`, \"GET\");\n const comments = Array.isArray(res) ? res : (res.comments ?? []);\n return comments.map((c: any) => normalizeComment(c));\n }\n\n async comment(featureId: string, body: string): Promise<Comment> {\n this.ensureInit();\n const res = await this.request<any>(`/sdk/features/${featureId}/comments`, \"POST\", {\n end_user_id: this.endUserId,\n body,\n });\n return normalizeComment(res);\n }\n\n private ensureInit() {\n if (!this.endUserId) throw new Error(\"HeedKit not initialized — call init() first\");\n }\n\n private async request<T>(path: string, method: string, body?: unknown): Promise<T> {\n const res = await fetch(`${this.apiUrl}${path}`, {\n method,\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Project-Key\": this.projectKey,\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n if (!res.ok) {\n let detail = `HTTP ${res.status}`;\n try {\n const j = await res.json();\n detail = j.error || j.detail || detail; // Rails uses `error`; legacy used `detail`.\n } catch { /* non-JSON body */ }\n throw new Error(detail);\n }\n return res.json() as Promise<T>;\n }\n}\n","import {\n HeedKitClient,\n type Comment,\n type Feature,\n type FeatureKind,\n type HeedKitConfig,\n type InitResult,\n type Interaction,\n type Theme,\n} from \"./client\";\n\n// ---------------------------------------------------------------------------\n// Configuration tables\n// ---------------------------------------------------------------------------\n\nconst KIND_OPTIONS: Record<FeatureKind, { label: string; placeholder: string; tabIcon: string }> = {\n feature_request: { label: \"Features\", placeholder: \"What should we build?\", tabIcon: \"💡\" },\n bug_report: { label: \"Bugs\", placeholder: \"What's broken?\", tabIcon: \"🐞\" },\n improvement: { label: \"Improvements\", placeholder: \"What could be better?\", tabIcon: \"✨\" },\n appreciation: { label: \"Appreciation\", placeholder: \"What did you love?\", tabIcon: \"❤️\" },\n other: { label: \"Other\", placeholder: \"Tell us anything\", tabIcon: \"💬\" },\n};\n\n// Icons + a11y labels per interaction. We render only those the admin enabled.\nconst INTERACTION_META: Record<Interaction, { icon: string; label: string }> = {\n upvote: { icon: \"▲\", label: \"Upvote\" },\n downvote: { icon: \"▼\", label: \"Downvote\" },\n plus_one: { icon: \"+1\", label: \"+1\" },\n like: { icon: \"♥\", label: \"Like\" },\n};\n\nconst FONT_SIZES = { sm: \"13px\", md: \"14px\", lg: \"16px\" } as const;\n\n// ---------------------------------------------------------------------------\n// Public surface\n// ---------------------------------------------------------------------------\n\nexport type MountOptions = HeedKitConfig & {\n label?: string;\n hideLauncher?: boolean;\n container?: HTMLElement;\n};\n\nexport type Widget = {\n client: HeedKitClient;\n open: () => void;\n close: () => void;\n destroy: () => void;\n};\n\n// ---------------------------------------------------------------------------\n// Style sheet (single inject; relies on CSS custom properties for theming)\n// ---------------------------------------------------------------------------\n\nconst STYLE_ID = \"heedkit-styles\";\n\nconst CSS = `\n.fk-launcher {\n position: fixed; bottom: 24px; right: 24px; z-index: 2147483645;\n background: var(--fh-primary); color: #fff; border: 0; border-radius: 999px;\n padding: 12px 18px; font-weight: 600; font-size: var(--fh-fs); cursor: pointer;\n box-shadow: 0 10px 24px rgba(0,0,0,.18); font-family: var(--fh-font);\n transition: transform .15s ease;\n}\n.fk-launcher:hover { transform: translateY(-1px); }\n.fk-overlay {\n position: fixed; inset: 0; z-index: 2147483646; display: flex;\n align-items: center; justify-content: center; padding: 16px;\n background: rgba(0,0,0,.45); backdrop-filter: blur(2px);\n font-family: var(--fh-font); font-size: var(--fh-fs);\n}\n.fk-panel {\n width: 100%; max-width: 520px; max-height: 85vh; display: flex; flex-direction: column;\n background: var(--fh-bg); color: var(--fh-fg);\n border-radius: calc(var(--fh-radius) * 1.5); overflow: hidden;\n box-shadow: 0 20px 60px rgba(0,0,0,.3);\n}\n.fk-head { padding: 18px 20px 12px; border-bottom: 1px solid var(--fh-border); }\n.fk-titlerow { display:flex; justify-content:space-between; align-items:center; }\n.fk-title { font-size: calc(var(--fh-fs) + 6px); font-weight: 700; }\n.fk-close {\n background: transparent; border: 0; color: var(--fh-muted);\n font-size: 22px; cursor: pointer; line-height: 1; padding: 0 4px;\n}\n.fk-modes { display:flex; gap: 6px; margin-top: 12px; }\n.fk-mode {\n border: 0; background: transparent; padding: 6px 12px; border-radius: 999px;\n font-size: calc(var(--fh-fs) - 1px); font-weight: 600; cursor: pointer;\n color: var(--fh-muted); font-family: inherit;\n}\n.fk-mode[data-active=\"true\"] { background: var(--fh-primary); color: #fff; }\n.fk-tabs {\n display: flex; flex-wrap: wrap; gap: 6px; padding: 10px 20px; border-bottom: 1px solid var(--fh-border);\n}\n.fk-tab {\n border: 0; background: var(--fh-row); color: var(--fh-fg);\n padding: 6px 12px; border-radius: 999px;\n font-size: calc(var(--fh-fs) - 1px); font-weight: 500; cursor: pointer; font-family: inherit;\n display: inline-flex; align-items: center; gap: 6px;\n}\n.fk-tab[data-active=\"true\"] { background: var(--fh-primary); color: #fff; }\n.fk-body { flex: 1; overflow-y: auto; padding: 16px 20px; }\n.fk-empty { text-align: center; padding: 32px; opacity: .6; }\n.fk-loading { text-align: center; padding: 32px; opacity: .6; }\n.fk-row {\n display: flex; gap: 12px; padding: 12px; margin-bottom: 8px;\n background: var(--fh-row); border-radius: var(--fh-radius);\n}\n.fk-actions { display:flex; flex-direction: column; gap: 4px; }\n.fk-act {\n border: 1px solid var(--fh-border); background: transparent;\n color: var(--fh-fg); border-radius: calc(var(--fh-radius) - 4px);\n min-width: 44px; padding: 6px 8px; cursor: pointer;\n display: flex; flex-direction: column; align-items: center; gap: 2px;\n font-weight: 600; font-size: calc(var(--fh-fs) - 1px); font-family: inherit;\n}\n.fk-act[data-voted=\"true\"] {\n border: 2px solid var(--fh-primary);\n background: color-mix(in srgb, var(--fh-primary) 14%, transparent);\n color: var(--fh-primary);\n}\n.fk-act[disabled] { cursor: default; opacity: .85; }\n.fk-act .fk-glyph { font-size: calc(var(--fh-fs) + 1px); line-height: 1; }\n.fk-meta { flex:1; min-width:0; cursor: pointer; }\n.fk-item-title { font-weight: 600; }\n.fk-item-desc { opacity: .7; font-size: calc(var(--fh-fs) - 1px); margin-top: 4px; }\n.fk-item-badges { display:flex; gap:6px; margin-top:6px; flex-wrap: wrap; }\n.fk-badge {\n font-size: calc(var(--fh-fs) - 3px); padding: 2px 8px; border-radius: 999px;\n background: var(--fh-border); color: var(--fh-muted); text-transform: uppercase; letter-spacing: .04em;\n}\n.fk-badge[data-status=\"planned\"] { background: rgba(59,130,246,.15); color: rgb(37,99,235); }\n.fk-badge[data-status=\"in_progress\"] { background: rgba(234,179,8,.18); color: rgb(161,98,7); }\n.fk-badge[data-status=\"shipped\"] { background: rgba(34,197,94,.18); color: rgb(22,101,52); }\n.fk-comments { margin-top: 10px; border-top: 1px solid var(--fh-border); padding-top: 10px; }\n.fk-comment { padding: 6px 0; border-top: 1px dashed var(--fh-border); font-size: calc(var(--fh-fs) - 1px); }\n.fk-comment:first-child { border-top: 0; }\n.fk-comment-author { font-weight: 600; }\n.fk-form { display: flex; flex-direction: column; gap: 12px; }\n.fk-label { font-size: calc(var(--fh-fs) - 1px); font-weight: 500; }\n.fk-input, .fk-textarea {\n width: 100%; padding: 10px 12px; margin-top: 4px;\n border-radius: calc(var(--fh-radius) - 2px);\n border: 1px solid var(--fh-input-border); background: var(--fh-input-bg);\n color: var(--fh-fg); font-size: var(--fh-fs); font-family: inherit;\n box-sizing: border-box;\n}\n.fk-textarea { resize: vertical; min-height: 96px; }\n.fk-input:focus, .fk-textarea:focus { outline: 2px solid var(--fh-primary); outline-offset: 1px; }\n.fk-submit {\n background: var(--fh-primary); color: #fff; border: 0;\n padding: 12px 14px; border-radius: var(--fh-radius);\n font-weight: 600; font-size: var(--fh-fs); cursor: pointer; font-family: inherit;\n}\n.fk-submit[disabled] { opacity: .6; cursor: not-allowed; }\n.fk-segmented {\n display: inline-flex; gap: 4px; padding: 4px; margin-top: 6px;\n background: var(--fh-row); border-radius: 999px;\n}\n.fk-seg {\n border: 0; background: transparent; cursor: pointer;\n padding: 6px 12px; border-radius: 999px; font-size: calc(var(--fh-fs) - 2px);\n font-weight: 500; color: var(--fh-muted); font-family: inherit;\n}\n.fk-seg[data-active=\"true\"] {\n background: var(--fh-bg); color: var(--fh-fg);\n box-shadow: 0 1px 2px rgba(0,0,0,.06), 0 2px 8px rgba(0,0,0,.04);\n}\n.fk-error { color: rgb(220,38,38); font-size: calc(var(--fh-fs) - 1px); }\n`;\n\nfunction injectStyles() {\n if (document.getElementById(STYLE_ID)) return;\n const style = document.createElement(\"style\");\n style.id = STYLE_ID;\n style.textContent = CSS;\n document.head.appendChild(style);\n}\n\n// Resolve \"system\" mode by reading the OS preference at render time. The theme\n// object is otherwise untouched.\nfunction effectiveMode(theme: Theme): \"light\" | \"dark\" {\n const m = theme.mode || \"light\";\n if (m === \"system\") {\n return typeof window !== \"undefined\" &&\n window.matchMedia &&\n window.matchMedia(\"(prefers-color-scheme: dark)\").matches\n ? \"dark\"\n : \"light\";\n }\n return m === \"dark\" ? \"dark\" : \"light\";\n}\n\nfunction applyTheme(root: HTMLElement, theme: Theme) {\n const primary = theme.primary || \"#0D9488\";\n const radius = `${theme.radius ?? 12}px`;\n const dark = effectiveMode(theme) === \"dark\";\n const font = theme.font_family || theme.fontFamily || \"system-ui, -apple-system, sans-serif\";\n const fs = FONT_SIZES[theme.font_size ?? \"md\"] ?? FONT_SIZES.md;\n const vars: Record<string, string> = {\n \"--fh-primary\": primary,\n \"--fh-radius\": radius,\n \"--fh-font\": font,\n \"--fh-fs\": fs,\n \"--fh-bg\": dark ? \"#0F172A\" : \"#FFFFFF\",\n \"--fh-fg\": dark ? \"#F1F5F9\" : \"#0F172A\",\n \"--fh-muted\": dark ? \"#94A3B8\" : \"#64748B\",\n \"--fh-row\": dark ? \"#1E293B\" : \"#F8FAFC\",\n \"--fh-border\": dark ? \"#1E293B\" : \"#E2E8F0\",\n \"--fh-input-bg\": dark ? \"#0F172A\" : \"#FFFFFF\",\n \"--fh-input-border\": dark ? \"#334155\" : \"#CBD5E1\",\n };\n for (const [k, v] of Object.entries(vars)) root.style.setProperty(k, v);\n}\n\nfunction el<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n attrs?: Record<string, string>,\n children?: (Node | string | null | undefined)[]\n): HTMLElementTagNameMap[K] {\n const node = document.createElement(tag);\n if (attrs) for (const [k, v] of Object.entries(attrs)) {\n if (k === \"class\") node.className = v;\n else node.setAttribute(k, v);\n }\n if (children) {\n for (const c of children) {\n if (c == null) continue;\n node.appendChild(typeof c === \"string\" ? document.createTextNode(c) : c);\n }\n }\n return node;\n}\n\n// ---------------------------------------------------------------------------\n// mount() — entry point\n// ---------------------------------------------------------------------------\n\nexport function mount(options: MountOptions): Widget {\n injectStyles();\n\n const container = options.container || document.body;\n const client = new HeedKitClient(options);\n\n // Silence init failures (bad project key, network down, API offline) so\n // they don't surface as unhandled promise rejections in the host page —\n // a marketing visitor should never see a Next.js error overlay because\n // our init 401'd. The launcher just doesn't show; open() no-ops.\n const initPromise: Promise<InitResult | null> = client.init(options.user || {}).catch((e) => {\n // eslint-disable-next-line no-console\n console.warn(\"[HeedKit] widget init failed; launcher disabled.\", e);\n return null;\n });\n\n let overlay: HTMLDivElement | null = null;\n let launcher: HTMLButtonElement | null = null;\n\n function close() {\n if (overlay) {\n overlay.remove();\n overlay = null;\n }\n }\n\n async function open() {\n const r = await initPromise;\n if (!r) return; // init failed; nothing to render\n if (overlay) return;\n overlay = renderPanel(client, client.getTheme(), close);\n container.appendChild(overlay);\n }\n\n if (!options.hideLauncher) {\n initPromise.then((r) => {\n if (!r) return; // init failed; skip the launcher entirely\n launcher = el(\"button\", { class: \"fk-launcher\", type: \"button\" }, [\n options.label || \"Feedback\",\n ]) as HTMLButtonElement;\n applyTheme(launcher, client.getTheme());\n launcher.addEventListener(\"click\", open);\n container.appendChild(launcher);\n });\n }\n\n return {\n client,\n open,\n close,\n destroy() {\n close();\n launcher?.remove();\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Panel rendering\n// ---------------------------------------------------------------------------\n\nfunction renderPanel(\n client: HeedKitClient,\n theme: Theme,\n onClose: () => void,\n): HTMLDivElement {\n const overlay = el(\"div\", { class: \"fk-overlay\", role: \"dialog\" }) as HTMLDivElement;\n applyTheme(overlay, theme);\n overlay.addEventListener(\"click\", (e) => {\n if (e.target === overlay) onClose();\n });\n\n const panel = el(\"div\", { class: \"fk-panel\" });\n overlay.appendChild(panel);\n\n // -- state -------------------------------------------------------------\n type Mode = \"browse\" | \"suggest\";\n const enabledKinds = client.getEnabledKinds();\n const groupMode = theme.group_mode || \"tabs\";\n // tabs mode: one tab per enabled kind, plus mixed \"All\". list mode: no kind tabs.\n let mode: Mode = \"browse\";\n let activeKind: FeatureKind | \"all\" = groupMode === \"tabs\" && enabledKinds.length > 0 ? enabledKinds[0] : \"all\";\n let features: Feature[] = [];\n\n // -- header -----------------------------------------------------------\n const head = el(\"div\", { class: \"fk-head\" });\n const titlerow = el(\"div\", { class: \"fk-titlerow\" });\n titlerow.appendChild(el(\"div\", { class: \"fk-title\" }, [client.getProjectName() || \"Feedback\"]));\n const closeBtn = el(\"button\", { class: \"fk-close\", type: \"button\", \"aria-label\": \"Close\" }, [\"×\"]);\n closeBtn.addEventListener(\"click\", onClose);\n titlerow.appendChild(closeBtn);\n head.appendChild(titlerow);\n\n const modes = el(\"div\", { class: \"fk-modes\" });\n const modeBrowse = el(\"button\", { class: \"fk-mode\", type: \"button\" }, [\"Browse\"]) as HTMLButtonElement;\n const modeSuggest = el(\"button\", { class: \"fk-mode\", type: \"button\" }, [\"Suggest\"]) as HTMLButtonElement;\n modes.append(modeBrowse, modeSuggest);\n head.appendChild(modes);\n panel.appendChild(head);\n\n // -- tabs row (tabs mode only) ----------------------------------------\n let tabsEl: HTMLElement | null = null;\n if (groupMode === \"tabs\" && enabledKinds.length > 0) {\n tabsEl = el(\"div\", { class: \"fk-tabs\" });\n const all = el(\"button\", { class: \"fk-tab\", type: \"button\" }, [\"All\"]) as HTMLButtonElement;\n all.dataset.kind = \"all\";\n tabsEl.appendChild(all);\n for (const k of enabledKinds) {\n const meta = KIND_OPTIONS[k];\n const tab = el(\"button\", { class: \"fk-tab\", type: \"button\" }, [\n el(\"span\", {}, [meta.tabIcon]),\n el(\"span\", {}, [meta.label]),\n ]) as HTMLButtonElement;\n tab.dataset.kind = k;\n tabsEl.appendChild(tab);\n }\n panel.appendChild(tabsEl);\n tabsEl.addEventListener(\"click\", (e) => {\n const target = (e.target as HTMLElement).closest(\"[data-kind]\") as HTMLElement | null;\n if (!target) return;\n activeKind = target.dataset.kind as FeatureKind | \"all\";\n paintTabs();\n refresh();\n });\n }\n\n // -- body -------------------------------------------------------------\n const body = el(\"div\", { class: \"fk-body\" });\n panel.appendChild(body);\n\n // -- helpers ----------------------------------------------------------\n\n function paintModes() {\n modeBrowse.setAttribute(\"data-active\", String(mode === \"browse\"));\n modeSuggest.setAttribute(\"data-active\", String(mode === \"suggest\"));\n if (tabsEl) tabsEl.style.display = mode === \"browse\" ? \"\" : \"none\";\n }\n\n function paintTabs() {\n if (!tabsEl) return;\n for (const t of Array.from(tabsEl.children)) {\n (t as HTMLElement).setAttribute(\n \"data-active\",\n String((t as HTMLElement).dataset.kind === String(activeKind)),\n );\n }\n }\n\n async function refresh() {\n body.replaceChildren(el(\"div\", { class: \"fk-loading\" }, [\"Loading…\"]));\n try {\n const opts: { sort: \"top\" | \"new\"; kind?: FeatureKind } = { sort: \"top\" };\n if (activeKind !== \"all\") opts.kind = activeKind;\n features = await client.list(opts);\n } catch (e) {\n body.replaceChildren(\n el(\"div\", { class: \"fk-empty\" }, [`Failed to load: ${(e as Error).message}`]),\n );\n return;\n }\n renderList();\n }\n\n // Toggle a single interaction. For upvote/downvote we mirror \"press one,\n // press it again to undo, press the other to flip\". For +1/like we just toggle.\n async function performAction(f: Feature, interaction: Interaction) {\n // The backend currently exposes a single /vote toggle endpoint regardless of\n // interaction type — all four collapse to the same row-count in this MVP.\n // (When a separate downvote endpoint is added we can branch here.)\n const r = await client.vote(f.id);\n f.voted = r.voted;\n f.vote_count = r.vote_count;\n renderList();\n }\n\n function renderActions(f: Feature): HTMLElement {\n const interactions = client.getInteractionsFor(f.kind);\n const showCount = (client.getTheme().show_counts || {})[f.kind] !== false;\n const wrap = el(\"div\", { class: \"fk-actions\" });\n\n if (interactions.length === 0) {\n // Read-only mode: just show the count (if visible).\n if (showCount) {\n const btn = el(\"button\", { class: \"fk-act\", type: \"button\", disabled: \"true\" }, [\n el(\"span\", {}, [String(f.vote_count)]),\n ]);\n wrap.appendChild(btn);\n }\n return wrap;\n }\n\n for (const i of interactions) {\n const meta = INTERACTION_META[i];\n const btn = el(\"button\", {\n class: \"fk-act\",\n type: \"button\",\n \"aria-label\": meta.label,\n }, [\n el(\"span\", { class: \"fk-glyph\" }, [meta.icon]),\n ...(showCount ? [el(\"span\", {}, [String(f.vote_count)])] : []),\n ]) as HTMLButtonElement;\n if (i === \"upvote\" || i === \"like\" || i === \"plus_one\") {\n btn.setAttribute(\"data-voted\", String(f.voted));\n }\n btn.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n performAction(f, i);\n });\n wrap.appendChild(btn);\n }\n return wrap;\n }\n\n function renderList() {\n body.innerHTML = \"\";\n if (features.length === 0) {\n body.appendChild(el(\"div\", { class: \"fk-empty\" }, [\"No items yet — be the first!\"]));\n return;\n }\n for (const f of features) {\n body.appendChild(renderRow(f));\n }\n }\n\n // A row collapses by default; clicking the meta column reveals comments.\n function renderRow(f: Feature): HTMLElement {\n const row = el(\"div\", { class: \"fk-row\" });\n row.appendChild(renderActions(f));\n\n const meta = el(\"div\", { class: \"fk-meta\" });\n meta.appendChild(el(\"div\", { class: \"fk-item-title\" }, [f.title]));\n if (f.description) meta.appendChild(el(\"div\", { class: \"fk-item-desc\" }, [f.description]));\n\n const badges = el(\"div\", { class: \"fk-item-badges\" });\n if (f.status && f.status !== \"open\") {\n const b = el(\"span\", { class: \"fk-badge\" }, [f.status.replace(\"_\", \" \")]);\n b.setAttribute(\"data-status\", f.status);\n badges.appendChild(b);\n }\n if (f.tag) badges.appendChild(el(\"span\", { class: \"fk-badge\" }, [f.tag]));\n if (badges.children.length) meta.appendChild(badges);\n\n let commentsLoaded = false;\n const commentsEl = el(\"div\", { class: \"fk-comments\" });\n commentsEl.style.display = \"none\";\n meta.appendChild(commentsEl);\n\n meta.addEventListener(\"click\", async () => {\n const opening = commentsEl.style.display === \"none\";\n commentsEl.style.display = opening ? \"\" : \"none\";\n if (opening && !commentsLoaded) {\n commentsLoaded = true;\n commentsEl.replaceChildren(el(\"div\", { class: \"fk-loading\" }, [\"Loading…\"]));\n try {\n const cs = await client.listComments(f.id);\n commentsEl.replaceChildren(...renderComments(f, cs));\n } catch (e) {\n commentsEl.replaceChildren(\n el(\"div\", { class: \"fk-error\" }, [(e as Error).message]),\n );\n }\n }\n });\n\n row.appendChild(meta);\n return row;\n }\n\n function renderComments(f: Feature, comments: Comment[]): HTMLElement[] {\n const nodes: HTMLElement[] = [];\n if (comments.length === 0) {\n nodes.push(el(\"div\", { class: \"fk-empty\" }, [\"No replies yet.\"]));\n } else {\n for (const c of comments) {\n nodes.push(el(\"div\", { class: \"fk-comment\" }, [\n el(\"span\", { class: \"fk-comment-author\" }, [c.author_name || \"Anonymous\"]),\n \" — \",\n c.body,\n ]));\n }\n }\n // Comment input\n const input = el(\"textarea\", {\n class: \"fk-textarea\",\n placeholder: \"Add a reply…\",\n rows: \"2\",\n }) as HTMLTextAreaElement;\n const send = el(\"button\", { class: \"fk-submit\", type: \"button\" }, [\"Reply\"]) as HTMLButtonElement;\n send.addEventListener(\"click\", async (e) => {\n e.stopPropagation();\n if (!input.value.trim()) return;\n send.disabled = true;\n try {\n const c = await client.comment(f.id, input.value);\n input.value = \"\";\n send.disabled = false;\n // Refresh inline.\n const refreshed = await client.listComments(f.id);\n // Replace the surrounding comments block. Find parent and re-render.\n const parent = send.parentElement?.parentElement;\n if (parent) parent.replaceChildren(...renderComments(f, refreshed));\n } catch (err) {\n send.disabled = false;\n alert((err as Error).message);\n }\n });\n nodes.push(el(\"div\", {}, [input, send]));\n return nodes;\n }\n\n // ---- Suggest form --------------------------------------------------\n\n function renderForm() {\n body.innerHTML = \"\";\n const enabled = client.getEnabledKinds();\n const enabledOptions = enabled.map((value) => ({ value, ...KIND_OPTIONS[value] }));\n const safeOptions = enabledOptions.length > 0 ? enabledOptions : [\n { value: \"other\" as FeatureKind, ...KIND_OPTIONS.other },\n ];\n let kind: FeatureKind = safeOptions[0].value;\n\n const form = el(\"form\", { class: \"fk-form\" }) as HTMLFormElement;\n\n const kindLabel = el(\"label\", { class: \"fk-label\" }, [\"What's this about?\"]);\n const segmented = el(\"div\", { class: \"fk-segmented\" });\n const segButtons: HTMLButtonElement[] = [];\n for (const opt of safeOptions) {\n const btn = el(\"button\", { class: \"fk-seg\", type: \"button\" }, [opt.label]) as HTMLButtonElement;\n btn.setAttribute(\"data-active\", String(opt.value === kind));\n btn.addEventListener(\"click\", () => {\n kind = opt.value;\n segButtons.forEach((b, i) =>\n b.setAttribute(\"data-active\", String(safeOptions[i].value === kind)),\n );\n titleInput.placeholder = safeOptions.find((o) => o.value === kind)!.placeholder;\n });\n segButtons.push(btn);\n segmented.appendChild(btn);\n }\n kindLabel.appendChild(segmented);\n\n const titleLabel = el(\"label\", { class: \"fk-label\" }, [\"Title\"]);\n const titleInput = el(\"input\", {\n class: \"fk-input\",\n type: \"text\",\n placeholder: safeOptions[0].placeholder,\n required: \"true\",\n }) as HTMLInputElement;\n titleLabel.appendChild(titleInput);\n\n const descLabel = el(\"label\", { class: \"fk-label\" }, [\"Description\"]);\n const descInput = el(\"textarea\", {\n class: \"fk-textarea\",\n placeholder: \"Any extra context helps.\",\n rows: \"4\",\n }) as HTMLTextAreaElement;\n descLabel.appendChild(descInput);\n\n const submit = el(\"button\", { class: \"fk-submit\", type: \"submit\" }, [\"Submit\"]) as HTMLButtonElement;\n\n form.append(kindLabel, titleLabel, descLabel, submit);\n body.appendChild(form);\n\n form.addEventListener(\"submit\", async (e) => {\n e.preventDefault();\n if (!titleInput.value.trim()) return;\n submit.disabled = true;\n submit.textContent = \"Submitting…\";\n try {\n await client.submit({\n title: titleInput.value,\n description: descInput.value,\n kind,\n });\n titleInput.value = \"\";\n descInput.value = \"\";\n setMode(\"browse\");\n // Land on the tab of what they just posted, so they see their submission.\n if (tabsEl) {\n activeKind = kind;\n paintTabs();\n }\n await refresh();\n } catch (err) {\n submit.disabled = false;\n submit.textContent = \"Submit\";\n alert((err as Error).message);\n }\n });\n }\n\n function setMode(m: Mode) {\n mode = m;\n paintModes();\n if (mode === \"browse\") refresh();\n else renderForm();\n }\n\n modeBrowse.addEventListener(\"click\", () => setMode(\"browse\"));\n modeSuggest.addEventListener(\"click\", () => setMode(\"suggest\"));\n\n paintModes();\n paintTabs();\n setMode(\"browse\");\n\n return overlay;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACyFA,IAAM,cAAc;AACpB,IAAM,gBAAgB;AAUf,SAAS,sBAAqC;AApGrD;AAqGE,MAAI;AACF,QAAI,OAAO,WAAW,eAAe,CAAC,OAAO,aAAc,QAAO;AAClE,UAAM,WAAW,OAAO,aAAa,QAAQ,aAAa;AAC1D,QAAI,SAAU,QAAO;AACrB,UAAM,OAAO,WAAU,4CAAQ,eAAR,gDAA0B,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC;AACnF,WAAO,aAAa,QAAQ,eAAe,IAAI;AAC/C,WAAO;AAAA,EACT,SAAQ;AAEN,WAAO;AAAA,EACT;AACF;AAIA,SAAS,iBAAiB,GAAiB;AApH3C;AAqHE,SAAO;AAAA,IACL,IAAI,OAAO,EAAE,EAAE;AAAA,IACf,OAAO,EAAE;AAAA,IACT,cAAa,OAAE,gBAAF,YAAiB;AAAA,IAC9B,QAAQ,EAAE;AAAA,IACV,MAAM,EAAE;AAAA,IACR,YAAY,EAAE;AAAA,IACd,aAAY,OAAE,eAAF,YAAgB;AAAA,IAC5B,MAAK,OAAE,QAAF,YAAS;AAAA,IACd,aAAY,OAAE,eAAF,YAAgB;AAAA,IAC5B,QAAO,OAAE,UAAF,YAAW;AAAA,IAClB,WAAU,OAAE,aAAF,YAAc;AAAA,IACxB,cAAa,aAAE,gBAAF,YAAiB,EAAE,WAAnB,YAA6B;AAAA,IAC1C,YAAY,EAAE;AAAA,EAChB;AACF;AAEA,SAAS,iBAAiB,GAAiB;AAtI3C;AAuIE,SAAO;AAAA,IACL,IAAI,OAAO,EAAE,EAAE;AAAA,IACf,MAAM,EAAE;AAAA,IACR,cAAa,aAAE,gBAAF,YAAiB,EAAE,WAAnB,YAA6B;AAAA;AAAA,IAE1C,cAAa,OAAE,gBAAF,YAAiB;AAAA,IAC9B,YAAY,EAAE;AAAA,EAChB;AACF;AAEO,IAAM,gBAAN,MAAoB;AAAA,EAUzB,YAAY,QAAuB;AAPnC,SAAQ,YAA2B;AACnC,SAAQ,QAAe,CAAC;AACxB,SAAQ,cAAc;AACtB,SAAQ,eAA8B,CAAC;AACvC,SAAQ,iBAA2D,CAAC;AACpE,SAAQ,mBAAmE,CAAC;AAG1E,SAAK,SAAS,OAAO,UAAU;AAC/B,SAAK,aAAa,OAAO;AAAA,EAC3B;AAAA,EAEA,MAAM,KAAK,OAAgB,CAAC,GAAwB;AAhKtD;AAmKI,UAAM,cAAa,gBAAK,eAAL,YAAmB,oBAAoB,MAAvC,YAA4C;AAC/D,UAAM,OAAO;AAAA,MACX,aAAa;AAAA,MACb,OAAO,KAAK;AAAA,MACZ,MAAM,KAAK;AAAA,MACX,YAAY,KAAK;AAAA,MACjB,UAAU,KAAK,YAAY;AAAA,IAC7B;AACA,UAAM,MAAM,MAAM,KAAK,QAAoB,aAAa,QAAQ,IAAI;AACpE,SAAK,YAAY,IAAI;AAGrB,UAAM,KAAU,SAAY,YAAZ,YAAuB;AACvC,SAAK,QAAQ,EAAE,SAAS,CAAC;AACzB,SAAK,eAAc,aAAE,SAAF,YAAU,EAAE,iBAAZ,YAA4B;AAC/C,SAAK,eAAe,EAAE,iBAAiB,CAAC;AACxC,SAAK,iBAAiB,EAAE,mBAAmB,CAAC;AAC5C,SAAK,mBAAmB,EAAE,qBAAqB,CAAC;AAChD,WAAO;AAAA,EACT;AAAA,EAEA,WAAW;AAAE,WAAO,KAAK;AAAA,EAAO;AAAA,EAChC,kBAAiC;AAAE,WAAO,KAAK;AAAA,EAAc;AAAA,EAC7D,oBAAoB;AAAE,WAAO,KAAK;AAAA,EAAgB;AAAA,EAClD,sBAAsB;AAAE,WAAO,KAAK;AAAA,EAAkB;AAAA,EACtD,iBAAiB;AAAE,WAAO,KAAK;AAAA,EAAa;AAAA,EAC5C,eAAe;AAAE,WAAO,KAAK;AAAA,EAAW;AAAA;AAAA,EAGxC,mBAAmB,MAAkC;AACnD,UAAM,MAAM,KAAK,iBAAiB,IAAI,KAAK,CAAC;AAC5C,WAAQ,CAAC,UAAU,YAAY,YAAY,MAAM,EAAoB;AAAA,MACnE,CAAC,MAAM,IAAI,CAAC;AAAA,IACd;AAAA,EACF;AAAA,EAEA,MAAM,KACJ,OAAsE,CAAC,GACnD;AAzMxB;AA0MI,SAAK,WAAW;AAChB,UAAM,SAAS,IAAI,gBAAgB,EAAE,aAAa,KAAK,UAAW,CAAC;AACnE,QAAI,KAAK,OAAQ,QAAO,IAAI,UAAU,KAAK,MAAM;AACjD,QAAI,KAAK,KAAM,QAAO,IAAI,QAAQ,KAAK,IAAI;AAC3C,QAAI,KAAK,KAAM,QAAO,IAAI,QAAQ,KAAK,IAAI;AAE3C,UAAM,MAAM,MAAM,KAAK,QAAa,iBAAiB,MAAM,IAAI,KAAK;AACpE,UAAM,WAAW,MAAM,QAAQ,GAAG,IAAI,OAAO,SAAI,aAAJ,YAAgB,CAAC;AAC9D,WAAO,SAAS,IAAI,CAAC,MAAW,iBAAiB,CAAC,CAAC;AAAA,EACrD;AAAA,EAEA,MAAM,OAAO,OAKQ;AACnB,SAAK,WAAW;AAChB,UAAM,MAAM,MAAM,KAAK,QAAa,iBAAiB,QAAQ;AAAA,MAC3D,aAAa,KAAK;AAAA,MAClB,OAAO,MAAM;AAAA,MACb,aAAa,MAAM,eAAe;AAAA,MAClC,KAAK,MAAM,OAAO;AAAA,MAClB,MAAM,MAAM,QAAQ;AAAA,IACtB,CAAC;AACD,WAAO,iBAAiB,GAAG;AAAA,EAC7B;AAAA,EAEA,MAAM,KAAK,WAAoE;AAC7E,SAAK,WAAW;AAChB,WAAO,KAAK,QAAQ,iBAAiB,SAAS,SAAS,QAAQ;AAAA,MAC7D,aAAa,KAAK;AAAA,IACpB,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,aAAa,WAAuC;AA7O5D;AA8OI,UAAM,MAAM,MAAM,KAAK,QAAa,iBAAiB,SAAS,aAAa,KAAK;AAChF,UAAM,WAAW,MAAM,QAAQ,GAAG,IAAI,OAAO,SAAI,aAAJ,YAAgB,CAAC;AAC9D,WAAO,SAAS,IAAI,CAAC,MAAW,iBAAiB,CAAC,CAAC;AAAA,EACrD;AAAA,EAEA,MAAM,QAAQ,WAAmB,MAAgC;AAC/D,SAAK,WAAW;AAChB,UAAM,MAAM,MAAM,KAAK,QAAa,iBAAiB,SAAS,aAAa,QAAQ;AAAA,MACjF,aAAa,KAAK;AAAA,MAClB;AAAA,IACF,CAAC;AACD,WAAO,iBAAiB,GAAG;AAAA,EAC7B;AAAA,EAEQ,aAAa;AACnB,QAAI,CAAC,KAAK,UAAW,OAAM,IAAI,MAAM,kDAA6C;AAAA,EACpF;AAAA,EAEA,MAAc,QAAW,MAAc,QAAgB,MAA4B;AACjF,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,MAAM,GAAG,IAAI,IAAI;AAAA,MAC/C;AAAA,MACA,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,iBAAiB,KAAK;AAAA,MACxB;AAAA,MACA,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;AAAA,IACtC,CAAC;AACD,QAAI,CAAC,IAAI,IAAI;AACX,UAAI,SAAS,QAAQ,IAAI,MAAM;AAC/B,UAAI;AACF,cAAM,IAAI,MAAM,IAAI,KAAK;AACzB,iBAAS,EAAE,SAAS,EAAE,UAAU;AAAA,MAClC,SAAQ;AAAA,MAAsB;AAC9B,YAAM,IAAI,MAAM,MAAM;AAAA,IACxB;AACA,WAAO,IAAI,KAAK;AAAA,EAClB;AACF;;;ACpQA,IAAM,eAA6F;AAAA,EACjG,iBAAiB,EAAE,OAAO,YAAiB,aAAa,yBAA0B,SAAS,YAAK;AAAA,EAChG,YAAiB,EAAE,OAAO,QAAiB,aAAa,kBAA2B,SAAS,YAAK;AAAA,EACjG,aAAiB,EAAE,OAAO,gBAAiB,aAAa,yBAA2B,SAAS,SAAI;AAAA,EAChG,cAAiB,EAAE,OAAO,gBAAiB,aAAa,sBAA2B,SAAS,eAAK;AAAA,EACjG,OAAiB,EAAE,OAAO,SAAiB,aAAa,oBAA2B,SAAS,YAAK;AACnG;AAGA,IAAM,mBAAyE;AAAA,EAC7E,QAAU,EAAE,MAAM,UAAM,OAAO,SAAS;AAAA,EACxC,UAAU,EAAE,MAAM,UAAM,OAAO,WAAW;AAAA,EAC1C,UAAU,EAAE,MAAM,MAAM,OAAO,KAAK;AAAA,EACpC,MAAU,EAAE,MAAM,UAAM,OAAO,OAAO;AACxC;AAEA,IAAM,aAAa,EAAE,IAAI,QAAQ,IAAI,QAAQ,IAAI,OAAO;AAuBxD,IAAM,WAAW;AAEjB,IAAM,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmHZ,SAAS,eAAe;AACtB,MAAI,SAAS,eAAe,QAAQ,EAAG;AACvC,QAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,QAAM,KAAK;AACX,QAAM,cAAc;AACpB,WAAS,KAAK,YAAY,KAAK;AACjC;AAIA,SAAS,cAAc,OAAgC;AACrD,QAAM,IAAI,MAAM,QAAQ;AACxB,MAAI,MAAM,UAAU;AAClB,WAAO,OAAO,WAAW,eACvB,OAAO,cACP,OAAO,WAAW,8BAA8B,EAAE,UAChD,SACA;AAAA,EACN;AACA,SAAO,MAAM,SAAS,SAAS;AACjC;AAEA,SAAS,WAAW,MAAmB,OAAc;AAjMrD;AAkME,QAAM,UAAU,MAAM,WAAW;AACjC,QAAM,SAAS,IAAG,WAAM,WAAN,YAAgB,EAAE;AACpC,QAAM,OAAO,cAAc,KAAK,MAAM;AACtC,QAAM,OAAO,MAAM,eAAe,MAAM,cAAc;AACtD,QAAM,MAAK,iBAAW,WAAM,cAAN,YAAmB,IAAI,MAAlC,YAAuC,WAAW;AAC7D,QAAM,OAA+B;AAAA,IACnC,gBAAgB;AAAA,IAChB,eAAe;AAAA,IACf,aAAa;AAAA,IACb,WAAW;AAAA,IACX,WAAW,OAAO,YAAY;AAAA,IAC9B,WAAW,OAAO,YAAY;AAAA,IAC9B,cAAc,OAAO,YAAY;AAAA,IACjC,YAAY,OAAO,YAAY;AAAA,IAC/B,eAAe,OAAO,YAAY;AAAA,IAClC,iBAAiB,OAAO,YAAY;AAAA,IACpC,qBAAqB,OAAO,YAAY;AAAA,EAC1C;AACA,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,IAAI,EAAG,MAAK,MAAM,YAAY,GAAG,CAAC;AACxE;AAEA,SAAS,GACP,KACA,OACA,UAC0B;AAC1B,QAAM,OAAO,SAAS,cAAc,GAAG;AACvC,MAAI,MAAO,YAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,KAAK,GAAG;AACrD,QAAI,MAAM,QAAS,MAAK,YAAY;AAAA,QAC/B,MAAK,aAAa,GAAG,CAAC;AAAA,EAC7B;AACA,MAAI,UAAU;AACZ,eAAW,KAAK,UAAU;AACxB,UAAI,KAAK,KAAM;AACf,WAAK,YAAY,OAAO,MAAM,WAAW,SAAS,eAAe,CAAC,IAAI,CAAC;AAAA,IACzE;AAAA,EACF;AACA,SAAO;AACT;AAMO,SAAS,MAAM,SAA+B;AACnD,eAAa;AAEb,QAAM,YAAY,QAAQ,aAAa,SAAS;AAChD,QAAM,SAAS,IAAI,cAAc,OAAO;AAMxC,QAAM,cAA0C,OAAO,KAAK,QAAQ,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM;AAE3F,YAAQ,KAAK,oDAAoD,CAAC;AAClE,WAAO;AAAA,EACT,CAAC;AAED,MAAI,UAAiC;AACrC,MAAI,WAAqC;AAEzC,WAAS,QAAQ;AACf,QAAI,SAAS;AACX,cAAQ,OAAO;AACf,gBAAU;AAAA,IACZ;AAAA,EACF;AAEA,iBAAe,OAAO;AACpB,UAAM,IAAI,MAAM;AAChB,QAAI,CAAC,EAAG;AACR,QAAI,QAAS;AACb,cAAU,YAAY,QAAQ,OAAO,SAAS,GAAG,KAAK;AACtD,cAAU,YAAY,OAAO;AAAA,EAC/B;AAEA,MAAI,CAAC,QAAQ,cAAc;AACzB,gBAAY,KAAK,CAAC,MAAM;AACtB,UAAI,CAAC,EAAG;AACR,iBAAW,GAAG,UAAU,EAAE,OAAO,eAAe,MAAM,SAAS,GAAG;AAAA,QAChE,QAAQ,SAAS;AAAA,MACnB,CAAC;AACD,iBAAW,UAAU,OAAO,SAAS,CAAC;AACtC,eAAS,iBAAiB,SAAS,IAAI;AACvC,gBAAU,YAAY,QAAQ;AAAA,IAChC,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU;AACR,YAAM;AACN,2CAAU;AAAA,IACZ;AAAA,EACF;AACF;AAMA,SAAS,YACP,QACA,OACA,SACgB;AAChB,QAAM,UAAU,GAAG,OAAO,EAAE,OAAO,cAAc,MAAM,SAAS,CAAC;AACjE,aAAW,SAAS,KAAK;AACzB,UAAQ,iBAAiB,SAAS,CAAC,MAAM;AACvC,QAAI,EAAE,WAAW,QAAS,SAAQ;AAAA,EACpC,CAAC;AAED,QAAM,QAAQ,GAAG,OAAO,EAAE,OAAO,WAAW,CAAC;AAC7C,UAAQ,YAAY,KAAK;AAIzB,QAAM,eAAe,OAAO,gBAAgB;AAC5C,QAAM,YAAY,MAAM,cAAc;AAEtC,MAAI,OAAa;AACjB,MAAI,aAAkC,cAAc,UAAU,aAAa,SAAS,IAAI,aAAa,CAAC,IAAI;AAC1G,MAAI,WAAsB,CAAC;AAG3B,QAAM,OAAO,GAAG,OAAO,EAAE,OAAO,UAAU,CAAC;AAC3C,QAAM,WAAW,GAAG,OAAO,EAAE,OAAO,cAAc,CAAC;AACnD,WAAS,YAAY,GAAG,OAAO,EAAE,OAAO,WAAW,GAAG,CAAC,OAAO,eAAe,KAAK,UAAU,CAAC,CAAC;AAC9F,QAAM,WAAW,GAAG,UAAU,EAAE,OAAO,YAAY,MAAM,UAAU,cAAc,QAAQ,GAAG,CAAC,MAAG,CAAC;AACjG,WAAS,iBAAiB,SAAS,OAAO;AAC1C,WAAS,YAAY,QAAQ;AAC7B,OAAK,YAAY,QAAQ;AAEzB,QAAM,QAAQ,GAAG,OAAO,EAAE,OAAO,WAAW,CAAC;AAC7C,QAAM,aAAa,GAAG,UAAU,EAAE,OAAO,WAAW,MAAM,SAAS,GAAG,CAAC,QAAQ,CAAC;AAChF,QAAM,cAAc,GAAG,UAAU,EAAE,OAAO,WAAW,MAAM,SAAS,GAAG,CAAC,SAAS,CAAC;AAClF,QAAM,OAAO,YAAY,WAAW;AACpC,OAAK,YAAY,KAAK;AACtB,QAAM,YAAY,IAAI;AAGtB,MAAI,SAA6B;AACjC,MAAI,cAAc,UAAU,aAAa,SAAS,GAAG;AACnD,aAAS,GAAG,OAAO,EAAE,OAAO,UAAU,CAAC;AACvC,UAAM,MAAM,GAAG,UAAU,EAAE,OAAO,UAAU,MAAM,SAAS,GAAG,CAAC,KAAK,CAAC;AACrE,QAAI,QAAQ,OAAO;AACnB,WAAO,YAAY,GAAG;AACtB,eAAW,KAAK,cAAc;AAC5B,YAAM,OAAO,aAAa,CAAC;AAC3B,YAAM,MAAM,GAAG,UAAU,EAAE,OAAO,UAAU,MAAM,SAAS,GAAG;AAAA,QAC5D,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC;AAAA,QAC7B,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC;AAAA,MAC7B,CAAC;AACD,UAAI,QAAQ,OAAO;AACnB,aAAO,YAAY,GAAG;AAAA,IACxB;AACA,UAAM,YAAY,MAAM;AACxB,WAAO,iBAAiB,SAAS,CAAC,MAAM;AACtC,YAAM,SAAU,EAAE,OAAuB,QAAQ,aAAa;AAC9D,UAAI,CAAC,OAAQ;AACb,mBAAa,OAAO,QAAQ;AAC5B,gBAAU;AACV,cAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAGA,QAAM,OAAO,GAAG,OAAO,EAAE,OAAO,UAAU,CAAC;AAC3C,QAAM,YAAY,IAAI;AAItB,WAAS,aAAa;AACpB,eAAW,aAAa,eAAe,OAAO,SAAS,QAAQ,CAAC;AAChE,gBAAY,aAAa,eAAe,OAAO,SAAS,SAAS,CAAC;AAClE,QAAI,OAAQ,QAAO,MAAM,UAAU,SAAS,WAAW,KAAK;AAAA,EAC9D;AAEA,WAAS,YAAY;AACnB,QAAI,CAAC,OAAQ;AACb,eAAW,KAAK,MAAM,KAAK,OAAO,QAAQ,GAAG;AAC3C,MAAC,EAAkB;AAAA,QACjB;AAAA,QACA,OAAQ,EAAkB,QAAQ,SAAS,OAAO,UAAU,CAAC;AAAA,MAC/D;AAAA,IACF;AAAA,EACF;AAEA,iBAAe,UAAU;AACvB,SAAK,gBAAgB,GAAG,OAAO,EAAE,OAAO,aAAa,GAAG,CAAC,eAAU,CAAC,CAAC;AACrE,QAAI;AACF,YAAM,OAAoD,EAAE,MAAM,MAAM;AACxE,UAAI,eAAe,MAAO,MAAK,OAAO;AACtC,iBAAW,MAAM,OAAO,KAAK,IAAI;AAAA,IACnC,SAAS,GAAG;AACV,WAAK;AAAA,QACH,GAAG,OAAO,EAAE,OAAO,WAAW,GAAG,CAAC,mBAAoB,EAAY,OAAO,EAAE,CAAC;AAAA,MAC9E;AACA;AAAA,IACF;AACA,eAAW;AAAA,EACb;AAIA,iBAAe,cAAc,GAAY,aAA0B;AAIjE,UAAM,IAAI,MAAM,OAAO,KAAK,EAAE,EAAE;AAChC,MAAE,QAAQ,EAAE;AACZ,MAAE,aAAa,EAAE;AACjB,eAAW;AAAA,EACb;AAEA,WAAS,cAAc,GAAyB;AAC9C,UAAM,eAAe,OAAO,mBAAmB,EAAE,IAAI;AACrD,UAAM,aAAa,OAAO,SAAS,EAAE,eAAe,CAAC,GAAG,EAAE,IAAI,MAAM;AACpE,UAAM,OAAO,GAAG,OAAO,EAAE,OAAO,aAAa,CAAC;AAE9C,QAAI,aAAa,WAAW,GAAG;AAE7B,UAAI,WAAW;AACb,cAAM,MAAM,GAAG,UAAU,EAAE,OAAO,UAAU,MAAM,UAAU,UAAU,OAAO,GAAG;AAAA,UAC9E,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AAAA,QACvC,CAAC;AACD,aAAK,YAAY,GAAG;AAAA,MACtB;AACA,aAAO;AAAA,IACT;AAEA,eAAW,KAAK,cAAc;AAC5B,YAAM,OAAO,iBAAiB,CAAC;AAC/B,YAAM,MAAM,GAAG,UAAU;AAAA,QACvB,OAAO;AAAA,QACP,MAAM;AAAA,QACN,cAAc,KAAK;AAAA,MACrB,GAAG;AAAA,QACD,GAAG,QAAQ,EAAE,OAAO,WAAW,GAAG,CAAC,KAAK,IAAI,CAAC;AAAA,QAC7C,GAAI,YAAY,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;AAAA,MAC9D,CAAC;AACD,UAAI,MAAM,YAAY,MAAM,UAAU,MAAM,YAAY;AACtD,YAAI,aAAa,cAAc,OAAO,EAAE,KAAK,CAAC;AAAA,MAChD;AACA,UAAI,iBAAiB,SAAS,CAAC,MAAM;AACnC,UAAE,gBAAgB;AAClB,sBAAc,GAAG,CAAC;AAAA,MACpB,CAAC;AACD,WAAK,YAAY,GAAG;AAAA,IACtB;AACA,WAAO;AAAA,EACT;AAEA,WAAS,aAAa;AACpB,SAAK,YAAY;AACjB,QAAI,SAAS,WAAW,GAAG;AACzB,WAAK,YAAY,GAAG,OAAO,EAAE,OAAO,WAAW,GAAG,CAAC,mCAA8B,CAAC,CAAC;AACnF;AAAA,IACF;AACA,eAAW,KAAK,UAAU;AACxB,WAAK,YAAY,UAAU,CAAC,CAAC;AAAA,IAC/B;AAAA,EACF;AAGA,WAAS,UAAU,GAAyB;AAC1C,UAAM,MAAM,GAAG,OAAO,EAAE,OAAO,SAAS,CAAC;AACzC,QAAI,YAAY,cAAc,CAAC,CAAC;AAEhC,UAAM,OAAO,GAAG,OAAO,EAAE,OAAO,UAAU,CAAC;AAC3C,SAAK,YAAY,GAAG,OAAO,EAAE,OAAO,gBAAgB,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;AACjE,QAAI,EAAE,YAAa,MAAK,YAAY,GAAG,OAAO,EAAE,OAAO,eAAe,GAAG,CAAC,EAAE,WAAW,CAAC,CAAC;AAEzF,UAAM,SAAS,GAAG,OAAO,EAAE,OAAO,iBAAiB,CAAC;AACpD,QAAI,EAAE,UAAU,EAAE,WAAW,QAAQ;AACnC,YAAM,IAAI,GAAG,QAAQ,EAAE,OAAO,WAAW,GAAG,CAAC,EAAE,OAAO,QAAQ,KAAK,GAAG,CAAC,CAAC;AACxE,QAAE,aAAa,eAAe,EAAE,MAAM;AACtC,aAAO,YAAY,CAAC;AAAA,IACtB;AACA,QAAI,EAAE,IAAK,QAAO,YAAY,GAAG,QAAQ,EAAE,OAAO,WAAW,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;AACxE,QAAI,OAAO,SAAS,OAAQ,MAAK,YAAY,MAAM;AAEnD,QAAI,iBAAiB;AACrB,UAAM,aAAa,GAAG,OAAO,EAAE,OAAO,cAAc,CAAC;AACrD,eAAW,MAAM,UAAU;AAC3B,SAAK,YAAY,UAAU;AAE3B,SAAK,iBAAiB,SAAS,YAAY;AACzC,YAAM,UAAU,WAAW,MAAM,YAAY;AAC7C,iBAAW,MAAM,UAAU,UAAU,KAAK;AAC1C,UAAI,WAAW,CAAC,gBAAgB;AAC9B,yBAAiB;AACjB,mBAAW,gBAAgB,GAAG,OAAO,EAAE,OAAO,aAAa,GAAG,CAAC,eAAU,CAAC,CAAC;AAC3E,YAAI;AACF,gBAAM,KAAK,MAAM,OAAO,aAAa,EAAE,EAAE;AACzC,qBAAW,gBAAgB,GAAG,eAAe,GAAG,EAAE,CAAC;AAAA,QACrD,SAAS,GAAG;AACV,qBAAW;AAAA,YACT,GAAG,OAAO,EAAE,OAAO,WAAW,GAAG,CAAE,EAAY,OAAO,CAAC;AAAA,UACzD;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAED,QAAI,YAAY,IAAI;AACpB,WAAO;AAAA,EACT;AAEA,WAAS,eAAe,GAAY,UAAoC;AACtE,UAAM,QAAuB,CAAC;AAC9B,QAAI,SAAS,WAAW,GAAG;AACzB,YAAM,KAAK,GAAG,OAAO,EAAE,OAAO,WAAW,GAAG,CAAC,iBAAiB,CAAC,CAAC;AAAA,IAClE,OAAO;AACL,iBAAW,KAAK,UAAU;AACxB,cAAM,KAAK,GAAG,OAAO,EAAE,OAAO,aAAa,GAAG;AAAA,UAC5C,GAAG,QAAQ,EAAE,OAAO,oBAAoB,GAAG,CAAC,EAAE,eAAe,WAAW,CAAC;AAAA,UACzE;AAAA,UACA,EAAE;AAAA,QACJ,CAAC,CAAC;AAAA,MACJ;AAAA,IACF;AAEA,UAAM,QAAQ,GAAG,YAAY;AAAA,MAC3B,OAAO;AAAA,MACP,aAAa;AAAA,MACb,MAAM;AAAA,IACR,CAAC;AACD,UAAM,OAAO,GAAG,UAAU,EAAE,OAAO,aAAa,MAAM,SAAS,GAAG,CAAC,OAAO,CAAC;AAC3E,SAAK,iBAAiB,SAAS,OAAO,MAAM;AA9gBhD;AA+gBM,QAAE,gBAAgB;AAClB,UAAI,CAAC,MAAM,MAAM,KAAK,EAAG;AACzB,WAAK,WAAW;AAChB,UAAI;AACF,cAAM,IAAI,MAAM,OAAO,QAAQ,EAAE,IAAI,MAAM,KAAK;AAChD,cAAM,QAAQ;AACd,aAAK,WAAW;AAEhB,cAAM,YAAY,MAAM,OAAO,aAAa,EAAE,EAAE;AAEhD,cAAM,UAAS,UAAK,kBAAL,mBAAoB;AACnC,YAAI,OAAQ,QAAO,gBAAgB,GAAG,eAAe,GAAG,SAAS,CAAC;AAAA,MACpE,SAAS,KAAK;AACZ,aAAK,WAAW;AAChB,cAAO,IAAc,OAAO;AAAA,MAC9B;AAAA,IACF,CAAC;AACD,UAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC;AACvC,WAAO;AAAA,EACT;AAIA,WAAS,aAAa;AACpB,SAAK,YAAY;AACjB,UAAM,UAAU,OAAO,gBAAgB;AACvC,UAAM,iBAAiB,QAAQ,IAAI,CAAC,WAAW,EAAE,OAAO,GAAG,aAAa,KAAK,EAAE,EAAE;AACjF,UAAM,cAAc,eAAe,SAAS,IAAI,iBAAiB;AAAA,MAC/D,EAAE,OAAO,SAAwB,GAAG,aAAa,MAAM;AAAA,IACzD;AACA,QAAI,OAAoB,YAAY,CAAC,EAAE;AAEvC,UAAM,OAAO,GAAG,QAAQ,EAAE,OAAO,UAAU,CAAC;AAE5C,UAAM,YAAY,GAAG,SAAS,EAAE,OAAO,WAAW,GAAG,CAAC,oBAAoB,CAAC;AAC3E,UAAM,YAAY,GAAG,OAAO,EAAE,OAAO,eAAe,CAAC;AACrD,UAAM,aAAkC,CAAC;AACzC,eAAW,OAAO,aAAa;AAC7B,YAAM,MAAM,GAAG,UAAU,EAAE,OAAO,UAAU,MAAM,SAAS,GAAG,CAAC,IAAI,KAAK,CAAC;AACzE,UAAI,aAAa,eAAe,OAAO,IAAI,UAAU,IAAI,CAAC;AAC1D,UAAI,iBAAiB,SAAS,MAAM;AAClC,eAAO,IAAI;AACX,mBAAW;AAAA,UAAQ,CAAC,GAAG,MACrB,EAAE,aAAa,eAAe,OAAO,YAAY,CAAC,EAAE,UAAU,IAAI,CAAC;AAAA,QACrE;AACA,mBAAW,cAAc,YAAY,KAAK,CAAC,MAAM,EAAE,UAAU,IAAI,EAAG;AAAA,MACtE,CAAC;AACD,iBAAW,KAAK,GAAG;AACnB,gBAAU,YAAY,GAAG;AAAA,IAC3B;AACA,cAAU,YAAY,SAAS;AAE/B,UAAM,aAAa,GAAG,SAAS,EAAE,OAAO,WAAW,GAAG,CAAC,OAAO,CAAC;AAC/D,UAAM,aAAa,GAAG,SAAS;AAAA,MAC7B,OAAO;AAAA,MACP,MAAM;AAAA,MACN,aAAa,YAAY,CAAC,EAAE;AAAA,MAC5B,UAAU;AAAA,IACZ,CAAC;AACD,eAAW,YAAY,UAAU;AAEjC,UAAM,YAAY,GAAG,SAAS,EAAE,OAAO,WAAW,GAAG,CAAC,aAAa,CAAC;AACpE,UAAM,YAAY,GAAG,YAAY;AAAA,MAC/B,OAAO;AAAA,MACP,aAAa;AAAA,MACb,MAAM;AAAA,IACR,CAAC;AACD,cAAU,YAAY,SAAS;AAE/B,UAAM,SAAS,GAAG,UAAU,EAAE,OAAO,aAAa,MAAM,SAAS,GAAG,CAAC,QAAQ,CAAC;AAE9E,SAAK,OAAO,WAAW,YAAY,WAAW,MAAM;AACpD,SAAK,YAAY,IAAI;AAErB,SAAK,iBAAiB,UAAU,OAAO,MAAM;AAC3C,QAAE,eAAe;AACjB,UAAI,CAAC,WAAW,MAAM,KAAK,EAAG;AAC9B,aAAO,WAAW;AAClB,aAAO,cAAc;AACrB,UAAI;AACF,cAAM,OAAO,OAAO;AAAA,UAClB,OAAO,WAAW;AAAA,UAClB,aAAa,UAAU;AAAA,UACvB;AAAA,QACF,CAAC;AACD,mBAAW,QAAQ;AACnB,kBAAU,QAAQ;AAClB,gBAAQ,QAAQ;AAEhB,YAAI,QAAQ;AACV,uBAAa;AACb,oBAAU;AAAA,QACZ;AACA,cAAM,QAAQ;AAAA,MAChB,SAAS,KAAK;AACZ,eAAO,WAAW;AAClB,eAAO,cAAc;AACrB,cAAO,IAAc,OAAO;AAAA,MAC9B;AAAA,IACF,CAAC;AAAA,EACH;AAEA,WAAS,QAAQ,GAAS;AACxB,WAAO;AACP,eAAW;AACX,QAAI,SAAS,SAAU,SAAQ;AAAA,QAC1B,YAAW;AAAA,EAClB;AAEA,aAAW,iBAAiB,SAAS,MAAM,QAAQ,QAAQ,CAAC;AAC5D,cAAY,iBAAiB,SAAS,MAAM,QAAQ,SAAS,CAAC;AAE9D,aAAW;AACX,YAAU;AACV,UAAQ,QAAQ;AAEhB,SAAO;AACT;","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
type Visibility = "public" | "private";
|
|
2
|
+
type Interaction = "upvote" | "downvote" | "plus_one" | "like";
|
|
3
|
+
type KindInteractions = Partial<Record<Interaction, boolean>>;
|
|
4
|
+
type ShowCounts = Partial<Record<FeatureKind, boolean>>;
|
|
5
|
+
type GroupMode = "tabs" | "list";
|
|
6
|
+
type Theme = {
|
|
7
|
+
primary?: string;
|
|
8
|
+
primaryDark?: string;
|
|
9
|
+
radius?: number;
|
|
10
|
+
mode?: "light" | "dark" | "system";
|
|
11
|
+
font_family?: string;
|
|
12
|
+
font_size?: "sm" | "md" | "lg";
|
|
13
|
+
group_mode?: GroupMode;
|
|
14
|
+
show_counts?: ShowCounts;
|
|
15
|
+
fontFamily?: string;
|
|
16
|
+
};
|
|
17
|
+
type EndUser = {
|
|
18
|
+
externalId?: string;
|
|
19
|
+
email?: string;
|
|
20
|
+
name?: string;
|
|
21
|
+
avatarUrl?: string;
|
|
22
|
+
platform?: string;
|
|
23
|
+
};
|
|
24
|
+
type FeatureKind = "feature_request" | "bug_report" | "improvement" | "appreciation" | "other";
|
|
25
|
+
type Feature = {
|
|
26
|
+
id: string;
|
|
27
|
+
title: string;
|
|
28
|
+
description: string;
|
|
29
|
+
status: "open" | "planned" | "in_progress" | "shipped" | "declined";
|
|
30
|
+
kind: FeatureKind;
|
|
31
|
+
visibility: Visibility;
|
|
32
|
+
on_roadmap: boolean;
|
|
33
|
+
tag: string | null;
|
|
34
|
+
vote_count: number;
|
|
35
|
+
voted: boolean;
|
|
36
|
+
platform: string | null;
|
|
37
|
+
author_name: string | null;
|
|
38
|
+
created_at: string;
|
|
39
|
+
};
|
|
40
|
+
type Comment = {
|
|
41
|
+
id: string;
|
|
42
|
+
body: string;
|
|
43
|
+
author_name: string | null;
|
|
44
|
+
is_internal: boolean;
|
|
45
|
+
created_at: string;
|
|
46
|
+
};
|
|
47
|
+
type HeedKitConfig = {
|
|
48
|
+
projectKey: string;
|
|
49
|
+
apiUrl?: string;
|
|
50
|
+
user?: EndUser;
|
|
51
|
+
};
|
|
52
|
+
type ProjectConfig = {
|
|
53
|
+
name: string;
|
|
54
|
+
theme: Theme;
|
|
55
|
+
enabled_kinds: FeatureKind[];
|
|
56
|
+
kind_visibility: Record<FeatureKind, Visibility>;
|
|
57
|
+
kind_interactions: Record<FeatureKind, KindInteractions>;
|
|
58
|
+
is_public_roadmap?: boolean;
|
|
59
|
+
};
|
|
60
|
+
type InitResult = {
|
|
61
|
+
end_user_id: string;
|
|
62
|
+
project: ProjectConfig;
|
|
63
|
+
};
|
|
64
|
+
declare class HeedKitClient {
|
|
65
|
+
private apiUrl;
|
|
66
|
+
private projectKey;
|
|
67
|
+
private endUserId;
|
|
68
|
+
private theme;
|
|
69
|
+
private projectName;
|
|
70
|
+
private enabledKinds;
|
|
71
|
+
private kindVisibility;
|
|
72
|
+
private kindInteractions;
|
|
73
|
+
constructor(config: HeedKitConfig);
|
|
74
|
+
init(user?: EndUser): Promise<InitResult>;
|
|
75
|
+
getTheme(): Theme;
|
|
76
|
+
getEnabledKinds(): FeatureKind[];
|
|
77
|
+
getKindVisibility(): Partial<Record<FeatureKind, Visibility>>;
|
|
78
|
+
getKindInteractions(): Partial<Record<FeatureKind, Partial<Record<Interaction, boolean>>>>;
|
|
79
|
+
getProjectName(): string;
|
|
80
|
+
getEndUserId(): string | null;
|
|
81
|
+
getInteractionsFor(kind: FeatureKind): Interaction[];
|
|
82
|
+
list(opts?: {
|
|
83
|
+
status?: string;
|
|
84
|
+
kind?: FeatureKind;
|
|
85
|
+
sort?: "top" | "new";
|
|
86
|
+
}): Promise<Feature[]>;
|
|
87
|
+
submit(input: {
|
|
88
|
+
title: string;
|
|
89
|
+
description?: string;
|
|
90
|
+
tag?: string;
|
|
91
|
+
kind?: FeatureKind;
|
|
92
|
+
}): Promise<Feature>;
|
|
93
|
+
vote(featureId: string): Promise<{
|
|
94
|
+
voted: boolean;
|
|
95
|
+
vote_count: number;
|
|
96
|
+
}>;
|
|
97
|
+
listComments(featureId: string): Promise<Comment[]>;
|
|
98
|
+
comment(featureId: string, body: string): Promise<Comment>;
|
|
99
|
+
private ensureInit;
|
|
100
|
+
private request;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
type MountOptions = HeedKitConfig & {
|
|
104
|
+
label?: string;
|
|
105
|
+
hideLauncher?: boolean;
|
|
106
|
+
container?: HTMLElement;
|
|
107
|
+
};
|
|
108
|
+
type Widget = {
|
|
109
|
+
client: HeedKitClient;
|
|
110
|
+
open: () => void;
|
|
111
|
+
close: () => void;
|
|
112
|
+
destroy: () => void;
|
|
113
|
+
};
|
|
114
|
+
declare function mount(options: MountOptions): Widget;
|
|
115
|
+
|
|
116
|
+
export { type Comment, type EndUser, type Feature, type FeatureKind, type GroupMode, HeedKitClient, type HeedKitConfig, type InitResult, type Interaction, type KindInteractions, type MountOptions, type ShowCounts, type Theme, type Visibility, type Widget, mount };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
type Visibility = "public" | "private";
|
|
2
|
+
type Interaction = "upvote" | "downvote" | "plus_one" | "like";
|
|
3
|
+
type KindInteractions = Partial<Record<Interaction, boolean>>;
|
|
4
|
+
type ShowCounts = Partial<Record<FeatureKind, boolean>>;
|
|
5
|
+
type GroupMode = "tabs" | "list";
|
|
6
|
+
type Theme = {
|
|
7
|
+
primary?: string;
|
|
8
|
+
primaryDark?: string;
|
|
9
|
+
radius?: number;
|
|
10
|
+
mode?: "light" | "dark" | "system";
|
|
11
|
+
font_family?: string;
|
|
12
|
+
font_size?: "sm" | "md" | "lg";
|
|
13
|
+
group_mode?: GroupMode;
|
|
14
|
+
show_counts?: ShowCounts;
|
|
15
|
+
fontFamily?: string;
|
|
16
|
+
};
|
|
17
|
+
type EndUser = {
|
|
18
|
+
externalId?: string;
|
|
19
|
+
email?: string;
|
|
20
|
+
name?: string;
|
|
21
|
+
avatarUrl?: string;
|
|
22
|
+
platform?: string;
|
|
23
|
+
};
|
|
24
|
+
type FeatureKind = "feature_request" | "bug_report" | "improvement" | "appreciation" | "other";
|
|
25
|
+
type Feature = {
|
|
26
|
+
id: string;
|
|
27
|
+
title: string;
|
|
28
|
+
description: string;
|
|
29
|
+
status: "open" | "planned" | "in_progress" | "shipped" | "declined";
|
|
30
|
+
kind: FeatureKind;
|
|
31
|
+
visibility: Visibility;
|
|
32
|
+
on_roadmap: boolean;
|
|
33
|
+
tag: string | null;
|
|
34
|
+
vote_count: number;
|
|
35
|
+
voted: boolean;
|
|
36
|
+
platform: string | null;
|
|
37
|
+
author_name: string | null;
|
|
38
|
+
created_at: string;
|
|
39
|
+
};
|
|
40
|
+
type Comment = {
|
|
41
|
+
id: string;
|
|
42
|
+
body: string;
|
|
43
|
+
author_name: string | null;
|
|
44
|
+
is_internal: boolean;
|
|
45
|
+
created_at: string;
|
|
46
|
+
};
|
|
47
|
+
type HeedKitConfig = {
|
|
48
|
+
projectKey: string;
|
|
49
|
+
apiUrl?: string;
|
|
50
|
+
user?: EndUser;
|
|
51
|
+
};
|
|
52
|
+
type ProjectConfig = {
|
|
53
|
+
name: string;
|
|
54
|
+
theme: Theme;
|
|
55
|
+
enabled_kinds: FeatureKind[];
|
|
56
|
+
kind_visibility: Record<FeatureKind, Visibility>;
|
|
57
|
+
kind_interactions: Record<FeatureKind, KindInteractions>;
|
|
58
|
+
is_public_roadmap?: boolean;
|
|
59
|
+
};
|
|
60
|
+
type InitResult = {
|
|
61
|
+
end_user_id: string;
|
|
62
|
+
project: ProjectConfig;
|
|
63
|
+
};
|
|
64
|
+
declare class HeedKitClient {
|
|
65
|
+
private apiUrl;
|
|
66
|
+
private projectKey;
|
|
67
|
+
private endUserId;
|
|
68
|
+
private theme;
|
|
69
|
+
private projectName;
|
|
70
|
+
private enabledKinds;
|
|
71
|
+
private kindVisibility;
|
|
72
|
+
private kindInteractions;
|
|
73
|
+
constructor(config: HeedKitConfig);
|
|
74
|
+
init(user?: EndUser): Promise<InitResult>;
|
|
75
|
+
getTheme(): Theme;
|
|
76
|
+
getEnabledKinds(): FeatureKind[];
|
|
77
|
+
getKindVisibility(): Partial<Record<FeatureKind, Visibility>>;
|
|
78
|
+
getKindInteractions(): Partial<Record<FeatureKind, Partial<Record<Interaction, boolean>>>>;
|
|
79
|
+
getProjectName(): string;
|
|
80
|
+
getEndUserId(): string | null;
|
|
81
|
+
getInteractionsFor(kind: FeatureKind): Interaction[];
|
|
82
|
+
list(opts?: {
|
|
83
|
+
status?: string;
|
|
84
|
+
kind?: FeatureKind;
|
|
85
|
+
sort?: "top" | "new";
|
|
86
|
+
}): Promise<Feature[]>;
|
|
87
|
+
submit(input: {
|
|
88
|
+
title: string;
|
|
89
|
+
description?: string;
|
|
90
|
+
tag?: string;
|
|
91
|
+
kind?: FeatureKind;
|
|
92
|
+
}): Promise<Feature>;
|
|
93
|
+
vote(featureId: string): Promise<{
|
|
94
|
+
voted: boolean;
|
|
95
|
+
vote_count: number;
|
|
96
|
+
}>;
|
|
97
|
+
listComments(featureId: string): Promise<Comment[]>;
|
|
98
|
+
comment(featureId: string, body: string): Promise<Comment>;
|
|
99
|
+
private ensureInit;
|
|
100
|
+
private request;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
type MountOptions = HeedKitConfig & {
|
|
104
|
+
label?: string;
|
|
105
|
+
hideLauncher?: boolean;
|
|
106
|
+
container?: HTMLElement;
|
|
107
|
+
};
|
|
108
|
+
type Widget = {
|
|
109
|
+
client: HeedKitClient;
|
|
110
|
+
open: () => void;
|
|
111
|
+
close: () => void;
|
|
112
|
+
destroy: () => void;
|
|
113
|
+
};
|
|
114
|
+
declare function mount(options: MountOptions): Widget;
|
|
115
|
+
|
|
116
|
+
export { type Comment, type EndUser, type Feature, type FeatureKind, type GroupMode, HeedKitClient, type HeedKitConfig, type InitResult, type Interaction, type KindInteractions, type MountOptions, type ShowCounts, type Theme, type Visibility, type Widget, mount };
|