feedtack 1.0.1 → 1.2.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/README.md +49 -40
- package/dist/{theme-BdqpMipn.d.ts → adapter-Cn59URIG.d.ts} +68 -22
- package/dist/chunk-GD2SY64K.js +1308 -0
- package/dist/index.d.ts +75 -4
- package/dist/index.js +50 -3
- package/dist/node/index.d.ts +27 -0
- package/dist/node/index.js +169 -0
- package/dist/react/index.d.ts +61 -7
- package/dist/react/index.js +911 -1204
- package/dist/types-CHrWe7xT.d.ts +61 -0
- package/package.json +5 -1
- package/dist/chunk-2A5LLDLP.js +0 -237
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { F as FeedtackAdapter, a as FeedtackPayload, b as FeedtackReply, c as FeedtackResolution, d as FeedtackFilter, e as FeedbackItem, f as
|
|
2
|
-
export { A as AncestorNode,
|
|
1
|
+
import { F as FeedtackAdapter, a as FeedtackPayload, b as FeedtackReply, c as FeedtackResolution, d as FeedtackFilter, e as FeedbackItem, C as ContentAdapter, f as ContentEditAdapter, g as FieldFilter, h as FieldApprovalState, i as FieldApproval, j as FeedtackDeviceMeta, k as FeedtackPageMeta, l as FeedtackViewportMeta, m as FeedtackPinTarget } from './adapter-Cn59URIG.js';
|
|
2
|
+
export { A as AncestorNode, n as FeedtackArchive, o as FeedtackBoundingRect, p as FeedtackPin, q as FeedtackScope, r as FeedtackSentiment, s as FeedtackUser, t as FieldChange, u as FocusedFieldInfo, S as SCHEMA_VERSION, v as isContentAdapter, w as isContentEditAdapter, x as warnIfNotContentAdapter, y as warnIfNotContentEditAdapter } from './adapter-Cn59URIG.js';
|
|
3
|
+
import { F as FeedtackEngineOpts, a as FeedtackEngineState, b as FeedtackStateListener } from './types-CHrWe7xT.js';
|
|
4
|
+
export { c as FeedtackFlushEvent, d as FeedtackTheme, g as generateId, t as themeToCSS } from './types-CHrWe7xT.js';
|
|
3
5
|
|
|
4
6
|
/** Development adapter — logs all operations to the browser console */
|
|
5
7
|
declare class ConsoleAdapter implements FeedtackAdapter {
|
|
@@ -34,9 +36,13 @@ interface WebhookAdapterConfig {
|
|
|
34
36
|
updateUrl?: string;
|
|
35
37
|
/** Required: async function that returns persisted feedback items */
|
|
36
38
|
loadFeedback: (filter?: FeedtackFilter) => Promise<FeedbackItem[]>;
|
|
39
|
+
/** Optional: async function that returns stored field approvals */
|
|
40
|
+
loadApprovals?: (filter?: FieldFilter) => Promise<FieldApprovalState[]>;
|
|
41
|
+
/** Optional: async function that returns all stored field values for hydration */
|
|
42
|
+
loadFields?: () => Promise<Record<string, string>>;
|
|
37
43
|
}
|
|
38
44
|
/** Production adapter — POSTs feedback as JSON to a webhook endpoint */
|
|
39
|
-
declare class WebhookAdapter implements FeedtackAdapter {
|
|
45
|
+
declare class WebhookAdapter implements FeedtackAdapter, ContentAdapter, ContentEditAdapter {
|
|
40
46
|
private config;
|
|
41
47
|
constructor(config: WebhookAdapterConfig);
|
|
42
48
|
private post;
|
|
@@ -45,8 +51,28 @@ declare class WebhookAdapter implements FeedtackAdapter {
|
|
|
45
51
|
resolve(feedbackId: string, resolution: Omit<FeedtackResolution, 'feedbackId'>): Promise<void>;
|
|
46
52
|
archive(feedbackId: string, userId: string): Promise<void>;
|
|
47
53
|
loadFeedback(filter?: FeedtackFilter): Promise<FeedbackItem[]>;
|
|
54
|
+
approve(fieldPath: string, approval: FieldApproval): Promise<void>;
|
|
55
|
+
revokeApproval(fieldPath: string, userId: string): Promise<void>;
|
|
56
|
+
loadApprovals(filter?: FieldFilter): Promise<FieldApprovalState[]>;
|
|
57
|
+
loadFields(): Promise<Record<string, string>>;
|
|
58
|
+
saveField(fieldPath: string, value: string): Promise<void>;
|
|
48
59
|
}
|
|
49
60
|
|
|
61
|
+
interface ScannedField {
|
|
62
|
+
fieldPath: string;
|
|
63
|
+
element: Element;
|
|
64
|
+
content: string;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Scans the DOM for elements annotated with data-feedtack-field.
|
|
68
|
+
* @param root - Element to search within. Defaults to document.body.
|
|
69
|
+
*/
|
|
70
|
+
declare function scanFields(root?: Element): ScannedField[];
|
|
71
|
+
/**
|
|
72
|
+
* Computes a 12-character truncated SHA-256 hash of a string using Web Crypto API.
|
|
73
|
+
*/
|
|
74
|
+
declare function hashField(content: string): Promise<string>;
|
|
75
|
+
|
|
50
76
|
declare function getViewportMeta(): FeedtackViewportMeta;
|
|
51
77
|
declare function getPageMeta(): FeedtackPageMeta;
|
|
52
78
|
declare function getDeviceMeta(): FeedtackDeviceMeta;
|
|
@@ -65,4 +91,49 @@ declare function getCSSSelector(element: Element): string;
|
|
|
65
91
|
/** Capture DOM target metadata at the clicked element */
|
|
66
92
|
declare function getTargetMeta(element: Element): FeedtackPinTarget;
|
|
67
93
|
|
|
68
|
-
|
|
94
|
+
interface FlushController {
|
|
95
|
+
flushPath: (path: string) => void;
|
|
96
|
+
clearFlushed: (path: string) => void;
|
|
97
|
+
detach: () => void;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
declare class FeedtackEngine {
|
|
101
|
+
private readonly opts;
|
|
102
|
+
private state;
|
|
103
|
+
private listeners;
|
|
104
|
+
private actionCtx;
|
|
105
|
+
private styleEl;
|
|
106
|
+
private rootEl;
|
|
107
|
+
private spaNav;
|
|
108
|
+
flushCtrl: FlushController | null;
|
|
109
|
+
private inputHandles;
|
|
110
|
+
constructor(opts: FeedtackEngineOpts);
|
|
111
|
+
getState(): Readonly<FeedtackEngineState>;
|
|
112
|
+
subscribe(listener: FeedtackStateListener): () => void;
|
|
113
|
+
private setState;
|
|
114
|
+
mount(): void;
|
|
115
|
+
destroy(): void;
|
|
116
|
+
private onNavUpdate;
|
|
117
|
+
activatePinMode(): void;
|
|
118
|
+
deactivatePinMode(): void;
|
|
119
|
+
setComment(v: string): void;
|
|
120
|
+
setSentiment(v: FeedtackEngineState['sentiment']): void;
|
|
121
|
+
setCommentError(v: boolean): void;
|
|
122
|
+
setSelectedColor(c: string): void;
|
|
123
|
+
setOpenThreadId(id: string | null): void;
|
|
124
|
+
setReplyBody(v: string): void;
|
|
125
|
+
setComposeScope(s: 'site' | 'page'): void;
|
|
126
|
+
openModal(): void;
|
|
127
|
+
closeModal(): void;
|
|
128
|
+
getCurrentScope(): "site" | "page" | "element";
|
|
129
|
+
isArchivedForUser(item: FeedbackItem): boolean;
|
|
130
|
+
hasUnread(item: FeedbackItem): boolean;
|
|
131
|
+
hasValidPins(item: FeedbackItem): boolean;
|
|
132
|
+
handleSubmit(): Promise<void>;
|
|
133
|
+
handleModalSubmit(): Promise<void>;
|
|
134
|
+
handleReply(id: string): Promise<void>;
|
|
135
|
+
handleResolve(id: string): Promise<void>;
|
|
136
|
+
handleArchive(id: string): Promise<void>;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export { ConsoleAdapter, ContentAdapter, ContentEditAdapter, FeedbackItem, FeedtackAdapter, FeedtackDeviceMeta, FeedtackEngine, FeedtackEngineOpts, FeedtackEngineState, FeedtackFilter, FeedtackPageMeta, FeedtackPayload, FeedtackPinTarget, FeedtackReply, FeedtackResolution, FeedtackStateListener, FeedtackViewportMeta, FieldApproval, FieldApprovalState, FieldFilter, LocalStorageAdapter, type LocalStorageAdapterConfig, WebhookAdapter, type WebhookAdapterConfig, getCSSSelector, getDeviceMeta, getPageMeta, getPinCoords, getTargetMeta, getViewportMeta, hashField, scanFields };
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
|
+
FeedtackEngine,
|
|
2
3
|
SCHEMA_VERSION,
|
|
4
|
+
generateId,
|
|
3
5
|
getCSSSelector,
|
|
4
6
|
getDeviceMeta,
|
|
5
7
|
getPageMeta,
|
|
6
8
|
getPinCoords,
|
|
7
9
|
getTargetMeta,
|
|
8
10
|
getViewportMeta,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
hashField,
|
|
12
|
+
isContentAdapter,
|
|
13
|
+
isContentEditAdapter,
|
|
14
|
+
scanFields,
|
|
15
|
+
themeToCSS,
|
|
16
|
+
warnIfNotContentAdapter,
|
|
17
|
+
warnIfNotContentEditAdapter
|
|
18
|
+
} from "./chunk-GD2SY64K.js";
|
|
11
19
|
|
|
12
20
|
// src/adapters/ConsoleAdapter.ts
|
|
13
21
|
var ConsoleAdapter = class {
|
|
@@ -132,17 +140,56 @@ var WebhookAdapter = class {
|
|
|
132
140
|
async loadFeedback(filter) {
|
|
133
141
|
return this.config.loadFeedback(filter);
|
|
134
142
|
}
|
|
143
|
+
// ContentAdapter implementation
|
|
144
|
+
async approve(fieldPath, approval) {
|
|
145
|
+
const url = this.config.updateUrl ?? this.config.submitUrl;
|
|
146
|
+
await this.post(url, { type: "approve", fieldPath, ...approval });
|
|
147
|
+
}
|
|
148
|
+
async revokeApproval(fieldPath, userId) {
|
|
149
|
+
const url = this.config.updateUrl ?? this.config.submitUrl;
|
|
150
|
+
await this.post(url, { type: "revoke", fieldPath, userId });
|
|
151
|
+
}
|
|
152
|
+
async loadApprovals(filter) {
|
|
153
|
+
if (this.config.loadApprovals) {
|
|
154
|
+
return this.config.loadApprovals(filter);
|
|
155
|
+
}
|
|
156
|
+
return [];
|
|
157
|
+
}
|
|
158
|
+
// ContentEditAdapter implementation
|
|
159
|
+
async loadFields() {
|
|
160
|
+
if (this.config.loadFields) {
|
|
161
|
+
return this.config.loadFields();
|
|
162
|
+
}
|
|
163
|
+
return {};
|
|
164
|
+
}
|
|
165
|
+
async saveField(fieldPath, value) {
|
|
166
|
+
const url = this.config.updateUrl ?? this.config.submitUrl;
|
|
167
|
+
await this.post(url, {
|
|
168
|
+
type: "save-field",
|
|
169
|
+
fieldPath,
|
|
170
|
+
value,
|
|
171
|
+
clearApproval: true
|
|
172
|
+
});
|
|
173
|
+
}
|
|
135
174
|
};
|
|
136
175
|
export {
|
|
137
176
|
ConsoleAdapter,
|
|
177
|
+
FeedtackEngine,
|
|
138
178
|
LocalStorageAdapter,
|
|
139
179
|
SCHEMA_VERSION,
|
|
140
180
|
WebhookAdapter,
|
|
181
|
+
generateId,
|
|
141
182
|
getCSSSelector,
|
|
142
183
|
getDeviceMeta,
|
|
143
184
|
getPageMeta,
|
|
144
185
|
getPinCoords,
|
|
145
186
|
getTargetMeta,
|
|
146
187
|
getViewportMeta,
|
|
147
|
-
|
|
188
|
+
hashField,
|
|
189
|
+
isContentAdapter,
|
|
190
|
+
isContentEditAdapter,
|
|
191
|
+
scanFields,
|
|
192
|
+
themeToCSS,
|
|
193
|
+
warnIfNotContentAdapter,
|
|
194
|
+
warnIfNotContentEditAdapter
|
|
148
195
|
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { F as FeedtackAdapter, C as ContentAdapter, f as ContentEditAdapter, a as FeedtackPayload, b as FeedtackReply, c as FeedtackResolution, d as FeedtackFilter, e as FeedbackItem, i as FieldApproval, g as FieldFilter, h as FieldApprovalState } from '../adapter-Cn59URIG.js';
|
|
2
|
+
|
|
3
|
+
interface DiskAdapterConfig {
|
|
4
|
+
/** Directory to store JSON files in. Default: '.feedback' */
|
|
5
|
+
directory?: string;
|
|
6
|
+
}
|
|
7
|
+
/** Node.js adapter — persists each feedback item as a JSON file on disk */
|
|
8
|
+
declare class DiskAdapter implements FeedtackAdapter, ContentAdapter, ContentEditAdapter {
|
|
9
|
+
private dir;
|
|
10
|
+
private approvalsDir;
|
|
11
|
+
private fieldsDir;
|
|
12
|
+
constructor(config?: DiskAdapterConfig);
|
|
13
|
+
submit(payload: FeedtackPayload): Promise<void>;
|
|
14
|
+
reply(feedbackId: string, reply: Omit<FeedtackReply, 'id' | 'feedbackId'>): Promise<void>;
|
|
15
|
+
resolve(feedbackId: string, resolution: Omit<FeedtackResolution, 'feedbackId'>): Promise<void>;
|
|
16
|
+
archive(feedbackId: string, userId: string): Promise<void>;
|
|
17
|
+
loadFeedback(filter?: FeedtackFilter): Promise<FeedbackItem[]>;
|
|
18
|
+
approve(fieldPath: string, approval: FieldApproval): Promise<void>;
|
|
19
|
+
revokeApproval(fieldPath: string, userId: string): Promise<void>;
|
|
20
|
+
loadApprovals(filter?: FieldFilter): Promise<FieldApprovalState[]>;
|
|
21
|
+
loadFields(): Promise<Record<string, string>>;
|
|
22
|
+
saveField(fieldPath: string, value: string): Promise<void>;
|
|
23
|
+
private read;
|
|
24
|
+
private write;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export { DiskAdapter, type DiskAdapterConfig };
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// src/adapters/DiskAdapter.ts
|
|
2
|
+
import { mkdir, readdir, readFile, writeFile } from "fs/promises";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
var DiskAdapter = class {
|
|
5
|
+
constructor(config = {}) {
|
|
6
|
+
this.dir = config.directory ?? ".feedback";
|
|
7
|
+
this.approvalsDir = join(this.dir, "approvals");
|
|
8
|
+
this.fieldsDir = join(this.dir, "fields");
|
|
9
|
+
}
|
|
10
|
+
async submit(payload) {
|
|
11
|
+
await mkdir(this.dir, { recursive: true });
|
|
12
|
+
const item = {
|
|
13
|
+
payload,
|
|
14
|
+
replies: [],
|
|
15
|
+
resolutions: [],
|
|
16
|
+
archives: []
|
|
17
|
+
};
|
|
18
|
+
await this.write(payload.id, item);
|
|
19
|
+
}
|
|
20
|
+
async reply(feedbackId, reply) {
|
|
21
|
+
const item = await this.read(feedbackId);
|
|
22
|
+
item.replies.push({
|
|
23
|
+
id: `r_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 7)}`,
|
|
24
|
+
feedbackId,
|
|
25
|
+
...reply
|
|
26
|
+
});
|
|
27
|
+
await this.write(feedbackId, item);
|
|
28
|
+
}
|
|
29
|
+
async resolve(feedbackId, resolution) {
|
|
30
|
+
const item = await this.read(feedbackId);
|
|
31
|
+
item.resolutions.push({ feedbackId, ...resolution });
|
|
32
|
+
await this.write(feedbackId, item);
|
|
33
|
+
}
|
|
34
|
+
async archive(feedbackId, userId) {
|
|
35
|
+
const item = await this.read(feedbackId);
|
|
36
|
+
item.archives.push({
|
|
37
|
+
feedbackId,
|
|
38
|
+
archivedBy: { id: userId, name: "", role: "" },
|
|
39
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
40
|
+
});
|
|
41
|
+
await this.write(feedbackId, item);
|
|
42
|
+
}
|
|
43
|
+
async loadFeedback(filter) {
|
|
44
|
+
let files;
|
|
45
|
+
try {
|
|
46
|
+
await mkdir(this.dir, { recursive: true });
|
|
47
|
+
files = (await readdir(this.dir)).filter((f) => f.endsWith(".json"));
|
|
48
|
+
} catch {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
const items = await Promise.all(
|
|
52
|
+
files.map(
|
|
53
|
+
async (f) => JSON.parse(
|
|
54
|
+
await readFile(join(this.dir, f), "utf-8")
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
);
|
|
58
|
+
if (!filter) return items;
|
|
59
|
+
return items.filter((item) => {
|
|
60
|
+
if (filter.scope && item.payload.scope !== filter.scope) return false;
|
|
61
|
+
if (filter.pathname && item.payload.page.pathname !== filter.pathname)
|
|
62
|
+
return false;
|
|
63
|
+
if (filter.url && item.payload.page.url !== filter.url) return false;
|
|
64
|
+
if (filter.userId && item.payload.submittedBy.id !== filter.userId)
|
|
65
|
+
return false;
|
|
66
|
+
return true;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
// ContentAdapter implementation
|
|
70
|
+
async approve(fieldPath, approval) {
|
|
71
|
+
await mkdir(this.approvalsDir, { recursive: true });
|
|
72
|
+
const safeName = fieldPath.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
73
|
+
await writeFile(
|
|
74
|
+
join(this.approvalsDir, `${safeName}.json`),
|
|
75
|
+
JSON.stringify(approval, null, 2)
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
async revokeApproval(fieldPath, userId) {
|
|
79
|
+
await mkdir(this.approvalsDir, { recursive: true });
|
|
80
|
+
const safeName = fieldPath.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
81
|
+
const filePath = join(this.approvalsDir, `${safeName}.json`);
|
|
82
|
+
let approval;
|
|
83
|
+
try {
|
|
84
|
+
approval = JSON.parse(await readFile(filePath, "utf-8"));
|
|
85
|
+
} catch {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
approval.by = approval.by.filter((id) => id !== userId);
|
|
89
|
+
if (approval.by.length === 0) {
|
|
90
|
+
const { unlink } = await import("fs/promises");
|
|
91
|
+
await unlink(filePath).catch(() => {
|
|
92
|
+
});
|
|
93
|
+
} else {
|
|
94
|
+
await writeFile(filePath, JSON.stringify(approval, null, 2));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
async loadApprovals(filter) {
|
|
98
|
+
let files;
|
|
99
|
+
try {
|
|
100
|
+
await mkdir(this.approvalsDir, { recursive: true });
|
|
101
|
+
files = (await readdir(this.approvalsDir)).filter(
|
|
102
|
+
(f) => f.endsWith(".json")
|
|
103
|
+
);
|
|
104
|
+
} catch {
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
const states = await Promise.all(
|
|
108
|
+
files.map(async (f) => {
|
|
109
|
+
const fieldPath = f.replace(/\.json$/, "").replace(/_/g, ".");
|
|
110
|
+
const approval = JSON.parse(
|
|
111
|
+
await readFile(join(this.approvalsDir, f), "utf-8")
|
|
112
|
+
);
|
|
113
|
+
return {
|
|
114
|
+
fieldPath,
|
|
115
|
+
approval,
|
|
116
|
+
stale: false
|
|
117
|
+
};
|
|
118
|
+
})
|
|
119
|
+
);
|
|
120
|
+
if (!filter) return states;
|
|
121
|
+
return states.filter((s) => {
|
|
122
|
+
if (filter.fieldPath && s.fieldPath !== filter.fieldPath) return false;
|
|
123
|
+
return true;
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
// ContentEditAdapter implementation
|
|
127
|
+
async loadFields() {
|
|
128
|
+
let files;
|
|
129
|
+
try {
|
|
130
|
+
await mkdir(this.fieldsDir, { recursive: true });
|
|
131
|
+
files = (await readdir(this.fieldsDir)).filter((f) => f.endsWith(".json"));
|
|
132
|
+
} catch {
|
|
133
|
+
return {};
|
|
134
|
+
}
|
|
135
|
+
const entries = await Promise.all(
|
|
136
|
+
files.map(async (f) => {
|
|
137
|
+
const fieldPath = f.replace(/\.json$/, "").replace(/_/g, ".");
|
|
138
|
+
const value = JSON.parse(
|
|
139
|
+
await readFile(join(this.fieldsDir, f), "utf-8")
|
|
140
|
+
);
|
|
141
|
+
return [fieldPath, value];
|
|
142
|
+
})
|
|
143
|
+
);
|
|
144
|
+
return Object.fromEntries(entries);
|
|
145
|
+
}
|
|
146
|
+
async saveField(fieldPath, value) {
|
|
147
|
+
const safeName = fieldPath.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
148
|
+
await mkdir(this.fieldsDir, { recursive: true });
|
|
149
|
+
await writeFile(
|
|
150
|
+
join(this.fieldsDir, `${safeName}.json`),
|
|
151
|
+
JSON.stringify(value, null, 2)
|
|
152
|
+
);
|
|
153
|
+
const approvalPath = join(this.approvalsDir, `${safeName}.json`);
|
|
154
|
+
const { unlink } = await import("fs/promises");
|
|
155
|
+
await unlink(approvalPath).catch(() => {
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
async read(id) {
|
|
159
|
+
return JSON.parse(
|
|
160
|
+
await readFile(join(this.dir, `${id}.json`), "utf-8")
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
async write(id, item) {
|
|
164
|
+
await writeFile(join(this.dir, `${id}.json`), JSON.stringify(item, null, 2));
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
export {
|
|
168
|
+
DiskAdapter
|
|
169
|
+
};
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1,11 +1,49 @@
|
|
|
1
|
+
import { d as FeedtackTheme, c as FeedtackFlushEvent } from '../types-CHrWe7xT.js';
|
|
1
2
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
|
+
import { h as FieldApprovalState, F as FeedtackAdapter, u as FocusedFieldInfo, t as FieldChange, s as FeedtackUser, e as FeedbackItem } from '../adapter-Cn59URIG.js';
|
|
2
4
|
import React from 'react';
|
|
3
|
-
import { e as FeedbackItem, F as FeedtackAdapter, p as FeedtackUser, o as FeedtackTheme } from '../theme-BdqpMipn.js';
|
|
4
5
|
|
|
5
6
|
/** Fixed palette of 6 colors for pin markers */
|
|
6
7
|
declare const PIN_PALETTE: readonly ["#ef4444", "#3b82f6", "#22c55e", "#f59e0b", "#a855f7", "#ec4899"];
|
|
7
8
|
type PinColor = (typeof PIN_PALETTE)[number];
|
|
8
9
|
|
|
10
|
+
interface DeployCheckResult {
|
|
11
|
+
approved: boolean;
|
|
12
|
+
pending: string[];
|
|
13
|
+
}
|
|
14
|
+
interface UseContentApprovalOptions {
|
|
15
|
+
/**
|
|
16
|
+
* When provided, hash computation uses stored values from this map instead of
|
|
17
|
+
* reading element.textContent from the DOM. Use when static-build values may
|
|
18
|
+
* differ from live stored values (e.g. when used alongside useContentEdit).
|
|
19
|
+
*/
|
|
20
|
+
storedValues?: Map<string, string>;
|
|
21
|
+
}
|
|
22
|
+
interface UseContentApprovalResult {
|
|
23
|
+
fields: FieldApprovalState[];
|
|
24
|
+
approve: (fieldPath: string) => Promise<void>;
|
|
25
|
+
revoke: (fieldPath: string) => Promise<void>;
|
|
26
|
+
rescan: () => Promise<void>;
|
|
27
|
+
checkDeploy: () => Promise<DeployCheckResult>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Hook for managing content field approvals.
|
|
31
|
+
* Requires an adapter that implements ContentAdapter.
|
|
32
|
+
*/
|
|
33
|
+
declare function useContentApproval(adapter: FeedtackAdapter, userId: string, options?: UseContentApprovalOptions): UseContentApprovalResult;
|
|
34
|
+
|
|
35
|
+
interface ContentEditToolbarProps {
|
|
36
|
+
focusedField: FocusedFieldInfo | null;
|
|
37
|
+
approvalState: FieldApprovalState | null;
|
|
38
|
+
changes: FieldChange[];
|
|
39
|
+
saving: string | null;
|
|
40
|
+
onApprove: (fieldPath: string) => Promise<void>;
|
|
41
|
+
onRevoke: (fieldPath: string) => Promise<void>;
|
|
42
|
+
onRevert: (fieldPath: string) => Promise<void>;
|
|
43
|
+
onCheckDeploy: () => Promise<DeployCheckResult>;
|
|
44
|
+
}
|
|
45
|
+
declare function ContentEditToolbar({ focusedField, approvalState, changes, saving, onApprove, onRevoke, onRevert, onCheckDeploy, }: ContentEditToolbarProps): react_jsx_runtime.JSX.Element;
|
|
46
|
+
|
|
9
47
|
interface FeedtackContextValue {
|
|
10
48
|
activatePinMode: () => void;
|
|
11
49
|
deactivatePinMode: () => void;
|
|
@@ -18,11 +56,6 @@ interface FeedtackContextValue {
|
|
|
18
56
|
isModalOpen: boolean;
|
|
19
57
|
}
|
|
20
58
|
|
|
21
|
-
interface FeedtackFlushEvent {
|
|
22
|
-
pathname: string;
|
|
23
|
-
items: FeedbackItem[];
|
|
24
|
-
}
|
|
25
|
-
|
|
26
59
|
interface FeedtackClasses {
|
|
27
60
|
button?: string;
|
|
28
61
|
form?: string;
|
|
@@ -53,10 +86,31 @@ interface FeedtackProviderProps {
|
|
|
53
86
|
flushIdleMs?: number;
|
|
54
87
|
/** User roles that trigger re-scope on reply (default: any non-'agent' role) */
|
|
55
88
|
rescopeRoles?: string[];
|
|
89
|
+
/**
|
|
90
|
+
* Called by the consumer (e.g. on a Deploy button click) to check whether all
|
|
91
|
+
* content fields have current approvals. Feedtack surfaces the data; the consumer
|
|
92
|
+
* decides what to do with the result.
|
|
93
|
+
*/
|
|
94
|
+
onDeployCheck?: () => Promise<{
|
|
95
|
+
approved: boolean;
|
|
96
|
+
pending: string[];
|
|
97
|
+
}>;
|
|
56
98
|
}
|
|
57
99
|
declare function FeedtackProvider({ children, adapter, currentUser, hotkey, adminOnly, theme, classes, sentimentLabels, onError, disabled, renderPinIcon, onFlush, flushIdleMs, rescopeRoles, }: FeedtackProviderProps): react_jsx_runtime.JSX.Element;
|
|
58
100
|
|
|
101
|
+
interface UseContentEditResult extends UseContentApprovalResult {
|
|
102
|
+
active: boolean;
|
|
103
|
+
activate: () => Promise<void>;
|
|
104
|
+
deactivate: () => void;
|
|
105
|
+
changes: FieldChange[];
|
|
106
|
+
revert: (fieldPath: string) => Promise<void>;
|
|
107
|
+
saving: string | null;
|
|
108
|
+
focusedField: FocusedFieldInfo | null;
|
|
109
|
+
toolbarProps: ContentEditToolbarProps;
|
|
110
|
+
}
|
|
111
|
+
declare function useContentEdit(adapter: FeedtackAdapter, userId: string): UseContentEditResult;
|
|
112
|
+
|
|
59
113
|
/** Hook for host app to programmatically control feedtack */
|
|
60
114
|
declare function useFeedtack(): FeedtackContextValue;
|
|
61
115
|
|
|
62
|
-
export { type FeedtackClasses, type FeedtackContextValue,
|
|
116
|
+
export { ContentEditToolbar, type ContentEditToolbarProps, type DeployCheckResult, type FeedtackClasses, type FeedtackContextValue, FeedtackFlushEvent, FeedtackProvider, type FeedtackProviderProps, type FeedtackSentimentLabels, PIN_PALETTE, type PinColor, type UseContentApprovalOptions, type UseContentApprovalResult, type UseContentEditResult, useContentApproval, useContentEdit, useFeedtack };
|