@contractspec/bundle.marketing 3.8.9 → 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 (49) hide show
  1. package/.turbo/turbo-build.log +54 -42
  2. package/CHANGELOG.md +33 -0
  3. package/dist/browser/components/templates/TemplatesBrowseControls.js +37 -22
  4. package/dist/browser/components/templates/TemplatesCatalogSection.js +29 -6
  5. package/dist/browser/components/templates/TemplatesClientPage.js +269 -89
  6. package/dist/browser/components/templates/TemplatesOverlays.js +2874 -0
  7. package/dist/browser/components/templates/index.js +301 -121
  8. package/dist/browser/components/templates/template-catalog.js +5 -3
  9. package/dist/browser/components/templates/template-filters.js +99 -0
  10. package/dist/browser/components/templates/template-tag-visibility.js +40 -0
  11. package/dist/browser/components/templates/useTemplateBrowseState.js +191 -0
  12. package/dist/browser/index.js +301 -121
  13. package/dist/components/templates/TemplatesBrowseControls.d.ts +7 -2
  14. package/dist/components/templates/TemplatesBrowseControls.js +37 -22
  15. package/dist/components/templates/TemplatesCatalogSection.d.ts +4 -1
  16. package/dist/components/templates/TemplatesCatalogSection.js +29 -6
  17. package/dist/components/templates/TemplatesClientPage.js +269 -89
  18. package/dist/components/templates/TemplatesOverlays.d.ts +10 -0
  19. package/dist/components/templates/TemplatesOverlays.js +2869 -0
  20. package/dist/components/templates/index.js +301 -121
  21. package/dist/components/templates/template-catalog.d.ts +1 -0
  22. package/dist/components/templates/template-catalog.js +5 -3
  23. package/dist/components/templates/template-filters.d.ts +12 -0
  24. package/dist/components/templates/template-filters.js +94 -0
  25. package/dist/components/templates/template-tag-visibility.d.ts +10 -0
  26. package/dist/components/templates/template-tag-visibility.js +35 -0
  27. package/dist/components/templates/useTemplateBrowseState.d.ts +22 -0
  28. package/dist/components/templates/useTemplateBrowseState.js +186 -0
  29. package/dist/index.js +301 -121
  30. package/dist/node/components/templates/TemplatesBrowseControls.js +37 -22
  31. package/dist/node/components/templates/TemplatesCatalogSection.js +29 -6
  32. package/dist/node/components/templates/TemplatesClientPage.js +269 -89
  33. package/dist/node/components/templates/TemplatesOverlays.js +2869 -0
  34. package/dist/node/components/templates/index.js +301 -121
  35. package/dist/node/components/templates/template-catalog.js +5 -3
  36. package/dist/node/components/templates/template-filters.js +94 -0
  37. package/dist/node/components/templates/template-tag-visibility.js +35 -0
  38. package/dist/node/components/templates/useTemplateBrowseState.js +186 -0
  39. package/dist/node/index.js +301 -121
  40. package/package.json +82 -26
  41. package/src/components/templates/TemplatesBrowseControls.tsx +59 -35
  42. package/src/components/templates/TemplatesCatalogSection.tsx +29 -4
  43. package/src/components/templates/TemplatesClientPage.tsx +41 -97
  44. package/src/components/templates/TemplatesOverlays.tsx +65 -0
  45. package/src/components/templates/template-catalog.test.ts +96 -0
  46. package/src/components/templates/template-catalog.ts +14 -6
  47. package/src/components/templates/template-filters.ts +57 -0
  48. package/src/components/templates/template-tag-visibility.ts +58 -0
  49. package/src/components/templates/useTemplateBrowseState.ts +101 -0
package/dist/index.js CHANGED
@@ -2834,7 +2834,11 @@ function TemplatesBrowseControls({
2834
2834
  onSearchChange,
2835
2835
  selectedTag,
2836
2836
  onTagChange,
2837
- availableTags
2837
+ showTagFilters,
2838
+ visibleTagFacets,
2839
+ hiddenTagFacets,
2840
+ showAllTags,
2841
+ onShowAllTagsChange
2838
2842
  }) {
2839
2843
  return /* @__PURE__ */ jsxDEV14("section", {
2840
2844
  className: "editorial-section",
@@ -2895,29 +2899,40 @@ function TemplatesBrowseControls({
2895
2899
  }, undefined, false, undefined, this)
2896
2900
  ]
2897
2901
  }, undefined, true, undefined, this),
