@heedkit/sdk-vue 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 +75 -0
- package/dist/FeedbackButton.vue.d.ts +9 -0
- package/dist/client.d.ts +110 -0
- package/dist/index.cjs +114 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +584 -0
- package/dist/plugin.d.ts +14 -0
- package/dist/widget.d.ts +13 -0
- package/package.json +52 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 HeedKit
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# @heedkit/sdk-vue
|
|
2
|
+
|
|
3
|
+
Vue 3 SDK for [HeedKit](https://heedkit.com). Drop-in feature requests,
|
|
4
|
+
voting, comments — themed from your console.
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npm i @heedkit/sdk-vue
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Wire the plugin
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
// main.ts
|
|
16
|
+
import { createApp } from "vue";
|
|
17
|
+
import { createHeedKit } from "@heedkit/sdk-vue";
|
|
18
|
+
import App from "./App.vue";
|
|
19
|
+
|
|
20
|
+
createApp(App)
|
|
21
|
+
.use(createHeedKit({
|
|
22
|
+
projectKey: "fh_xxx",
|
|
23
|
+
user: { externalId: "user-123", email: "alice@example.com", name: "Alice" },
|
|
24
|
+
}))
|
|
25
|
+
.mount("#app");
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Mount the widget anywhere
|
|
29
|
+
|
|
30
|
+
```vue
|
|
31
|
+
<script setup lang="ts">
|
|
32
|
+
import { FeedbackButton } from "@heedkit/sdk-vue";
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<template>
|
|
36
|
+
<YourApp />
|
|
37
|
+
<FeedbackButton label="Feedback" />
|
|
38
|
+
</template>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`FeedbackButton` is a thin Vue wrapper that mounts the shared JS widget into
|
|
42
|
+
`document.body` — same UI as every other HeedKit SDK.
|
|
43
|
+
|
|
44
|
+
## Imperative open / close
|
|
45
|
+
|
|
46
|
+
```vue
|
|
47
|
+
<script setup lang="ts">
|
|
48
|
+
import { ref } from "vue";
|
|
49
|
+
import { FeedbackButton } from "@heedkit/sdk-vue";
|
|
50
|
+
|
|
51
|
+
const btn = ref<InstanceType<typeof FeedbackButton> | null>(null);
|
|
52
|
+
function openPanel() { btn.value?.open(); }
|
|
53
|
+
</script>
|
|
54
|
+
|
|
55
|
+
<template>
|
|
56
|
+
<FeedbackButton ref="btn" :hide-launcher="true" />
|
|
57
|
+
<button @click="openPanel">Custom trigger</button>
|
|
58
|
+
</template>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Headless
|
|
62
|
+
|
|
63
|
+
Inject the underlying client for custom UI:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { inject } from "vue";
|
|
67
|
+
import { HEEDKIT_KEY } from "@heedkit/sdk-vue";
|
|
68
|
+
|
|
69
|
+
const fh = inject(HEEDKIT_KEY)!;
|
|
70
|
+
await fh.client.list({ sort: "top" });
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## License
|
|
74
|
+
|
|
75
|
+
MIT
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
label?: string;
|
|
3
|
+
hideLauncher?: boolean;
|
|
4
|
+
};
|
|
5
|
+
declare const _default: import("vue").DefineComponent<__VLS_Props, {
|
|
6
|
+
open: () => void | undefined;
|
|
7
|
+
close: () => void | undefined;
|
|
8
|
+
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
9
|
+
export default _default;
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
export type Visibility = "public" | "private";
|
|
2
|
+
export type Interaction = "upvote" | "downvote" | "plus_one" | "like";
|
|
3
|
+
export type KindInteractions = Partial<Record<Interaction, boolean>>;
|
|
4
|
+
export type ShowCounts = Partial<Record<FeatureKind, boolean>>;
|
|
5
|
+
export type GroupMode = "tabs" | "list";
|
|
6
|
+
export 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
|
+
export type EndUser = {
|
|
18
|
+
externalId?: string;
|
|
19
|
+
email?: string;
|
|
20
|
+
name?: string;
|
|
21
|
+
avatarUrl?: string;
|
|
22
|
+
platform?: string;
|
|
23
|
+
};
|
|
24
|
+
export type FeatureKind = "feature_request" | "bug_report" | "improvement" | "appreciation" | "other";
|
|
25
|
+
export 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
|
+
export type Comment = {
|
|
41
|
+
id: string;
|
|
42
|
+
body: string;
|
|
43
|
+
author_name: string | null;
|
|
44
|
+
is_internal: boolean;
|
|
45
|
+
created_at: string;
|
|
46
|
+
};
|
|
47
|
+
export type HeedKitConfig = {
|
|
48
|
+
projectKey: string;
|
|
49
|
+
apiUrl?: string;
|
|
50
|
+
user?: EndUser;
|
|
51
|
+
};
|
|
52
|
+
export 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
|
+
export type InitResult = {
|
|
61
|
+
end_user_id: string;
|
|
62
|
+
project: ProjectConfig;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Stable per-browser identifier persisted in localStorage. When the customer
|
|
66
|
+
* doesn't pass `externalId`, we still want votes/submissions to stick to the
|
|
67
|
+
* same EndUser across page loads — otherwise every refresh would create a new
|
|
68
|
+
* anonymous account.
|
|
69
|
+
*
|
|
70
|
+
* Returns null on the server (SSR), so callers should fall back to a fresh id.
|
|
71
|
+
*/
|
|
72
|
+
export declare function getOrCreateDeviceId(): string | null;
|
|
73
|
+
export declare class HeedKitClient {
|
|
74
|
+
private apiUrl;
|
|
75
|
+
private projectKey;
|
|
76
|
+
private endUserId;
|
|
77
|
+
private theme;
|
|
78
|
+
private projectName;
|
|
79
|
+
private enabledKinds;
|
|
80
|
+
private kindVisibility;
|
|
81
|
+
private kindInteractions;
|
|
82
|
+
constructor(config: HeedKitConfig);
|
|
83
|
+
init(user?: EndUser): Promise<InitResult>;
|
|
84
|
+
getTheme(): Theme;
|
|
85
|
+
getEnabledKinds(): FeatureKind[];
|
|
86
|
+
getKindVisibility(): Partial<Record<FeatureKind, Visibility>>;
|
|
87
|
+
getKindInteractions(): Partial<Record<FeatureKind, Partial<Record<Interaction, boolean>>>>;
|
|
88
|
+
getProjectName(): string;
|
|
89
|
+
getEndUserId(): string | null;
|
|
90
|
+
getInteractionsFor(kind: FeatureKind): Interaction[];
|
|
91
|
+
list(opts?: {
|
|
92
|
+
status?: string;
|
|
93
|
+
kind?: FeatureKind;
|
|
94
|
+
sort?: "top" | "new";
|
|
95
|
+
}): Promise<Feature[]>;
|
|
96
|
+
submit(input: {
|
|
97
|
+
title: string;
|
|
98
|
+
description?: string;
|
|
99
|
+
tag?: string;
|
|
100
|
+
kind?: FeatureKind;
|
|
101
|
+
}): Promise<Feature>;
|
|
102
|
+
vote(featureId: string): Promise<{
|
|
103
|
+
voted: boolean;
|
|
104
|
+
vote_count: number;
|
|
105
|
+
}>;
|
|
106
|
+
listComments(featureId: string): Promise<Comment[]>;
|
|
107
|
+
comment(featureId: string, body: string): Promise<Comment>;
|
|
108
|
+
private ensureInit;
|
|
109
|
+
private request;
|
|
110
|
+
}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const w=require("vue"),ae="https://api.heedkit.com",V="heedkit.device_id";function re(){var e;try{if(typeof window>"u"||!window.localStorage)return null;const t=window.localStorage.getItem(V);if(t)return t;const i="dev_"+(((e=crypto==null?void 0:crypto.randomUUID)==null?void 0:e.call(crypto))??Math.random().toString(36).slice(2));return window.localStorage.setItem(V,i),i}catch{return null}}function W(e){return{id:String(e.id),title:e.title,description:e.description??"",status:e.status,kind:e.kind,visibility:e.visibility,on_roadmap:e.on_roadmap??!1,tag:e.tag??null,vote_count:e.vote_count??0,voted:e.voted??!1,platform:e.platform??null,author_name:e.author_name??e.author??null,created_at:e.created_at}}function Y(e){return{id:String(e.id),body:e.body,author_name:e.author_name??e.author??null,is_internal:e.is_internal??!1,created_at:e.created_at}}class O{constructor(t){this.endUserId=null,this.theme={},this.projectName="",this.enabledKinds=[],this.kindVisibility={},this.kindInteractions={},this.apiUrl=t.apiUrl||ae,this.projectKey=t.projectKey}async init(t={}){const o={external_id:t.externalId??re()??void 0,email:t.email,name:t.name,avatar_url:t.avatarUrl,platform:t.platform||"web"},n=await this.request("/sdk/init","POST",o);this.endUserId=n.end_user_id;const s=n.project??n;return this.theme=s.theme||{},this.projectName=s.name??s.project_name??"",this.enabledKinds=s.enabled_kinds||[],this.kindVisibility=s.kind_visibility||{},this.kindInteractions=s.kind_interactions||{},n}getTheme(){return this.theme}getEnabledKinds(){return this.enabledKinds}getKindVisibility(){return this.kindVisibility}getKindInteractions(){return this.kindInteractions}getProjectName(){return this.projectName}getEndUserId(){return this.endUserId}getInteractionsFor(t){const i=this.kindInteractions[t]||{};return["upvote","downvote","plus_one","like"].filter(o=>i[o])}async list(t={}){this.ensureInit();const i=new URLSearchParams({end_user_id:this.endUserId});t.status&&i.set("status",t.status),t.kind&&i.set("kind",t.kind),t.sort&&i.set("sort",t.sort);const o=await this.request(`/sdk/features?${i}`,"GET");return(Array.isArray(o)?o:o.features??[]).map(s=>W(s))}async submit(t){this.ensureInit();const i=await this.request("/sdk/features","POST",{end_user_id:this.endUserId,title:t.title,description:t.description||"",tag:t.tag||null,kind:t.kind||"feature_request"});return W(i)}async vote(t){return this.ensureInit(),this.request(`/sdk/features/${t}/vote`,"POST",{end_user_id:this.endUserId})}async listComments(t){const i=await this.request(`/sdk/features/${t}/comments`,"GET");return(Array.isArray(i)?i:i.comments??[]).map(n=>Y(n))}async comment(t,i){this.ensureInit();const o=await this.request(`/sdk/features/${t}/comments`,"POST",{end_user_id:this.endUserId,body:i});return Y(o)}ensureInit(){if(!this.endUserId)throw new Error("HeedKit not initialized — call init() first")}async request(t,i,o){const n=await fetch(`${this.apiUrl}${t}`,{method:i,headers:{"Content-Type":"application/json","X-Project-Key":this.projectKey},body:o?JSON.stringify(o):void 0});if(!n.ok){let s=`HTTP ${n.status}`;try{const h=await n.json();s=h.error||h.detail||s}catch{}throw new Error(s)}return n.json()}}const B=Symbol("heedkit");function ie(e){return{install(t){const i=new O(e),o=w.ref(!1),n=w.ref({});i.init(e.user||{}).then(()=>{n.value=i.getTheme(),o.value=!0}),t.provide(B,{client:i,ready:o,theme:n,projectKey:e.projectKey,apiUrl:e.apiUrl,user:e.user})}}}const P={feature_request:{label:"Features",placeholder:"What should we build?",tabIcon:"💡"},bug_report:{label:"Bugs",placeholder:"What's broken?",tabIcon:"🐞"},improvement:{label:"Improvements",placeholder:"What could be better?",tabIcon:"✨"},appreciation:{label:"Appreciation",placeholder:"What did you love?",tabIcon:"❤️"},other:{label:"Other",placeholder:"Tell us anything",tabIcon:"💬"}},oe={upvote:{icon:"▲",label:"Upvote"},downvote:{icon:"▼",label:"Downvote"},plus_one:{icon:"+1",label:"+1"},like:{icon:"♥",label:"Like"}},R={sm:"13px",md:"14px",lg:"16px"},G="heedkit-styles",se=`
|
|
2
|
+
.fk-launcher {
|
|
3
|
+
position: fixed; bottom: 24px; right: 24px; z-index: 2147483645;
|
|
4
|
+
background: var(--fh-primary); color: #fff; border: 0; border-radius: 999px;
|
|
5
|
+
padding: 12px 18px; font-weight: 600; font-size: var(--fh-fs); cursor: pointer;
|
|
6
|
+
box-shadow: 0 10px 24px rgba(0,0,0,.18); font-family: var(--fh-font);
|
|
7
|
+
transition: transform .15s ease;
|
|
8
|
+
}
|
|
9
|
+
.fk-launcher:hover { transform: translateY(-1px); }
|
|
10
|
+
.fk-overlay {
|
|
11
|
+
position: fixed; inset: 0; z-index: 2147483646; display: flex;
|
|
12
|
+
align-items: center; justify-content: center; padding: 16px;
|
|
13
|
+
background: rgba(0,0,0,.45); backdrop-filter: blur(2px);
|
|
14
|
+
font-family: var(--fh-font); font-size: var(--fh-fs);
|
|
15
|
+
}
|
|
16
|
+
.fk-panel {
|
|
17
|
+
width: 100%; max-width: 520px; max-height: 85vh; display: flex; flex-direction: column;
|
|
18
|
+
background: var(--fh-bg); color: var(--fh-fg);
|
|
19
|
+
border-radius: calc(var(--fh-radius) * 1.5); overflow: hidden;
|
|
20
|
+
box-shadow: 0 20px 60px rgba(0,0,0,.3);
|
|
21
|
+
}
|
|
22
|
+
.fk-head { padding: 18px 20px 12px; border-bottom: 1px solid var(--fh-border); }
|
|
23
|
+
.fk-titlerow { display:flex; justify-content:space-between; align-items:center; }
|
|
24
|
+
.fk-title { font-size: calc(var(--fh-fs) + 6px); font-weight: 700; }
|
|
25
|
+
.fk-close {
|
|
26
|
+
background: transparent; border: 0; color: var(--fh-muted);
|
|
27
|
+
font-size: 22px; cursor: pointer; line-height: 1; padding: 0 4px;
|
|
28
|
+
}
|
|
29
|
+
.fk-modes { display:flex; gap: 6px; margin-top: 12px; }
|
|
30
|
+
.fk-mode {
|
|
31
|
+
border: 0; background: transparent; padding: 6px 12px; border-radius: 999px;
|
|
32
|
+
font-size: calc(var(--fh-fs) - 1px); font-weight: 600; cursor: pointer;
|
|
33
|
+
color: var(--fh-muted); font-family: inherit;
|
|
34
|
+
}
|
|
35
|
+
.fk-mode[data-active="true"] { background: var(--fh-primary); color: #fff; }
|
|
36
|
+
.fk-tabs {
|
|
37
|
+
display: flex; flex-wrap: wrap; gap: 6px; padding: 10px 20px; border-bottom: 1px solid var(--fh-border);
|
|
38
|
+
}
|
|
39
|
+
.fk-tab {
|
|
40
|
+
border: 0; background: var(--fh-row); color: var(--fh-fg);
|
|
41
|
+
padding: 6px 12px; border-radius: 999px;
|
|
42
|
+
font-size: calc(var(--fh-fs) - 1px); font-weight: 500; cursor: pointer; font-family: inherit;
|
|
43
|
+
display: inline-flex; align-items: center; gap: 6px;
|
|
44
|
+
}
|
|
45
|
+
.fk-tab[data-active="true"] { background: var(--fh-primary); color: #fff; }
|
|
46
|
+
.fk-body { flex: 1; overflow-y: auto; padding: 16px 20px; }
|
|
47
|
+
.fk-empty { text-align: center; padding: 32px; opacity: .6; }
|
|
48
|
+
.fk-loading { text-align: center; padding: 32px; opacity: .6; }
|
|
49
|
+
.fk-row {
|
|
50
|
+
display: flex; gap: 12px; padding: 12px; margin-bottom: 8px;
|
|
51
|
+
background: var(--fh-row); border-radius: var(--fh-radius);
|
|
52
|
+
}
|
|
53
|
+
.fk-actions { display:flex; flex-direction: column; gap: 4px; }
|
|
54
|
+
.fk-act {
|
|
55
|
+
border: 1px solid var(--fh-border); background: transparent;
|
|
56
|
+
color: var(--fh-fg); border-radius: calc(var(--fh-radius) - 4px);
|
|
57
|
+
min-width: 44px; padding: 6px 8px; cursor: pointer;
|
|
58
|
+
display: flex; flex-direction: column; align-items: center; gap: 2px;
|
|
59
|
+
font-weight: 600; font-size: calc(var(--fh-fs) - 1px); font-family: inherit;
|
|
60
|
+
}
|
|
61
|
+
.fk-act[data-voted="true"] {
|
|
62
|
+
border: 2px solid var(--fh-primary);
|
|
63
|
+
background: color-mix(in srgb, var(--fh-primary) 14%, transparent);
|
|
64
|
+
color: var(--fh-primary);
|
|
65
|
+
}
|
|
66
|
+
.fk-act[disabled] { cursor: default; opacity: .85; }
|
|
67
|
+
.fk-act .fk-glyph { font-size: calc(var(--fh-fs) + 1px); line-height: 1; }
|
|
68
|
+
.fk-meta { flex:1; min-width:0; cursor: pointer; }
|
|
69
|
+
.fk-item-title { font-weight: 600; }
|
|
70
|
+
.fk-item-desc { opacity: .7; font-size: calc(var(--fh-fs) - 1px); margin-top: 4px; }
|
|
71
|
+
.fk-item-badges { display:flex; gap:6px; margin-top:6px; flex-wrap: wrap; }
|
|
72
|
+
.fk-badge {
|
|
73
|
+
font-size: calc(var(--fh-fs) - 3px); padding: 2px 8px; border-radius: 999px;
|
|
74
|
+
background: var(--fh-border); color: var(--fh-muted); text-transform: uppercase; letter-spacing: .04em;
|
|
75
|
+
}
|
|
76
|
+
.fk-badge[data-status="planned"] { background: rgba(59,130,246,.15); color: rgb(37,99,235); }
|
|
77
|
+
.fk-badge[data-status="in_progress"] { background: rgba(234,179,8,.18); color: rgb(161,98,7); }
|
|
78
|
+
.fk-badge[data-status="shipped"] { background: rgba(34,197,94,.18); color: rgb(22,101,52); }
|
|
79
|
+
.fk-comments { margin-top: 10px; border-top: 1px solid var(--fh-border); padding-top: 10px; }
|
|
80
|
+
.fk-comment { padding: 6px 0; border-top: 1px dashed var(--fh-border); font-size: calc(var(--fh-fs) - 1px); }
|
|
81
|
+
.fk-comment:first-child { border-top: 0; }
|
|
82
|
+
.fk-comment-author { font-weight: 600; }
|
|
83
|
+
.fk-form { display: flex; flex-direction: column; gap: 12px; }
|
|
84
|
+
.fk-label { font-size: calc(var(--fh-fs) - 1px); font-weight: 500; }
|
|
85
|
+
.fk-input, .fk-textarea {
|
|
86
|
+
width: 100%; padding: 10px 12px; margin-top: 4px;
|
|
87
|
+
border-radius: calc(var(--fh-radius) - 2px);
|
|
88
|
+
border: 1px solid var(--fh-input-border); background: var(--fh-input-bg);
|
|
89
|
+
color: var(--fh-fg); font-size: var(--fh-fs); font-family: inherit;
|
|
90
|
+
box-sizing: border-box;
|
|
91
|
+
}
|
|
92
|
+
.fk-textarea { resize: vertical; min-height: 96px; }
|
|
93
|
+
.fk-input:focus, .fk-textarea:focus { outline: 2px solid var(--fh-primary); outline-offset: 1px; }
|
|
94
|
+
.fk-submit {
|
|
95
|
+
background: var(--fh-primary); color: #fff; border: 0;
|
|
96
|
+
padding: 12px 14px; border-radius: var(--fh-radius);
|
|
97
|
+
font-weight: 600; font-size: var(--fh-fs); cursor: pointer; font-family: inherit;
|
|
98
|
+
}
|
|
99
|
+
.fk-submit[disabled] { opacity: .6; cursor: not-allowed; }
|
|
100
|
+
.fk-segmented {
|
|
101
|
+
display: inline-flex; gap: 4px; padding: 4px; margin-top: 6px;
|
|
102
|
+
background: var(--fh-row); border-radius: 999px;
|
|
103
|
+
}
|
|
104
|
+
.fk-seg {
|
|
105
|
+
border: 0; background: transparent; cursor: pointer;
|
|
106
|
+
padding: 6px 12px; border-radius: 999px; font-size: calc(var(--fh-fs) - 2px);
|
|
107
|
+
font-weight: 500; color: var(--fh-muted); font-family: inherit;
|
|
108
|
+
}
|
|
109
|
+
.fk-seg[data-active="true"] {
|
|
110
|
+
background: var(--fh-bg); color: var(--fh-fg);
|
|
111
|
+
box-shadow: 0 1px 2px rgba(0,0,0,.06), 0 2px 8px rgba(0,0,0,.04);
|
|
112
|
+
}
|
|
113
|
+
.fk-error { color: rgb(220,38,38); font-size: calc(var(--fh-fs) - 1px); }
|
|
114
|
+
`;function de(){if(document.getElementById(G))return;const e=document.createElement("style");e.id=G,e.textContent=se,document.head.appendChild(e)}function le(e){const t=e.mode||"light";return t==="system"?typeof window<"u"&&window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":t==="dark"?"dark":"light"}function J(e,t){const i=t.primary||"#0D9488",o=`${t.radius??12}px`,n=le(t)==="dark",s=t.font_family||t.fontFamily||"system-ui, -apple-system, sans-serif",h=R[t.font_size??"md"]??R.md,m={"--fh-primary":i,"--fh-radius":o,"--fh-font":s,"--fh-fs":h,"--fh-bg":n?"#0F172A":"#FFFFFF","--fh-fg":n?"#F1F5F9":"#0F172A","--fh-muted":n?"#94A3B8":"#64748B","--fh-row":n?"#1E293B":"#F8FAFC","--fh-border":n?"#1E293B":"#E2E8F0","--fh-input-bg":n?"#0F172A":"#FFFFFF","--fh-input-border":n?"#334155":"#CBD5E1"};for(const[b,C]of Object.entries(m))e.style.setProperty(b,C)}function a(e,t,i){const o=document.createElement(e);if(t)for(const[n,s]of Object.entries(t))n==="class"?o.className=s:o.setAttribute(n,s);if(i)for(const n of i)n!=null&&o.appendChild(typeof n=="string"?document.createTextNode(n):n);return o}function X(e){de();const t=e.container||document.body,i=new O(e),o=i.init(e.user||{}).catch(b=>(console.warn("[HeedKit] widget init failed; launcher disabled.",b),null));let n=null,s=null;function h(){n&&(n.remove(),n=null)}async function m(){await o&&(n||(n=ce(i,i.getTheme(),h),t.appendChild(n)))}return e.hideLauncher||o.then(b=>{b&&(s=a("button",{class:"fk-launcher",type:"button"},[e.label||"Feedback"]),J(s,i.getTheme()),s.addEventListener("click",m),t.appendChild(s))}),{client:i,open:m,close:h,destroy(){h(),s==null||s.remove()}}}function ce(e,t,i){const o=a("div",{class:"fk-overlay",role:"dialog"});J(o,t),o.addEventListener("click",r=>{r.target===o&&i()});const n=a("div",{class:"fk-panel"});o.appendChild(n);const s=e.getEnabledKinds(),h=t.group_mode||"tabs";let m="browse",b=h==="tabs"&&s.length>0?s[0]:"all",C=[];const F=a("div",{class:"fk-head"}),K=a("div",{class:"fk-titlerow"});K.appendChild(a("div",{class:"fk-title"},[e.getProjectName()||"Feedback"]));const N=a("button",{class:"fk-close",type:"button","aria-label":"Close"},["×"]);N.addEventListener("click",i),K.appendChild(N),F.appendChild(K);const D=a("div",{class:"fk-modes"}),T=a("button",{class:"fk-mode",type:"button"},["Browse"]),A=a("button",{class:"fk-mode",type:"button"},["Suggest"]);D.append(T,A),F.appendChild(D),n.appendChild(F);let k=null;if(h==="tabs"&&s.length>0){k=a("div",{class:"fk-tabs"});const r=a("button",{class:"fk-tab",type:"button"},["All"]);r.dataset.kind="all",k.appendChild(r);for(const p of s){const d=P[p],l=a("button",{class:"fk-tab",type:"button"},[a("span",{},[d.tabIcon]),a("span",{},[d.label])]);l.dataset.kind=p,k.appendChild(l)}n.appendChild(k),k.addEventListener("click",p=>{const d=p.target.closest("[data-kind]");d&&(b=d.dataset.kind,L(),j())})}const v=a("div",{class:"fk-body"});n.appendChild(v);function H(){T.setAttribute("data-active",String(m==="browse")),A.setAttribute("data-active",String(m==="suggest")),k&&(k.style.display=m==="browse"?"":"none")}function L(){if(k)for(const r of Array.from(k.children))r.setAttribute("data-active",String(r.dataset.kind===String(b)))}async function j(){v.replaceChildren(a("div",{class:"fk-loading"},["Loading…"]));try{const r={sort:"top"};b!=="all"&&(r.kind=b),C=await e.list(r)}catch(r){v.replaceChildren(a("div",{class:"fk-empty"},[`Failed to load: ${r.message}`]));return}M()}async function Z(r,p){const d=await e.vote(r.id);r.voted=d.voted,r.vote_count=d.vote_count,M()}function Q(r){const p=e.getInteractionsFor(r.kind),d=(e.getTheme().show_counts||{})[r.kind]!==!1,l=a("div",{class:"fk-actions"});if(p.length===0){if(d){const c=a("button",{class:"fk-act",type:"button",disabled:"true"},[a("span",{},[String(r.vote_count)])]);l.appendChild(c)}return l}for(const c of p){const f=oe[c],u=a("button",{class:"fk-act",type:"button","aria-label":f.label},[a("span",{class:"fk-glyph"},[f.icon]),...d?[a("span",{},[String(r.vote_count)])]:[]]);(c==="upvote"||c==="like"||c==="plus_one")&&u.setAttribute("data-voted",String(r.voted)),u.addEventListener("click",g=>{g.stopPropagation(),Z(r)}),l.appendChild(u)}return l}function M(){if(v.innerHTML="",C.length===0){v.appendChild(a("div",{class:"fk-empty"},["No items yet — be the first!"]));return}for(const r of C)v.appendChild(ee(r))}function ee(r){const p=a("div",{class:"fk-row"});p.appendChild(Q(r));const d=a("div",{class:"fk-meta"});d.appendChild(a("div",{class:"fk-item-title"},[r.title])),r.description&&d.appendChild(a("div",{class:"fk-item-desc"},[r.description]));const l=a("div",{class:"fk-item-badges"});if(r.status&&r.status!=="open"){const u=a("span",{class:"fk-badge"},[r.status.replace("_"," ")]);u.setAttribute("data-status",r.status),l.appendChild(u)}r.tag&&l.appendChild(a("span",{class:"fk-badge"},[r.tag])),l.children.length&&d.appendChild(l);let c=!1;const f=a("div",{class:"fk-comments"});return f.style.display="none",d.appendChild(f),d.addEventListener("click",async()=>{const u=f.style.display==="none";if(f.style.display=u?"":"none",u&&!c){c=!0,f.replaceChildren(a("div",{class:"fk-loading"},["Loading…"]));try{const g=await e.listComments(r.id);f.replaceChildren(...q(r,g))}catch(g){f.replaceChildren(a("div",{class:"fk-error"},[g.message]))}}}),p.appendChild(d),p}function q(r,p){const d=[];if(p.length===0)d.push(a("div",{class:"fk-empty"},["No replies yet."]));else for(const f of p)d.push(a("div",{class:"fk-comment"},[a("span",{class:"fk-comment-author"},[f.author_name||"Anonymous"])," — ",f.body]));const l=a("textarea",{class:"fk-textarea",placeholder:"Add a reply…",rows:"2"}),c=a("button",{class:"fk-submit",type:"button"},["Reply"]);return c.addEventListener("click",async f=>{var u;if(f.stopPropagation(),!!l.value.trim()){c.disabled=!0;try{const g=await e.comment(r.id,l.value);l.value="",c.disabled=!1;const S=await e.listComments(r.id),x=(u=c.parentElement)==null?void 0:u.parentElement;x&&x.replaceChildren(...q(r,S))}catch(g){c.disabled=!1,alert(g.message)}}}),d.push(a("div",{},[l,c])),d}function te(){v.innerHTML="";const p=e.getEnabledKinds().map(y=>({value:y,...P[y]})),d=p.length>0?p:[{value:"other",...P.other}];let l=d[0].value;const c=a("form",{class:"fk-form"}),f=a("label",{class:"fk-label"},["What's this about?"]),u=a("div",{class:"fk-segmented"}),g=[];for(const y of d){const _=a("button",{class:"fk-seg",type:"button"},[y.label]);_.setAttribute("data-active",String(y.value===l)),_.addEventListener("click",()=>{l=y.value,g.forEach((U,ne)=>U.setAttribute("data-active",String(d[ne].value===l))),x.placeholder=d.find(U=>U.value===l).placeholder}),g.push(_),u.appendChild(_)}f.appendChild(u);const S=a("label",{class:"fk-label"},["Title"]),x=a("input",{class:"fk-input",type:"text",placeholder:d[0].placeholder,required:"true"});S.appendChild(x);const $=a("label",{class:"fk-label"},["Description"]),z=a("textarea",{class:"fk-textarea",placeholder:"Any extra context helps.",rows:"4"});$.appendChild(z);const I=a("button",{class:"fk-submit",type:"submit"},["Submit"]);c.append(f,S,$,I),v.appendChild(c),c.addEventListener("submit",async y=>{if(y.preventDefault(),!!x.value.trim()){I.disabled=!0,I.textContent="Submitting…";try{await e.submit({title:x.value,description:z.value,kind:l}),x.value="",z.value="",E("browse"),k&&(b=l,L()),await j()}catch(_){I.disabled=!1,I.textContent="Submit",alert(_.message)}}})}function E(r){m=r,H(),m==="browse"?j():te()}return T.addEventListener("click",()=>E("browse")),A.addEventListener("click",()=>E("suggest")),H(),L(),E("browse"),o}const pe=w.defineComponent({__name:"FeedbackButton",props:{label:{},hideLauncher:{type:Boolean}},setup(e,{expose:t}){const i=e,o=w.inject(B);if(!o)throw new Error("Install the HeedKit plugin first: app.use(createHeedKit(...))");let n=null;function s(){n||(n=X({projectKey:o.projectKey,apiUrl:o.apiUrl,user:o.user,label:i.label,hideLauncher:i.hideLauncher}))}return w.watch(()=>o.ready.value,h=>{h&&s()},{immediate:!0}),w.onMounted(()=>{o.ready.value&&s()}),w.onBeforeUnmount(()=>{n==null||n.destroy(),n=null}),t({open:()=>n==null?void 0:n.open(),close:()=>n==null?void 0:n.close()}),(h,m)=>null}});exports.FeedbackButton=pe;exports.HEEDKIT_KEY=B;exports.HeedKitClient=O;exports.createHeedKit=ie;exports.mount=X;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { createHeedKit, HEEDKIT_KEY, type HeedKitInjection } from "./plugin";
|
|
2
|
+
export { HeedKitClient } from "./client";
|
|
3
|
+
export { mount, type Widget } from "./widget";
|
|
4
|
+
export type { Comment, EndUser, Feature, FeatureKind, HeedKitConfig, GroupMode, InitResult, Interaction, KindInteractions, ShowCounts, Theme, Visibility, } from "./client";
|
|
5
|
+
export { default as FeedbackButton } from "./FeedbackButton.vue";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
import { ref as q, defineComponent as ne, inject as ae, watch as re, onMounted as ie, onBeforeUnmount as se } from "vue";
|
|
2
|
+
const oe = "https://api.heedkit.com", H = "heedkit.device_id";
|
|
3
|
+
function de() {
|
|
4
|
+
var e;
|
|
5
|
+
try {
|
|
6
|
+
if (typeof window > "u" || !window.localStorage) return null;
|
|
7
|
+
const t = window.localStorage.getItem(H);
|
|
8
|
+
if (t) return t;
|
|
9
|
+
const i = "dev_" + (((e = crypto == null ? void 0 : crypto.randomUUID) == null ? void 0 : e.call(crypto)) ?? Math.random().toString(36).slice(2));
|
|
10
|
+
return window.localStorage.setItem(H, i), i;
|
|
11
|
+
} catch {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function $(e) {
|
|
16
|
+
return {
|
|
17
|
+
id: String(e.id),
|
|
18
|
+
title: e.title,
|
|
19
|
+
description: e.description ?? "",
|
|
20
|
+
status: e.status,
|
|
21
|
+
kind: e.kind,
|
|
22
|
+
visibility: e.visibility,
|
|
23
|
+
on_roadmap: e.on_roadmap ?? !1,
|
|
24
|
+
tag: e.tag ?? null,
|
|
25
|
+
vote_count: e.vote_count ?? 0,
|
|
26
|
+
voted: e.voted ?? !1,
|
|
27
|
+
platform: e.platform ?? null,
|
|
28
|
+
author_name: e.author_name ?? e.author ?? null,
|
|
29
|
+
created_at: e.created_at
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function V(e) {
|
|
33
|
+
return {
|
|
34
|
+
id: String(e.id),
|
|
35
|
+
body: e.body,
|
|
36
|
+
author_name: e.author_name ?? e.author ?? null,
|
|
37
|
+
// The SDK endpoint only ever returns public comments.
|
|
38
|
+
is_internal: e.is_internal ?? !1,
|
|
39
|
+
created_at: e.created_at
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
class Y {
|
|
43
|
+
constructor(t) {
|
|
44
|
+
this.endUserId = null, this.theme = {}, this.projectName = "", this.enabledKinds = [], this.kindVisibility = {}, this.kindInteractions = {}, this.apiUrl = t.apiUrl || oe, this.projectKey = t.projectKey;
|
|
45
|
+
}
|
|
46
|
+
async init(t = {}) {
|
|
47
|
+
const s = {
|
|
48
|
+
external_id: t.externalId ?? de() ?? void 0,
|
|
49
|
+
email: t.email,
|
|
50
|
+
name: t.name,
|
|
51
|
+
avatar_url: t.avatarUrl,
|
|
52
|
+
platform: t.platform || "web"
|
|
53
|
+
}, n = await this.request("/sdk/init", "POST", s);
|
|
54
|
+
this.endUserId = n.end_user_id;
|
|
55
|
+
const o = n.project ?? n;
|
|
56
|
+
return this.theme = o.theme || {}, this.projectName = o.name ?? o.project_name ?? "", this.enabledKinds = o.enabled_kinds || [], this.kindVisibility = o.kind_visibility || {}, this.kindInteractions = o.kind_interactions || {}, n;
|
|
57
|
+
}
|
|
58
|
+
getTheme() {
|
|
59
|
+
return this.theme;
|
|
60
|
+
}
|
|
61
|
+
getEnabledKinds() {
|
|
62
|
+
return this.enabledKinds;
|
|
63
|
+
}
|
|
64
|
+
getKindVisibility() {
|
|
65
|
+
return this.kindVisibility;
|
|
66
|
+
}
|
|
67
|
+
getKindInteractions() {
|
|
68
|
+
return this.kindInteractions;
|
|
69
|
+
}
|
|
70
|
+
getProjectName() {
|
|
71
|
+
return this.projectName;
|
|
72
|
+
}
|
|
73
|
+
getEndUserId() {
|
|
74
|
+
return this.endUserId;
|
|
75
|
+
}
|
|
76
|
+
/// Convenience: which interactions are enabled for a given kind, in stable order.
|
|
77
|
+
getInteractionsFor(t) {
|
|
78
|
+
const i = this.kindInteractions[t] || {};
|
|
79
|
+
return ["upvote", "downvote", "plus_one", "like"].filter(
|
|
80
|
+
(s) => i[s]
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
async list(t = {}) {
|
|
84
|
+
this.ensureInit();
|
|
85
|
+
const i = new URLSearchParams({ end_user_id: this.endUserId });
|
|
86
|
+
t.status && i.set("status", t.status), t.kind && i.set("kind", t.kind), t.sort && i.set("sort", t.sort);
|
|
87
|
+
const s = await this.request(`/sdk/features?${i}`, "GET");
|
|
88
|
+
return (Array.isArray(s) ? s : s.features ?? []).map((o) => $(o));
|
|
89
|
+
}
|
|
90
|
+
async submit(t) {
|
|
91
|
+
this.ensureInit();
|
|
92
|
+
const i = await this.request("/sdk/features", "POST", {
|
|
93
|
+
end_user_id: this.endUserId,
|
|
94
|
+
title: t.title,
|
|
95
|
+
description: t.description || "",
|
|
96
|
+
tag: t.tag || null,
|
|
97
|
+
kind: t.kind || "feature_request"
|
|
98
|
+
});
|
|
99
|
+
return $(i);
|
|
100
|
+
}
|
|
101
|
+
async vote(t) {
|
|
102
|
+
return this.ensureInit(), this.request(`/sdk/features/${t}/vote`, "POST", {
|
|
103
|
+
end_user_id: this.endUserId
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
async listComments(t) {
|
|
107
|
+
const i = await this.request(`/sdk/features/${t}/comments`, "GET");
|
|
108
|
+
return (Array.isArray(i) ? i : i.comments ?? []).map((n) => V(n));
|
|
109
|
+
}
|
|
110
|
+
async comment(t, i) {
|
|
111
|
+
this.ensureInit();
|
|
112
|
+
const s = await this.request(`/sdk/features/${t}/comments`, "POST", {
|
|
113
|
+
end_user_id: this.endUserId,
|
|
114
|
+
body: i
|
|
115
|
+
});
|
|
116
|
+
return V(s);
|
|
117
|
+
}
|
|
118
|
+
ensureInit() {
|
|
119
|
+
if (!this.endUserId) throw new Error("HeedKit not initialized — call init() first");
|
|
120
|
+
}
|
|
121
|
+
async request(t, i, s) {
|
|
122
|
+
const n = await fetch(`${this.apiUrl}${t}`, {
|
|
123
|
+
method: i,
|
|
124
|
+
headers: {
|
|
125
|
+
"Content-Type": "application/json",
|
|
126
|
+
"X-Project-Key": this.projectKey
|
|
127
|
+
},
|
|
128
|
+
body: s ? JSON.stringify(s) : void 0
|
|
129
|
+
});
|
|
130
|
+
if (!n.ok) {
|
|
131
|
+
let o = `HTTP ${n.status}`;
|
|
132
|
+
try {
|
|
133
|
+
const h = await n.json();
|
|
134
|
+
o = h.error || h.detail || o;
|
|
135
|
+
} catch {
|
|
136
|
+
}
|
|
137
|
+
throw new Error(o);
|
|
138
|
+
}
|
|
139
|
+
return n.json();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const G = Symbol("heedkit");
|
|
143
|
+
function me(e) {
|
|
144
|
+
return {
|
|
145
|
+
install(t) {
|
|
146
|
+
const i = new Y(e), s = q(!1), n = q({});
|
|
147
|
+
i.init(e.user || {}).then(() => {
|
|
148
|
+
n.value = i.getTheme(), s.value = !0;
|
|
149
|
+
}), t.provide(G, {
|
|
150
|
+
client: i,
|
|
151
|
+
ready: s,
|
|
152
|
+
theme: n,
|
|
153
|
+
projectKey: e.projectKey,
|
|
154
|
+
apiUrl: e.apiUrl,
|
|
155
|
+
user: e.user
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
const U = {
|
|
161
|
+
feature_request: { label: "Features", placeholder: "What should we build?", tabIcon: "💡" },
|
|
162
|
+
bug_report: { label: "Bugs", placeholder: "What's broken?", tabIcon: "🐞" },
|
|
163
|
+
improvement: { label: "Improvements", placeholder: "What could be better?", tabIcon: "✨" },
|
|
164
|
+
appreciation: { label: "Appreciation", placeholder: "What did you love?", tabIcon: "❤️" },
|
|
165
|
+
other: { label: "Other", placeholder: "Tell us anything", tabIcon: "💬" }
|
|
166
|
+
}, le = {
|
|
167
|
+
upvote: { icon: "▲", label: "Upvote" },
|
|
168
|
+
downvote: { icon: "▼", label: "Downvote" },
|
|
169
|
+
plus_one: { icon: "+1", label: "+1" },
|
|
170
|
+
like: { icon: "♥", label: "Like" }
|
|
171
|
+
}, W = { sm: "13px", md: "14px", lg: "16px" }, R = "heedkit-styles", ce = `
|
|
172
|
+
.fk-launcher {
|
|
173
|
+
position: fixed; bottom: 24px; right: 24px; z-index: 2147483645;
|
|
174
|
+
background: var(--fh-primary); color: #fff; border: 0; border-radius: 999px;
|
|
175
|
+
padding: 12px 18px; font-weight: 600; font-size: var(--fh-fs); cursor: pointer;
|
|
176
|
+
box-shadow: 0 10px 24px rgba(0,0,0,.18); font-family: var(--fh-font);
|
|
177
|
+
transition: transform .15s ease;
|
|
178
|
+
}
|
|
179
|
+
.fk-launcher:hover { transform: translateY(-1px); }
|
|
180
|
+
.fk-overlay {
|
|
181
|
+
position: fixed; inset: 0; z-index: 2147483646; display: flex;
|
|
182
|
+
align-items: center; justify-content: center; padding: 16px;
|
|
183
|
+
background: rgba(0,0,0,.45); backdrop-filter: blur(2px);
|
|
184
|
+
font-family: var(--fh-font); font-size: var(--fh-fs);
|
|
185
|
+
}
|
|
186
|
+
.fk-panel {
|
|
187
|
+
width: 100%; max-width: 520px; max-height: 85vh; display: flex; flex-direction: column;
|
|
188
|
+
background: var(--fh-bg); color: var(--fh-fg);
|
|
189
|
+
border-radius: calc(var(--fh-radius) * 1.5); overflow: hidden;
|
|
190
|
+
box-shadow: 0 20px 60px rgba(0,0,0,.3);
|
|
191
|
+
}
|
|
192
|
+
.fk-head { padding: 18px 20px 12px; border-bottom: 1px solid var(--fh-border); }
|
|
193
|
+
.fk-titlerow { display:flex; justify-content:space-between; align-items:center; }
|
|
194
|
+
.fk-title { font-size: calc(var(--fh-fs) + 6px); font-weight: 700; }
|
|
195
|
+
.fk-close {
|
|
196
|
+
background: transparent; border: 0; color: var(--fh-muted);
|
|
197
|
+
font-size: 22px; cursor: pointer; line-height: 1; padding: 0 4px;
|
|
198
|
+
}
|
|
199
|
+
.fk-modes { display:flex; gap: 6px; margin-top: 12px; }
|
|
200
|
+
.fk-mode {
|
|
201
|
+
border: 0; background: transparent; padding: 6px 12px; border-radius: 999px;
|
|
202
|
+
font-size: calc(var(--fh-fs) - 1px); font-weight: 600; cursor: pointer;
|
|
203
|
+
color: var(--fh-muted); font-family: inherit;
|
|
204
|
+
}
|
|
205
|
+
.fk-mode[data-active="true"] { background: var(--fh-primary); color: #fff; }
|
|
206
|
+
.fk-tabs {
|
|
207
|
+
display: flex; flex-wrap: wrap; gap: 6px; padding: 10px 20px; border-bottom: 1px solid var(--fh-border);
|
|
208
|
+
}
|
|
209
|
+
.fk-tab {
|
|
210
|
+
border: 0; background: var(--fh-row); color: var(--fh-fg);
|
|
211
|
+
padding: 6px 12px; border-radius: 999px;
|
|
212
|
+
font-size: calc(var(--fh-fs) - 1px); font-weight: 500; cursor: pointer; font-family: inherit;
|
|
213
|
+
display: inline-flex; align-items: center; gap: 6px;
|
|
214
|
+
}
|
|
215
|
+
.fk-tab[data-active="true"] { background: var(--fh-primary); color: #fff; }
|
|
216
|
+
.fk-body { flex: 1; overflow-y: auto; padding: 16px 20px; }
|
|
217
|
+
.fk-empty { text-align: center; padding: 32px; opacity: .6; }
|
|
218
|
+
.fk-loading { text-align: center; padding: 32px; opacity: .6; }
|
|
219
|
+
.fk-row {
|
|
220
|
+
display: flex; gap: 12px; padding: 12px; margin-bottom: 8px;
|
|
221
|
+
background: var(--fh-row); border-radius: var(--fh-radius);
|
|
222
|
+
}
|
|
223
|
+
.fk-actions { display:flex; flex-direction: column; gap: 4px; }
|
|
224
|
+
.fk-act {
|
|
225
|
+
border: 1px solid var(--fh-border); background: transparent;
|
|
226
|
+
color: var(--fh-fg); border-radius: calc(var(--fh-radius) - 4px);
|
|
227
|
+
min-width: 44px; padding: 6px 8px; cursor: pointer;
|
|
228
|
+
display: flex; flex-direction: column; align-items: center; gap: 2px;
|
|
229
|
+
font-weight: 600; font-size: calc(var(--fh-fs) - 1px); font-family: inherit;
|
|
230
|
+
}
|
|
231
|
+
.fk-act[data-voted="true"] {
|
|
232
|
+
border: 2px solid var(--fh-primary);
|
|
233
|
+
background: color-mix(in srgb, var(--fh-primary) 14%, transparent);
|
|
234
|
+
color: var(--fh-primary);
|
|
235
|
+
}
|
|
236
|
+
.fk-act[disabled] { cursor: default; opacity: .85; }
|
|
237
|
+
.fk-act .fk-glyph { font-size: calc(var(--fh-fs) + 1px); line-height: 1; }
|
|
238
|
+
.fk-meta { flex:1; min-width:0; cursor: pointer; }
|
|
239
|
+
.fk-item-title { font-weight: 600; }
|
|
240
|
+
.fk-item-desc { opacity: .7; font-size: calc(var(--fh-fs) - 1px); margin-top: 4px; }
|
|
241
|
+
.fk-item-badges { display:flex; gap:6px; margin-top:6px; flex-wrap: wrap; }
|
|
242
|
+
.fk-badge {
|
|
243
|
+
font-size: calc(var(--fh-fs) - 3px); padding: 2px 8px; border-radius: 999px;
|
|
244
|
+
background: var(--fh-border); color: var(--fh-muted); text-transform: uppercase; letter-spacing: .04em;
|
|
245
|
+
}
|
|
246
|
+
.fk-badge[data-status="planned"] { background: rgba(59,130,246,.15); color: rgb(37,99,235); }
|
|
247
|
+
.fk-badge[data-status="in_progress"] { background: rgba(234,179,8,.18); color: rgb(161,98,7); }
|
|
248
|
+
.fk-badge[data-status="shipped"] { background: rgba(34,197,94,.18); color: rgb(22,101,52); }
|
|
249
|
+
.fk-comments { margin-top: 10px; border-top: 1px solid var(--fh-border); padding-top: 10px; }
|
|
250
|
+
.fk-comment { padding: 6px 0; border-top: 1px dashed var(--fh-border); font-size: calc(var(--fh-fs) - 1px); }
|
|
251
|
+
.fk-comment:first-child { border-top: 0; }
|
|
252
|
+
.fk-comment-author { font-weight: 600; }
|
|
253
|
+
.fk-form { display: flex; flex-direction: column; gap: 12px; }
|
|
254
|
+
.fk-label { font-size: calc(var(--fh-fs) - 1px); font-weight: 500; }
|
|
255
|
+
.fk-input, .fk-textarea {
|
|
256
|
+
width: 100%; padding: 10px 12px; margin-top: 4px;
|
|
257
|
+
border-radius: calc(var(--fh-radius) - 2px);
|
|
258
|
+
border: 1px solid var(--fh-input-border); background: var(--fh-input-bg);
|
|
259
|
+
color: var(--fh-fg); font-size: var(--fh-fs); font-family: inherit;
|
|
260
|
+
box-sizing: border-box;
|
|
261
|
+
}
|
|
262
|
+
.fk-textarea { resize: vertical; min-height: 96px; }
|
|
263
|
+
.fk-input:focus, .fk-textarea:focus { outline: 2px solid var(--fh-primary); outline-offset: 1px; }
|
|
264
|
+
.fk-submit {
|
|
265
|
+
background: var(--fh-primary); color: #fff; border: 0;
|
|
266
|
+
padding: 12px 14px; border-radius: var(--fh-radius);
|
|
267
|
+
font-weight: 600; font-size: var(--fh-fs); cursor: pointer; font-family: inherit;
|
|
268
|
+
}
|
|
269
|
+
.fk-submit[disabled] { opacity: .6; cursor: not-allowed; }
|
|
270
|
+
.fk-segmented {
|
|
271
|
+
display: inline-flex; gap: 4px; padding: 4px; margin-top: 6px;
|
|
272
|
+
background: var(--fh-row); border-radius: 999px;
|
|
273
|
+
}
|
|
274
|
+
.fk-seg {
|
|
275
|
+
border: 0; background: transparent; cursor: pointer;
|
|
276
|
+
padding: 6px 12px; border-radius: 999px; font-size: calc(var(--fh-fs) - 2px);
|
|
277
|
+
font-weight: 500; color: var(--fh-muted); font-family: inherit;
|
|
278
|
+
}
|
|
279
|
+
.fk-seg[data-active="true"] {
|
|
280
|
+
background: var(--fh-bg); color: var(--fh-fg);
|
|
281
|
+
box-shadow: 0 1px 2px rgba(0,0,0,.06), 0 2px 8px rgba(0,0,0,.04);
|
|
282
|
+
}
|
|
283
|
+
.fk-error { color: rgb(220,38,38); font-size: calc(var(--fh-fs) - 1px); }
|
|
284
|
+
`;
|
|
285
|
+
function pe() {
|
|
286
|
+
if (document.getElementById(R)) return;
|
|
287
|
+
const e = document.createElement("style");
|
|
288
|
+
e.id = R, e.textContent = ce, document.head.appendChild(e);
|
|
289
|
+
}
|
|
290
|
+
function fe(e) {
|
|
291
|
+
const t = e.mode || "light";
|
|
292
|
+
return t === "system" ? typeof window < "u" && window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" : t === "dark" ? "dark" : "light";
|
|
293
|
+
}
|
|
294
|
+
function J(e, t) {
|
|
295
|
+
const i = t.primary || "#0D9488", s = `${t.radius ?? 12}px`, n = fe(t) === "dark", o = t.font_family || t.fontFamily || "system-ui, -apple-system, sans-serif", h = W[t.font_size ?? "md"] ?? W.md, m = {
|
|
296
|
+
"--fh-primary": i,
|
|
297
|
+
"--fh-radius": s,
|
|
298
|
+
"--fh-font": o,
|
|
299
|
+
"--fh-fs": h,
|
|
300
|
+
"--fh-bg": n ? "#0F172A" : "#FFFFFF",
|
|
301
|
+
"--fh-fg": n ? "#F1F5F9" : "#0F172A",
|
|
302
|
+
"--fh-muted": n ? "#94A3B8" : "#64748B",
|
|
303
|
+
"--fh-row": n ? "#1E293B" : "#F8FAFC",
|
|
304
|
+
"--fh-border": n ? "#1E293B" : "#E2E8F0",
|
|
305
|
+
"--fh-input-bg": n ? "#0F172A" : "#FFFFFF",
|
|
306
|
+
"--fh-input-border": n ? "#334155" : "#CBD5E1"
|
|
307
|
+
};
|
|
308
|
+
for (const [b, _] of Object.entries(m)) e.style.setProperty(b, _);
|
|
309
|
+
}
|
|
310
|
+
function a(e, t, i) {
|
|
311
|
+
const s = document.createElement(e);
|
|
312
|
+
if (t) for (const [n, o] of Object.entries(t))
|
|
313
|
+
n === "class" ? s.className = o : s.setAttribute(n, o);
|
|
314
|
+
if (i)
|
|
315
|
+
for (const n of i)
|
|
316
|
+
n != null && s.appendChild(typeof n == "string" ? document.createTextNode(n) : n);
|
|
317
|
+
return s;
|
|
318
|
+
}
|
|
319
|
+
function ue(e) {
|
|
320
|
+
pe();
|
|
321
|
+
const t = e.container || document.body, i = new Y(e), s = i.init(e.user || {}).catch((b) => (console.warn("[HeedKit] widget init failed; launcher disabled.", b), null));
|
|
322
|
+
let n = null, o = null;
|
|
323
|
+
function h() {
|
|
324
|
+
n && (n.remove(), n = null);
|
|
325
|
+
}
|
|
326
|
+
async function m() {
|
|
327
|
+
await s && (n || (n = he(i, i.getTheme(), h), t.appendChild(n)));
|
|
328
|
+
}
|
|
329
|
+
return e.hideLauncher || s.then((b) => {
|
|
330
|
+
b && (o = a("button", { class: "fk-launcher", type: "button" }, [
|
|
331
|
+
e.label || "Feedback"
|
|
332
|
+
]), J(o, i.getTheme()), o.addEventListener("click", m), t.appendChild(o));
|
|
333
|
+
}), {
|
|
334
|
+
client: i,
|
|
335
|
+
open: m,
|
|
336
|
+
close: h,
|
|
337
|
+
destroy() {
|
|
338
|
+
h(), o == null || o.remove();
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
function he(e, t, i) {
|
|
343
|
+
const s = a("div", { class: "fk-overlay", role: "dialog" });
|
|
344
|
+
J(s, t), s.addEventListener("click", (r) => {
|
|
345
|
+
r.target === s && i();
|
|
346
|
+
});
|
|
347
|
+
const n = a("div", { class: "fk-panel" });
|
|
348
|
+
s.appendChild(n);
|
|
349
|
+
const o = e.getEnabledKinds(), h = t.group_mode || "tabs";
|
|
350
|
+
let m = "browse", b = h === "tabs" && o.length > 0 ? o[0] : "all", _ = [];
|
|
351
|
+
const F = a("div", { class: "fk-head" }), S = a("div", { class: "fk-titlerow" });
|
|
352
|
+
S.appendChild(a("div", { class: "fk-title" }, [e.getProjectName() || "Feedback"]));
|
|
353
|
+
const P = a("button", { class: "fk-close", type: "button", "aria-label": "Close" }, ["×"]);
|
|
354
|
+
P.addEventListener("click", i), S.appendChild(P), F.appendChild(S);
|
|
355
|
+
const B = a("div", { class: "fk-modes" }), A = a("button", { class: "fk-mode", type: "button" }, ["Browse"]), T = a("button", { class: "fk-mode", type: "button" }, ["Suggest"]);
|
|
356
|
+
B.append(A, T), F.appendChild(B), n.appendChild(F);
|
|
357
|
+
let k = null;
|
|
358
|
+
if (h === "tabs" && o.length > 0) {
|
|
359
|
+
k = a("div", { class: "fk-tabs" });
|
|
360
|
+
const r = a("button", { class: "fk-tab", type: "button" }, ["All"]);
|
|
361
|
+
r.dataset.kind = "all", k.appendChild(r);
|
|
362
|
+
for (const p of o) {
|
|
363
|
+
const d = U[p], l = a("button", { class: "fk-tab", type: "button" }, [
|
|
364
|
+
a("span", {}, [d.tabIcon]),
|
|
365
|
+
a("span", {}, [d.label])
|
|
366
|
+
]);
|
|
367
|
+
l.dataset.kind = p, k.appendChild(l);
|
|
368
|
+
}
|
|
369
|
+
n.appendChild(k), k.addEventListener("click", (p) => {
|
|
370
|
+
const d = p.target.closest("[data-kind]");
|
|
371
|
+
d && (b = d.dataset.kind, K(), L());
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
const v = a("div", { class: "fk-body" });
|
|
375
|
+
n.appendChild(v);
|
|
376
|
+
function N() {
|
|
377
|
+
A.setAttribute("data-active", String(m === "browse")), T.setAttribute("data-active", String(m === "suggest")), k && (k.style.display = m === "browse" ? "" : "none");
|
|
378
|
+
}
|
|
379
|
+
function K() {
|
|
380
|
+
if (k)
|
|
381
|
+
for (const r of Array.from(k.children))
|
|
382
|
+
r.setAttribute(
|
|
383
|
+
"data-active",
|
|
384
|
+
String(r.dataset.kind === String(b))
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
async function L() {
|
|
388
|
+
v.replaceChildren(a("div", { class: "fk-loading" }, ["Loading…"]));
|
|
389
|
+
try {
|
|
390
|
+
const r = { sort: "top" };
|
|
391
|
+
b !== "all" && (r.kind = b), _ = await e.list(r);
|
|
392
|
+
} catch (r) {
|
|
393
|
+
v.replaceChildren(
|
|
394
|
+
a("div", { class: "fk-empty" }, [`Failed to load: ${r.message}`])
|
|
395
|
+
);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
O();
|
|
399
|
+
}
|
|
400
|
+
async function X(r, p) {
|
|
401
|
+
const d = await e.vote(r.id);
|
|
402
|
+
r.voted = d.voted, r.vote_count = d.vote_count, O();
|
|
403
|
+
}
|
|
404
|
+
function Z(r) {
|
|
405
|
+
const p = e.getInteractionsFor(r.kind), d = (e.getTheme().show_counts || {})[r.kind] !== !1, l = a("div", { class: "fk-actions" });
|
|
406
|
+
if (p.length === 0) {
|
|
407
|
+
if (d) {
|
|
408
|
+
const c = a("button", { class: "fk-act", type: "button", disabled: "true" }, [
|
|
409
|
+
a("span", {}, [String(r.vote_count)])
|
|
410
|
+
]);
|
|
411
|
+
l.appendChild(c);
|
|
412
|
+
}
|
|
413
|
+
return l;
|
|
414
|
+
}
|
|
415
|
+
for (const c of p) {
|
|
416
|
+
const f = le[c], u = a("button", {
|
|
417
|
+
class: "fk-act",
|
|
418
|
+
type: "button",
|
|
419
|
+
"aria-label": f.label
|
|
420
|
+
}, [
|
|
421
|
+
a("span", { class: "fk-glyph" }, [f.icon]),
|
|
422
|
+
...d ? [a("span", {}, [String(r.vote_count)])] : []
|
|
423
|
+
]);
|
|
424
|
+
(c === "upvote" || c === "like" || c === "plus_one") && u.setAttribute("data-voted", String(r.voted)), u.addEventListener("click", (g) => {
|
|
425
|
+
g.stopPropagation(), X(r);
|
|
426
|
+
}), l.appendChild(u);
|
|
427
|
+
}
|
|
428
|
+
return l;
|
|
429
|
+
}
|
|
430
|
+
function O() {
|
|
431
|
+
if (v.innerHTML = "", _.length === 0) {
|
|
432
|
+
v.appendChild(a("div", { class: "fk-empty" }, ["No items yet — be the first!"]));
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
for (const r of _)
|
|
436
|
+
v.appendChild(Q(r));
|
|
437
|
+
}
|
|
438
|
+
function Q(r) {
|
|
439
|
+
const p = a("div", { class: "fk-row" });
|
|
440
|
+
p.appendChild(Z(r));
|
|
441
|
+
const d = a("div", { class: "fk-meta" });
|
|
442
|
+
d.appendChild(a("div", { class: "fk-item-title" }, [r.title])), r.description && d.appendChild(a("div", { class: "fk-item-desc" }, [r.description]));
|
|
443
|
+
const l = a("div", { class: "fk-item-badges" });
|
|
444
|
+
if (r.status && r.status !== "open") {
|
|
445
|
+
const u = a("span", { class: "fk-badge" }, [r.status.replace("_", " ")]);
|
|
446
|
+
u.setAttribute("data-status", r.status), l.appendChild(u);
|
|
447
|
+
}
|
|
448
|
+
r.tag && l.appendChild(a("span", { class: "fk-badge" }, [r.tag])), l.children.length && d.appendChild(l);
|
|
449
|
+
let c = !1;
|
|
450
|
+
const f = a("div", { class: "fk-comments" });
|
|
451
|
+
return f.style.display = "none", d.appendChild(f), d.addEventListener("click", async () => {
|
|
452
|
+
const u = f.style.display === "none";
|
|
453
|
+
if (f.style.display = u ? "" : "none", u && !c) {
|
|
454
|
+
c = !0, f.replaceChildren(a("div", { class: "fk-loading" }, ["Loading…"]));
|
|
455
|
+
try {
|
|
456
|
+
const g = await e.listComments(r.id);
|
|
457
|
+
f.replaceChildren(...D(r, g));
|
|
458
|
+
} catch (g) {
|
|
459
|
+
f.replaceChildren(
|
|
460
|
+
a("div", { class: "fk-error" }, [g.message])
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}), p.appendChild(d), p;
|
|
465
|
+
}
|
|
466
|
+
function D(r, p) {
|
|
467
|
+
const d = [];
|
|
468
|
+
if (p.length === 0)
|
|
469
|
+
d.push(a("div", { class: "fk-empty" }, ["No replies yet."]));
|
|
470
|
+
else
|
|
471
|
+
for (const f of p)
|
|
472
|
+
d.push(a("div", { class: "fk-comment" }, [
|
|
473
|
+
a("span", { class: "fk-comment-author" }, [f.author_name || "Anonymous"]),
|
|
474
|
+
" — ",
|
|
475
|
+
f.body
|
|
476
|
+
]));
|
|
477
|
+
const l = a("textarea", {
|
|
478
|
+
class: "fk-textarea",
|
|
479
|
+
placeholder: "Add a reply…",
|
|
480
|
+
rows: "2"
|
|
481
|
+
}), c = a("button", { class: "fk-submit", type: "button" }, ["Reply"]);
|
|
482
|
+
return c.addEventListener("click", async (f) => {
|
|
483
|
+
var u;
|
|
484
|
+
if (f.stopPropagation(), !!l.value.trim()) {
|
|
485
|
+
c.disabled = !0;
|
|
486
|
+
try {
|
|
487
|
+
const g = await e.comment(r.id, l.value);
|
|
488
|
+
l.value = "", c.disabled = !1;
|
|
489
|
+
const E = await e.listComments(r.id), x = (u = c.parentElement) == null ? void 0 : u.parentElement;
|
|
490
|
+
x && x.replaceChildren(...D(r, E));
|
|
491
|
+
} catch (g) {
|
|
492
|
+
c.disabled = !1, alert(g.message);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}), d.push(a("div", {}, [l, c])), d;
|
|
496
|
+
}
|
|
497
|
+
function ee() {
|
|
498
|
+
v.innerHTML = "";
|
|
499
|
+
const p = e.getEnabledKinds().map((y) => ({ value: y, ...U[y] })), d = p.length > 0 ? p : [
|
|
500
|
+
{ value: "other", ...U.other }
|
|
501
|
+
];
|
|
502
|
+
let l = d[0].value;
|
|
503
|
+
const c = a("form", { class: "fk-form" }), f = a("label", { class: "fk-label" }, ["What's this about?"]), u = a("div", { class: "fk-segmented" }), g = [];
|
|
504
|
+
for (const y of d) {
|
|
505
|
+
const w = a("button", { class: "fk-seg", type: "button" }, [y.label]);
|
|
506
|
+
w.setAttribute("data-active", String(y.value === l)), w.addEventListener("click", () => {
|
|
507
|
+
l = y.value, g.forEach(
|
|
508
|
+
(z, te) => z.setAttribute("data-active", String(d[te].value === l))
|
|
509
|
+
), x.placeholder = d.find((z) => z.value === l).placeholder;
|
|
510
|
+
}), g.push(w), u.appendChild(w);
|
|
511
|
+
}
|
|
512
|
+
f.appendChild(u);
|
|
513
|
+
const E = a("label", { class: "fk-label" }, ["Title"]), x = a("input", {
|
|
514
|
+
class: "fk-input",
|
|
515
|
+
type: "text",
|
|
516
|
+
placeholder: d[0].placeholder,
|
|
517
|
+
required: "true"
|
|
518
|
+
});
|
|
519
|
+
E.appendChild(x);
|
|
520
|
+
const M = a("label", { class: "fk-label" }, ["Description"]), j = a("textarea", {
|
|
521
|
+
class: "fk-textarea",
|
|
522
|
+
placeholder: "Any extra context helps.",
|
|
523
|
+
rows: "4"
|
|
524
|
+
});
|
|
525
|
+
M.appendChild(j);
|
|
526
|
+
const C = a("button", { class: "fk-submit", type: "submit" }, ["Submit"]);
|
|
527
|
+
c.append(f, E, M, C), v.appendChild(c), c.addEventListener("submit", async (y) => {
|
|
528
|
+
if (y.preventDefault(), !!x.value.trim()) {
|
|
529
|
+
C.disabled = !0, C.textContent = "Submitting…";
|
|
530
|
+
try {
|
|
531
|
+
await e.submit({
|
|
532
|
+
title: x.value,
|
|
533
|
+
description: j.value,
|
|
534
|
+
kind: l
|
|
535
|
+
}), x.value = "", j.value = "", I("browse"), k && (b = l, K()), await L();
|
|
536
|
+
} catch (w) {
|
|
537
|
+
C.disabled = !1, C.textContent = "Submit", alert(w.message);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
function I(r) {
|
|
543
|
+
m = r, N(), m === "browse" ? L() : ee();
|
|
544
|
+
}
|
|
545
|
+
return A.addEventListener("click", () => I("browse")), T.addEventListener("click", () => I("suggest")), N(), K(), I("browse"), s;
|
|
546
|
+
}
|
|
547
|
+
const ge = /* @__PURE__ */ ne({
|
|
548
|
+
__name: "FeedbackButton",
|
|
549
|
+
props: {
|
|
550
|
+
label: {},
|
|
551
|
+
hideLauncher: { type: Boolean }
|
|
552
|
+
},
|
|
553
|
+
setup(e, { expose: t }) {
|
|
554
|
+
const i = e, s = ae(G);
|
|
555
|
+
if (!s) throw new Error("Install the HeedKit plugin first: app.use(createHeedKit(...))");
|
|
556
|
+
let n = null;
|
|
557
|
+
function o() {
|
|
558
|
+
n || (n = ue({
|
|
559
|
+
projectKey: s.projectKey,
|
|
560
|
+
apiUrl: s.apiUrl,
|
|
561
|
+
user: s.user,
|
|
562
|
+
label: i.label,
|
|
563
|
+
hideLauncher: i.hideLauncher
|
|
564
|
+
}));
|
|
565
|
+
}
|
|
566
|
+
return re(() => s.ready.value, (h) => {
|
|
567
|
+
h && o();
|
|
568
|
+
}, { immediate: !0 }), ie(() => {
|
|
569
|
+
s.ready.value && o();
|
|
570
|
+
}), se(() => {
|
|
571
|
+
n == null || n.destroy(), n = null;
|
|
572
|
+
}), t({
|
|
573
|
+
open: () => n == null ? void 0 : n.open(),
|
|
574
|
+
close: () => n == null ? void 0 : n.close()
|
|
575
|
+
}), (h, m) => null;
|
|
576
|
+
}
|
|
577
|
+
});
|
|
578
|
+
export {
|
|
579
|
+
ge as FeedbackButton,
|
|
580
|
+
G as HEEDKIT_KEY,
|
|
581
|
+
Y as HeedKitClient,
|
|
582
|
+
me as createHeedKit,
|
|
583
|
+
ue as mount
|
|
584
|
+
};
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type App, type InjectionKey, type Ref } from "vue";
|
|
2
|
+
import { HeedKitClient, type EndUser, type HeedKitConfig, type Theme } from "./client";
|
|
3
|
+
export type HeedKitInjection = {
|
|
4
|
+
client: HeedKitClient;
|
|
5
|
+
ready: Ref<boolean>;
|
|
6
|
+
theme: Ref<Theme>;
|
|
7
|
+
projectKey: string;
|
|
8
|
+
apiUrl?: string;
|
|
9
|
+
user?: EndUser;
|
|
10
|
+
};
|
|
11
|
+
export declare const HEEDKIT_KEY: InjectionKey<HeedKitInjection>;
|
|
12
|
+
export declare function createHeedKit(config: HeedKitConfig): {
|
|
13
|
+
install(app: App): void;
|
|
14
|
+
};
|
package/dist/widget.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { HeedKitClient, type HeedKitConfig } from "./client";
|
|
2
|
+
export type MountOptions = HeedKitConfig & {
|
|
3
|
+
label?: string;
|
|
4
|
+
hideLauncher?: boolean;
|
|
5
|
+
container?: HTMLElement;
|
|
6
|
+
};
|
|
7
|
+
export type Widget = {
|
|
8
|
+
client: HeedKitClient;
|
|
9
|
+
open: () => void;
|
|
10
|
+
close: () => void;
|
|
11
|
+
destroy: () => void;
|
|
12
|
+
};
|
|
13
|
+
export declare function mount(options: MountOptions): Widget;
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@heedkit/sdk-vue",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "HeedKit SDK for Vue 3.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "HeedKit",
|
|
7
|
+
"homepage": "https://github.com/heedkit/heedkit-sdk-vue#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/heedkit/heedkit-sdk-vue.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/heedkit/heedkit-sdk-vue/issues"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"main": "./dist/index.cjs",
|
|
17
|
+
"module": "./dist/index.js",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"import": "./dist/index.js",
|
|
22
|
+
"require": "./dist/index.cjs",
|
|
23
|
+
"types": "./dist/index.d.ts"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"sideEffects": false,
|
|
27
|
+
"files": [
|
|
28
|
+
"dist"
|
|
29
|
+
],
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=18"
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "vite build && vue-tsc --emitDeclarationOnly --declaration --outDir dist",
|
|
38
|
+
"test": "vitest run src/client.test.ts",
|
|
39
|
+
"prepublishOnly": "npm run build && npm test"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"vue": "^3.0.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@vitejs/plugin-vue": "^5.1.4",
|
|
46
|
+
"typescript": "^5.6.3",
|
|
47
|
+
"vite": "^5.4.10",
|
|
48
|
+
"vitest": "^3.2.4",
|
|
49
|
+
"vue": "^3.5.12",
|
|
50
|
+
"vue-tsc": "^2.1.10"
|
|
51
|
+
}
|
|
52
|
+
}
|