fontdue-js 3.0.6 → 3.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.
Files changed (61) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/__generated__/FeatureTesterCard_fontStyle.graphql.d.ts +23 -0
  3. package/dist/__generated__/FeatureTesterCard_fontStyle.graphql.js +62 -0
  4. package/dist/__generated__/FeatureTesterStandaloneQuery.graphql.d.ts +25 -0
  5. package/dist/__generated__/FeatureTesterStandaloneQuery.graphql.js +240 -0
  6. package/dist/__generated__/FeatureTestersIdQuery.graphql.d.ts +21 -0
  7. package/dist/__generated__/FeatureTestersIdQuery.graphql.js +344 -0
  8. package/dist/__generated__/FeatureTestersSlugQuery.graphql.d.ts +25 -0
  9. package/dist/__generated__/FeatureTestersSlugQuery.graphql.js +364 -0
  10. package/dist/__generated__/FeatureTesters_collection.graphql.d.ts +39 -0
  11. package/dist/__generated__/FeatureTesters_collection.graphql.js +145 -0
  12. package/dist/__generated__/NodePasswordFormAccessNodeMutation.graphql.d.ts +2 -1
  13. package/dist/__generated__/NodePasswordFormAccessNodeMutation.graphql.js +10 -4
  14. package/dist/__generated__/NodePasswordFormElementIDQuery.graphql.d.ts +21 -0
  15. package/dist/__generated__/NodePasswordFormElementIDQuery.graphql.js +86 -0
  16. package/dist/__generated__/NodePasswordFormElementSlugQuery.graphql.d.ts +25 -0
  17. package/dist/__generated__/NodePasswordFormElementSlugQuery.graphql.js +108 -0
  18. package/dist/__tests__/createFontdueFetch.test.js +128 -4
  19. package/dist/__tests__/highlight.test.js +141 -0
  20. package/dist/__tests__/middleware.test.js +132 -0
  21. package/dist/components/FeatureTester/FeatureTesterCard.d.ts +30 -0
  22. package/dist/components/FeatureTester/FeatureTesterCard.js +167 -0
  23. package/dist/components/FeatureTester/FeatureTesterElement.d.ts +19 -0
  24. package/dist/components/FeatureTester/FeatureTesterElement.js +42 -0
  25. package/dist/components/FeatureTester/FeatureTesters.d.ts +43 -0
  26. package/dist/components/FeatureTester/FeatureTesters.js +156 -0
  27. package/dist/components/FeatureTester/FeatureTesters.server.d.ts +9 -0
  28. package/dist/components/FeatureTester/FeatureTesters.server.js +40 -0
  29. package/dist/components/FeatureTester/FeatureTestersElement.d.ts +10 -0
  30. package/dist/components/FeatureTester/FeatureTestersElement.js +27 -0
  31. package/dist/components/FeatureTester/highlight.d.ts +19 -0
  32. package/dist/components/FeatureTester/highlight.js +71 -0
  33. package/dist/components/FeatureTester/index.d.ts +26 -0
  34. package/dist/components/FeatureTester/index.js +66 -0
  35. package/dist/components/FeatureTester/index.server.d.ts +7 -0
  36. package/dist/components/FeatureTester/index.server.js +30 -0
  37. package/dist/components/NodePasswordForm/NodePasswordFormElement.d.ts +3 -0
  38. package/dist/components/NodePasswordForm/NodePasswordFormElement.js +65 -0
  39. package/dist/components/NodePasswordForm/index.d.ts +16 -1
  40. package/dist/components/NodePasswordForm/index.js +30 -59
  41. package/dist/components/Root/index.js +2 -1
  42. package/dist/fontdue.css +145 -0
  43. package/dist/hooks/useFeatureTesterAutofit.d.ts +17 -0
  44. package/dist/hooks/useFeatureTesterAutofit.js +106 -0
  45. package/dist/next/index.d.ts +2 -0
  46. package/dist/next/index.js +16 -1
  47. package/dist/next/registerSingleTenantResolver.js +13 -1
  48. package/dist/next/tenant.d.ts +1 -0
  49. package/dist/next/tenant.js +27 -2
  50. package/dist/nodeAccess.d.ts +18 -0
  51. package/dist/nodeAccess.js +84 -0
  52. package/dist/preview/server.d.ts +1 -20
  53. package/dist/preview/server.js +16 -85
  54. package/dist/relay/environment.js +8 -2
  55. package/dist/relay/serverConfig.d.ts +3 -0
  56. package/dist/relay/serverConfig.js +22 -1
  57. package/dist/server/index.d.ts +8 -0
  58. package/dist/server/index.js +90 -7
  59. package/dist/server/middleware.d.ts +37 -0
  60. package/dist/server/middleware.js +144 -0
  61. package/package.json +8 -2
