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