@contractspec/bundle.marketing 3.8.8 → 3.8.10

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 (96) hide show
  1. package/.turbo/turbo-build.log +73 -29
  2. package/CHANGELOG.md +63 -0
  3. package/dist/browser/components/templates/TemplateCard.js +83 -0
  4. package/dist/browser/components/templates/TemplateCommandDialog.js +110 -0
  5. package/dist/browser/components/templates/TemplatePreviewContent.js +96 -0
  6. package/dist/browser/components/templates/TemplatesBrowseControls.js +130 -0
  7. package/dist/browser/components/templates/TemplatesCatalogSection.js +307 -0
  8. package/dist/browser/components/templates/TemplatesClientPage.js +1020 -917
  9. package/dist/browser/components/templates/TemplatesHeroSection.js +87 -0
  10. package/dist/browser/components/templates/TemplatesNextStepsSection.js +126 -0
  11. package/dist/browser/components/templates/TemplatesOverlays.js +2874 -0
  12. package/dist/browser/components/templates/TemplatesPreviewModal.js +136 -126
  13. package/dist/browser/components/templates/index.js +1055 -952
  14. package/dist/browser/components/templates/template-catalog.js +83 -0
  15. package/dist/browser/components/templates/template-filters.js +99 -0
  16. package/dist/browser/components/templates/template-new.js +23 -0
  17. package/dist/browser/components/templates/template-preview.js +43 -0
  18. package/dist/browser/components/templates/template-source.js +19 -0
  19. package/dist/browser/components/templates/template-tag-visibility.js +40 -0
  20. package/dist/browser/components/templates/useTemplateBrowseState.js +191 -0
  21. package/dist/browser/index.js +1055 -952
  22. package/dist/components/templates/TemplateCard.d.ts +12 -0
  23. package/dist/components/templates/TemplateCard.js +78 -0
  24. package/dist/components/templates/TemplateCommandDialog.d.ts +6 -0
  25. package/dist/components/templates/TemplateCommandDialog.js +105 -0
  26. package/dist/components/templates/TemplatePreviewContent.d.ts +5 -0
  27. package/dist/components/templates/TemplatePreviewContent.js +91 -0
  28. package/dist/components/templates/TemplatesBrowseControls.d.ts +18 -0
  29. package/dist/components/templates/TemplatesBrowseControls.js +125 -0
  30. package/dist/components/templates/TemplatesCatalogSection.d.ts +17 -0
  31. package/dist/components/templates/TemplatesCatalogSection.js +302 -0
  32. package/dist/components/templates/TemplatesClientPage.js +1020 -917
  33. package/dist/components/templates/TemplatesHeroSection.d.ts +5 -0
  34. package/dist/components/templates/TemplatesHeroSection.js +82 -0
  35. package/dist/components/templates/TemplatesNextStepsSection.d.ts +1 -0
  36. package/dist/components/templates/TemplatesNextStepsSection.js +121 -0
  37. package/dist/components/templates/TemplatesOverlays.d.ts +10 -0
  38. package/dist/components/templates/TemplatesOverlays.js +2869 -0
  39. package/dist/components/templates/TemplatesPreviewModal.d.ts +3 -4
  40. package/dist/components/templates/TemplatesPreviewModal.js +136 -126
  41. package/dist/components/templates/index.js +1055 -952
  42. package/dist/components/templates/template-catalog.d.ts +28 -0
  43. package/dist/components/templates/template-catalog.js +78 -0
  44. package/dist/components/templates/template-catalog.test.d.ts +1 -0
  45. package/dist/components/templates/template-filters.d.ts +12 -0
  46. package/dist/components/templates/template-filters.js +94 -0
  47. package/dist/components/templates/template-new.d.ts +2 -0
  48. package/dist/components/templates/template-new.js +18 -0
  49. package/dist/components/templates/template-preview.d.ts +18 -0
  50. package/dist/components/templates/template-preview.js +38 -0
  51. package/dist/components/templates/template-source.d.ts +3 -0
  52. package/dist/components/templates/template-source.js +14 -0
  53. package/dist/components/templates/template-tag-visibility.d.ts +10 -0
  54. package/dist/components/templates/template-tag-visibility.js +35 -0
  55. package/dist/components/templates/useTemplateBrowseState.d.ts +22 -0
  56. package/dist/components/templates/useTemplateBrowseState.js +186 -0
  57. package/dist/index.js +1055 -952
  58. package/dist/node/components/templates/TemplateCard.js +78 -0
  59. package/dist/node/components/templates/TemplateCommandDialog.js +105 -0
  60. package/dist/node/components/templates/TemplatePreviewContent.js +91 -0
  61. package/dist/node/components/templates/TemplatesBrowseControls.js +125 -0
  62. package/dist/node/components/templates/TemplatesCatalogSection.js +302 -0
  63. package/dist/node/components/templates/TemplatesClientPage.js +1020 -917
  64. package/dist/node/components/templates/TemplatesHeroSection.js +82 -0
  65. package/dist/node/components/templates/TemplatesNextStepsSection.js +121 -0
  66. package/dist/node/components/templates/TemplatesOverlays.js +2869 -0
  67. package/dist/node/components/templates/TemplatesPreviewModal.js +136 -126
  68. package/dist/node/components/templates/index.js +1055 -952
  69. package/dist/node/components/templates/template-catalog.js +78 -0
  70. package/dist/node/components/templates/template-filters.js +94 -0
  71. package/dist/node/components/templates/template-new.js +18 -0
  72. package/dist/node/components/templates/template-preview.js +38 -0
  73. package/dist/node/components/templates/template-source.js +14 -0
  74. package/dist/node/components/templates/template-tag-visibility.js +35 -0
  75. package/dist/node/components/templates/useTemplateBrowseState.js +186 -0
  76. package/dist/node/index.js +1055 -952
  77. package/package.json +237 -26
  78. package/src/components/templates/TemplateCard.tsx +74 -0
  79. package/src/components/templates/TemplateCommandDialog.tsx +92 -0
  80. package/src/components/templates/TemplatePreviewContent.tsx +182 -0
  81. package/src/components/templates/TemplatesBrowseControls.tsx +144 -0
  82. package/src/components/templates/TemplatesCatalogSection.tsx +191 -0
  83. package/src/components/templates/TemplatesClientPage.tsx +85 -773
  84. package/src/components/templates/TemplatesHeroSection.tsx +41 -0
  85. package/src/components/templates/TemplatesNextStepsSection.tsx +80 -0
  86. package/src/components/templates/TemplatesOverlays.tsx +65 -0
  87. package/src/components/templates/TemplatesPreviewModal.tsx +19 -294
  88. package/src/components/templates/template-catalog.test.ts +162 -0
  89. package/src/components/templates/template-catalog.ts +140 -0
  90. package/src/components/templates/template-filters.ts +57 -0
  91. package/src/components/templates/template-new.ts +12 -0
  92. package/src/components/templates/template-preview.ts +57 -0
  93. package/src/components/templates/template-source.ts +13 -0
  94. package/src/components/templates/template-tag-visibility.ts +58 -0
  95. package/src/components/templates/useTemplateBrowseState.ts +101 -0
  96. package/.turbo/turbo-prebuild.log +0 -1
