@rightkit/onboarding 0.3.2 → 0.3.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/README.md CHANGED
@@ -20,9 +20,9 @@ First-run onboarding framework for Right Suite apps. The legal agreement gate co
20
20
 
21
21
  | Peer | Range |
22
22
  |---|---|
23
- | `@rightkit/app-shell` | `>=0.2.0 <1` |
23
+ | `@rightkit/react-shell` | `>=0.2.0 <1` |
24
24
  | `@rightkit/legal-ui` | `>=0.1.2 <1` |
25
- | `react` | `>=18 <20` |
25
+ | `react` | `>=18 <21` |
26
26
 
27
27
  ## Example
28
28
 
@@ -45,6 +45,10 @@ import "@rightkit/onboarding/onboarding.css";
45
45
 
46
46
  Acceptance is stored as `legalAcceptance: { version, documentsHash, acceptedAt, eligibilityBasis }`. An app that already keeps its own acceptance receipt can pass `legalReceipt` (`read()` and `write(record)`) and keep that store as the source of truth. Steps can set `canContinue(context)`.
47
47
 
48
+ ## When the legal gate re-prompts
49
+
50
+ `isLegalAccepted(state, requiredVersion?, isAccepted?, requiredDocumentsHash?)` decides it, in this order: a custom `isAccepted` wins outright; a `requiredDocumentsHash` must equal the stored acceptance's `documentsHash`; a `requiredVersion` must equal its `version`; with neither, the stored `legalAccepted` flag is used. Pass `requiredDocumentsHash` (the hash of the material documents, from `@rightkit/legal`) to catch a text change that did not bump the version: without it only the version is compared. `documentsHash` is recorded with the acceptance and never compared; by default it covers document titles and URLs, not their text.
51
+
48
52
  ## Known issues
49
53
 
50
- - Peer React is `<20`, while `doc-render`, `i18n`, `media-ui` and `pdf-ui` accept `<21` (npm audit 2026-10-08).
54
+ - Peer React is `<21`, the same range as `doc-render`, `i18n`, `media-ui` and `pdf-ui` (`package.json:29`).
@@ -1,5 +1,5 @@
1
1
  import type { OnboardingProps, PersistedOnboardingState } from "./types.js";
2
2
  /** True when the stored acceptance satisfies the host's requirement. */
3
- export declare function isLegalAccepted(state: PersistedOnboardingState, requiredVersion?: string, custom?: (state: PersistedOnboardingState) => boolean): boolean;
3
+ export declare function isLegalAccepted(state: PersistedOnboardingState, requiredVersion?: string, custom?: (state: PersistedOnboardingState) => boolean, requiredDocumentsHash?: string): boolean;
4
4
  export declare function onboardingStateKey(): string;
5
- export declare function Onboarding({ settings, legal, steps, permissions, models, license, onComplete, onSkip, className, requiredVersion, isAccepted, documentsHash, beforeLegalGate, requireModel, autoStartDownload, shouldAutoStart, onModelsStepEnter, modelsIntro, allowSkip, titlebar, dragRegion, legalDragRegion, legalReceipt }: OnboardingProps): import("react").JSX.Element;
5
+ export declare function Onboarding({ settings, legal, steps, permissions, models, license, onComplete, onSkip, className, requiredVersion, requiredDocumentsHash, isAccepted, documentsHash, beforeLegalGate, requireModel, autoStartDownload, shouldAutoStart, onModelsStepEnter, modelsIntro, allowSkip, titlebar, dragRegion, legalDragRegion, legalReceipt }: OnboardingProps): import("react").JSX.Element;
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
3
3
  import { LegalGate } from "@rightkit/legal-ui";
4
- import { Button } from "@rightkit/app-shell/react";
4
+ import { Button } from "@rightkit/react-shell/react";
5
5
  import { cx } from "./ui.js";
6
6
  import { licenseStep, modelsStep, permissionsStep } from "./steps.js";
7
7
  const KEY = "onboarding.state";
@@ -39,11 +39,17 @@ function withReceipt(state, record) {
39
39
  return record ? { ...rest, legalAccepted: true, eligibilityBasis: record.eligibilityBasis, legalAcceptance: record } : { ...rest, legalAccepted: false };
40
40
  }
41
41
  /** True when the stored acceptance satisfies the host's requirement. */