@@ -0,0 +1,167 @@
1
+ 'use client';
2
+
3
+ function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
4
+ import _FeatureTesterCard_fontStyle from "../../__generated__/FeatureTesterCard_fontStyle.graphql.js";
5
+ import React, { useMemo, useState } from 'react';
6
+ import { graphql, useFragment } from 'react-relay';
7
+ import FontStyle from '../FontStyle/index.js';
8
+ import useFeaturesData from '../TypeTester/useFeaturesData.js';
9
+ import useFeatureTesterAutofit from '../../hooks/useFeatureTesterAutofit.js';
10
+ import { sensitiveStringsForFeature, computeSensitivityFlags, segmentByFlags } from './highlight.js';
11
+ const fragment = (_FeatureTesterCard_fontStyle.hash && _FeatureTesterCard_fontStyle.hash !== "687d8afca93d615ed0794f963702d082" && console.error("The definition of 'FeatureTesterCard_fontStyle' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _FeatureTesterCard_fontStyle);
12
+ export default function FeatureTesterCard(_ref) {
13
+ let {
14
+ fontStyle: fontStyleKey,
15
+ feature,
16
+ content,
17
+ label,
18
+ fontSize = 72,
19
+ lineHeight = 1.2,
20
+ letterSpacing = 0,
21
+ alignment,
22
+ defaultOn = true,
23
+ highlightColor,
24
+ showLabel = true,
25
+ showTag = true,
26
+ toggle = 'button',
27
+ autofit = false,
28
+ changedRanges,
29
+ variableSettings
30
+ } = _ref;
31
+ // `data` carries the merged fragment spreads, so the child fragment hooks
32
+ // (FontStyle, useFeaturesData) read their refs off it rather than the raw key.
33
+ const data = useFragment(fragment, fontStyleKey);
34
+ const {
35
+ featureNames
36
+ } = useFeaturesData({
37
+ fontStyle: data
38
+ });
39
+ const [on, setOn] = useState(defaultOn);
40
+ const hover = toggle === 'hover';
41
+ const handleToggle = () => setOn(o => !o);
42
+
43
+ // The server's shaped diff (`changedRanges`) is exact for THIS string —
44
+ // context-free and contextual substitutions alike — so it wins whenever the
45
+ // collection materialized it. The static GSUB map is the fallback for cards
46
+ // with no shaped diff (no OpenType file server-side, or a hand-authored
47
+ // standalone embed); it only covers single + ligature substitutions, and for
48
+ // a mixed feature like frac it can hold just the slash while the real
49
+ // substitution is contextual over the digits — trusting it over the diff
50
+ // would highlight (and per-span apply) the wrong subset.
51
+ const staticStrings = useMemo(() => sensitiveStringsForFeature(data.glyphNames, feature), [data.glyphNames, feature]);
52
+ const contextFree = staticStrings.length > 0;
53
+ const changedMarks = useMemo(() => (changedRanges ?? []).map(r => ({
54
+ start: r.start,
55
+ end: r.start + r.length - 1
56
+ })), [changedRanges]);
57
+ const contextual = changedMarks.length > 0;
58
+ const segments = useMemo(() => segmentByFlags(content,
59
+ // A card with a shaped diff applies the feature container-wide (a
60
+ // per-span apply would sever the context contextual substitutions
61
+ // depend on), so the set that actually changes is exactly the diff —
62
+ // the highlight equals that set. The static-map fallback applies
63
+ // per-span.
64
+ contextual ? computeSensitivityFlags(content, {
65
+ addMarks: changedMarks
66
+ }) : computeSensitivityFlags(content, {
67
+ autoStrings: staticStrings
68
+ })), [contextual, content, staticStrings, changedMarks]);
69
+ const displayLabel = label || featureNames[feature] || feature;
70
+ const featureSetting = `"${feature}" ${on ? 1 : 0}`;
71
+
72
+ // The chosen variable instance's coordinates, as a font-variation-settings
73
+ // value. Applied to the sample and to the autofit measurement so both see
74
+ // the same glyph widths.
75
+ const fontVariationSettings = useMemo(() => variableSettings && variableSettings.length > 0 ? variableSettings.map(s => `"${s.axis}" ${s.value}`).join(', ') : undefined, [variableSettings]);
76
+
77
+ // A contextual feature with no shaped diff and no static entries (a
78
+ // hand-authored standalone embed) has nothing to tint, but the feature must
79
+ // still apply container-wide or the toggle would do nothing.
80
+ const anySensitive = segments.some(seg => seg.sensitive);
81
+ const containerWide = contextual || !contextFree && !anySensitive;
82
+
83
+ // Autofit measures the sample against the card's width in both toggle states
84
+ // and returns a size that fits the wider one; disabled, it returns `fontSize`.
85
+ const {
86
+ ref: rootRef,
87
+ fontSize: renderSize
88
+ } = useFeatureTesterAutofit({
89
+ enabled: autofit,
90
+ text: content,
91
+ fontFamily: `${data.cssFamily} ${data.name}`,
92
+ feature,
93
+ fontSize: fontSize ?? 72,
94
+ letterSpacing: letterSpacing ?? 0,
95
+ fontVariationSettings
96
+ });
97
+ return /*#__PURE__*/React.createElement("div", _extends({
98
+ ref: rootRef,
99
+ className: "fontdue-feature-tester",
100
+ "data-feature": feature,
101
+ "data-on": on,
102
+ "data-toggle": toggle
103
+ // Drives the muted-context styling: only a card with affected glyphs
104
+ // dims its surroundings, so an unmarked hand-authored sample stays at
105
+ // full text colour.
106
+ ,
107
+ "data-highlighted": anySensitive
108
+ }, hover ? {
109
+ onMouseEnter: () => setOn(!defaultOn),
110
+ onMouseLeave: () => setOn(defaultOn)
111
+ } : {}), showLabel && /*#__PURE__*/React.createElement("div", {
112
+ className: "fontdue-feature-tester__bar"
113
+ }, /*#__PURE__*/React.createElement("button", {
114
+ type: "button",
115
+ className: "fontdue-feature-tester__toggle",
116
+ role: "switch",
117
+ "aria-checked": on,
118
+ "aria-label": displayLabel,
119
+ onClick: handleToggle
120
+ }, /*#__PURE__*/React.createElement("span", {
121
+ className: "fontdue-feature-tester__switch",
122
+ "aria-hidden": "true"
123
+ })), /*#__PURE__*/React.createElement("span", {
124
+ className: "fontdue-feature-tester__label",
125
+ onClick: hover ? undefined : handleToggle
126
+ }, displayLabel), showTag && /*#__PURE__*/React.createElement("span", {
127
+ className: "fontdue-feature-tester__tag"
128
+ }, feature)), /*#__PURE__*/React.createElement(FontStyle, {
129
+ Component: "div",
130
+ fontStyle: data,
131
+ className: "fontdue-feature-tester__sample",
132
+ style: {
133
+ fontSize: `${renderSize}px`,
134
+ lineHeight: lineHeight ?? undefined,
135
+ letterSpacing: letterSpacing ? `${letterSpacing}em` : undefined,
136
+ textAlign: alignment ?? undefined,
137
+ fontVariationSettings,
138
+ // Contextual features must shape with full context, so the feature is
139
+ // applied to the whole line. The shaped diff guarantees only the
140
+ // highlighted characters actually change, so the highlight still
141
+ // means exactly "what changes".
142
+ ...(containerWide ? {
143
+ fontFeatureSettings: featureSetting
144
+ } : {})
145
+ }
146
+ }, segments.map((seg, i) => seg.sensitive ? /*#__PURE__*/React.createElement("span", {
147
+ key: i,
148
+ className: "fontdue-feature-tester__hit"
149
+ // Context-free features are applied per-highlighted-span, so
150
+ // toggling changes exactly what's tinted (and honours manual
151
+ // subsetting). `font-feature-settings` leaves unlisted (default)
152
+ // features alone, so default ligatures/kerning are preserved. The
153
+ // highlight colour comes from CSS (`--fontdue-feature-tester-
154
+ // highlight`); `highlightColor` forces it inline when set.
155
+ ,
156
+ style: {
157
+ ...(highlightColor ? {
158
+ color: highlightColor
159
+ } : {}),
160
+ ...(containerWide ? {} : {
161
+ fontFeatureSettings: featureSetting
162
+ })
163
+ }
164
+ }, seg.text) : /*#__PURE__*/React.createElement(React.Fragment, {
165
+ key: i
166
+ }, seg.text))));
167
+ }
@@ -0,0 +1,19 @@
1
+ import React from 'react';
2
+ interface FeatureTesterElement_props {
3
+ familyName: string;
4
+ styleName: string;
5
+ feature: string;
6
+ content?: string;
7
+ label?: string;
8
+ fontSize?: string;
9
+ lineHeight?: string;
10
+ letterSpacing?: string;
11
+ defaultOn?: string;
12
+ highlightColor?: string;
13
+ showLabel?: string;
14
+ showTag?: string;
15
+ toggle?: string;
16
+ autofit?: string;
17
+ }
18
+ export declare const FeatureTesterElement: React.FC<FeatureTesterElement_props>;
19
+ export default FeatureTesterElement;
@@ -0,0 +1,42 @@
1
+ // Deliberately unexposed: this standalone element is not registered in the
2
+ // Root custom-element registry and `fontdue-js/FeatureTester` is not in the
3
+ // package exports. Only the collection-bound `<fontdue-feature-testers>`
4
+ // ships for now; this file is kept for when the standalone is ready.
5
+ import React from 'react';
6
+ import FeatureTester from './index.js';
7
+ const parseBool = (input, fallback) => input === undefined || input === '' ? fallback : input === 'true';
8
+ export const FeatureTesterElement = _ref => {
9
+ let {
10
+ familyName,
11
+ styleName,
12
+ feature,
13
+ content,
14
+ label,
15
+ fontSize,
16
+ lineHeight,
17
+ letterSpacing,
18
+ defaultOn,
19
+ highlightColor,
20
+ showLabel,
21
+ showTag,
22
+ toggle,
23
+ autofit
24
+ } = _ref;
25
+ return /*#__PURE__*/React.createElement(FeatureTester, {
26
+ familyName: familyName,
27
+ styleName: styleName,
28
+ feature: feature,
29
+ content: content || 'Hamburgefonts',
30
+ label: label || undefined,
31
+ fontSize: fontSize ? parseFloat(fontSize) : undefined,
32
+ lineHeight: lineHeight ? parseFloat(lineHeight) : undefined,
33
+ letterSpacing: letterSpacing ? parseFloat(letterSpacing) : undefined,
34
+ defaultOn: parseBool(defaultOn, true),
35
+ highlightColor: highlightColor || undefined,
36
+ showLabel: parseBool(showLabel, true),
37
+ showTag: parseBool(showTag, true),
38
+ toggle: toggle === 'hover' ? 'hover' : 'button',
39
+ autofit: parseBool(autofit, false)
40
+ });
41
+ };
42
+ export default FeatureTesterElement;
@@ -0,0 +1,43 @@
1
+ import React from 'react';
2
+ import { Config } from '../ConfigContext.js';
3
+ import { FeatureTestersIdQuery } from '../../__generated__/FeatureTestersIdQuery.graphql.js';
4
+ import { FeatureTestersSlugQuery } from '../../__generated__/FeatureTestersSlugQuery.graphql.js';
5
+ import { ToggleMode } from './FeatureTesterCard.js';
6
+ import { SerializablePreloadedQuery, type LoadQueryOptions } from '../../relay/loadSerializableQuery.js';
7
+ interface ListOptions {
8
+ highlightColor?: string;
9
+ toggle?: ToggleMode;
10
+ autofit?: boolean;
11
+ }
12
+ export type FeatureTestersPreloadedQuery = SerializablePreloadedQuery<FeatureTestersIdQuery> | SerializablePreloadedQuery<FeatureTestersSlugQuery>;
13
+ export type LoadFeatureTestersQueryVariables = {
14
+ collectionId: string;
15
+ collectionSlug?: never;
16
+ } | {
17
+ collectionId?: never;
18
+ collectionSlug: string;
19
+ };
20
+ export declare function loadFeatureTestersQuery(variables: LoadFeatureTestersQueryVariables, options?: LoadQueryOptions): Promise<FeatureTestersPreloadedQuery>;
21
+ export declare function FeatureTestersPreloadedIDQueryRenderer({ preloadedQuery, ...options }: {
22
+ preloadedQuery: SerializablePreloadedQuery<FeatureTestersIdQuery>;
23
+ } & ListOptions): React.JSX.Element | null;
24
+ export declare function FeatureTestersPreloadedSlugQueryRenderer({ preloadedQuery, ...options }: {
25
+ preloadedQuery: SerializablePreloadedQuery<FeatureTestersSlugQuery>;
26
+ } & ListOptions): React.JSX.Element | null;
27
+ export type FeatureTestersProps = {
28
+ config?: Config;
29
+ } & ListOptions & ({
30
+ preloadedQuery: FeatureTestersPreloadedQuery;
31
+ collectionId?: never;
32
+ collectionSlug?: never;
33
+ } | {
34
+ preloadedQuery?: never;
35
+ collectionId: string;
36
+ collectionSlug?: never;
37
+ } | {
38
+ preloadedQuery?: never;
39
+ collectionSlug: string;
40
+ collectionId?: never;
41
+ });
42
+ export default function FeatureTesters({ config, ...props }: FeatureTestersProps): React.JSX.Element;
43
+ export {};
@@ -0,0 +1,156 @@
1
+ 'use client';
2
+
3
+ function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
4
+ import _FeatureTestersSlugQuery from "../../__generated__/FeatureTestersSlugQuery.graphql.js";
5
+ import _FeatureTestersIdQuery from "../../__generated__/FeatureTestersIdQuery.graphql.js";
6
+ import _FeatureTesters_collection from "../../__generated__/FeatureTesters_collection.graphql.js";
7
+ import React from 'react';
8
+ import { graphql, useFragment, useLazyLoadQuery, usePreloadedQuery } from 'react-relay';
9
+ import FeatureTesterCard from './FeatureTesterCard.js';
10
+ import { EnsureFontdueContext } from '../FontdueContextProvider/index.js';
11
+ import FeatureTestersIdQueryNode from '../../__generated__/FeatureTestersIdQuery.graphql.js';
12
+ import FeatureTestersSlugQueryNode from '../../__generated__/FeatureTestersSlugQuery.graphql.js';
13
+ import loadSerializableQuery from '../../relay/loadSerializableQuery.js';
14
+ import useSerializablePreloadedQuery from '../../relay/useSerializablePreloadedQuery.js';
15
+ const collectionFragment = (_FeatureTesters_collection.hash && _FeatureTesters_collection.hash !== "933c122b193ca5df430509d87e9c4e1d" && console.error("The definition of 'FeatureTesters_collection' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _FeatureTesters_collection);
16
+ function FeatureTestersList(_ref) {
17
+ let {
18
+ collection,
19
+ highlightColor,
20
+ toggle,
21
+ autofit
22
+ } = _ref;
23
+ const data = useFragment(collectionFragment, collection);
24
+ return /*#__PURE__*/React.createElement(React.Fragment, null, data.featureTesters.map(ft => ft.fontStyle ? /*#__PURE__*/React.createElement(FeatureTesterCard, {
25
+ key: ft.id,
26
+ fontStyle: ft.fontStyle,
27
+ feature: ft.feature,
28
+ label: ft.label ?? undefined,
29
+ content: ft.content,
30
+ fontSize: ft.size != null ? Number(ft.size) : undefined,
31
+ lineHeight: ft.lineHeight != null ? Number(ft.lineHeight) : undefined,
32
+ alignment: ft.alignment === 'left' || ft.alignment === 'center' || ft.alignment === 'right' ? ft.alignment : undefined,
33
+ defaultOn: data.featureTesterDefaultOn,
34
+ changedRanges: ft.changedRanges,
35
+ variableSettings: ft.variableSettings,
36
+ highlightColor: highlightColor,
37
+ toggle: toggle,
38
+ autofit: autofit ?? ft.autofit,
39
+ showTag: data.featureTesterShowTag
40
+ }) : null));
41
+ }
42
+ const idQuery = (_FeatureTestersIdQuery.hash && _FeatureTestersIdQuery.hash !== "55f152ae85db4ebafd21ff7945f4079a" && console.error("The definition of 'FeatureTestersIdQuery' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _FeatureTestersIdQuery);
43
+ const slugQuery = (_FeatureTestersSlugQuery.hash && _FeatureTestersSlugQuery.hash !== "726fca18c4a30ca9dd8f5aaf074c8139" && console.error("The definition of 'FeatureTestersSlugQuery' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _FeatureTestersSlugQuery);
44
+ function ById(_ref2) {
45
+ let {
46
+ collectionId,
47
+ ...rest
48
+ } = _ref2;
49
+ const data = useLazyLoadQuery(idQuery, {
50
+ id: collectionId
51
+ });
52
+ return data.node ? /*#__PURE__*/React.createElement(FeatureTestersList, _extends({
53
+ collection: data.node
54
+ }, rest)) : null;
55
+ }
56
+ function BySlug(_ref3) {
57
+ var _data$viewer, _data$viewer$slug;
58
+ let {
59
+ collectionSlug,
60
+ ...rest
61
+ } = _ref3;
62
+ const data = useLazyLoadQuery(slugQuery, {
63
+ slug: collectionSlug
64
+ });
65
+ const coll = (_data$viewer = data.viewer) === null || _data$viewer === void 0 ? void 0 : (_data$viewer$slug = _data$viewer.slug) === null || _data$viewer$slug === void 0 ? void 0 : _data$viewer$slug.fontCollection;
66
+ return coll ? /*#__PURE__*/React.createElement(FeatureTestersList, _extends({
67
+ collection: coll
68
+ }, rest)) : null;
69
+ }
70
+
71
+ // ---- Server-preload path (RSC): the server awaits the query and hands the
72
+ // serialized response to these client renderers, which commit it into the store
73
+ // so `usePreloadedQuery` resolves synchronously on the client (no refetch on
74
+ // hydration). Mirrors CharacterViewer.
75
+
76
+ export async function loadFeatureTestersQuery(variables, options) {
77
+ if (variables.collectionId) {
78
+ return loadSerializableQuery(FeatureTestersIdQueryNode, {
79
+ id: variables.collectionId
80
+ }, options);
81
+ }
82
+ if (variables.collectionSlug) {
83
+ return loadSerializableQuery(FeatureTestersSlugQueryNode, {
84
+ slug: variables.collectionSlug
85
+ }, options);
86
+ }
87
+ throw new Error('loadFeatureTestersQuery expected either collectionId or collectionSlug');
88
+ }
89
+ export function FeatureTestersPreloadedIDQueryRenderer(_ref4) {
90
+ let {
91
+ preloadedQuery,
92
+ ...options
93
+ } = _ref4;
94
+ const queryRef = useSerializablePreloadedQuery(preloadedQuery, 'store-or-network', idQuery);
95
+ const data = usePreloadedQuery(idQuery, queryRef);
96
+ return data.node ? /*#__PURE__*/React.createElement(FeatureTestersList, _extends({
97
+ collection: data.node
98
+ }, options)) : null;
99
+ }
100
+ export function FeatureTestersPreloadedSlugQueryRenderer(_ref5) {
101
+ var _data$viewer2, _data$viewer2$slug;
102
+ let {
103
+ preloadedQuery,
104
+ ...options
105
+ } = _ref5;
106
+ const queryRef = useSerializablePreloadedQuery(preloadedQuery, 'store-or-network', slugQuery);
107
+ const data = usePreloadedQuery(slugQuery, queryRef);
108
+ const coll = (_data$viewer2 = data.viewer) === null || _data$viewer2 === void 0 ? void 0 : (_data$viewer2$slug = _data$viewer2.slug) === null || _data$viewer2$slug === void 0 ? void 0 : _data$viewer2$slug.fontCollection;
109
+ return coll ? /*#__PURE__*/React.createElement(FeatureTestersList, _extends({
110
+ collection: coll
111
+ }, options)) : null;
112
+ }
113
+ function FeatureTestersPreloadedRenderer(_ref6) {
114
+ let {
115
+ preloadedQuery,
116
+ ...options
117
+ } = _ref6;
118
+ if (preloadedQuery.params.name === 'FeatureTestersIdQuery') {
119
+ return /*#__PURE__*/React.createElement(FeatureTestersPreloadedIDQueryRenderer, _extends({
120
+ preloadedQuery: preloadedQuery
121
+ }, options));
122
+ }
123
+ return /*#__PURE__*/React.createElement(FeatureTestersPreloadedSlugQueryRenderer, _extends({
124
+ preloadedQuery: preloadedQuery
125
+ }, options));
126
+ }
127
+
128
+ // Renders every Feature Tester card a tenant has configured on a font
129
+ // collection. Accepts a preloaded query (server-side preload path — Astro/RR7
130
+ // frontmatter or a manual loader call) or `{collectionId}`/`{collectionSlug}`
131
+ // for lazy fetch.
132
+ export default function FeatureTesters(_ref7) {
133
+ let {
134
+ config,
135
+ ...props
136
+ } = _ref7;
137
+ const {
138
+ highlightColor,
139
+ toggle,
140
+ autofit
141
+ } = props;
142
+ const options = {
143
+ highlightColor,
144
+ toggle,
145
+ autofit
146
+ };
147
+ return /*#__PURE__*/React.createElement(EnsureFontdueContext, {
148
+ config: config
149
+ }, 'preloadedQuery' in props && props.preloadedQuery ? /*#__PURE__*/React.createElement(FeatureTestersPreloadedRenderer, _extends({
150
+ preloadedQuery: props.preloadedQuery
151
+ }, options)) : 'collectionId' in props && props.collectionId ? /*#__PURE__*/React.createElement(ById, _extends({
152
+ collectionId: props.collectionId
153
+ }, options)) : /*#__PURE__*/React.createElement(BySlug, _extends({
154
+ collectionSlug: props.collectionSlug
155
+ }, options)));
156
+ }
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import { FeatureTestersProps } from './FeatureTesters.js';
3
+ export type { FeatureTestersPreloadedQuery } from './FeatureTesters.js';
4
+ export declare function loadFeatureTestersQuery(): never;
5
+ export default function FeatureTesters({ collectionId, collectionSlug, ...rest }: Extract<FeatureTestersProps, {
6
+ collectionId: string;
7
+ } | {
8
+ collectionSlug: string;
9
+ }>): Promise<React.JSX.Element | null>;
@@ -0,0 +1,40 @@
1
+ function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
2
+ import React from 'react';
3
+ import loadSerializableQuery from '../../relay/loadSerializableQuery.js';
4
+ import { FeatureTestersPreloadedIDQueryRenderer, FeatureTestersPreloadedSlugQueryRenderer } from './FeatureTesters.js';
5
+ import FeatureTestersIdQueryNode from '../../__generated__/FeatureTestersIdQuery.graphql.js';
6
+ import FeatureTestersSlugQueryNode from '../../__generated__/FeatureTestersSlugQuery.graphql.js';
7
+ // Stub for the RSC export condition. See FontdueProvider/index.server.tsx
8
+ // for the rationale.
9
+ export function loadFeatureTestersQuery() {
10
+ throw new Error("loadFeatureTestersQuery isn't needed in React Server Components — " + 'the <FeatureTesters> server entrypoint awaits its query automatically. ' + 'Drop the manual call.');
11
+ }
12
+
13
+ // Next RSC variant: awaits the query on the server and hands the response to
14
+ // the client `*PreloadedQueryRenderer`. Lets pages just write
15
+ // `<FeatureTesters collectionSlug="..." />` in an RSC without manually invoking
16
+ // `loadFeatureTestersQuery` + threading `preloadedQuery` through props.
17
+ export default async function FeatureTesters(_ref) {
18
+ let {
19
+ collectionId,
20
+ collectionSlug,
21
+ ...rest
22
+ } = _ref;
23
+ if (collectionId) {
24
+ const preloadedQuery = await loadSerializableQuery(FeatureTestersIdQueryNode, {
25
+ id: collectionId
26
+ });
27
+ return /*#__PURE__*/React.createElement(FeatureTestersPreloadedIDQueryRenderer, _extends({
28
+ preloadedQuery: preloadedQuery
29
+ }, rest));
30
+ }
31
+ if (collectionSlug) {
32
+ const preloadedQuery = await loadSerializableQuery(FeatureTestersSlugQueryNode, {
33
+ slug: collectionSlug
34
+ });
35
+ return /*#__PURE__*/React.createElement(FeatureTestersPreloadedSlugQueryRenderer, _extends({
36
+ preloadedQuery: preloadedQuery
37
+ }, rest));
38
+ }
39
+ return null;
40
+ }
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ interface FeatureTestersElement_props {
3
+ collectionId?: string;
4
+ collectionSlug?: string;
5
+ highlightColor?: string;
6
+ toggle?: string;
7
+ autofit?: string;
8
+ }
9
+ export declare const FeatureTestersElement: React.FC<FeatureTestersElement_props>;
10
+ export default FeatureTestersElement;
@@ -0,0 +1,27 @@
1
+ function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
2
+ import React from 'react';
3
+ import FeatureTesters from './FeatureTesters.js';
4
+ const parseBool = (input, fallback) => input === undefined || input === '' ? fallback : input === 'true';
5
+ export const FeatureTestersElement = _ref => {
6
+ let {
7
+ collectionId,
8
+ collectionSlug,
9
+ highlightColor,
10
+ toggle,
11
+ autofit
12
+ } = _ref;
13
+ const shared = {
14
+ highlightColor: highlightColor || undefined,
15
+ // Only override the per-card dashboard settings when the attribute is
16
+ // actually present on the element; otherwise leave undefined so each card
17
+ // uses its own.
18
+ toggle: toggle === undefined ? undefined : toggle === 'hover' ? 'hover' : 'button',
19
+ autofit: autofit === undefined ? undefined : parseBool(autofit, false)
20
+ };
21
+ return collectionId ? /*#__PURE__*/React.createElement(FeatureTesters, _extends({
22
+ collectionId: collectionId
23
+ }, shared)) : /*#__PURE__*/React.createElement(FeatureTesters, _extends({
24
+ collectionSlug: collectionSlug || ''
25
+ }, shared));
26
+ };
27
+ export default FeatureTestersElement;
@@ -0,0 +1,19 @@
1
+ export interface GlyphNameEntry {
2
+ readonly characters: string;
3
+ readonly features?: readonly string[] | null;
4
+ }
5
+ export interface Segment {
6
+ text: string;
7
+ sensitive: boolean;
8
+ }
9
+ export interface Mark {
10
+ start: number;
11
+ end: number;
12
+ }
13
+ export declare function sensitiveStringsForFeature(glyphNames: readonly GlyphNameEntry[] | null | undefined, feature: string): string[];
14
+ export interface SensitivityOptions {
15
+ autoStrings?: readonly string[];
16
+ addMarks?: readonly Mark[];
17
+ }
18
+ export declare function computeSensitivityFlags(content: string, opts: SensitivityOptions): boolean[];
19
+ export declare function segmentByFlags(content: string, flags: readonly boolean[]): Segment[];
@@ -0,0 +1,71 @@
1
+ // Decide which characters in a sample string are "sensitive" to an OpenType
2
+ // feature.
3
+ //
4
+ // Auto-detection: `FontStyle.glyphNames` lists `{ characters, features }` where
5
+ // `characters` is the INPUT string a feature substitutes (a single character
6
+ // for single substitutions like ss03/smcp, or a sequence for ligatures like
7
+ // dlig). "Which characters does feature X touch" is the set of `characters`
8
+ // whose `features` includes X. That static map only covers single + ligature
9
+ // substitutions; the exact set for any feature comes from the server's shaped
10
+ // diff (`changedRanges`), which FeatureTesterCard passes in as marks.
11
+ //
12
+ // Everything is computed as a boolean-per-codepoint array so the sources
13
+ // compose cleanly. A contiguous run of `true` becomes one highlight span, which
14
+ // keeps ligature clusters whole (we never split a cluster across spans).
15
+
16
+ // An inclusive code-point index range. FeatureTesterCard builds these from the
17
+ // server-materialized `changedRanges`.
18
+
19
+ // The de-duplicated set of input strings the feature substitutes.
20
+ export function sensitiveStringsForFeature(glyphNames, feature) {
21
+ if (!glyphNames) return [];
22
+ const set = new Set();
23
+ for (const g of glyphNames) {
24
+ if (g.characters && g.features && g.features.includes(feature)) {
25
+ set.add(g.characters);
26
+ }
27
+ }
28
+ return Array.from(set);
29
+ }
30
+
31
+ // Set flags[i] = true for every occurrence of `value` (matched by code point).
32
+ function applyText(chars, value, flags) {
33
+ const seq = Array.from(value);
34
+ if (seq.length === 0) return;
35
+ for (let i = 0; i + seq.length <= chars.length; i++) {
36
+ if (seq.every((c, k) => chars[i + k] === c)) {
37
+ for (let k = 0; k < seq.length; k++) flags[i + k] = true;
38
+ }
39
+ }
40
+ }
41
+ export function computeSensitivityFlags(content, opts) {
42
+ const chars = Array.from(content);
43
+ const flags = new Array(chars.length).fill(false);
44
+ if (opts.autoStrings) {
45
+ for (const value of opts.autoStrings) applyText(chars, value, flags);
46
+ }
47
+ if (opts.addMarks) {
48
+ for (const mark of opts.addMarks) {
49
+ for (let i = Math.max(0, mark.start); i <= mark.end && i < chars.length; i++) {
50
+ flags[i] = true;
51
+ }
52
+ }
53
+ }
54
+ return flags;
55
+ }
56
+
57
+ // Collapse a per-codepoint flag array into consecutive sensitive/non-sensitive
58
+ // runs (one highlight span each).
59
+ export function segmentByFlags(content, flags) {
60
+ const chars = Array.from(content);
61
+ const segments = [];
62
+ for (let i = 0; i < chars.length; i++) {
63
+ const sensitive = flags[i] ?? false;
64
+ const last = segments[segments.length - 1];
65
+ if (last && last.sensitive === sensitive) last.text += chars[i];else segments.push({
66
+ text: chars[i],
67
+ sensitive
68
+ });
69
+ }
70
+ return segments;
71
+ }
@@ -0,0 +1,26 @@
1
+ import React from 'react';
2
+ import { FeatureTesterCardOptions } from './FeatureTesterCard.js';
3
+ import { Config } from '../ConfigContext.js';
4
+ import { SerializablePreloadedQuery, type LoadQueryOptions } from '../../relay/loadSerializableQuery.js';
5
+ import { FeatureTesterStandaloneQuery } from '../../__generated__/FeatureTesterStandaloneQuery.graphql.js';
6
+ export type FeatureTesterPreloadedQuery = SerializablePreloadedQuery<FeatureTesterStandaloneQuery>;
7
+ export interface LoadFeatureTesterQueryVariables {
8
+ familyName: string;
9
+ styleName: string;
10
+ }
11
+ export declare function loadFeatureTesterQuery(variables: LoadFeatureTesterQueryVariables, options?: LoadQueryOptions): Promise<FeatureTesterPreloadedQuery>;
12
+ export declare function FeatureTesterPreloadedRenderer({ preloadedQuery, ...rest }: FeatureTesterCardOptions & {
13
+ preloadedQuery: FeatureTesterPreloadedQuery;
14
+ }): React.JSX.Element;
15
+ export type FeatureTesterProps = FeatureTesterCardOptions & {
16
+ config?: Config;
17
+ } & ({
18
+ preloadedQuery: FeatureTesterPreloadedQuery;
19
+ familyName?: never;
20
+ styleName?: never;
21
+ } | {
22
+ preloadedQuery?: never;
23
+ familyName: string;
24
+ styleName: string;
25
+ });
26
+ export default function FeatureTester(props: FeatureTesterProps): React.JSX.Element;