2898
- /* @__PURE__ */ jsxDEV14("div", {
2899
- className: "flex flex-wrap gap-2",
2902
+ showTagFilters ? /* @__PURE__ */ jsxDEV14("div", {
2903
+ className: "space-y-3",
2900
2904
  children: [
2901
- /* @__PURE__ */ jsxDEV14("button", {
2902
- onClick: () => onTagChange(null),
2903
- className: cn("rounded-full px-4 py-2 font-medium text-sm transition-colors", {
2904
- "bg-primary text-primary-foreground": selectedTag === null,
2905
- "border border-border bg-card hover:bg-card/80": selectedTag !== null
2906
- }),
2907
- "aria-pressed": selectedTag === null,
2908
- children: "All"
2909
- }, undefined, false, undefined, this),
2910
- availableTags.map((tag) => /* @__PURE__ */ jsxDEV14("button", {
2911
- onClick: () => onTagChange(tag),
2912
- className: cn("rounded-full px-4 py-2 font-medium text-sm transition-colors", {
2913
- "bg-primary text-primary-foreground": selectedTag === tag,
2914
- "border border-border bg-card hover:bg-card/80": selectedTag !== tag
2915
- }),
2916
- "aria-pressed": selectedTag === tag,
2917
- children: tag
2918
- }, tag, false, undefined, this))
2905
+ /* @__PURE__ */ jsxDEV14("div", {
2906
+ className: "flex flex-wrap gap-2",
2907
+ children: [
2908
+ /* @__PURE__ */ jsxDEV14("button", {
2909
+ onClick: () => onTagChange(null),
2910
+ className: cn("rounded-full px-4 py-2 font-medium text-sm transition-colors", {
2911
+ "bg-primary text-primary-foreground": selectedTag === null,
2912
+ "border border-border bg-card hover:bg-card/80": selectedTag !== null
2913
+ }),
2914
+ "aria-pressed": selectedTag === null,
2915
+ children: "All"
2916
+ }, undefined, false, undefined, this),
2917
+ visibleTagFacets.map((facet) => /* @__PURE__ */ jsxDEV14("button", {
2918
+ onClick: () => onTagChange(facet.tag),
2919
+ className: cn("rounded-full px-4 py-2 font-medium text-sm transition-colors", {
2920
+ "bg-primary text-primary-foreground": selectedTag === facet.tag,
2921
+ "border border-border bg-card hover:bg-card/80": selectedTag !== facet.tag
2922
+ }),
2923
+ "aria-pressed": selectedTag === facet.tag,
2924
+ children: facet.tag
2925
+ }, facet.tag, false, undefined, this))
2926
+ ]
2927
+ }, undefined, true, undefined, this),
2928
+ hiddenTagFacets.length > 0 || showAllTags ? /* @__PURE__ */ jsxDEV14("button", {
2929
+ type: "button",
2930
+ onClick: () => onShowAllTagsChange(!showAllTags),
2931
+ className: "text-muted-foreground text-sm transition-colors hover:text-foreground",
2932
+ children: showAllTags ? "Show fewer" : "More tags"
2933
+ }, undefined, false, undefined, this) : null
2919
2934
  ]
2920
- }, undefined, true, undefined, this)
2935
+ }, undefined, true, undefined, this) : null
2921
2936
  ]
2922
2937
  }, undefined, true, undefined, this)
2923
2938
  ]
@@ -2962,15 +2977,16 @@ function buildLocalTemplateCatalog(examples = listExamples(), templates = listTe
2962
2977
  }).sort(compareLocalTemplateCatalogItems);
2963
2978
  }
2964
2979
  function matchesTemplateFilters(template, search, selectedTag) {
2980
+ return matchesTemplateSearch(template, search) && (selectedTag === null || template.tags.includes(selectedTag));
2981
+ }
2982
+ function matchesTemplateSearch(template, search) {
2965
2983
  const haystack = [
2966
2984
  template.title,
2967
2985
  template.description,
2968
2986
  template.tags.join(" ")
2969
2987
  ].join(" ").toLowerCase();
2970
2988
  const searchTokens = search.trim().toLowerCase().split(/\s+/).filter(Boolean);
2971
- const matchesSearch = searchTokens.length === 0 || searchTokens.every((token) => haystack.includes(token));
2972
- const matchesTag = selectedTag === null || template.tags.includes(selectedTag);
2973
- return matchesSearch && matchesTag;
2989
+ return searchTokens.length === 0 || searchTokens.every((token) => haystack.includes(token));
2974
2990
  }