42
- export function isLegalAccepted(state, requiredVersion, custom) {
42
+ export function isLegalAccepted(state, requiredVersion, custom, requiredDocumentsHash) {
43
43
  if (custom)
44
44
  return custom(state);
45
+ // A host that supplies the documents' hash is asking for the text itself to be what was accepted:
46
+ // a changed text re-prompts even when the version string did not move.
47
+ if (requiredDocumentsHash !== undefined && state.legalAcceptance?.documentsHash !== requiredDocumentsHash)
48
+ return false;
45
49
  if (requiredVersion !== undefined)
46
50
  return state.legalAcceptance?.version === requiredVersion;
51
+ if (requiredDocumentsHash !== undefined)
52
+ return state.legalAcceptance !== undefined;
47
53
  return state.legalAccepted;
48
54
  }
49
55
  /** FNV-1a fallback when SubtleCrypto is unavailable. */
@@ -73,7 +79,7 @@ function TitleStrip({ titlebar, dragRegion }) {
73
79
  return null;
74
80
  return _jsx("div", { className: "rk-onboarding__titlebar", "data-testid": "onboarding-titlebar", ...(dragRegion ? { "data-tauri-drag-region": "" } : {}), children: titlebar });
75
81
  }
76
- export function Onboarding({ settings, legal, steps = [], permissions, models, license, onComplete, onSkip, className, requiredVersion, isAccepted, documentsHash, beforeLegalGate, requireModel, autoStartDownload, shouldAutoStart, onModelsStepEnter, modelsIntro, allowSkip = true, titlebar, dragRegion, legalDragRegion = false, legalReceipt }) {
82
+ export function Onboarding({ settings, legal, steps = [], permissions, models, license, onComplete, onSkip, className, requiredVersion, requiredDocumentsHash, isAccepted, documentsHash, beforeLegalGate, requireModel, autoStartDownload, shouldAutoStart, onModelsStepEnter, modelsIntro, allowSkip = true, titlebar, dragRegion, legalDragRegion = false, legalReceipt }) {
77
83
  const [state, setState] = useState(null);
78
84
  const [saving, setSaving] = useState(false);
79
85
  const [error, setError] = useState(null);
@@ -145,7 +151,7 @@ export function Onboarding({ settings, legal, steps = [], permissions, models, l
145
151
  ...steps,
146
152
  ], [license, models, permissions, steps, requireModel, autoStartDownload, shouldAutoStart, onModelsStepEnter, modelsIntro]);
147
153
  const activeState = state ?? INITIAL;
148
- const accepted = isLegalAccepted(activeState, requiredVersion, isAccepted);
154
+ const accepted = isLegalAccepted(activeState, requiredVersion, isAccepted, requiredDocumentsHash);
149
155
  const needsGate = state !== null && !accepted;
150
156
  // The agreement gate does not depend on the saved step: until it is accepted, the active step is the first.
151
157
  // Once accepted, the Agreement step (index 0) is never shown again when other steps exist.
@@ -199,7 +205,7 @@ export function Onboarding({ settings, legal, steps = [], permissions, models, l
199
205
  return _jsxs("div", { className: cx("rk-onboarding", className), "aria-busy": "true", children: [_jsx(TitleStrip, { titlebar: titlebar, dragRegion: legalDragRegion }), _jsx("p", { className: "rk-onboarding__muted", children: "Loading setup\u2026" })] });
200
206
  const reacceptance = activeState.legalAccepted || activeState.legalAcceptance !== undefined;
201
207
  return _jsxs("div", { className: cx("rk-onboarding rk-onboarding--legal", className), "data-testid": "onboarding-legal", "data-variant": reacceptance ? "re-acceptance" : "first-run", children: [_jsx(TitleStrip, { titlebar: titlebar, dragRegion: legalDragRegion }), _jsx(LegalGate, { ...legal, variant: reacceptance ? "re-acceptance" : "first-run", onQuit: () => void legal.onQuit?.(), onAgree: async ({ eligibilityBasis }) => {
202
- const legalAcceptance = { version: requiredVersion ?? "1", documentsHash: documentsHash ?? await hashDocuments(legal.documents), acceptedAt: new Date().toISOString(), eligibilityBasis };
208
+ const legalAcceptance = { version: requiredVersion ?? "1", documentsHash: documentsHash ?? requiredDocumentsHash ?? await hashDocuments(legal.documents), acceptedAt: new Date().toISOString(), eligibilityBasis };
203
209
  if (legalReceipt) {
204
210
  try {
205
211
  await legalReceipt.write(legalAcceptance);
package/dist/steps.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useCallback, useEffect, useRef, useState } from "react";
3
- import { Button, Input, Badge } from "@rightkit/app-shell/react";
3
+ import { Button, Input, Badge } from "@rightkit/react-shell/react";
4
4
  function ProgressBar({ value, label }) {
5
5
  const pct = Math.max(0, Math.min(1, value));
6
6
  return _jsx("div", { className: "rk-onboarding__progress", role: "progressbar", "aria-label": label, "aria-valuemin": 0, "aria-valuemax": 100, "aria-valuenow": Math.round(pct * 100), children: _jsx("i", { style: { width: `${pct * 100}%` } }) });
package/dist/types.d.ts CHANGED
@@ -5,7 +5,7 @@ export interface SettingsSnapshot {
5
5
  values: SettingsValues;
6
6
  revision: number;
7
7
  }
8
- /** Structural port shared with @rightkit/settings-ui and rightkit-settings. */
8
+ /** Structural port shared with @rightkit/settings-ui and rightkit-app-settings. */
9
9
  export interface SettingsBackend {
10
10
  load(): Promise<SettingsSnapshot>;
11
11
  patch(patch: SettingsValues, expectedRevision?: number): Promise<SettingsSnapshot>;
@@ -129,7 +129,13 @@ export interface OnboardingProps {
129
129
  requiredVersion?: string;
130
130
  /** Custom acceptance check; takes precedence over `requiredVersion`. */
131
131
  isAccepted?: (state: PersistedOnboardingState) => boolean;
132
- /** Hash of the legal documents recorded with the acceptance. Default: hash of document titles and URLs. */
132
+ /**
133
+ * Hash the stored acceptance must carry. A stored acceptance with a different (or no) hash re-prompts even when
134
+ * the version is unchanged, so a text change without a version bump is caught. Pass the hash of the MATERIAL
135
+ * documents only (see `@rightkit/legal`), not of appendices that regenerate on dependency bumps.
136
+ */
137
+ requiredDocumentsHash?: string;
138
+ /** Hash of the legal documents recorded with the acceptance. Default: `requiredDocumentsHash`, else a hash of document titles and URLs. Recorded only; never compared. */
133
139
  documentsHash?: string;
134
140
  /** Runs (and is awaited) before the legal gate is shown, e.g. to resize or reveal the window. */
135
141
  beforeLegalGate?: () => void | Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rightkit/onboarding",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "First-run onboarding framework: ordered steps, progress, skip/resume, legal gate first, permissions, model download and licence/trial steps.",
5
5
  "license": "MIT OR Apache-2.0",
6
6
  "type": "module",
@@ -24,14 +24,14 @@
24
24
  "*.css"
25
25
  ],
26
26
  "peerDependencies": {
27
- "@rightkit/app-shell": ">=0.2.0 <1",
27
+ "@rightkit/react-shell": ">=0.2.0 <1",
28
28
  "@rightkit/legal-ui": ">=0.1.2 <1",
29
29
  "react": ">=18 <21"
30
30
  },
31
31
  "devDependencies": {
32
- "@rightkit/app-shell": "0.4.1",
33
- "@rightkit/legal-ui": "0.2.0",
34
- "@rightkit/qa-chrome": "0.1.3",
32
+ "@rightkit/react-shell": "0.6.4",
33
+ "@rightkit/legal-ui": "0.2.2",
34
+ "@rightkit/qa-chrome": "0.1.6",
35
35
  "@testing-library/jest-dom": "^6.6.3",
36
36
  "@testing-library/react": "^16.1.0",
37
37
  "@testing-library/user-event": "^14.5.2",
@@ -50,7 +50,7 @@
50
50
  },
51
51
  "repository": {
52
52
  "type": "git",
53
- "url": "git+https://github.com/bogusyogi/rightkit.git",
53
+ "url": "git+https://github.com/Orthic-Labs/rightkit.git",
54
54
  "directory": "packages/onboarding"
55
55
  },
56
56
  "scripts": {