@@ -0,0 +1,140 @@
1
+ import type {
2
+ ExampleKind,
3
+ ExampleSandboxMode,
4
+ ExampleSpec,
5
+ } from '@contractspec/lib.contracts-spec/examples/types';
6
+ import type { Stability } from '@contractspec/lib.contracts-spec/ownership';
7
+ import type {
8
+ TemplateDefinition,
9
+ TemplateId,
10
+ } from '@contractspec/lib.example-shared-ui';
11
+ import { listExamples, listTemplates } from '@contractspec/module.examples';
12
+ import { isNewTemplateId, NEW_TEMPLATE_IDS } from './template-new';
13
+
14
+ export interface LocalTemplateCatalogItem {
15
+ id: TemplateId;
16
+ title: string;
17
+ description: string;
18
+ tags: string[];
19
+ kind: ExampleKind;
20
+ stability: Stability;
21
+ previewUrl: string;
22
+ featureList: string[];
23
+ sandboxModes: readonly ExampleSandboxMode[];
24
+ renderTargets: string[];
25
+ isNew: boolean;
26
+ packageName: string;
27
+ }
28
+
29
+ interface TemplateFilterCandidate {
30
+ title: string;
31
+ description: string;
32
+ tags: readonly string[];
33
+ }
34
+
35
+ const NEW_TEMPLATE_INDEX = new Map<string, number>(
36
+ NEW_TEMPLATE_IDS.map((templateId, index) => [templateId, index] as const)
37
+ );
38
+
39
+ export function buildLocalTemplateCatalog(
40
+ examples: readonly ExampleSpec[] = listExamples(),
41
+ templates: readonly TemplateDefinition[] = listTemplates()
42
+ ): LocalTemplateCatalogItem[] {
43
+ const templatesById = new Map(
44
+ templates.map((template) => [template.id, template])
45
+ );
46
+
47
+ return examples
48
+ .filter(
49
+ (example) =>
50
+ example.meta.visibility === 'public' && example.surfaces.templates
51
+ )
52
+ .map((example) => {
53
+ const template = templatesById.get(example.meta.key);
54
+ const tags = Array.from(
55
+ new Set(example.meta.tags.map((tag) => tag.trim()).filter(Boolean))
56
+ ).sort((left, right) => left.localeCompare(right));
57
+
58
+ return {
59
+ id: example.meta.key,
60
+ title: example.meta.title ?? template?.name ?? example.meta.key,
61
+ description: example.meta.summary ?? example.meta.description,
62
+ tags,
63
+ kind: example.meta.kind,
64
+ stability: example.meta.stability,
65
+ previewUrl:
66
+ template?.preview?.demoUrl ??
67
+ `/sandbox?template=${encodeURIComponent(example.meta.key)}`,
68
+ featureList: [...(template?.features ?? [])],
69
+ sandboxModes: example.surfaces.sandbox.modes,
70
+ renderTargets: [...(template?.renderTargets ?? [])],
71
+ isNew: isNewTemplateId(example.meta.key),
72
+ packageName: example.entrypoints.packageName,
73
+ };
74
+ })
75
+ .sort(compareLocalTemplateCatalogItems);
76
+ }
77
+
78
+ export function matchesTemplateFilters(
79
+ template: TemplateFilterCandidate,
80
+ search: string,
81
+ selectedTag: string | null
82
+ ): boolean {
83
+ return (
84
+ matchesTemplateSearch(template, search) &&
85
+ (selectedTag === null || template.tags.includes(selectedTag))
86
+ );
87
+ }
88
+
89
+ export function matchesTemplateSearch(
90
+ template: TemplateFilterCandidate,
91
+ search: string
92
+ ): boolean {
93
+ const haystack = [
94
+ template.title,
95
+ template.description,
96
+ template.tags.join(' '),
97
+ ]
98
+ .join(' ')
99
+ .toLowerCase();
100
+ const searchTokens = search.trim().toLowerCase().split(/\s+/).filter(Boolean);
101
+
102
+ return (
103
+ searchTokens.length === 0 ||
104
+ searchTokens.every((token) => haystack.includes(token))
105
+ );
106
+ }
107
+
108
+ export function formatExampleKindLabel(kind: ExampleKind): string {
109
+ return kind
110
+ .split('-')
111
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
112
+ .join(' ');
113
+ }
114
+
115
+ export function formatStabilityLabel(stability: Stability): string {
116
+ return stability
117
+ .split('_')
118
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
119
+ .join(' ');
120
+ }
121
+
122
+ function compareLocalTemplateCatalogItems(
123
+ left: LocalTemplateCatalogItem,
124
+ right: LocalTemplateCatalogItem
125
+ ): number {
126
+ const leftNewIndex = NEW_TEMPLATE_INDEX.get(left.id);
127
+ const rightNewIndex = NEW_TEMPLATE_INDEX.get(right.id);
128
+
129
+ if (leftNewIndex !== undefined || rightNewIndex !== undefined) {
130
+ if (leftNewIndex === undefined) {
131
+ return 1;
132
+ }
133
+ if (rightNewIndex === undefined) {
134
+ return -1;
135
+ }
136
+ return leftNewIndex - rightNewIndex;
137
+ }
138
+
139
+ return left.title.localeCompare(right.title);
140
+ }
@@ -0,0 +1,57 @@
1
+ import { matchesTemplateSearch } from './template-catalog';
2
+ import type { TemplateTagFacet } from './template-tag-visibility';
3
+
4
+ export interface TemplateFilterCandidate {
5
+ title: string;
6
+ description: string;
7
+ tags: readonly string[];
8
+ }
9
+
10
+ export interface TemplateFilterState<TTemplate> {
11
+ searchScopedTemplates: TTemplate[];
12
+ finalTemplates: TTemplate[];
13
+ tagFacets: TemplateTagFacet[];
14
+ }
15
+
16
+ export function buildTemplateFilterState<TTemplate>(
17
+ templates: readonly TTemplate[],
18
+ search: string,
19
+ selectedTag: string | null,
20
+ getCandidate: (template: TTemplate) => TemplateFilterCandidate
21
+ ): TemplateFilterState<TTemplate> {
22
+ const searchScopedTemplates = templates.filter((template) =>
23
+ matchesTemplateSearch(getCandidate(template), search)
24
+ );
25
+ const finalTemplates =
26
+ selectedTag === null
27
+ ? searchScopedTemplates
28
+ : searchScopedTemplates.filter((template) =>
29
+ getCandidate(template).tags.includes(selectedTag)
30
+ );
31
+
32
+ return {
33
+ searchScopedTemplates,
34
+ finalTemplates,
35
+ tagFacets: buildTemplateTagFacets(searchScopedTemplates, getCandidate),
36
+ };
37
+ }
38
+
39
+ function buildTemplateTagFacets<TTemplate>(
40
+ templates: readonly TTemplate[],
41
+ getCandidate: (template: TTemplate) => TemplateFilterCandidate
42
+ ): TemplateTagFacet[] {
43
+ const counts = new Map<string, number>();
44
+
45
+ for (const template of templates) {
46
+ for (const tag of new Set(getCandidate(template).tags)) {
47
+ counts.set(tag, (counts.get(tag) ?? 0) + 1);
48
+ }
49
+ }
50
+
51
+ return [...counts.entries()]
52
+ .map(([tag, count]) => ({ tag, count }))
53
+ .sort(
54
+ (left, right) =>
55
+ right.count - left.count || left.tag.localeCompare(right.tag)
56
+ );
57
+ }
@@ -0,0 +1,12 @@
1
+ export const NEW_TEMPLATE_IDS = [
2
+ 'minimal',
3
+ 'messaging-agent-actions',
4
+ 'policy-safe-knowledge-assistant',
5
+ 'visualization-showcase',
6
+ ] as const;
7
+
8
+ const NEW_TEMPLATE_ID_SET = new Set<string>(NEW_TEMPLATE_IDS);
9
+
10
+ export function isNewTemplateId(templateId: string): boolean {
11
+ return NEW_TEMPLATE_ID_SET.has(templateId);
12
+ }
@@ -0,0 +1,57 @@
1
+ import type {
2
+ RegistryTemplate,
3
+ TemplateId,
4
+ } from '@contractspec/lib.example-shared-ui';
5
+ import type { LocalTemplateCatalogItem } from './template-catalog';
6
+
7
+ export type TemplatePreviewAction =
8
+ | { kind: 'modal'; templateId: TemplateId }
9
+ | { kind: 'sandbox'; href: string }
10
+ | { kind: 'disabled' };
11
+
12
+ export type LocalTemplatePreviewAction = Exclude<
13
+ TemplatePreviewAction,
14
+ { kind: 'disabled' }
15
+ >;
16
+
17
+ export const INLINE_TEMPLATE_PREVIEW_IDS = [
18
+ 'agent-console',
19
+ 'ai-chat-assistant',
20
+ 'analytics-dashboard',
21
+ 'crm-pipeline',
22
+ 'data-grid-showcase',
23
+ 'integration-hub',
24
+ 'marketplace',
25
+ 'saas-boilerplate',
26
+ 'visualization-showcase',
27
+ 'workflow-system',
28
+ ] as const;
29
+
30
+ const INLINE_TEMPLATE_PREVIEW_SET = new Set<string>(
31
+ INLINE_TEMPLATE_PREVIEW_IDS
32
+ );
33
+
34
+ export function supportsInlineTemplatePreview(templateId: TemplateId): boolean {
35
+ return INLINE_TEMPLATE_PREVIEW_SET.has(templateId);
36
+ }
37
+
38
+ export function getLocalTemplatePreviewAction(
39
+ template: LocalTemplateCatalogItem
40
+ ): LocalTemplatePreviewAction {
41
+ if (supportsInlineTemplatePreview(template.id)) {
42
+ return { kind: 'modal', templateId: template.id };
43
+ }
44
+
45
+ return { kind: 'sandbox', href: template.previewUrl };
46
+ }
47
+
48
+ export function getRegistryTemplatePreviewAction(
49
+ template: RegistryTemplate,
50
+ localTemplate?: LocalTemplateCatalogItem
51
+ ): TemplatePreviewAction {
52
+ if (!localTemplate) {
53
+ return { kind: 'disabled' };
54
+ }
55
+
56
+ return getLocalTemplatePreviewAction(localTemplate);
57
+ }
@@ -0,0 +1,13 @@
1
+ export type TemplateSource = 'local' | 'registry';
2
+
3
+ export function isRegistryConfigured(
4
+ registryUrl: string | null | undefined
5
+ ): boolean {
6
+ return Boolean(registryUrl?.trim());
7
+ }
8
+
9
+ export function getAvailableTemplateSources(
10
+ registryUrl: string | null | undefined
11
+ ): readonly TemplateSource[] {
12
+ return isRegistryConfigured(registryUrl) ? ['local', 'registry'] : ['local'];
13
+ }
@@ -0,0 +1,58 @@
1
+ export const DEFAULT_VISIBLE_TEMPLATE_TAGS = 10;
2
+
3
+ export interface TemplateTagFacet {
4
+ tag: string;
5
+ count: number;
6
+ }
7
+
8
+ export interface VisibleTemplateTagFacets {
9
+ visibleTagFacets: TemplateTagFacet[];
10
+ hiddenTagFacets: TemplateTagFacet[];
11
+ }
12
+
13
+ export function getVisibleTemplateTagFacets(
14
+ tagFacets: readonly TemplateTagFacet[],
15
+ selectedTag: string | null,
16
+ expanded: boolean,
17
+ visibleCount = DEFAULT_VISIBLE_TEMPLATE_TAGS
18
+ ): VisibleTemplateTagFacets {
19
+ if (expanded) {
20
+ return {
21
+ visibleTagFacets: pinSelectedTagFacet(tagFacets, selectedTag),
22
+ hiddenTagFacets: [],
23
+ };
24
+ }
25
+
26
+ const visibleTagFacets = pinSelectedTagFacet(
27
+ tagFacets.slice(0, visibleCount),
28
+ selectedTag,
29
+ tagFacets
30
+ );
31
+ const visibleTags = new Set(visibleTagFacets.map((facet) => facet.tag));
32
+
33
+ return {
34
+ visibleTagFacets,
35
+ hiddenTagFacets: tagFacets.filter((facet) => !visibleTags.has(facet.tag)),
36
+ };
37
+ }
38
+
39
+ function pinSelectedTagFacet(
40
+ tagFacets: readonly TemplateTagFacet[],
41
+ selectedTag: string | null,
42
+ fallbackTagFacets: readonly TemplateTagFacet[] = tagFacets
43
+ ): TemplateTagFacet[] {
44
+ if (
45
+ selectedTag === null ||
46
+ tagFacets.some((facet) => facet.tag === selectedTag)
47
+ ) {
48
+ return [...tagFacets];
49
+ }
50
+
51
+ return [
52
+ ...tagFacets,
53
+ fallbackTagFacets.find((facet) => facet.tag === selectedTag) ?? {
54
+ tag: selectedTag,
55
+ count: 0,
56
+ },
57
+ ];
58
+ }
@@ -0,0 +1,101 @@
1
+ 'use client';
2
+
3
+ import { useRegistryTemplates } from '@contractspec/lib.example-shared-ui';
4
+ import { useEffect, useMemo, useState } from 'react';
5
+ import { buildLocalTemplateCatalog } from './template-catalog';
6
+ import { buildTemplateFilterState } from './template-filters';
7
+ import {
8
+ getAvailableTemplateSources,
9
+ isRegistryConfigured,
10
+ type TemplateSource,
11
+ } from './template-source';
12
+ import { getVisibleTemplateTagFacets } from './template-tag-visibility';
13
+
14
+ const REGISTRY_URL = process.env.NEXT_PUBLIC_CONTRACTSPEC_REGISTRY_URL;
15
+
16
+ export function useTemplateBrowseState() {
17
+ const [selectedTag, setSelectedTag] = useState<string | null>(null);
18
+ const [search, setSearch] = useState('');
19
+ const [source, setSource] = useState<TemplateSource>('local');
20
+ const [showAllTags, setShowAllTags] = useState(false);
21
+ const registryConfigured = isRegistryConfigured(REGISTRY_URL);
22
+ const availableSources = getAvailableTemplateSources(REGISTRY_URL);
23
+ const localTemplates = useMemo(() => buildLocalTemplateCatalog(), []);
24
+ const localTemplateById = useMemo(
25
+ () => new Map(localTemplates.map((template) => [template.id, template])),
26
+ [localTemplates]
27
+ );
28
+ const { data: registryTemplates = [], isLoading: registryLoading } =
29
+ useRegistryTemplates();
30
+ const localFilterState = useMemo(
31
+ () =>
32
+ buildTemplateFilterState(
33
+ localTemplates,
34
+ search,
35
+ selectedTag,
36
+ (template) => ({
37
+ title: template.title,
38
+ description: template.description,
39
+ tags: template.tags,
40
+ })
41
+ ),
42
+ [localTemplates, search, selectedTag]
43
+ );
44
+ const registryFilterState = useMemo(
45
+ () =>
46
+ buildTemplateFilterState(
47
+ registryTemplates,
48
+ search,
49
+ selectedTag,
50
+ (template) => ({
51
+ title: template.name,
52
+ description: template.description,
53
+ tags: template.tags,
54
+ })
55
+ ),
56
+ [registryTemplates, search, selectedTag]
57
+ );
58
+ const activeFilterState =
59
+ source === 'registry' ? registryFilterState : localFilterState;
60
+ const suppressTagRail =
61
+ source === 'registry' &&
62
+ (registryLoading || registryTemplates.length === 0);
63
+ const { visibleTagFacets, hiddenTagFacets } = useMemo(
64
+ () =>
65
+ getVisibleTemplateTagFacets(
66
+ activeFilterState.tagFacets,
67
+ selectedTag,
68
+ showAllTags
69
+ ),
70
+ [activeFilterState.tagFacets, selectedTag, showAllTags]
71
+ );
72
+ const showTagFilters =
73
+ !suppressTagRail &&
74
+ (visibleTagFacets.length > 0 || hiddenTagFacets.length > 0);
75
+
76
+ useEffect(() => {
77
+ setShowAllTags(false);
78
+ }, [search, showTagFilters, source]);
79
+
80
+ return {
81
+ selectedTag,
82
+ setSelectedTag,
83
+ search,
84
+ setSearch,
85
+ source,
86
+ setSource,
87
+ showAllTags,
88
+ setShowAllTags,
89
+ registryConfigured,
90
+ availableSources,
91
+ localTemplates,
92
+ localTemplateById,
93
+ registryTemplates,
94
+ registryLoading,
95
+ localFilterState,
96
+ registryFilterState,
97
+ visibleTagFacets,
98
+ hiddenTagFacets,
99
+ showTagFilters,
100
+ };
101
+ }
@@ -1 +0,0 @@
1
- $ contractspec-bun-build prebuild