@bridge-ds/toolbar 0.0.0-stage → 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.
@@ -0,0 +1,226 @@
1
+ export interface StorageLike {
2
+ getItem(key: string): string | null;
3
+ setItem(key: string, value: string): void;
4
+ removeItem(key: string): void;
5
+ }
6
+ /**
7
+ * What the toolbar knows about the product and its design system: from bridge.config.json (the product's entry in
8
+ * products[] and previews), read at build time (the Admin demo) or from the design system repository at run time.
9
+ */
10
+ export interface BridgeBuild {
11
+ /** The repository whose pull requests `?bridge=<n>` names: the product's own repository, else the design system's. */
12
+ owner: string;
13
+ repo: string;
14
+ /** Production Studio (previews.production.docs): the link back and the shipped tokens.css. */
15
+ studio: string | null;
16
+ /** The product in production (products[].production, else previews.production.<key>): Before of a code change. */
17
+ production: string | null;
18
+ /** The product's preview template (products[].preview): only these hosts may hand a code change over to production. */
19
+ preview: string | null;
20
+ /** VERCEL_ENV of the build ('production', 'preview', 'development'), null when unknown. */
21
+ deployEnv: string | null;
22
+ /** The library branch (repo.defaultBranch) and the token files' root (tokens.root): changes tried together. */
23
+ defaultBranch?: string;
24
+ tokensRoot?: string;
25
+ /** The product's key (products[].key) and the design system repository ("owner/name"), for the way back to Studio. */
26
+ product?: string;
27
+ ds?: string;
28
+ }
29
+ export declare const PARAM = "bridge";
30
+ /** Where After lives for a code change, carried to production on Before. */
31
+ export declare const RETURN_PARAM = "bridge-return";
32
+ /** Scroll position carried across a Before/After navigation. */
33
+ export declare const SCROLL_PARAM = "bridge-scroll";
34
+ export declare const SESSION_KEY = "bridge-admin:change";
35
+ export declare const RETURN_KEY = "bridge-admin:return";
36
+ export declare const DOCK_KEY = "bridge-admin:toolbar-dock";
37
+ export declare const OPEN_KEY = "bridge-admin:toolbar-open";
38
+ /** Changes tried together: the picks of the tokens they set differently (core encodePicks), from Studio. */
39
+ export declare const USE_PARAM = "bridge-use";
40
+ export declare const USE_KEY = "bridge-admin:use";
41
+ /** At most this many changes are tried together. */
42
+ export declare const MAX_TOGETHER = 4;
43
+ /**
44
+ * The change the toolbar follows: `?bridge=<n>` wins and is kept for the session (the admin's hash router keeps the
45
+ * query, a reload or a new path does not need it again); `?bridge=off` forgets it. Blocked storage only loses the
46
+ * memory, never the toolbar of the current page.
47
+ */
48
+ export declare function readChange(search: string, storage: StorageLike | null): number | null;
49
+ /**
50
+ * The change numbers in "101" or "101,118,120" (an encoded comma too): positive, at most 7 digits, no repeats, at
51
+ * most MAX_TOGETHER. Anything else in the list makes it unreadable (empty), never a guess.
52
+ */
53
+ export declare function parseChanges(raw: string | null | undefined): number[];
54
+ /**
55
+ * The changes the toolbar follows: one from "Try it in the product", 2 to 4 from "Try them together". Same memory as
56
+ * one change: `?bridge=` wins and is kept for the session, `?bridge=off` forgets it, blocked storage only loses the
57
+ * memory. The picks (`bridge-use`) travel with the list and are kept, or forgotten, with it.
58
+ */
59
+ export declare function readChanges(search: string, storage: StorageLike | null): number[];
60
+ /** The picks that came with the list: from the address, else the session. */
61
+ export declare function readUse(search: string, storage: StorageLike | null): string | null;
62
+ /** A host that matches the product's preview template (`bridge-admin-git-{branch}-team.vercel.app`), https only. */
63
+ export declare function isPreviewUrl(raw: string | null | undefined, template: string | null): boolean;
64
+ /** The preview to return to from production, from `?bridge-return=` or the session. Only a preview host counts. */
65
+ export declare function readReturn(search: string, storage: StorageLike | null, template: string | null): string | null;
66
+ export type Where = 'production' | 'preview' | 'local';
67
+ /** Which deployment this page is: the product in production by host or by build, a preview, or a local build. */
68
+ export declare function whereAmI(host: string, build: Pick<BridgeBuild, 'production' | 'deployEnv'>): Where;
69
+ /**
70
+ * Whether the toolbar renders. Never without a change from Studio. In production only for the Before side of a code
71
+ * change, that is with a preview to return to: production on its own renders nothing.
72
+ */
73
+ export declare function shouldRender(input: {
74
+ change: number | null;
75
+ where: Where;
76
+ returnTo: string | null;
77
+ together?: boolean;
78
+ }): boolean;
79
+ /** "These 3 changes": the After side of changes tried together, counting the ones switched on. */
80
+ export declare const togetherLabel: (n: number) => string;
81
+ /** The one hint line of changes tried together: where they run and why. */
82
+ export declare const TOGETHER_HINT = "The live product, with these changes' tokens applied in your browser only. No preview has all of them together.";
83
+ /** Show what changed found nothing on this screen for changes tried together. */
84
+ export declare const TOGETHER_EMPTY_LINE = "These changes don't touch this screen. Try another screen.";
85
+ /** Switches one change on or off, keeping the order Studio listed them in. */
86
+ export declare function toggleIncluded(all: number[], included: number[], n: number, on: boolean): number[];
87
+ /** Studio's Changes list, where changes are tried together (and the way back). */
88
+ export declare function studioChangesUrl(build: Pick<BridgeBuild, 'studio' | 'owner' | 'repo'>): string;
89
+ export type ChangeMode = 'tokens' | 'code';
90
+ /** Token files (DTCG and the contrast gate) and changesets only: Before swaps tokens live. Anything else is code. */
91
+ export declare function changeMode(files: string[]): ChangeMode;
92
+ /** The live tokens (main's build, served by production Studio at `/tokens.css`), or null when not configured. */
93
+ export declare function liveTokensUrl(build: Pick<BridgeBuild, 'studio'>): string | null;
94
+ /**
95
+ * Studio's page for the change. A change in a product's own repository (an exploration there) opens as
96
+ * `?exploration=<n>&product=<key>`; a change of the design system repository as `?pr=<n>`.
97
+ */
98
+ export declare function studioChangeUrl(build: Pick<BridgeBuild, 'studio' | 'owner' | 'repo' | 'product' | 'ds'>, change: number): string;
99
+ /** Shape of a visual impact manifest that the toolbar reads (written by @bridge-ds/visual). */
100
+ export interface ManifestShot {
101
+ kind: 'story' | 'route';
102
+ label: string;
103
+ route?: string;
104
+ tenant?: string;
105
+ theme: string;
106
+ status: string;
107
+ percent: number;
108
+ }
109
+ export interface Screen {
110
+ label: string;
111
+ route: string;
112
+ tenant: string;
113
+ theme: string;
114
+ percent: number;
115
+ }
116
+ /** The product screens the change alters, most changed first, one entry per screen, shop and theme. */
117
+ export declare function changedScreens(shots: ManifestShot[] | null | undefined): Screen[];
118
+ /**
119
+ * The same page on another deployment (code changes: production for Before, the preview for After), with the
120
+ * change, the way back and the scroll position in the query. The hash route (ra-core) is kept.
121
+ */
122
+ export declare function sameScreenOn(target: string, here: {
123
+ search: string;
124
+ hash: string;
125
+ }, extra: {
126
+ change: number;
127
+ returnTo?: string | null;
128
+ scroll?: number;
129
+ }): string;
130
+ /** The scroll position a Before/After navigation carried, once: the caller removes it from the address. */
131
+ export declare function readScroll(search: string): number | null;
132
+ export type Dock = 'start' | 'center' | 'end';
133
+ /** Bottom right by default: the corner a back office leaves free most often. */
134
+ export declare function readDock(storage: StorageLike | null): Dock;
135
+ /** One step left (-1) or right (+1), stopping at the edges. */
136
+ export declare function stepDock(dock: Dock, step: -1 | 1): Dock;
137
+ /** The dock nearest to where a drag ended: thirds of the viewport width. */
138
+ export declare function dockAt(x: number, width: number): Dock;
139
+ /** Title of a change as people read it: conventional prefixes ("tokens: ") dropped, first letter up. */
140
+ export declare function readableTitle(title: string): string;
141
+ /** The B shortcut flips Before and After, unless the person is typing or holds a modifier. */
142
+ export declare function isFlipKey(e: {
143
+ key: string;
144
+ altKey: boolean;
145
+ ctrlKey: boolean;
146
+ metaKey: boolean;
147
+ target: unknown;
148
+ }): boolean;
149
+ export interface Rect {
150
+ left: number;
151
+ top: number;
152
+ right: number;
153
+ bottom: number;
154
+ }
155
+ /**
156
+ * Where the toolbar goes so it never covers a primary or submit button of the page: the chosen dock if it is free,
157
+ * else the opposite corner, else the centre, else the chosen dock lifted just above the buttons it would cover. The
158
+ * docks sit inside the area [left, right] (the content column, so it never sits over the sidebar).
159
+ */
160
+ export declare function placeToolbar(input: {
161
+ left: number;
162
+ right: number;
163
+ vh: number;
164
+ w: number;
165
+ h: number;
166
+ gutter: number;
167
+ preferred: Dock;
168
+ obstacles: Rect[];
169
+ margin?: number;
170
+ }): {
171
+ dock: Dock;
172
+ lift: number;
173
+ };
174
+ export type NoDifference = {
175
+ kind: 'theme';
176
+ here: string;
177
+ touched: string;
178
+ } | {
179
+ kind: 'brand';
180
+ here: string;
181
+ touched: string;
182
+ };
183
+ /**
184
+ * Whether a token change shows nothing in this theme and brand, from the token files it edits (DTCG layers named
185
+ * semantic/<theme>, brands/<brand>.<theme>, brands/<brand>). Null when it may show, or when that cannot be told:
186
+ * never claims "no difference" on a guess.
187
+ */
188
+ export declare function noVisibleDifference(files: string[], here: {
189
+ theme: string;
190
+ brand: string;
191
+ }): NoDifference | null;
192
+ /** "darker default background." The change title as a short phrase after "This change:". */
193
+ export declare function changeSummary(title: string): string;
194
+ export interface TokenChange {
195
+ /** Token path, "color.bg.default". */
196
+ path: string;
197
+ /** CSS variable the token builds to, "--color-bg-default". */
198
+ cssVar: string;
199
+ /** The role it plays, what designers call it: "Page canvas" (its description), "Brand primary" (a palette role). */
200
+ name: string;
201
+ /** The exact step, second: "Primary 700" for a palette step, null for a semantic token. */
202
+ step: string | null;
203
+ type: string | null;
204
+ /** The theme and brand its file applies to (null: every one). */
205
+ theme: string | null;
206
+ brand: string | null;
207
+ }
208
+ /** The role a token plays: its description's first phrase, a palette role ("Brand primary"), else its path in words. */
209
+ export declare function roleOfToken(path: string, description: string | null): string;
210
+ /** The palette step, shown second: "palette.primary.700" -> "Primary 700"; null for other tokens. */
211
+ export declare function stepOfToken(path: string): string | null;
212
+ /** Designer name of a token: the first phrase of its description, else its path in words ("Bg default"). */
213
+ export declare function designerName(path: string, description: string | null): string;
214
+ /**
215
+ * The tokens one file changes between two versions of it (parsed DTCG JSON, null when the file is added or removed):
216
+ * every leaf whose $value differs, with the theme and brand its layer applies to.
217
+ */
218
+ export declare function tokenChanges(file: string, before: unknown, after: unknown): TokenChange[];
219
+ /** WCAG contrast ratio between two rgb() colours, or null when either is not rgb (a non-colour value). */
220
+ export declare function contrastRatio(a: string, b: string): number | null;
221
+ /** Two colours under 1.1:1 read as the same at a glance: the swatch says "Subtle change" so it does not look broken. */
222
+ export declare const isSubtle: (a: string, b: string) => boolean;
223
+ /** rgb(245, 247, 249) -> #f5f7f9; anything else as is. */
224
+ export declare function toHex(color: string): string;
225
+ /** "from Darker default background", "from #101 and #118": which change a token's value comes from (changes tried together). */
226
+ export declare function fromText(from: number[], titleOf: (n: number) => string | null): string;
@@ -0,0 +1,45 @@
1
+ import { type ChangeEdits, type CombinePicks, type LayoutOptions, type ModeConfig, type TokenSet } from '@bridge-ds/core';
2
+ import { type TokenChange } from './session';
3
+ export { changeEdits, decodePicks } from '@bridge-ds/core';
4
+ export interface TogetherBase {
5
+ /** Token files of the latest library, relative to the tokens root. */
6
+ latest: Record<string, string>;
7
+ options: LayoutOptions;
8
+ modes: Pick<ModeConfig, 'brands' | 'themes' | 'densities'>;
9
+ /** The latest library as a token set, built once. */
10
+ main: TokenSet;
11
+ }
12
+ export declare function togetherBase(latest: Record<string, string>, options: LayoutOptions, modes: TogetherBase['modes']): TogetherBase;
13
+ /** A token the combination edits, as the toolbar names it, with the changes whose value it takes. */
14
+ export interface TogetherToken extends TokenChange {
15
+ from: number[];
16
+ }
17
+ export interface TogetherResult {
18
+ included: number[];
19
+ /** The stylesheet that paints the combination over the latest library (empty when nothing differs). */
20
+ css: string;
21
+ tokens: TogetherToken[];
22
+ /** Tokens two switched-on changes set differently with no pick that counts: the first change's value is used. */
23
+ unpicked: string[];
24
+ problems: string[];
25
+ next: TokenSet;
26
+ }
27
+ /** The scope attributes ThemeProvider sets together on the root and on every popup. */
28
+ export declare const scopeSelector: (theme: string, brand: string, density: string) => string;
29
+ /**
30
+ * The combination of the switched-on changes. A token two of them set differently takes the pick from Studio; when
31
+ * the picked change is switched off and two others still disagree, the first one listed is used (and said).
32
+ */
33
+ export declare function together(base: TogetherBase, changes: ChangeEdits[], picks: CombinePicks, include: number[]): TogetherResult;
34
+ export interface TogetherPair {
35
+ token: TogetherToken;
36
+ before: string;
37
+ after: string;
38
+ color: boolean;
39
+ }
40
+ /** Before (latest library) and after (the combination) of each edited token where the page is: theme, brand, density. */
41
+ export declare function togetherPairs(base: TogetherBase, result: TogetherResult, here: {
42
+ theme: string;
43
+ brand: string;
44
+ density: string;
45
+ }): TogetherPair[];
@@ -0,0 +1,57 @@
1
+ import { combineChanges, buildTokenSet, previewOverrides, cssVarOf, resolve, parseColor, cssOf } from "@bridge-ds/core";
2
+ import { changeEdits, decodePicks } from "@bridge-ds/core";
3
+ import { s as stepOfToken, a as roleOfToken } from "./session-CzlIsFDY.js";
4
+ function togetherBase(latest, options, modes) {
5
+ return { latest, options, modes, main: buildTokenSet(latest, options) };
6
+ }
7
+ const scopeSelector = (theme, brand, density) => `[data-theme="${theme}"][data-brand="${brand}"][data-density="${density}"]`;
8
+ function together(base, changes, picks, include) {
9
+ const combo = combineChanges(base.latest, changes, picks, { include, unpicked: "first" });
10
+ const next = buildTokenSet(combo.files, base.options);
11
+ const rules = [];
12
+ for (const theme of base.modes.themes) {
13
+ for (const brand of base.modes.brands) {
14
+ for (const density of base.modes.densities.length ? base.modes.densities : ["comfortable"]) {
15
+ const vars = previewOverrides(base.main, next, { theme, brand, density });
16
+ const body = Object.entries(vars).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => ` ${k}: ${v.replace(/[;{}<]/g, "")};`).join("\n");
17
+ if (body) rules.push(`${scopeSelector(theme, brand, density)} {
18
+ ${body}
19
+ }`);
20
+ }
21
+ }
22
+ }
23
+ const tokens = Object.entries(combo.from).sort(([a], [b]) => a.localeCompare(b)).map(([path, from]) => {
24
+ const t = next.byPath.get(path) ?? base.main.byPath.get(path);
25
+ return { path, cssVar: cssVarOf(path), name: roleOfToken(path, t?.description ?? null), step: stepOfToken(path), type: t?.type ?? null, theme: null, brand: null, from };
26
+ });
27
+ return { included: combo.included, css: rules.join("\n"), tokens, unpicked: combo.missing.map((m) => m.path), problems: combo.problems, next };
28
+ }
29
+ function shownValue(type, value) {
30
+ if (value === void 0) return null;
31
+ if (type === "color") {
32
+ const c = parseColor(value);
33
+ if (!c) return null;
34
+ const [r, g, b] = [c.r, c.g, c.b].map((x) => Math.round(x * 255));
35
+ return c.a < 1 ? `rgba(${r}, ${g}, ${b}, ${c.a})` : `rgb(${r}, ${g}, ${b})`;
36
+ }
37
+ return cssOf(type ?? "unknown", value) ?? null;
38
+ }
39
+ function togetherPairs(base, result, here) {
40
+ const out = [];
41
+ for (const token of result.tokens) {
42
+ const a = shownValue(token.type, resolve(base.main.byPath, token.path, { ...here, overrides: base.main.overrideIndex }).value);
43
+ const b = shownValue(token.type, resolve(result.next.byPath, token.path, { ...here, overrides: result.next.overrideIndex }).value);
44
+ if (a === null || b === null || a === b) continue;
45
+ out.push({ token, before: a, after: b, color: token.type === "color" });
46
+ }
47
+ return out;
48
+ }
49
+ export {
50
+ changeEdits,
51
+ decodePicks,
52
+ scopeSelector,
53
+ together,
54
+ togetherBase,
55
+ togetherPairs
56
+ };
57
+ //# sourceMappingURL=together.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"together.js","sources":["../src/together.ts"],"sourcesContent":["// Changes tried together, the toolbar's combination: the latest library (this build's own token files, what the page\n// already wears) plus the token edits of the changes switched on, through @bridge-ds/core token-combine. It becomes\n// one stylesheet that paints every theme, brand and density, and the before and after of each token it edits, with\n// the changes it comes from. Pure (no DOM): loaded only when changes are tried together, tested in together.test.ts.\nimport {\n buildTokenSet,\n combineChanges,\n cssOf,\n cssVarOf,\n parseColor,\n previewOverrides,\n resolve,\n type ChangeEdits,\n type CombinePicks,\n type LayoutOptions,\n type ModeConfig,\n type TokenSet,\n} from '@bridge-ds/core';\nimport { roleOfToken, stepOfToken, type TokenChange } from './session';\n\n// The toolbar reaches the combination through this one lazy module, so a product's main bundle never carries\n// @bridge-ds/core: the edits of a change and the picks from Studio come from here too.\nexport { changeEdits, decodePicks } from '@bridge-ds/core';\n\nexport interface TogetherBase {\n /** Token files of the latest library, relative to the tokens root. */\n latest: Record<string, string>;\n options: LayoutOptions;\n modes: Pick<ModeConfig, 'brands' | 'themes' | 'densities'>;\n /** The latest library as a token set, built once. */\n main: TokenSet;\n}\n\nexport function togetherBase(latest: Record<string, string>, options: LayoutOptions, modes: TogetherBase['modes']): TogetherBase {\n return { latest, options, modes, main: buildTokenSet(latest, options) };\n}\n\n/** A token the combination edits, as the toolbar names it, with the changes whose value it takes. */\nexport interface TogetherToken extends TokenChange {\n from: number[];\n}\n\nexport interface TogetherResult {\n included: number[];\n /** The stylesheet that paints the combination over the latest library (empty when nothing differs). */\n css: string;\n tokens: TogetherToken[];\n /** Tokens two switched-on changes set differently with no pick that counts: the first change's value is used. */\n unpicked: string[];\n problems: string[];\n next: TokenSet;\n}\n\n/** The scope attributes ThemeProvider sets together on the root and on every popup. */\nexport const scopeSelector = (theme: string, brand: string, density: string) => `[data-theme=\"${theme}\"][data-brand=\"${brand}\"][data-density=\"${density}\"]`;\n\n/**\n * The combination of the switched-on changes. A token two of them set differently takes the pick from Studio; when\n * the picked change is switched off and two others still disagree, the first one listed is used (and said).\n */\nexport function together(base: TogetherBase, changes: ChangeEdits[], picks: CombinePicks, include: number[]): TogetherResult {\n const combo = combineChanges(base.latest, changes, picks, { include, unpicked: 'first' });\n const next = buildTokenSet(combo.files, base.options);\n const rules: string[] = [];\n for (const theme of base.modes.themes) {\n for (const brand of base.modes.brands) {\n for (const density of base.modes.densities.length ? base.modes.densities : ['comfortable']) {\n const vars = previewOverrides(base.main, next, { theme, brand, density });\n const body = Object.entries(vars)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([k, v]) => ` ${k}: ${v.replace(/[;{}<]/g, '')};`)\n .join('\\n');\n if (body) rules.push(`${scopeSelector(theme, brand, density)} {\\n${body}\\n}`);\n }\n }\n }\n const tokens: TogetherToken[] = Object.entries(combo.from)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([path, from]) => {\n const t = next.byPath.get(path) ?? base.main.byPath.get(path);\n return { path, cssVar: cssVarOf(path), name: roleOfToken(path, t?.description ?? null), step: stepOfToken(path), type: t?.type ?? null, theme: null, brand: null, from };\n });\n return { included: combo.included, css: rules.join('\\n'), tokens, unpicked: combo.missing.map((m) => m.path), problems: combo.problems, next };\n}\n\n/** A resolved value as the toolbar compares it: a colour as rgb()/rgba() (what computed styles read), else its CSS. */\nfunction shownValue(type: string | null, value: unknown): string | null {\n if (value === undefined) return null;\n if (type === 'color') {\n const c = parseColor(value);\n if (!c) return null;\n const [r, g, b] = [c.r, c.g, c.b].map((x) => Math.round(x * 255));\n return c.a < 1 ? `rgba(${r}, ${g}, ${b}, ${c.a})` : `rgb(${r}, ${g}, ${b})`;\n }\n return cssOf(type ?? 'unknown', value) ?? null;\n}\n\nexport interface TogetherPair {\n token: TogetherToken;\n before: string;\n after: string;\n color: boolean;\n}\n\n/** Before (latest library) and after (the combination) of each edited token where the page is: theme, brand, density. */\nexport function togetherPairs(base: TogetherBase, result: TogetherResult, here: { theme: string; brand: string; density: string }): TogetherPair[] {\n const out: TogetherPair[] = [];\n for (const token of result.tokens) {\n const a = shownValue(token.type, resolve(base.main.byPath, token.path, { ...here, overrides: base.main.overrideIndex }).value);\n const b = shownValue(token.type, resolve(result.next.byPath, token.path, { ...here, overrides: result.next.overrideIndex }).value);\n if (a === null || b === null || a === b) continue;\n out.push({ token, before: a, after: b, color: token.type === 'color' });\n }\n return out;\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { type DiffResult } from './outline';
2
+ export declare function useChangedElements(on: boolean, live: HTMLLinkElement | HTMLStyleElement | null, vars: string[], screen: string, sheetIs?: 'before' | 'after'): DiffResult | null;
@@ -0,0 +1,24 @@
1
+ import { type TogetherFacts } from './github';
2
+ import { type BridgeBuild, type NoDifference } from './session';
3
+ import type { TogetherBase, TogetherPair, TogetherResult } from './together';
4
+ export interface Together {
5
+ /** The changes in Studio's order, once read (null while reading). */
6
+ list: TogetherFacts[] | null;
7
+ error: boolean;
8
+ /** Switched on, in Studio's order. */
9
+ included: number[];
10
+ toggle: (n: number, on: boolean) => void;
11
+ result: TogetherResult | null;
12
+ /** Tokens each change edits (by path), whether or not its value is the one applied. */
13
+ editedBy: Map<number, string[]>;
14
+ pairs: TogetherPair[];
15
+ /** Nothing differs where the page is, but it does in another theme (or brand): say so, never a silent "After". */
16
+ noDiff: NoDifference | null;
17
+ /** The stylesheet with the combination, once in the page; its media switches sides. */
18
+ sheet: HTMLStyleElement | null;
19
+ }
20
+ export declare function useTogether(build: BridgeBuild, latest: (() => Promise<TogetherBase>) | null, changes: number[] | null, use: string | null, here: {
21
+ theme: string;
22
+ brand: string;
23
+ density: string;
24
+ }, applied: boolean): Together | null;
package/package.json CHANGED
@@ -1,6 +1,60 @@
1
1
  {
2
2
  "name": "@bridge-ds/toolbar",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "The Bridge toolbar for a product built with the Bridge design system: opened from Bridge Studio with ?bridge=<change>, it flips the real product between before and after and shows what changed.",
6
+ "license": "MIT",
7
+ "author": "Florian Gouloubi",
8
+ "homepage": "https://github.com/peterbono/ai-design-system-demo/tree/main/packages/toolbar#readme",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/peterbono/ai-design-system-demo.git",
12
+ "directory": "packages/toolbar"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/peterbono/ai-design-system-demo/issues"
16
+ },
17
+ "keywords": [
18
+ "react",
19
+ "design-system",
20
+ "bridge",
21
+ "preview",
22
+ "before-after"
23
+ ],
24
+ "sideEffects": false,
25
+ "main": "./dist/index.js",
26
+ "types": "./dist/index.d.ts",
27
+ "exports": {
28
+ ".": {
29
+ "source": "./src/index.ts",
30
+ "types": "./dist/index.d.ts",
31
+ "default": "./dist/index.js"
32
+ },
33
+ "./together": {
34
+ "source": "./src/together.ts",
35
+ "types": "./dist/together.d.ts",
36
+ "default": "./dist/together.js"
37
+ },
38
+ "./package.json": "./package.json"
39
+ },
40
+ "files": [
41
+ "dist",
42
+ "README.md"
43
+ ],
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "scripts": {
48
+ "typecheck": "tsc --noEmit -p tsconfig.json",
49
+ "build:package": "node ../../scripts/build-packages.mjs toolbar"
50
+ },
51
+ "dependencies": {
52
+ "@bridge-ds/core": "^0.22.0",
53
+ "lucide-react": "1.58.0"
54
+ },
55
+ "peerDependencies": {
56
+ "@bridge-ds/react": ">=1.8.0",
57
+ "react": ">=18",
58
+ "react-dom": ">=18"
59
+ }
60
+ }