@journeeze/sdk 0.0.0-stage → 0.1.0-rc.3
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/AGENTS.md +80 -0
- package/CHANGELOG.md +41 -0
- package/LICENSE.md +27 -0
- package/README.md +343 -2
- package/dist/THIRD_PARTY_LICENSES.txt +246 -0
- package/dist/anchor-cPvFFTOS.js +1 -0
- package/dist/attach-DpioALxW.js +21 -0
- package/dist/chat-BgmB0rnW.js +64 -0
- package/dist/consent-DckCUvYR.js +1 -0
- package/dist/context-UnG6_X61.js +17 -0
- package/dist/contract.js +10 -0
- package/dist/feedback-CtH1a_kQ.js +1 -0
- package/dist/help-w0O5Qu-4.js +105 -0
- package/dist/hook-DGWypoEO.js +1 -0
- package/dist/index-B_nob8S0.js +1 -0
- package/dist/index-CwG2LrhO.js +20 -0
- package/dist/index.js +10 -0
- package/dist/jobs-CAXbM5-g.js +45 -0
- package/dist/loader-BZiIDD1D.js +96 -0
- package/dist/mask-audit-Du3NPoG_.js +5 -0
- package/dist/react.js +71 -0
- package/dist/resolve-BfWByuTp.js +1 -0
- package/dist/script.js +14 -0
- package/dist/signals-amjMQyOy.js +1 -0
- package/dist/telemetry-CyRJDaVs.js +1 -0
- package/dist/transport-B64CfIcF.js +1 -0
- package/dist/types/chat/capture/attach.d.ts +41 -0
- package/dist/types/chat/capture/draw.d.ts +10 -0
- package/dist/types/chat/capture/geometry.d.ts +30 -0
- package/dist/types/chat/capture/styles.d.ts +1 -0
- package/dist/types/chat/shell/chat.d.ts +27 -0
- package/dist/types/chat/shell/converse.d.ts +95 -0
- package/dist/types/chat/shell/copy.d.ts +8 -0
- package/dist/types/chat/shell/model.d.ts +36 -0
- package/dist/types/chat/shell/session.d.ts +148 -0
- package/dist/types/chat/shell/styles.d.ts +1 -0
- package/dist/types/chat/shell/view.d.ts +18 -0
- package/dist/types/chat/transport.d.ts +5 -0
- package/dist/types/config.d.ts +34 -0
- package/dist/types/context.d.ts +6 -0
- package/dist/types/contract.d.ts +13 -0
- package/dist/types/help/core/client.d.ts +26 -0
- package/dist/types/help/core/controller.d.ts +184 -0
- package/dist/types/help/core/events.d.ts +32 -0
- package/dist/types/help/core/help.d.ts +25 -0
- package/dist/types/help/core/highlight.d.ts +38 -0
- package/dist/types/help/core/panel.d.ts +27 -0
- package/dist/types/help/core/routing.d.ts +21 -0
- package/dist/types/help/core/search.d.ts +7 -0
- package/dist/types/help/ui/callout.d.ts +2 -0
- package/dist/types/help/ui/jobs/jobs.d.ts +24 -0
- package/dist/types/help/ui/jobs/model.d.ts +33 -0
- package/dist/types/help/ui/jobs/styles.d.ts +1 -0
- package/dist/types/help/ui/panel.d.ts +3 -0
- package/dist/types/help/ui/request.d.ts +2 -0
- package/dist/types/help/ui/styles.d.ts +1 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/invite.d.ts +4 -0
- package/dist/types/loader.d.ts +59 -0
- package/dist/types/mask-detector.d.ts +22 -0
- package/dist/types/mask.d.ts +10 -0
- package/dist/types/preview/flag.d.ts +2 -0
- package/dist/types/preview/mask-audit.d.ts +29 -0
- package/dist/types/react.d.ts +9 -0
- package/dist/types/resolve/resolve.d.ts +12 -0
- package/dist/types/schemas/feedback.d.ts +76 -0
- package/dist/types/schemas/finding.d.ts +22 -0
- package/dist/types/schemas/index.d.ts +6 -0
- package/dist/types/schemas/turn.d.ts +26 -0
- package/dist/types/script.d.ts +6 -0
- package/dist/types/signals/cel.d.ts +8 -0
- package/dist/types/signals/hook.d.ts +10 -0
- package/dist/types/signals/signals.d.ts +149 -0
- package/dist/types/signals/steps.d.ts +33 -0
- package/dist/types/telemetry/telemetry.d.ts +73 -0
- package/dist/types/transport.d.ts +30 -0
- package/dist/types/types.d.ts +462 -0
- package/dist/types/widget/anchor.d.ts +11 -0
- package/dist/types/widget/capture.d.ts +39 -0
- package/dist/types/widget/consent.d.ts +25 -0
- package/dist/types/widget/feedback.d.ts +36 -0
- package/dist/types/widget/icons.d.ts +14 -0
- package/dist/types/widget/outbox.d.ts +69 -0
- package/dist/types/widget/styles.d.ts +1 -0
- package/dist/types/widget/upload.d.ts +14 -0
- package/dist/types/widget.d.ts +7 -0
- package/dist/types/wire.d.ts +66 -0
- package/dist/upload-Fu2qvSJV.js +14 -0
- package/dist/widget-B6mR1J3P.js +118 -0
- package/package.json +70 -4
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import type { FeedbackSeverity, FeedbackShape, FeedbackVerdict } from "../../schemas/feedback";
|
|
2
|
+
import { type ConverseAction, type ConverseMenuItem, type ConverseTransport, type FailureKind } from "./converse";
|
|
3
|
+
import type { Copy } from "./copy";
|
|
4
|
+
import { type Chip, type ContextChip, type ReportFacts } from "./model";
|
|
5
|
+
export type Entry = {
|
|
6
|
+
kind: "assistant";
|
|
7
|
+
text: string;
|
|
8
|
+
copyId: string;
|
|
9
|
+
} | {
|
|
10
|
+
kind: "user";
|
|
11
|
+
text: string;
|
|
12
|
+
} | {
|
|
13
|
+
kind: "card";
|
|
14
|
+
job: string;
|
|
15
|
+
step: number;
|
|
16
|
+
of?: number | undefined;
|
|
17
|
+
title?: string | undefined;
|
|
18
|
+
instruction?: string | undefined;
|
|
19
|
+
live: boolean;
|
|
20
|
+
} | {
|
|
21
|
+
kind: "note";
|
|
22
|
+
text: string;
|
|
23
|
+
};
|
|
24
|
+
/** A draft screenshot or file ("Show us where", "Attach a file"). */
|
|
25
|
+
export interface Attachment {
|
|
26
|
+
id: string;
|
|
27
|
+
/** An image URL for the thumbnail (a `blob:` URL of the masked capture). */
|
|
28
|
+
thumbnail: string;
|
|
29
|
+
label?: string;
|
|
30
|
+
/** Set once uploaded: only uploaded attachments travel with a turn. */
|
|
31
|
+
screenshotId?: string;
|
|
32
|
+
/** Shapes drawn over the image, normalised (feedback/v2 `attachments[].shapes`). */
|
|
33
|
+
shapes?: FeedbackShape[];
|
|
34
|
+
/** Uploads the masked image; called only after consent. Resolves its `screenshotId`. */
|
|
35
|
+
upload?: () => Promise<string | undefined>;
|
|
36
|
+
/** Frees the thumbnail when the attachment leaves the draft. */
|
|
37
|
+
release?: () => void;
|
|
38
|
+
}
|
|
39
|
+
/** "Point at it": the element's tflo id (feedback/v2 message `anchor`), never its values. */
|
|
40
|
+
export interface DraftAnchor {
|
|
41
|
+
tflowId: string;
|
|
42
|
+
}
|
|
43
|
+
/** Screenshots per message (feedback/v2 `attachments` maxItems). */
|
|
44
|
+
export declare const MAX_ATTACHMENTS = 5;
|
|
45
|
+
export interface TeamDetails {
|
|
46
|
+
verdict?: FeedbackVerdict;
|
|
47
|
+
severity?: FeedbackSeverity;
|
|
48
|
+
}
|
|
49
|
+
export interface SessionDeps {
|
|
50
|
+
siteKey: string;
|
|
51
|
+
/** A reviewer session: Team details travel with each turn as turn/v1 `team`. */
|
|
52
|
+
reviewer?: boolean;
|
|
53
|
+
transport: ConverseTransport;
|
|
54
|
+
/** The site's widget copy (`widget.ui.*`, ADR 0020 §5). */
|
|
55
|
+
t: Copy;
|
|
56
|
+
/** Page facts for the report's context, read at each turn. */
|
|
57
|
+
facts(): ReportFacts;
|
|
58
|
+
hasConsent(): boolean;
|
|
59
|
+
giveConsent(): void;
|
|
60
|
+
}
|
|
61
|
+
export declare class ChatSession {
|
|
62
|
+
private readonly deps;
|
|
63
|
+
/** New when a closed conversation is reopened. */
|
|
64
|
+
conversationId: string;
|
|
65
|
+
entries: Entry[];
|
|
66
|
+
state: string;
|
|
67
|
+
actions: ConverseAction[];
|
|
68
|
+
requiresConsent: boolean;
|
|
69
|
+
pending: boolean;
|
|
70
|
+
error: FailureKind | undefined;
|
|
71
|
+
/** Free text is off: Jev is down (a 503, or the server's tap-only clarify). Chips still work. */
|
|
72
|
+
tapOnly: boolean;
|
|
73
|
+
/** Set when a send was held back for consent: the view moves focus to Allow. */
|
|
74
|
+
consentAsked: boolean;
|
|
75
|
+
filed: string | undefined;
|
|
76
|
+
attachments: Attachment[];
|
|
77
|
+
anchor: DraftAnchor | undefined;
|
|
78
|
+
readonly removed: Set<string>;
|
|
79
|
+
team: TeamDetails;
|
|
80
|
+
private job;
|
|
81
|
+
private step;
|
|
82
|
+
private last;
|
|
83
|
+
/** Closed by the user: the next open starts a new conversation. */
|
|
84
|
+
private ended;
|
|
85
|
+
private opening;
|
|
86
|
+
/** Runtime over-masking since the last consented turn, per detector rule (counts only; the
|
|
87
|
+
* capture chunk adds to it). They travel with the next consented turn as `overMasked`. */
|
|
88
|
+
readonly overMask: Map<string, number>;
|
|
89
|
+
private readonly listeners;
|
|
90
|
+
private consent;
|
|
91
|
+
/** The site's widget copy. */
|
|
92
|
+
readonly t: Copy;
|
|
93
|
+
constructor(deps: SessionDeps);
|
|
94
|
+
subscribe(fn: () => void): () => void;
|
|
95
|
+
private emit;
|
|
96
|
+
/** The opening question with the site's known jobs (no turn is sent to show it). */
|
|
97
|
+
start(chips: Chip[]): void;
|
|
98
|
+
/** The user closed the chat (✕, Esc). A conversation the server knows ends with a `cancel`
|
|
99
|
+
* turn (state `closed`; nothing is filed). Fire and forget: its failure shows nothing. */
|
|
100
|
+
close(): void;
|
|
101
|
+
/** Opening the chat again after a close starts a new conversation (consent is kept). */
|
|
102
|
+
reopen(): void;
|
|
103
|
+
offers(type: ConverseAction["type"]): boolean;
|
|
104
|
+
/** The server's label of an action on offer ("" when it is not offered or has none). */
|
|
105
|
+
label(type: ConverseAction["type"]): string;
|
|
106
|
+
chips(): Chip[];
|
|
107
|
+
/** The "+" menu with the server's labels and hints, in `items` order. */
|
|
108
|
+
plusItems(): ConverseMenuItem[];
|
|
109
|
+
/** The consent line shows: the server offers consent or needs it, and the user has not allowed. */
|
|
110
|
+
needsConsent(): boolean;
|
|
111
|
+
allow(): void;
|
|
112
|
+
contextChips(): ContextChip[];
|
|
113
|
+
removeContext(key: string): void;
|
|
114
|
+
setTeam(team: TeamDetails): void;
|
|
115
|
+
/** Team details without unset fields (turn/v1 `team` is a closed object). */
|
|
116
|
+
private teamOnWire;
|
|
117
|
+
/** Adds (or replaces) a draft attachment; false when the message already has five. */
|
|
118
|
+
addAttachment(a: Attachment): boolean;
|
|
119
|
+
removeAttachment(id: string): void;
|
|
120
|
+
setAnchor(anchor: DraftAnchor | undefined): void;
|
|
121
|
+
/** A calm line in the conversation (never sent). */
|
|
122
|
+
note(text: string): void;
|
|
123
|
+
/** Uploads draft images not yet uploaded, after consent only; a failed one leaves the draft. */
|
|
124
|
+
private uploadPending;
|
|
125
|
+
/** The draft message a turn carries (after consent only): uploaded images with their shapes,
|
|
126
|
+
* and the anchor. */
|
|
127
|
+
private draft;
|
|
128
|
+
pickJob(chip: Chip): void;
|
|
129
|
+
other(): void;
|
|
130
|
+
next(): void;
|
|
131
|
+
back(): void;
|
|
132
|
+
didntWork(): void;
|
|
133
|
+
talk(): void;
|
|
134
|
+
/** `Journeeze.feedback()`: the conversation opens at "Report a problem". */
|
|
135
|
+
reportProblem(): void;
|
|
136
|
+
/** A typed message. Held back (false) while consent is needed, or while typing is off. */
|
|
137
|
+
sendText(text: string): boolean;
|
|
138
|
+
/** "Send report" (the server's `send` action). */
|
|
139
|
+
sendReport(): boolean;
|
|
140
|
+
/** Sends the failed turn again with the same `turnId` (the collector de-duplicates it). */
|
|
141
|
+
retry(): void;
|
|
142
|
+
private askConsent;
|
|
143
|
+
private facts;
|
|
144
|
+
private run;
|
|
145
|
+
private exec;
|
|
146
|
+
private send;
|
|
147
|
+
private apply;
|
|
148
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const CHAT_CSS = "\n:host{all:initial;--c:#F7F7F5;--s:#FFF;--r:#F0F0EC;--i:#1D2024;--m:#5D636B;--f:#6C727B;--l:rgb(29 32 36/12%);--a:#3D63C9;--ai:#FFF;--as:rgb(61 99 201/10%);--sh:0 1px 3px rgb(0 0 0/12%),0 4px 12px rgb(0 0 0/8%);\nfont:400 13px/1.45 Inter,system-ui,-apple-system,\"Segoe UI\",sans-serif;color:var(--i);-webkit-font-smoothing:antialiased}\n@media (prefers-color-scheme:dark){:host{--c:#111316;--s:#181B1F;--r:#20242A;--i:#E8EAED;--m:#A3A9B2;--f:#8A919B;--l:rgb(232 234 237/12%);--a:#7F9CF0;--ai:#111316;--as:rgb(127 156 240/14%)}}\n*,*::before,*::after{box-sizing:border-box}\nbutton,input,select{font:inherit;color:inherit}\nbutton{cursor:pointer}\nbutton:disabled,input:disabled{cursor:not-allowed;opacity:.55}\n:focus-visible{outline:2px solid var(--a);outline-offset:2px}\n[hidden]{display:none!important}\n.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}\n.launcher{position:fixed;right:24px;bottom:24px;z-index:2147483646;width:56px;height:56px;border-radius:999px;border:0;background:var(--a);color:var(--ai);font-size:24px;font-weight:600;box-shadow:var(--sh)}\n.panel{position:fixed;right:24px;bottom:92px;z-index:2147483646;width:min(360px,calc(100vw - 48px));height:min(600px,calc(100vh - 116px));display:flex;flex-direction:column;background:var(--s);border:1px solid var(--l);border-radius:10px;box-shadow:var(--sh);overflow:hidden}\n.head{display:flex;justify-content:space-between;align-items:center;gap:8px;padding:8px 8px 8px 16px;border-bottom:1px solid var(--l);background:var(--c)}\n.head h2{margin:0;font-size:13px;font-weight:600}\n.head p{margin:0;font-size:12px;color:var(--f)}\n.x{width:44px;height:44px;border:0;border-radius:6px;background:transparent;color:var(--m);font-size:20px}\n.log{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:12px}\n.msg{max-width:85%;padding:8px 12px;border:1px solid var(--l);border-radius:12px;white-space:pre-wrap;overflow-wrap:anywhere}\n.as{align-self:flex-start;background:var(--s);border-bottom-left-radius:2px}\n.us{align-self:flex-end;background:var(--as);border-bottom-right-radius:2px}\n.note{align-self:flex-start;margin:0;font-size:12px;color:var(--m)}\n.card{align-self:flex-start;width:90%;padding:12px;border:1px solid var(--l);border-radius:10px;background:var(--c)}\n.card h3{margin:0 0 4px;font-size:12px;font-weight:600;color:var(--a)}\n.card p{margin:0 0 8px;color:var(--m)}\n.row{display:flex;gap:8px;flex-wrap:wrap}\n.btn{min-height:44px;padding:0 16px;border:1px solid var(--l);border-radius:6px;background:var(--s)}\n.pri{background:var(--a);border-color:var(--a);color:var(--ai);font-weight:600}\n.link{min-height:44px;padding:0 4px;border:0;background:none;color:var(--a);text-decoration:underline}\n.foot{border-top:1px solid var(--l);padding:12px;display:flex;flex-direction:column;gap:8px;background:var(--s)}\n.dyn{display:flex;flex-direction:column;gap:8px}.dyn:empty{display:none}\n.chips{display:flex;flex-wrap:wrap;gap:6px}\n.chip{min-height:44px;padding:0 12px;border:1px solid var(--l);border-radius:999px;background:var(--s);color:var(--m)}\n.chip.job{border-color:var(--a);color:var(--a)}\n.err{margin:0;padding:8px;border-radius:6px;background:var(--r);color:var(--i)}\n.consent{margin:0;font-size:12px;color:var(--m)}\n.consent.ask{color:var(--i);font-weight:600}\ndetails{font-size:12px;color:var(--m)}\nsummary{cursor:pointer;min-height:24px}\n.ctx{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px;padding:0;list-style:none}\n.ctx li{display:flex;align-items:center;gap:2px;padding-left:8px;border:1px solid var(--l);border-radius:6px;background:var(--r);color:var(--i)}\n.ctx li.fixed{padding-right:8px;min-height:32px}\n.ctx button,.att button{width:32px;height:32px;border:0;background:none;color:var(--m)}\n.team{display:flex;gap:8px;margin-top:6px}\n.team label{display:flex;flex-direction:column;gap:2px;color:var(--i)}\n.team select{min-height:32px;border:1px solid var(--l);border-radius:6px;background:var(--s)}\n.atts{display:flex;flex-wrap:wrap;gap:8px;margin:0;padding:0;list-style:none}\n.att{position:relative;width:64px;height:48px;border:1px solid var(--l);border-radius:6px;overflow:hidden;background:var(--r)}\n.att img{width:100%;height:100%;object-fit:cover}\n.pt{display:flex;align-items:center;gap:2px;padding-left:8px;border:1px solid var(--l);border-radius:6px;background:var(--r);font-size:12px;max-width:100%;overflow-wrap:anywhere}\n.pt button{width:32px;height:32px;border:0;background:none;color:var(--m)}\n.att button{position:absolute;top:0;right:0;background:var(--i);color:var(--s);border-radius:0 0 0 6px}\n.comp{position:relative;display:flex;gap:6px;align-items:center}\n.comp input{flex:1;min-width:0;min-height:44px;padding:0 12px;border:1px solid var(--l);border-radius:6px;background:var(--c)}\n.ico{width:44px;height:44px;flex:none;border:1px solid var(--l);border-radius:6px;background:var(--s);font-size:18px}\n.send{background:var(--a);border-color:var(--a);color:var(--ai)}\n.menu{position:absolute;left:0;right:0;bottom:52px;z-index:1;padding:4px;margin:0;list-style:none;background:var(--s);border:1px solid var(--l);border-radius:10px;box-shadow:var(--sh)}\n.menu button{display:block;width:100%;min-height:44px;padding:4px 12px;border:0;border-radius:6px;background:none;text-align:left}\n.menu button:hover,.menu button:focus-visible{background:var(--c)}\n.menu small{display:block;color:var(--f)}\n.dots{align-self:flex-start;color:var(--f)}\n@media (max-width:640px){.panel{inset:0;width:auto;height:auto;border-radius:0;border:0}.launcher[aria-expanded=true]{display:none}}\n@media (prefers-reduced-motion:reduce){*{transition-duration:1ms!important}}\n";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { PlusItem } from "./converse";
|
|
2
|
+
import type { ChatSession } from "./session";
|
|
3
|
+
export interface ChatViewOptions {
|
|
4
|
+
/** A reviewer session (the host passed a reviewer token): shows "Team details". */
|
|
5
|
+
reviewer: boolean;
|
|
6
|
+
/** A "+" menu item was chosen (HC8b wires "Show us where" and friends here). */
|
|
7
|
+
onPlusItem(item: PlusItem): void;
|
|
8
|
+
}
|
|
9
|
+
export interface ChatView {
|
|
10
|
+
open(): void;
|
|
11
|
+
close(): void;
|
|
12
|
+
isOpen(): boolean;
|
|
13
|
+
/** Hides the chat while the user captures or points at the page; the returned function brings
|
|
14
|
+
* it back with focus on the composer's "+" (or the input). */
|
|
15
|
+
suspend(): () => void;
|
|
16
|
+
destroy(): void;
|
|
17
|
+
}
|
|
18
|
+
export declare function chatView(root: ShadowRoot, session: ChatSession, opts: ChatViewOptions): ChatView;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { MountOptions } from "../loader";
|
|
2
|
+
import { type ConverseTransport } from "./shell/converse";
|
|
3
|
+
/** {@link converseTransport} with the caller's identity on each turn, read live from the mount
|
|
4
|
+
* options (`setUser` can change the id). */
|
|
5
|
+
export declare function liveTransport(endpoint: string, options: MountOptions, fetchImpl?: typeof fetch): ConverseTransport;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { JourneezeConfigV1, JourneezeContext } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* The collector base URL (contract §5): `endpoint` (deprecated, wins), else `apiHost` + `apiPath`,
|
|
4
|
+
* else the Journeeze collector itself (direct mode, the v1 default: C0a §5 "Change [J3c]", C0b §1).
|
|
5
|
+
* A v0 host that relied on its own same-origin proxy without `endpoint` must set
|
|
6
|
+
* `apiHost: location.origin`. `apiHost` is an https origin (http only for localhost/127.0.0.1)
|
|
7
|
+
* without path, query or fragment.
|
|
8
|
+
*/
|
|
9
|
+
export declare function collectorBase(config: Pick<JourneezeConfigV1, "endpoint" | "apiHost" | "apiPath">, page?: string): string;
|
|
10
|
+
/** The context fields the collector accepts (§3): an invalid value is dropped, never sent. */
|
|
11
|
+
export declare function checkContext(next: Readonly<JourneezeContext>): JourneezeContext;
|
|
12
|
+
/**
|
|
13
|
+
* The page path feedback carries (contract §3): {@link pagePath} with identifier-like segments
|
|
14
|
+
* replaced by `:id`, since they can name a person or a record. `pagePath: "none"` (Allumata review
|
|
15
|
+
* B7) drops it entirely, in the feedback channel.
|
|
16
|
+
*/
|
|
17
|
+
export declare function sentPath(href: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* The host's signature over the user id (contract §4, v1.3; the widget's uploads and feedback,
|
|
20
|
+
* the chat's Converse turns), only while it is well formed and
|
|
21
|
+
* unexpired; otherwise the id travels unsigned (sites that require signed identity then treat
|
|
22
|
+
* the request as anonymous). Fixed into the request when it is written, like the id.
|
|
23
|
+
*/
|
|
24
|
+
export declare function signedIdentity(o: {
|
|
25
|
+
userIdSig?: string | undefined;
|
|
26
|
+
userIdExp?: number | undefined;
|
|
27
|
+
signature?: {
|
|
28
|
+
sig: string;
|
|
29
|
+
exp: number;
|
|
30
|
+
} | "" | undefined;
|
|
31
|
+
}): {
|
|
32
|
+
userIdSig?: string;
|
|
33
|
+
userIdExp?: number;
|
|
34
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** The tflo id grammar (ADR 0009): an analytics contract name, never user content. */
|
|
2
|
+
export declare const TFLOW_ID: RegExp;
|
|
3
|
+
/** The nearest `data-tflow-id` at or above `el`, if it is a valid contract name. */
|
|
4
|
+
export declare function tflowIdAt(el: Element | null): string | undefined;
|
|
5
|
+
/** Path only: query strings and fragments can carry tokens and PII, so they never leave the page. */
|
|
6
|
+
export declare function pagePath(href: string): string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { SITE_KEY_PATTERN, USER_ID_PATTERN } from "./loader";
|
|
2
|
+
/** `apiPath`: one or more `/segment`s of URL-safe characters; no trailing `/`, query or fragment. */
|
|
3
|
+
export declare const API_PATH_PATTERN: RegExp;
|
|
4
|
+
/** A context value the collector accepts: 1..=128 printable characters (no control characters). */
|
|
5
|
+
export declare const CONTEXT_VALUE_MAX_LEN = 128;
|
|
6
|
+
/**
|
|
7
|
+
* The recommended context-id grammar (the tflo id grammar, ADR 0009): an id, never user content.
|
|
8
|
+
* The SDK warns once per field when a value does not match it, and still sends it if it is valid
|
|
9
|
+
* for the collector ({@link CONTEXT_VALUE_MAX_LEN}).
|
|
10
|
+
*/
|
|
11
|
+
export declare const CONTEXT_ID_PATTERN: RegExp;
|
|
12
|
+
/** The context fields, in wire order. `setContext` merges; `undefined` clears one field. */
|
|
13
|
+
export declare const CONTEXT_FIELDS: readonly ["persona", "journeyId", "journeyStep", "device", "businessType", "state"];
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { GetGuideResponse, GetHelpIndexResponse } from "../../types";
|
|
2
|
+
export interface HelpClientOptions {
|
|
3
|
+
endpoint: string;
|
|
4
|
+
siteKey: string;
|
|
5
|
+
/** Sent on help reads when set: reviewers sites require it, public sites validate it (ADR 0013 §1). */
|
|
6
|
+
reviewerToken?: string | undefined;
|
|
7
|
+
fetchImpl?: typeof fetch;
|
|
8
|
+
now?: () => number;
|
|
9
|
+
/** How long a value is used without asking the collector again. */
|
|
10
|
+
freshMs?: number;
|
|
11
|
+
}
|
|
12
|
+
/** Refetch a guide this long before its asset links stop working. */
|
|
13
|
+
export declare const LINK_MARGIN_MS = 60000;
|
|
14
|
+
export declare class HelpClient {
|
|
15
|
+
private readonly opts;
|
|
16
|
+
private indexEntry;
|
|
17
|
+
private readonly guides;
|
|
18
|
+
private readonly inflight;
|
|
19
|
+
private readonly now;
|
|
20
|
+
private readonly freshMs;
|
|
21
|
+
constructor(opts: HelpClientOptions);
|
|
22
|
+
index(): Promise<GetHelpIndexResponse>;
|
|
23
|
+
guide(journeyId: string): Promise<GetGuideResponse>;
|
|
24
|
+
private body;
|
|
25
|
+
private read;
|
|
26
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import type { GetGuideResponse, HelpIndexEntry, HelpJobEntry, WireJobRequest } from "../../types";
|
|
2
|
+
import type { AnchorResult, StepTarget } from "./highlight";
|
|
3
|
+
export type CheckinAnswer = "understood" | "confused";
|
|
4
|
+
export type CheckinPhase = "idle" | "asking" | "needs-consent" | "sending";
|
|
5
|
+
export type SendOutcome = "sent" | "queued" | "dropped";
|
|
6
|
+
export interface GuideStepView {
|
|
7
|
+
index: number;
|
|
8
|
+
title: string;
|
|
9
|
+
instruction: string;
|
|
10
|
+
tflowId?: string | undefined;
|
|
11
|
+
/** Ways to find the step's element, most stable first (a lone tflo anchor when none were stored). */
|
|
12
|
+
targets?: StepTarget[] | undefined;
|
|
13
|
+
/** The host's `journeyStep` id for this step, when known (contract C0c v1.1-r2). */
|
|
14
|
+
stepRef?: string | undefined;
|
|
15
|
+
/** Absolute, short-lived URL (same origin as the collector). */
|
|
16
|
+
screenshotUrl?: string | undefined;
|
|
17
|
+
videoStartMs?: number | undefined;
|
|
18
|
+
videoEndMs?: number | undefined;
|
|
19
|
+
}
|
|
20
|
+
export interface GuideView {
|
|
21
|
+
journeyId: string;
|
|
22
|
+
/** The journey's id within its project (`r1-j09`); check-ins are recorded against it. */
|
|
23
|
+
slug: string;
|
|
24
|
+
title: string;
|
|
25
|
+
goal: string;
|
|
26
|
+
steps: GuideStepView[];
|
|
27
|
+
videoUrl?: string | undefined;
|
|
28
|
+
captionsUrl?: string | undefined;
|
|
29
|
+
/** The guide content shown (`GetGuide.contentVersion`); help events quote it. */
|
|
30
|
+
version?: string | undefined;
|
|
31
|
+
}
|
|
32
|
+
export type HelpState = {
|
|
33
|
+
view: "closed";
|
|
34
|
+
}
|
|
35
|
+
/** The site has help switched off (collector `failed_precondition`). Render nothing; final. */
|
|
36
|
+
| {
|
|
37
|
+
view: "disabled";
|
|
38
|
+
} | {
|
|
39
|
+
view: "index";
|
|
40
|
+
status: "loading" | "ready" | "error";
|
|
41
|
+
query: string;
|
|
42
|
+
results: HelpIndexEntry[];
|
|
43
|
+
total: number;
|
|
44
|
+
/** The site's jobs ("What are you trying to do?", P4e1), in index order; absent or empty: none. */
|
|
45
|
+
jobs?: HelpJobEntry[] | undefined;
|
|
46
|
+
/** A request in progress (absent: idle). */
|
|
47
|
+
signal?: SignalPhase | undefined;
|
|
48
|
+
/** Result of the last callout/request send. */
|
|
49
|
+
signalNotice?: SendOutcome | undefined;
|
|
50
|
+
} | {
|
|
51
|
+
view: "guide";
|
|
52
|
+
status: "loading" | "ready" | "error";
|
|
53
|
+
journeyId: string;
|
|
54
|
+
guide?: GuideView | undefined;
|
|
55
|
+
/** Position in `guide.steps`. */
|
|
56
|
+
step: number;
|
|
57
|
+
/** Whether the current step's element was found and highlighted. */
|
|
58
|
+
anchor: AnchorResult;
|
|
59
|
+
checkin: CheckinPhase;
|
|
60
|
+
/** Result of the last check-in send, for a short confirmation. */
|
|
61
|
+
notice?: SendOutcome | undefined;
|
|
62
|
+
/** A callout or request in progress (absent: idle). */
|
|
63
|
+
signal?: SignalPhase | undefined;
|
|
64
|
+
/** Result of the last callout/request send. */
|
|
65
|
+
signalNotice?: SendOutcome | undefined;
|
|
66
|
+
} | {
|
|
67
|
+
view: "finished";
|
|
68
|
+
guide: GuideView;
|
|
69
|
+
notice?: SendOutcome | undefined;
|
|
70
|
+
};
|
|
71
|
+
export interface CheckinRequest {
|
|
72
|
+
guide: GuideView;
|
|
73
|
+
step: GuideStepView;
|
|
74
|
+
answer: CheckinAnswer;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* A help signal the reader chose to send (north-star N4): a one-tap "This is confusing" on a
|
|
78
|
+
* guide step, or an "I couldn't find how to …" request in their own words. Never automatic:
|
|
79
|
+
* only an explicit command creates one, and nothing is sent before consent.
|
|
80
|
+
*/
|
|
81
|
+
export type HelpSignal = {
|
|
82
|
+
kind: "callout";
|
|
83
|
+
guide: GuideView;
|
|
84
|
+
step: GuideStepView;
|
|
85
|
+
} | {
|
|
86
|
+
kind: "request";
|
|
87
|
+
text: string;
|
|
88
|
+
guide?: GuideView | undefined;
|
|
89
|
+
step?: GuideStepView | undefined;
|
|
90
|
+
jobRequest?: WireJobRequest | undefined;
|
|
91
|
+
};
|
|
92
|
+
/** A signal's progress: consent first (nothing leaves the page before), then sending. */
|
|
93
|
+
export type SignalPhase = "idle" | "needs-consent" | "sending";
|
|
94
|
+
/** Longest request text, in characters (the collector's comment limit). */
|
|
95
|
+
export declare const MAX_REQUEST_CHARS = 4000;
|
|
96
|
+
export interface HelpControllerDeps {
|
|
97
|
+
/** Collector endpoint, to make asset paths absolute. */
|
|
98
|
+
endpoint: string;
|
|
99
|
+
loadIndex(): Promise<{
|
|
100
|
+
guides?: HelpIndexEntry[];
|
|
101
|
+
jobs?: HelpJobEntry[];
|
|
102
|
+
}>;
|
|
103
|
+
loadGuide(journeyId: string): Promise<GetGuideResponse>;
|
|
104
|
+
/** Highlights the step's element by its targets (`undefined` or empty: the step names none). */
|
|
105
|
+
highlight(targets: readonly StepTarget[] | undefined): AnchorResult | Promise<AnchorResult>;
|
|
106
|
+
clearHighlight(): void;
|
|
107
|
+
hasConsent(): boolean;
|
|
108
|
+
giveConsent(): void;
|
|
109
|
+
/** Queues the check-in through the feedback outbox. Called only after consent. */
|
|
110
|
+
sendCheckin(request: CheckinRequest): Promise<SendOutcome>;
|
|
111
|
+
/** Queues a callout or request through the feedback outbox. Called only after consent. */
|
|
112
|
+
sendSignal(signal: HelpSignal): Promise<SendOutcome>;
|
|
113
|
+
/**
|
|
114
|
+
* Whether this reader may send callouts and requests (default: yes). An anonymous reader on a
|
|
115
|
+
* site that refuses anonymous feedback may not (ADR 0013 §4): the view hides those entry
|
|
116
|
+
* points (`HelpController.canSignal`) and the controller drops the commands.
|
|
117
|
+
*/
|
|
118
|
+
signalsAllowed?(): boolean;
|
|
119
|
+
}
|
|
120
|
+
export type HelpListener = (state: HelpState) => void;
|
|
121
|
+
/** The collector's answer when the site's help is switched off (board settings, lane N6a). */
|
|
122
|
+
export declare function isHelpDisabled(err: unknown): boolean;
|
|
123
|
+
export declare class HelpController {
|
|
124
|
+
private readonly deps;
|
|
125
|
+
private current;
|
|
126
|
+
private readonly listeners;
|
|
127
|
+
private entries;
|
|
128
|
+
/** Invalidates async results after a later command (open another guide, close). */
|
|
129
|
+
private generation;
|
|
130
|
+
private readonly asked;
|
|
131
|
+
private pending;
|
|
132
|
+
private pendingSignal;
|
|
133
|
+
constructor(deps: HelpControllerDeps);
|
|
134
|
+
get state(): HelpState;
|
|
135
|
+
subscribe(listener: HelpListener): () => void;
|
|
136
|
+
/** Whether the site serves help (the cached index read). A disabled site goes `disabled`. */
|
|
137
|
+
probe(): Promise<void>;
|
|
138
|
+
/** Opens the panel on the guide list (loads the index). */
|
|
139
|
+
open(): void;
|
|
140
|
+
close(): void;
|
|
141
|
+
/** Filters the loaded index locally; the query is never sent. */
|
|
142
|
+
search(query: string): void;
|
|
143
|
+
openGuide(journeyId: string): void;
|
|
144
|
+
/** Help is off for this site: no panel, no launcher, no retries. */
|
|
145
|
+
private disable;
|
|
146
|
+
/** Back one step; from the first step, back to the list. */
|
|
147
|
+
back(): void;
|
|
148
|
+
/** Ends the current step: asks "Did this step make sense?" once per step, then moves on. */
|
|
149
|
+
next(): void;
|
|
150
|
+
/** Answers the check-in. Without consent, asks for it first (nothing is sent before). */
|
|
151
|
+
answer(answer: CheckinAnswer): void;
|
|
152
|
+
/** Skips the check-in for this step. */
|
|
153
|
+
skipCheckin(): void;
|
|
154
|
+
giveConsent(): void;
|
|
155
|
+
/** "Not now": nothing is sent (a pending callout/request is dropped; the guide continues). */
|
|
156
|
+
declineConsent(): void;
|
|
157
|
+
canSignal(): boolean;
|
|
158
|
+
/** Whether the site's consent is given (views send nothing about the reader before it). */
|
|
159
|
+
hasConsent(): boolean;
|
|
160
|
+
/** "This is confusing" on step `index`: guide, step and element only. After consent. */
|
|
161
|
+
sendCallout(stepIndex: number): void;
|
|
162
|
+
/**
|
|
163
|
+
* "I couldn't find how to …" (or help's "Something else") in the reader's words, after
|
|
164
|
+
* consent; never the raw query. `jobRequest` (index only): the "so you can…" answer and job.
|
|
165
|
+
*/
|
|
166
|
+
sendRequest(text: string, jobRequest?: WireJobRequest): void;
|
|
167
|
+
private signal;
|
|
168
|
+
private patchSignal;
|
|
169
|
+
private sendSignalNow;
|
|
170
|
+
private send;
|
|
171
|
+
/** Re-finds the current step's element (the host's route or layout changed). */
|
|
172
|
+
refreshHighlight(): void;
|
|
173
|
+
private markAsked;
|
|
174
|
+
private advance;
|
|
175
|
+
private showStep;
|
|
176
|
+
/**
|
|
177
|
+
* Highlights the step's element. A result that arrives later (the fallback resolver loads
|
|
178
|
+
* lazily) is applied only while the same guide is still on the same step.
|
|
179
|
+
*/
|
|
180
|
+
private anchorFor;
|
|
181
|
+
private set;
|
|
182
|
+
}
|
|
183
|
+
/** Wire guide → view model: defaults filled, steps ordered, asset paths made absolute. */
|
|
184
|
+
export declare function toView(g: GetGuideResponse, journeyId: string, endpoint: string): GuideView;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { CheckinRequest, HelpController } from "./controller";
|
|
2
|
+
/** The answer to "Got it done?" (engagement.proto `JobAnswer`). */
|
|
3
|
+
export type JobAnswer = "YES" | "PARTLY" | "NO";
|
|
4
|
+
/**
|
|
5
|
+
* A job engagement event (P4e1, ADR 0014 addendum): the job's id and the entry version the reader
|
|
6
|
+
* saw, plus the answer for an outcome. Never text: "Something else" is feedback, not an event.
|
|
7
|
+
* `persona` (engagement.proto field 13, follow-up 21) is the host's `persona` context id, which
|
|
8
|
+
* the jobs view adds when it has the context-id shape (`withPersona`), so answers can be read per
|
|
9
|
+
* job × persona.
|
|
10
|
+
*/
|
|
11
|
+
export type JobEvent = {
|
|
12
|
+
event: "PICKED" | "NOT_POSSIBLE";
|
|
13
|
+
jobId: string;
|
|
14
|
+
jobVersion?: string | undefined;
|
|
15
|
+
persona?: string | undefined;
|
|
16
|
+
} | {
|
|
17
|
+
event: "OUTCOME";
|
|
18
|
+
jobId: string;
|
|
19
|
+
jobVersion?: string | undefined;
|
|
20
|
+
persona?: string | undefined;
|
|
21
|
+
answer: JobAnswer;
|
|
22
|
+
harder?: boolean | undefined;
|
|
23
|
+
};
|
|
24
|
+
export interface TelemetryHook {
|
|
25
|
+
start(): void;
|
|
26
|
+
checkin(req: CheckinRequest): void;
|
|
27
|
+
/** A job event; dropped when telemetry was never started (no consent yet). */
|
|
28
|
+
job(e: JobEvent): void;
|
|
29
|
+
/** `discard` (a logout): drop queued events instead of sending them. */
|
|
30
|
+
stop(discard?: boolean): void;
|
|
31
|
+
}
|
|
32
|
+
export declare function telemetryHook(endpoint: string, siteKey: string, controller: HelpController): TelemetryHook;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { MountedWidget, MountOptions, WidgetModule } from "../../loader";
|
|
2
|
+
import { type JourneezeContext, type SubmitFeedbackRequest, type WireFeedbackContext } from "../../types";
|
|
3
|
+
import { type CheckinRequest, type GuideStepView, type GuideView, type HelpSignal } from "./controller";
|
|
4
|
+
import { type HelpView } from "./panel";
|
|
5
|
+
export { HelpController, isHelpDisabled, toView } from "./controller";
|
|
6
|
+
export type { JobAnswer, JobEvent } from "./events";
|
|
7
|
+
export type { CheckinAnswer, CheckinPhase, CheckinRequest, GuideStepView, GuideView, HelpControllerDeps, HelpListener, HelpSignal, HelpState, SendOutcome, SignalPhase } from "./controller";
|
|
8
|
+
export { MAX_REQUEST_CHARS } from "./controller";
|
|
9
|
+
export { HelpClient } from "./client";
|
|
10
|
+
export { searchGuides } from "./search";
|
|
11
|
+
export { findAnchor, Highlighter, type AnchorResult, type StepTarget } from "./highlight";
|
|
12
|
+
export { PANEL_CSS, placeholderView, type HelpView, type HelpViewOptions } from "./panel";
|
|
13
|
+
/** Feedback context of a guide step: the host's ids, with the guide's journey and step. */
|
|
14
|
+
export declare function guideStepContext(host: Readonly<JourneezeContext>, guide: GuideView, step: GuideStepView, href: string, width: number): WireFeedbackContext;
|
|
15
|
+
/** Feedback context of a check-in: the host's ids, with the guide's journey and step. */
|
|
16
|
+
export declare function checkinContext(host: Readonly<JourneezeContext>, req: CheckinRequest, href: string, width: number): WireFeedbackContext;
|
|
17
|
+
/** Feedback context outside a guide: the host's ids and the page path only. */
|
|
18
|
+
export declare function pageContext(host: Readonly<JourneezeContext>, href: string, width: number): WireFeedbackContext;
|
|
19
|
+
/**
|
|
20
|
+
* The collector request for a help signal. A callout carries the guide step (and its element id)
|
|
21
|
+
* with no comment and no reaction; a request carries the reader's own text as its comment.
|
|
22
|
+
*/
|
|
23
|
+
export declare function signalRequest(siteKey: string, host: Readonly<JourneezeContext>, sig: HelpSignal, href: string, width: number): SubmitFeedbackRequest;
|
|
24
|
+
export declare function mountHelp(options: MountOptions, getContext: () => Readonly<JourneezeContext>, view?: HelpView): MountedWidget;
|
|
25
|
+
export declare const mount: WidgetModule["mount"];
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export type AnchorResult = "found" | "missing" | "none";
|
|
2
|
+
/** One way to find a step's element (contract C0c v1.1 § Targets), most stable first. */
|
|
3
|
+
export interface StepTarget {
|
|
4
|
+
by: "tflow" | "testId" | "role" | "css";
|
|
5
|
+
value: string;
|
|
6
|
+
/** For `role`: the accessible name. */
|
|
7
|
+
name?: string | undefined;
|
|
8
|
+
}
|
|
9
|
+
/** The lazily loaded fallback resolver (its own chunk: `src/resolve/resolve.ts`). */
|
|
10
|
+
export type ResolverLoader = () => Promise<{
|
|
11
|
+
resolveTarget(targets: readonly StepTarget[], doc: Document): Element | undefined;
|
|
12
|
+
}>;
|
|
13
|
+
/**
|
|
14
|
+
* The visible element named `tflowId`, or `undefined`. The id is checked against the tflo
|
|
15
|
+
* grammar first, so it can never inject into the selector.
|
|
16
|
+
*/
|
|
17
|
+
export declare function findAnchor(tflowId: string, doc?: Document): Element | undefined;
|
|
18
|
+
export declare function prefersReducedMotion(win?: Window): boolean;
|
|
19
|
+
export declare class Highlighter {
|
|
20
|
+
private readonly win;
|
|
21
|
+
private readonly load;
|
|
22
|
+
private target;
|
|
23
|
+
private readonly box;
|
|
24
|
+
private readonly onMove;
|
|
25
|
+
/** Bumped on every show/hide, so a late resolver result never outlines a step left behind. */
|
|
26
|
+
private seq;
|
|
27
|
+
constructor(layer: HTMLElement | ShadowRoot, win?: Window, load?: ResolverLoader);
|
|
28
|
+
/**
|
|
29
|
+
* Outlines the step's element: `"none"` when the step names none, `"missing"` when no target
|
|
30
|
+
* resolves. A lone tflo anchor resolves at once; other targets load the fallback resolver
|
|
31
|
+
* first (a failed load is `"missing"`: the step's text still guides).
|
|
32
|
+
*/
|
|
33
|
+
show(targets: readonly StepTarget[] | undefined): AnchorResult | Promise<AnchorResult>;
|
|
34
|
+
private outline;
|
|
35
|
+
hide(): void;
|
|
36
|
+
dispose(): void;
|
|
37
|
+
private place;
|
|
38
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { HelpController } from "./controller";
|
|
2
|
+
import type { JobEvent } from "./events";
|
|
3
|
+
import type { Routed } from "./routing";
|
|
4
|
+
export interface HelpViewOptions {
|
|
5
|
+
/** The consent copy to show before the first check-in is sent (the site's or the default). */
|
|
6
|
+
consentText(): Promise<string>;
|
|
7
|
+
/** The host's `persona` context id, read live (orders "Common for you"). */
|
|
8
|
+
persona?(): string | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* A job event (P4e1). Help forwards it to telemetry only after consent, with help on and
|
|
11
|
+
* without Global Privacy Control; otherwise it is dropped. Never carries text.
|
|
12
|
+
*/
|
|
13
|
+
jobEvent?(e: JobEvent): void;
|
|
14
|
+
/** Adds a stylesheet to help's shadow root (with the host's CSP nonce): lazily loaded parts. */
|
|
15
|
+
addStyle?(css: string): void;
|
|
16
|
+
/**
|
|
17
|
+
* Intent routing (P6d): the reader's "Something else" words → a job and its guide, or
|
|
18
|
+
* `undefined`. Sends only after consent, to a reader allowed to send requests; never rejects.
|
|
19
|
+
*/
|
|
20
|
+
routeIntent?(text: string): Promise<Routed | undefined>;
|
|
21
|
+
}
|
|
22
|
+
/** A view renders the controller's state into the help's closed shadow root. */
|
|
23
|
+
export type HelpView = (root: ShadowRoot, controller: HelpController, options: HelpViewOptions) => {
|
|
24
|
+
destroy(): void;
|
|
25
|
+
};
|
|
26
|
+
export declare const PANEL_CSS = "\n:host { all: initial; }\n.launcher { position:fixed; left:24px; bottom:24px; z-index:2147483646; width:44px; height:44px; border-radius:22px;\n border:1px solid #1D2024; background:#1D2024; color:#FFFFFF; font:600 18px/1 system-ui,sans-serif; cursor:pointer; }\n.panel { position:fixed; left:24px; bottom:80px; z-index:2147483646; width:min(340px, calc(100vw - 48px));\n max-height:min(70vh, 560px); overflow:auto; box-sizing:border-box; padding:16px; border:1px solid rgb(29 32 36/12%);\n border-radius:10px; background:#FFFFFF; color:#1D2024; font:14px/1.45 system-ui,sans-serif; box-shadow:0 8px 24px rgb(0 0 0/12%); }\n.panel h2 { font-size:15px; margin:0 0 8px; }\n.panel p { margin:0 0 8px; }\n.panel ul { list-style:none; padding:0; margin:8px 0 0; }\n.panel li button { display:block; width:100%; text-align:left; padding:8px; margin:0 0 4px; border:1px solid rgb(29 32 36/12%);\n border-radius:6px; background:#FFFFFF; color:inherit; font:inherit; cursor:pointer; }\n.panel input { width:100%; box-sizing:border-box; padding:8px; border:1px solid rgb(29 32 36/24%); border-radius:6px; font:inherit; }\n.panel .row { display:flex; gap:8px; flex-wrap:wrap; margin-top:12px; }\n.panel .row button { padding:6px 12px; border:1px solid #1D2024; border-radius:6px; background:#FFFFFF; color:#1D2024; font:inherit; cursor:pointer; }\n.panel .muted { color:#5D636B; font-size:13px; }\n.panel img { max-width:100%; border:1px solid rgb(29 32 36/12%); border-radius:6px; }\n:focus-visible { outline:2px solid #3D63C9; outline-offset:2px; }\n.jz-help-highlight { position:fixed; z-index:2147483645; pointer-events:none; box-sizing:border-box;\n border:2px solid #3D63C9; border-radius:6px; box-shadow:0 0 0 4px rgb(61 99 201/20%); }\n";
|
|
27
|
+
export declare const placeholderView: HelpView;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Longest text routed, in characters (the collector's limit). */
|
|
2
|
+
export declare const MAX_ROUTE_CHARS = 500;
|
|
3
|
+
/** How long the reader waits for a route before their request path is offered as is. */
|
|
4
|
+
export declare const ROUTE_TIMEOUT_MS = 6000;
|
|
5
|
+
export interface RouteDeps {
|
|
6
|
+
endpoint: string;
|
|
7
|
+
siteKey: string;
|
|
8
|
+
reviewerToken?: string | undefined;
|
|
9
|
+
/** The site's consent, read on every call. */
|
|
10
|
+
hasConsent(): boolean;
|
|
11
|
+
/** Whether this reader may send requests at all. */
|
|
12
|
+
canSignal(): boolean;
|
|
13
|
+
fetchImpl?: typeof fetch;
|
|
14
|
+
}
|
|
15
|
+
/** A routed job: its id and its guide for any persona (the view prefers the persona's guide). */
|
|
16
|
+
export interface Routed {
|
|
17
|
+
jobId: string;
|
|
18
|
+
journeyId?: string | undefined;
|
|
19
|
+
}
|
|
20
|
+
/** Routes `text` to a job, or resolves `undefined` (never rejects). */
|
|
21
|
+
export declare function routeIntent(deps: RouteDeps, text: string): Promise<Routed | undefined>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { HelpIndexEntry } from "../../types";
|
|
2
|
+
/**
|
|
3
|
+
* Guides matching every word of `query`, best first: a word in the title counts most, then the
|
|
4
|
+
* goal, then a step title. A blank query returns the index unchanged. No match returns `[]` (the
|
|
5
|
+
* UI offers "I couldn't find how to …" there).
|
|
6
|
+
*/
|
|
7
|
+
export declare function searchGuides(entries: readonly HelpIndexEntry[], query: string): HelpIndexEntry[];
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { HelpController, HelpState } from "../../core/controller";
|
|
2
|
+
import type { HelpViewOptions } from "../../core/panel";
|
|
3
|
+
type Child = Node | string | false | undefined;
|
|
4
|
+
/** What the styled panel lends the jobs view (so the jobs chunk does not repeat it). */
|
|
5
|
+
export interface JobsKit {
|
|
6
|
+
controller: HelpController;
|
|
7
|
+
options: HelpViewOptions;
|
|
8
|
+
el<K extends keyof HTMLElementTagNameMap>(tag: K, attrs?: Record<string, string>, ...children: Child[]): HTMLElementTagNameMap[K];
|
|
9
|
+
button(label: string, onClick: () => void, attrs?: Record<string, string>): HTMLButtonElement;
|
|
10
|
+
/** The shared consent group ("Allow and send" / "Not now"). */
|
|
11
|
+
consentGroup(): HTMLElement;
|
|
12
|
+
/** Re-renders the panel after a change local to the jobs view. */
|
|
13
|
+
rerender(): void;
|
|
14
|
+
/** The guide view's wording of a check-in send outcome. */
|
|
15
|
+
checkinNotice: Record<string, string>;
|
|
16
|
+
}
|
|
17
|
+
export interface JobsUi {
|
|
18
|
+
/** Whether the jobs view renders this state (the index with jobs, or the end of a job's guide). */
|
|
19
|
+
owns(s: HelpState): boolean;
|
|
20
|
+
content(s: HelpState): Child[];
|
|
21
|
+
destroy(): void;
|
|
22
|
+
}
|
|
23
|
+
export declare function jobsUi(kit: JobsKit): JobsUi;
|
|
24
|
+
export {};
|