2975
2991
  function formatExampleKindLabel(kind) {
2976
2992
  return kind.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
@@ -3031,13 +3047,17 @@ function TemplatesCatalogSection({
3031
3047
  source,
3032
3048
  registryConfigured,
3033
3049
  registryLoading,
3050
+ registryHasTemplates,
3034
3051
  localTemplates,
3035
3052
  registryTemplates,
3036
3053
  localTemplateById,
3037
3054
  onPreview,
3038
- onUseTemplate
3055
+ onUseTemplate,
3056
+ hasSearch,
3057
+ selectedTag
3039
3058
  }) {
3040
3059
  const showRegistry = source === "registry" && registryConfigured;
3060
+ const emptyStateMessage = getEmptyStateMessage(hasSearch, selectedTag);
3041
3061
  return /* @__PURE__ */ jsxDEV15("section", {
3042
3062
  className: "section-padding",
3043
3063
  children: /* @__PURE__ */ jsxDEV15("div", {
@@ -3048,12 +3068,18 @@ function TemplatesCatalogSection({
3048
3068
  className: "text-muted-foreground",
3049
3069
  children: "Loading community templates\u2026"
3050
3070
  }, undefined, false, undefined, this)
3051
- }, undefined, false, undefined, this) : registryTemplates.length === 0 ? /* @__PURE__ */ jsxDEV15("div", {
3071
+ }, undefined, false, undefined, this) : !registryHasTemplates ? /* @__PURE__ */ jsxDEV15("div", {
3052
3072
  className: "py-12 text-center",
3053
3073
  children: /* @__PURE__ */ jsxDEV15("p", {
3054
3074
  className: "text-muted-foreground",
3055
3075
  children: "No community templates found."
3056
3076
  }, undefined, false, undefined, this)
3077
+ }, undefined, false, undefined, this) : registryTemplates.length === 0 ? /* @__PURE__ */ jsxDEV15("div", {
3078
+ className: "py-12 text-center",
3079
+ children: /* @__PURE__ */ jsxDEV15("p", {
3080
+ className: "text-muted-foreground",
3081
+ children: emptyStateMessage
3082
+ }, undefined, false, undefined, this)
3057
3083
  }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV15("div", {
3058
3084
  className: "grid gap-6 md:grid-cols-2 lg:grid-cols-3",
3059
3085
  children: registryTemplates.map((template) => {
@@ -3089,7 +3115,7 @@ function TemplatesCatalogSection({
3089
3115
  className: "py-12 text-center",
3090
3116
  children: /* @__PURE__ */ jsxDEV15("p", {
3091
3117
  className: "text-muted-foreground",
3092
- children: "No templates match your filters. Try a different search."
3118
+ children: emptyStateMessage
3093
3119
  }, undefined, false, undefined, this)
3094
3120
  }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV15("div", {
3095
3121
  className: "grid gap-6 md:grid-cols-2 lg:grid-cols-3",
@@ -3125,6 +3151,18 @@ function TemplatesCatalogSection({
3125
3151
  }, undefined, false, undefined, this)
3126
3152
  }, undefined, false, undefined, this);
3127
3153
  }
3154
+ function getEmptyStateMessage(hasSearch, selectedTag) {
3155
+ if (selectedTag !== null && hasSearch) {
3156
+ return "No templates match this tag for the current search.";
3157
+ }
3158
+ if (selectedTag !== null) {
3159
+ return "No templates match this tag. Try another tag or reset filters.";
3160
+ }
3161
+ if (hasSearch) {
3162
+ return "No templates match your search. Try a different keyword.";
3163
+ }
3164
+ return "No templates match your filters. Try a different search.";
3165
+ }
3128
3166
 
3129
3167
  // src/components/templates/TemplatesHeroSection.tsx
3130
3168
  import { jsxDEV as jsxDEV16 } from "react/jsx-dev-runtime";
@@ -3351,6 +3389,82 @@ function TemplatePreviewModal({
3351
3389
  }, undefined, false, undefined, this);
3352
3390
  }
3353
3391
 
3392
+ // src/components/templates/TemplatesOverlays.tsx
3393
+ import {
3394
+ Dialog as Dialog4,
3395
+ DialogContent as DialogContent4,
3396
+ DialogDescription as DialogDescription3,
3397
+ DialogHeader as DialogHeader3,
3398
+ DialogTitle as DialogTitle3
3399
+ } from "@contractspec/lib.ui-kit-web/ui/dialog";
3400
+ import { jsxDEV as jsxDEV19, Fragment } from "react/jsx-dev-runtime";
3401
+ "use client";
3402
+ function TemplatesOverlays({
3403
+ previewTemplateId,
3404
+ onPreviewClose,
3405
+ studioSignupModalOpen,
3406
+ onStudioSignupModalOpenChange,
3407
+ selectedTemplateId,
3408
+ onTemplateCommandClose,
3409
+ onDeployStudio
3410
+ }) {
3411
+ return /* @__PURE__ */ jsxDEV19(Fragment, {
3412
+ children: [
3413
+ previewTemplateId ? /* @__PURE__ */ jsxDEV19(TemplatePreviewModal, {
3414
+ templateId: previewTemplateId,
3415
+ onClose: onPreviewClose
3416
+ }, undefined, false, undefined, this) : null,
3417
+ /* @__PURE__ */ jsxDEV19(Dialog4, {
3418
+ open: studioSignupModalOpen,
3419
+ onOpenChange: onStudioSignupModalOpenChange,
3420
+ children: /* @__PURE__ */ jsxDEV19(DialogContent4, {
3421
+ className: "max-h-[90vh] max-w-2xl overflow-y-auto",
3422
+ children: [
3423
+ /* @__PURE__ */ jsxDEV19(DialogHeader3, {
3424
+ children: [
3425
+ /* @__PURE__ */ jsxDEV19(DialogTitle3, {
3426
+ children: "Deploy in Studio"
3427
+ }, undefined, false, undefined, this),
3428
+ /* @__PURE__ */ jsxDEV19(DialogDescription3, {
3429
+ children: "Deploy templates in ContractSpec Studio and run the full evidence-to-spec loop with your team."
3430
+ }, undefined, false, undefined, this)
3431
+ ]
3432
+ }, undefined, true, undefined, this),
3433
+ /* @__PURE__ */ jsxDEV19(StudioSignupSection, {
3434
+ variant: "compact"
3435
+ }, undefined, false, undefined, this)
3436
+ ]
3437
+ }, undefined, true, undefined, this)
3438
+ }, undefined, false, undefined, this),
3439
+ /* @__PURE__ */ jsxDEV19(TemplateCommandDialog, {
3440
+ templateId: selectedTemplateId,
3441
+ onClose: onTemplateCommandClose,
3442
+ onDeployStudio
3443
+ }, undefined, false, undefined, this)
3444
+ ]
3445
+ }, undefined, true, undefined, this);
3446
+ }
3447
+
3448
+ // src/components/templates/template-filters.ts
3449
+ function buildTemplateFilterState(templates, search, selectedTag, getCandidate) {
3450
+ const searchScopedTemplates = templates.filter((template) => matchesTemplateSearch(getCandidate(template), search));
3451
+ const finalTemplates = selectedTag === null ? searchScopedTemplates : searchScopedTemplates.filter((template) => getCandidate(template).tags.includes(selectedTag));
3452
+ return {
3453
+ searchScopedTemplates,
3454
+ finalTemplates,
3455
+ tagFacets: buildTemplateTagFacets(searchScopedTemplates, getCandidate)
3456
+ };
3457
+ }
3458
+ function buildTemplateTagFacets(templates, getCandidate) {
3459
+ const counts = new Map;
3460
+ for (const template of templates) {
3461
+ for (const tag of new Set(getCandidate(template).tags)) {
3462
+ counts.set(tag, (counts.get(tag) ?? 0) + 1);
3463
+ }
3464
+ }
3465
+ return [...counts.entries()].map(([tag, count]) => ({ tag, count })).sort((left, right) => right.count - left.count || left.tag.localeCompare(right.tag));
3466
+ }
3467
+
3354
3468
  // src/components/templates/template-source.ts
3355
3469
  function isRegistryConfigured(registryUrl) {
3356
3470
  return Boolean(registryUrl?.trim());
@@ -3359,51 +3473,132 @@ function getAvailableTemplateSources(registryUrl) {
3359
3473
  return isRegistryConfigured(registryUrl) ? ["local", "registry"] : ["local"];
3360
3474
  }
3361
3475
 
3362
- // src/components/templates/TemplatesClientPage.tsx
3363
- import {
3364
- analyticsEventNames as analyticsEventNames3,
3365
- captureAnalyticsEvent as captureAnalyticsEvent3
3366
- } from "@contractspec/bundle.library/libs/posthog/client";
3476
+ // src/components/templates/template-tag-visibility.ts
3477
+ var DEFAULT_VISIBLE_TEMPLATE_TAGS = 10;
3478
+ function getVisibleTemplateTagFacets(tagFacets, selectedTag, expanded, visibleCount = DEFAULT_VISIBLE_TEMPLATE_TAGS) {
3479
+ if (expanded) {
3480
+ return {
3481
+ visibleTagFacets: pinSelectedTagFacet(tagFacets, selectedTag),
3482
+ hiddenTagFacets: []
3483
+ };
3484
+ }
3485
+ const visibleTagFacets = pinSelectedTagFacet(tagFacets.slice(0, visibleCount), selectedTag, tagFacets);
3486
+ const visibleTags = new Set(visibleTagFacets.map((facet) => facet.tag));
3487
+ return {
3488
+ visibleTagFacets,
3489
+ hiddenTagFacets: tagFacets.filter((facet) => !visibleTags.has(facet.tag))
3490
+ };
3491
+ }
3492
+ function pinSelectedTagFacet(tagFacets, selectedTag, fallbackTagFacets = tagFacets) {
3493
+ if (selectedTag === null || tagFacets.some((facet) => facet.tag === selectedTag)) {
3494
+ return [...tagFacets];
3495
+ }
3496
+ return [
3497
+ ...tagFacets,
3498
+ fallbackTagFacets.find((facet) => facet.tag === selectedTag) ?? {
3499
+ tag: selectedTag,
3500
+ count: 0
3501
+ }
3502
+ ];
3503
+ }
3504
+
3505
+ // src/components/templates/useTemplateBrowseState.ts
3367
3506
  import { useRegistryTemplates } from "@contractspec/lib.example-shared-ui";
3368
- import {
3369
- Dialog as Dialog4,
3370
- DialogContent as DialogContent4,
3371
- DialogDescription as DialogDescription3,
3372
- DialogHeader as DialogHeader3,
3373
- DialogTitle as DialogTitle3
3374
- } from "@contractspec/lib.ui-kit-web/ui/dialog";
3375
- import { useMemo, useState as useState2 } from "react";
3376
- import { jsxDEV as jsxDEV19, Fragment } from "react/jsx-dev-runtime";
3507
+ import { useEffect, useMemo, useState as useState2 } from "react";
3377
3508
  "use client";
3378
3509
  var REGISTRY_URL = process.env.NEXT_PUBLIC_CONTRACTSPEC_REGISTRY_URL;
3379
- var TemplatesPage = () => {
3510
+ function useTemplateBrowseState() {
3380
3511
  const [selectedTag, setSelectedTag] = useState2(null);
3381
3512
  const [search, setSearch] = useState2("");
3382
- const [previewTemplateId, setPreviewTemplateId] = useState2(null);
3383
- const [studioSignupModalOpen, setStudioSignupModalOpen] = useState2(false);
3384
- const [selectedTemplateId, setSelectedTemplateId] = useState2(null);
3385
3513
  const [source, setSource] = useState2("local");
3514
+ const [showAllTags, setShowAllTags] = useState2(false);
3386
3515
  const registryConfigured = isRegistryConfigured(REGISTRY_URL);
3387
3516
  const availableSources = getAvailableTemplateSources(REGISTRY_URL);
3388
3517
  const localTemplates = useMemo(() => buildLocalTemplateCatalog(), []);
3389
3518
  const localTemplateById = useMemo(() => new Map(localTemplates.map((template) => [template.id, template])), [localTemplates]);
3390
- const availableTags = useMemo(() => Array.from(new Set(localTemplates.flatMap((template) => template.tags))).sort((left, right) => left.localeCompare(right)), [localTemplates]);
3391
3519
  const { data: registryTemplates = [], isLoading: registryLoading } = useRegistryTemplates();
3392
- const filteredLocalTemplates = useMemo(() => localTemplates.filter((template) => matchesTemplateFilters(template, search, selectedTag)), [localTemplates, search, selectedTag]);
3393
- const filteredRegistryTemplates = useMemo(() => registryTemplates.filter((template) => matchesTemplateFilters({
3520
+ const localFilterState = useMemo(() => buildTemplateFilterState(localTemplates, search, selectedTag, (template) => ({
3521
+ title: template.title,
3522
+ description: template.description,
3523
+ tags: template.tags
3524
+ })), [localTemplates, search, selectedTag]);
3525
+ const registryFilterState = useMemo(() => buildTemplateFilterState(registryTemplates, search, selectedTag, (template) => ({
3394
3526
  title: template.name,
3395
3527
  description: template.description,
3396
3528
  tags: template.tags
3397
- }, search, selectedTag)), [registryTemplates, search, selectedTag]);
3398
- return /* @__PURE__ */ jsxDEV19(Fragment, {
3529
+ })), [registryTemplates, search, selectedTag]);
3530
+ const activeFilterState = source === "registry" ? registryFilterState : localFilterState;
3531
+ const suppressTagRail = source === "registry" && (registryLoading || registryTemplates.length === 0);
3532
+ const { visibleTagFacets, hiddenTagFacets } = useMemo(() => getVisibleTemplateTagFacets(activeFilterState.tagFacets, selectedTag, showAllTags), [activeFilterState.tagFacets, selectedTag, showAllTags]);
3533
+ const showTagFilters = !suppressTagRail && (visibleTagFacets.length > 0 || hiddenTagFacets.length > 0);
3534
+ useEffect(() => {
3535
+ setShowAllTags(false);
3536
+ }, [search, showTagFilters, source]);
3537
+ return {
3538
+ selectedTag,
3539
+ setSelectedTag,
3540
+ search,
3541
+ setSearch,
3542
+ source,
3543
+ setSource,
3544
+ showAllTags,
3545
+ setShowAllTags,
3546
+ registryConfigured,
3547
+ availableSources,
3548
+ localTemplates,
3549
+ localTemplateById,
3550
+ registryTemplates,
3551
+ registryLoading,
3552
+ localFilterState,
3553
+ registryFilterState,
3554
+ visibleTagFacets,
3555
+ hiddenTagFacets,
3556
+ showTagFilters
3557
+ };
3558
+ }
3559
+
3560
+ // src/components/templates/TemplatesClientPage.tsx
3561
+ import {
3562
+ analyticsEventNames as analyticsEventNames3,
3563
+ captureAnalyticsEvent as captureAnalyticsEvent3
3564
+ } from "@contractspec/bundle.library/libs/posthog/client";
3565
+ import { useState as useState3 } from "react";
3566
+ import { jsxDEV as jsxDEV20, Fragment as Fragment2 } from "react/jsx-dev-runtime";
3567
+ "use client";
3568
+ var TemplatesPage = () => {
3569
+ const [previewTemplateId, setPreviewTemplateId] = useState3(null);
3570
+ const [studioSignupModalOpen, setStudioSignupModalOpen] = useState3(false);
3571
+ const [selectedTemplateId, setSelectedTemplateId] = useState3(null);
3572
+ const {
3573
+ selectedTag,
3574
+ setSelectedTag,
3575
+ search,
3576
+ setSearch,
3577
+ source,
3578
+ setSource,
3579
+ showAllTags,
3580
+ setShowAllTags,
3581
+ registryConfigured,
3582
+ availableSources,
3583
+ localTemplates,
3584
+ localTemplateById,
3585
+ registryTemplates,
3586
+ registryLoading,
3587
+ localFilterState,
3588
+ registryFilterState,
3589
+ visibleTagFacets,
3590
+ hiddenTagFacets,
3591
+ showTagFilters
3592
+ } = useTemplateBrowseState();
3593
+ return /* @__PURE__ */ jsxDEV20(Fragment2, {
3399
3594
  children: [
3400
- /* @__PURE__ */ jsxDEV19("main", {
3595
+ /* @__PURE__ */ jsxDEV20("main", {
3401
3596
  children: [
3402
- /* @__PURE__ */ jsxDEV19(TemplatesHeroSection, {
3597
+ /* @__PURE__ */ jsxDEV20(TemplatesHeroSection, {
3403
3598
  localTemplateCount: localTemplates.length,
3404
3599
  sourceCount: availableSources.length
3405
3600
  }, undefined, false, undefined, this),
3406
- /* @__PURE__ */ jsxDEV19(TemplatesBrowseControls, {
3601
+ /* @__PURE__ */ jsxDEV20(TemplatesBrowseControls, {
3407
3602
  registryConfigured,
3408
3603
  availableSources,
3409
3604
  source,
@@ -3412,14 +3607,19 @@ var TemplatesPage = () => {
3412
3607
  onSearchChange: setSearch,
3413
3608
  selectedTag,
3414
3609
  onTagChange: setSelectedTag,
3415
- availableTags
3610
+ showTagFilters,
3611
+ visibleTagFacets,
3612
+ hiddenTagFacets,
3613
+ showAllTags,
3614
+ onShowAllTagsChange: setShowAllTags
3416
3615
  }, undefined, false, undefined, this),
3417
- /* @__PURE__ */ jsxDEV19(TemplatesCatalogSection, {
3616
+ /* @__PURE__ */ jsxDEV20(TemplatesCatalogSection, {
3418
3617
  source,
3419
3618
  registryConfigured,
3420
3619
  registryLoading,
3421
- localTemplates: filteredLocalTemplates,
3422
- registryTemplates: filteredRegistryTemplates,
3620
+ registryHasTemplates: registryTemplates.length > 0,
3621
+ localTemplates: localFilterState.finalTemplates,
3622
+ registryTemplates: registryFilterState.finalTemplates,
3423
3623
  localTemplateById,
3424
3624
  onPreview: setPreviewTemplateId,
3425
3625
  onUseTemplate: (templateId, templateSource) => {
@@ -3429,40 +3629,20 @@ var TemplatesPage = () => {
3429
3629
  source: templateSource
3430
3630
  });
3431
3631
  setSelectedTemplateId(templateId);
3432
- }
3632
+ },
3633
+ hasSearch: search.trim().length > 0,
3634
+ selectedTag
3433
3635
  }, undefined, false, undefined, this),
3434
- /* @__PURE__ */ jsxDEV19(TemplatesNextStepsSection, {}, undefined, false, undefined, this)
3636
+ /* @__PURE__ */ jsxDEV20(TemplatesNextStepsSection, {}, undefined, false, undefined, this)
3435
3637
  ]
3436
3638
  }, undefined, true, undefined, this),
3437
- previewTemplateId ? /* @__PURE__ */ jsxDEV19(TemplatePreviewModal, {
3438
- templateId: previewTemplateId,
3439
- onClose: () => setPreviewTemplateId(null)
3440
- }, undefined, false, undefined, this) : null,
3441
- /* @__PURE__ */ jsxDEV19(Dialog4, {
3442
- open: studioSignupModalOpen,
3443
- onOpenChange: setStudioSignupModalOpen,
3444
- children: /* @__PURE__ */ jsxDEV19(DialogContent4, {
3445
- className: "max-h-[90vh] max-w-2xl overflow-y-auto",
3446
- children: [
3447
- /* @__PURE__ */ jsxDEV19(DialogHeader3, {
3448
- children: [
3449
- /* @__PURE__ */ jsxDEV19(DialogTitle3, {
3450
- children: "Deploy in Studio"
3451
- }, undefined, false, undefined, this),
3452
- /* @__PURE__ */ jsxDEV19(DialogDescription3, {
3453
- children: "Deploy templates in ContractSpec Studio and run the full evidence-to-spec loop with your team."
3454
- }, undefined, false, undefined, this)
3455
- ]
3456
- }, undefined, true, undefined, this),
3457
- /* @__PURE__ */ jsxDEV19(StudioSignupSection, {
3458
- variant: "compact"
3459
- }, undefined, false, undefined, this)
3460
- ]
3461
- }, undefined, true, undefined, this)
3462
- }, undefined, false, undefined, this),
3463
- /* @__PURE__ */ jsxDEV19(TemplateCommandDialog, {
3464
- templateId: selectedTemplateId,
3465
- onClose: () => setSelectedTemplateId(null),
3639
+ /* @__PURE__ */ jsxDEV20(TemplatesOverlays, {
3640
+ previewTemplateId,
3641
+ onPreviewClose: () => setPreviewTemplateId(null),
3642
+ studioSignupModalOpen,
3643
+ onStudioSignupModalOpenChange: setStudioSignupModalOpen,
3644
+ selectedTemplateId,
3645
+ onTemplateCommandClose: () => setSelectedTemplateId(null),
3466
3646
  onDeployStudio: () => {
3467
3647
  setSelectedTemplateId(null);
3468
3648
  setStudioSignupModalOpen(true);
@@ -3486,8 +3666,8 @@ import {
3486
3666
  } from "@contractspec/lib.design-system";
3487
3667
  import { HStack, VStack } from "@contractspec/lib.ui-kit-web/ui/stack";
3488
3668
  import { listTemplates as listTemplates2 } from "@contractspec/module.examples";
3489
- import { useMemo as useMemo2, useState as useState3 } from "react";
3490
- import { jsxDEV as jsxDEV20, Fragment as Fragment2 } from "react/jsx-dev-runtime";
3669
+ import { useMemo as useMemo2, useState as useState4 } from "react";
3670
+ import { jsxDEV as jsxDEV21, Fragment as Fragment3 } from "react/jsx-dev-runtime";
3491
3671
  "use client";
3492
3672
  function matchesQuery(t, query) {
3493
3673
  const q = query.trim().toLowerCase();
@@ -3497,38 +3677,38 @@ function matchesQuery(t, query) {
3497
3677
  return hay.includes(q);
3498
3678
  }
3499
3679
  function TemplatesMarketingPage() {
3500
- const [query, setQuery] = useState3("");
3680
+ const [query, setQuery] = useState4("");
3501
3681
  const templates = useMemo2(() => listTemplates2(), []);
3502
3682
  const filtered = useMemo2(() => templates.filter((t) => matchesQuery(t, query)), [templates, query]);
3503
- return /* @__PURE__ */ jsxDEV20(Fragment2, {
3683
+ return /* @__PURE__ */ jsxDEV21(Fragment3, {
3504
3684
  children: [
3505
- /* @__PURE__ */ jsxDEV20(MarketingSection, {
3685
+ /* @__PURE__ */ jsxDEV21(MarketingSection, {
3506
3686
  tone: "default",
3507
- children: /* @__PURE__ */ jsxDEV20(VStack, {
3687
+ children: /* @__PURE__ */ jsxDEV21(VStack, {
3508
3688
  as: "header",
3509
3689
  gap: "lg",
3510
3690
  align: "center",
3511
3691
  children: [
3512
- /* @__PURE__ */ jsxDEV20(VStack, {
3692
+ /* @__PURE__ */ jsxDEV21(VStack, {
3513
3693
  gap: "sm",
3514
3694
  align: "center",
3515
3695
  children: [
3516
- /* @__PURE__ */ jsxDEV20(ButtonLink, {
3696
+ /* @__PURE__ */ jsxDEV21(ButtonLink, {
3517
3697
  href: "/docs",
3518
3698
  variant: "ghost",
3519
3699
  children: "Docs"
3520
3700
  }, undefined, false, undefined, this),
3521
- /* @__PURE__ */ jsxDEV20(ButtonLink, {
3701
+ /* @__PURE__ */ jsxDEV21(ButtonLink, {
3522
3702
  href: "/sandbox",
3523
3703
  variant: "ghost",
3524
3704
  children: "Open Sandbox"
3525
3705
  }, undefined, false, undefined, this)
3526
3706
  ]
3527
3707
  }, undefined, true, undefined, this),
3528
- /* @__PURE__ */ jsxDEV20(VStack, {
3708
+ /* @__PURE__ */ jsxDEV21(VStack, {
3529
3709
  gap: "sm",
3530
3710
  align: "center",
3531
- children: /* @__PURE__ */ jsxDEV20(ButtonLink, {
3711
+ children: /* @__PURE__ */ jsxDEV21(ButtonLink, {
3532
3712
  href: "/templates",
3533
3713
  variant: "default",
3534
3714
  children: "Templates"
@@ -3537,25 +3717,25 @@ function TemplatesMarketingPage() {
3537
3717
  ]
3538
3718
  }, undefined, true, undefined, this)
3539
3719
  }, undefined, false, undefined, this),
3540
- /* @__PURE__ */ jsxDEV20(MarketingSection, {
3720
+ /* @__PURE__ */ jsxDEV21(MarketingSection, {
3541
3721
  tone: "muted",
3542
- children: /* @__PURE__ */ jsxDEV20(VStack, {
3722
+ children: /* @__PURE__ */ jsxDEV21(VStack, {
3543
3723
  gap: "lg",
3544
3724
  children: [
3545
- /* @__PURE__ */ jsxDEV20(VStack, {
3725
+ /* @__PURE__ */ jsxDEV21(VStack, {
3546
3726
  gap: "sm",
3547
- children: /* @__PURE__ */ jsxDEV20(ButtonLink, {
3727
+ children: /* @__PURE__ */ jsxDEV21(ButtonLink, {
3548
3728
  href: "/templates",
3549
3729
  variant: "ghost",
3550
3730
  children: "Browse all examples"
3551
3731
  }, undefined, false, undefined, this)
3552
3732
  }, undefined, false, undefined, this),
3553
- /* @__PURE__ */ jsxDEV20(HStack, {
3733
+ /* @__PURE__ */ jsxDEV21(HStack, {
3554
3734
  gap: "md",
3555
3735
  align: "center",
3556
3736
  justify: "between",
3557
3737
  wrap: "wrap",
3558
- children: /* @__PURE__ */ jsxDEV20(Input2, {
3738
+ children: /* @__PURE__ */ jsxDEV21(Input2, {
3559
3739
  "aria-label": "Search templates and examples",
3560
3740
  placeholder: "Search templates and examples\u2026",
3561
3741
  value: query,
@@ -3565,52 +3745,52 @@ function TemplatesMarketingPage() {
3565
3745
  ]
3566
3746
  }, undefined, true, undefined, this)
3567
3747
  }, undefined, false, undefined, this),
3568
- /* @__PURE__ */ jsxDEV20(MarketingSection, {
3748
+ /* @__PURE__ */ jsxDEV21(MarketingSection, {
3569
3749
  tone: "default",
3570
- children: /* @__PURE__ */ jsxDEV20(VStack, {
3750
+ children: /* @__PURE__ */ jsxDEV21(VStack, {
3571
3751
  gap: "lg",
3572
- children: /* @__PURE__ */ jsxDEV20(HStack, {
3752
+ children: /* @__PURE__ */ jsxDEV21(HStack, {
3573
3753
  gap: "md",
3574
3754
  wrap: "wrap",
3575
- children: filtered.map((t) => /* @__PURE__ */ jsxDEV20(MarketingCard, {
3755
+ children: filtered.map((t) => /* @__PURE__ */ jsxDEV21(MarketingCard, {
3576
3756
  className: "w-full md:w-[calc(50%-0.75rem)] lg:w-[calc(33.333%-1rem)]",
3577
3757
  children: [
3578
- /* @__PURE__ */ jsxDEV20(MarketingCardHeader, {
3758
+ /* @__PURE__ */ jsxDEV21(MarketingCardHeader, {
3579
3759
  children: [
3580
- /* @__PURE__ */ jsxDEV20(MarketingCardTitle, {
3760
+ /* @__PURE__ */ jsxDEV21(MarketingCardTitle, {
3581
3761
  children: [
3582
3762
  t.icon,
3583
3763
  " ",
3584
3764
  t.name
3585
3765
  ]
3586
3766
  }, undefined, true, undefined, this),
3587
- /* @__PURE__ */ jsxDEV20(MarketingCardDescription, {
3767
+ /* @__PURE__ */ jsxDEV21(MarketingCardDescription, {
3588
3768
  children: t.description
3589
3769
  }, undefined, false, undefined, this)
3590
3770
  ]
3591
3771
  }, undefined, true, undefined, this),
3592
- /* @__PURE__ */ jsxDEV20(MarketingCardContent, {
3593
- children: /* @__PURE__ */ jsxDEV20(VStack, {
3772
+ /* @__PURE__ */ jsxDEV21(MarketingCardContent, {
3773
+ children: /* @__PURE__ */ jsxDEV21(VStack, {
3594
3774
  gap: "md",
3595
3775
  children: [
3596
- /* @__PURE__ */ jsxDEV20(HStack, {
3776
+ /* @__PURE__ */ jsxDEV21(HStack, {
3597
3777
  gap: "sm",
3598
3778
  wrap: "wrap",
3599
- children: t.tags.slice(0, 6).map((tag) => /* @__PURE__ */ jsxDEV20(ButtonLink, {
3779
+ children: t.tags.slice(0, 6).map((tag) => /* @__PURE__ */ jsxDEV21(ButtonLink, {
3600
3780
  href: `/templates?tag=${encodeURIComponent(tag)}`,
3601
3781
  variant: "ghost",
3602
3782
  children: tag
3603
3783
  }, `${t.id}-${tag}`, false, undefined, this))
3604
3784
  }, undefined, false, undefined, this),
3605
- /* @__PURE__ */ jsxDEV20(HStack, {
3785
+ /* @__PURE__ */ jsxDEV21(HStack, {
3606
3786
  gap: "sm",
3607
3787
  justify: "between",
3608
3788
  wrap: "wrap",
3609
3789
  children: [
3610
- /* @__PURE__ */ jsxDEV20(ButtonLinkToSandbox, {
3790
+ /* @__PURE__ */ jsxDEV21(ButtonLinkToSandbox, {
3611
3791
  templateId: t.id
3612
3792
  }, undefined, false, undefined, this),
3613
- /* @__PURE__ */ jsxDEV20(Button3, {
3793
+ /* @__PURE__ */ jsxDEV21(Button3, {
3614
3794
  variant: "outline",
3615
3795
  onClick: () => {
3616
3796
  return;
@@ -3632,7 +3812,7 @@ function TemplatesMarketingPage() {
3632
3812
  }, undefined, true, undefined, this);
3633
3813
  }
3634
3814
  function ButtonLinkToSandbox({ templateId }) {
3635
- return /* @__PURE__ */ jsxDEV20(ButtonLink, {
3815
+ return /* @__PURE__ */ jsxDEV21(ButtonLink, {
3636
3816
  href: `/sandbox?template=${encodeURIComponent(templateId)}`,
3637
3817
  variant: "default",
3638
3818
  children: "Preview"