@anthusai/papyrus 0.0.0 → 1.0.0-next.1

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 (254) hide show
  1. package/LICENSE +21 -0
  2. package/amplify/auth/resource.js +61 -0
  3. package/amplify/auth/resource.ts +65 -0
  4. package/amplify/data/resource.js +2037 -0
  5. package/amplify/data/resource.ts +2148 -0
  6. package/amplify/data/schema.js +1850 -0
  7. package/amplify/data/schema.ts +1953 -0
  8. package/amplify/functions/assignment-action/handler.ts +1416 -0
  9. package/amplify/functions/assignment-action/resource.js +8 -0
  10. package/amplify/functions/assignment-action/resource.ts +9 -0
  11. package/amplify/functions/category-action/handler.ts +2259 -0
  12. package/amplify/functions/category-action/resource.js +8 -0
  13. package/amplify/functions/category-action/resource.ts +9 -0
  14. package/amplify/functions/console-chat-responder/Cargo.lock +3456 -0
  15. package/amplify/functions/console-chat-responder/Cargo.toml +26 -0
  16. package/amplify/functions/console-chat-responder/Dockerfile +19 -0
  17. package/amplify/functions/console-chat-responder/py/execute_tactus_runner.py +116 -0
  18. package/amplify/functions/console-chat-responder/resource.js +153 -0
  19. package/amplify/functions/console-chat-responder/resource.ts +179 -0
  20. package/amplify/functions/console-chat-responder/src/main.rs +3242 -0
  21. package/amplify/functions/email-submission-processor/handler.py +46 -0
  22. package/amplify/functions/email-submission-processor/requirements.txt +2 -0
  23. package/amplify/functions/email-submission-processor/resource.js +56 -0
  24. package/amplify/functions/email-submission-processor/resource.ts +69 -0
  25. package/amplify/functions/graphql-jwt-authorizer/handler.ts +193 -0
  26. package/amplify/functions/graphql-jwt-authorizer/resource.js +14 -0
  27. package/amplify/functions/graphql-jwt-authorizer/resource.ts +15 -0
  28. package/amplify/functions/knowledge-query/handler.py +1 -0
  29. package/amplify/functions/knowledge-query/requirements.txt +3 -0
  30. package/amplify/functions/knowledge-query/resource.js +50 -0
  31. package/amplify/functions/knowledge-query/resource.ts +61 -0
  32. package/amplify/functions/manage-user-role/handler.ts +799 -0
  33. package/amplify/functions/manage-user-role/resource.js +8 -0
  34. package/amplify/functions/manage-user-role/resource.ts +9 -0
  35. package/amplify/functions/model-attachment-upload/handler.ts +387 -0
  36. package/amplify/functions/model-attachment-upload/resource.js +8 -0
  37. package/amplify/functions/model-attachment-upload/resource.ts +9 -0
  38. package/amplify/functions/newsroom-summary/handler.ts +321 -0
  39. package/amplify/functions/newsroom-summary/resource.js +8 -0
  40. package/amplify/functions/newsroom-summary/resource.ts +9 -0
  41. package/amplify/functions/procedure-action/handler.ts +621 -0
  42. package/amplify/functions/procedure-action/resource.js +8 -0
  43. package/amplify/functions/procedure-action/resource.ts +9 -0
  44. package/amplify/functions/reader-settings/handler.ts +322 -0
  45. package/amplify/functions/reader-settings/resource.js +8 -0
  46. package/amplify/functions/reader-settings/resource.ts +9 -0
  47. package/amplify/functions/ses-inbound-receive/handler.ts +493 -0
  48. package/amplify/functions/ses-inbound-receive/resource.js +8 -0
  49. package/amplify/functions/ses-inbound-receive/resource.ts +9 -0
  50. package/amplify/functions/shared/amplify-secrets.ts +65 -0
  51. package/amplify/functions/shared/console-chat-enqueue.ts +117 -0
  52. package/amplify/functions/shared/email-mime-intake.ts +196 -0
  53. package/amplify/functions/shared/email-submission.ts +415 -0
  54. package/amplify/functions/shared/inbound-email-intake.ts +37 -0
  55. package/amplify/functions/shared/lambda-data-client.ts +21 -0
  56. package/amplify/functions/shared/model-payloads.ts +186 -0
  57. package/amplify/functions/shared/python-bundle.js +115 -0
  58. package/amplify/functions/shared/python-bundle.ts +141 -0
  59. package/amplify/functions/shared/slack-events.ts +111 -0
  60. package/amplify/functions/slack-delivery/handler.py +112 -0
  61. package/amplify/functions/slack-delivery/requirements.txt +2 -0
  62. package/amplify/functions/slack-delivery/resource.js +38 -0
  63. package/amplify/functions/slack-delivery/resource.ts +51 -0
  64. package/amplify/functions/slack-events/handler.ts +155 -0
  65. package/amplify/functions/slack-events/resource.js +11 -0
  66. package/amplify/functions/slack-events/resource.ts +12 -0
  67. package/amplify/inbound-email/stack.js +46 -0
  68. package/amplify/inbound-email/stack.ts +61 -0
  69. package/amplify/package.json +3 -0
  70. package/amplify/site-backend-config.js +1 -0
  71. package/amplify/site-backend-config.ts +30 -0
  72. package/amplify/site-backend.js +587 -0
  73. package/amplify/site-backend.ts +704 -0
  74. package/amplify/storage/resource.js +18 -0
  75. package/amplify/storage/resource.ts +19 -0
  76. package/app/[year]/[month]/[day]/[articleSlug]/page.tsx +62 -0
  77. package/app/[year]/[month]/[day]/edition-route-page.tsx +53 -0
  78. package/app/[year]/[month]/[day]/page/[pageNumber]/page.tsx +32 -0
  79. package/app/[year]/[month]/[day]/page.tsx +22 -0
  80. package/app/[year]/[month]/[day]/section/[sectionKey]/page.tsx +32 -0
  81. package/app/api/archive/editions/route.ts +18 -0
  82. package/app/api/media/[...path]/route.ts +35 -0
  83. package/app/api/revalidate/route.ts +94 -0
  84. package/app/archive/page.tsx +24 -0
  85. package/app/articles/[slug]/page.tsx +35 -0
  86. package/app/globals.css +10329 -0
  87. package/app/layout.tsx +146 -0
  88. package/app/newsroom/[section]/[...selection]/page.tsx +50 -0
  89. package/app/newsroom/[section]/page.tsx +47 -0
  90. package/app/newsroom/forum/[threadId]/page.tsx +35 -0
  91. package/app/newsroom/insights/[threadId]/page.tsx +22 -0
  92. package/app/newsroom/insights/page.tsx +14 -0
  93. package/app/newsroom/layout.tsx +12 -0
  94. package/app/newsroom/page.tsx +31 -0
  95. package/app/newsroom/sections/[sectionId]/page.tsx +17 -0
  96. package/app/page.tsx +272 -0
  97. package/app/settings/page.tsx +14 -0
  98. package/app/tailwind.css +65 -0
  99. package/bin/papyrus-app.mjs +10 -0
  100. package/bin/papyrus-infra.mjs +39 -0
  101. package/components/ai-elements/conversation.tsx +168 -0
  102. package/components/ai-elements/message.tsx +360 -0
  103. package/components/ai-elements/model-selector.tsx +30 -0
  104. package/components/ai-elements/prompt-input.tsx +1461 -0
  105. package/components/ai-elements/shimmer.tsx +76 -0
  106. package/components/ai-elements/suggestion.tsx +54 -0
  107. package/components/ai-elements/tool.tsx +173 -0
  108. package/components/amplify-client-provider.tsx +140 -0
  109. package/components/archive-grid.tsx +89 -0
  110. package/components/archive-shell.tsx +41 -0
  111. package/components/article-video.tsx +47 -0
  112. package/components/blog-page-background.tsx +20 -0
  113. package/components/dev-sandbox-editor-sign-in.tsx +60 -0
  114. package/components/news-desk-client-provider.tsx +211 -0
  115. package/components/news-desk-page.tsx +119 -0
  116. package/components/news-desk-taxonomy-client.ts +3684 -0
  117. package/components/newspaper.tsx +1929 -0
  118. package/components/newsroom-assignments-view.tsx +664 -0
  119. package/components/newsroom-ops-overview.tsx +226 -0
  120. package/components/newsroom-ops-shell.tsx +319 -0
  121. package/components/newsroom-references-view.tsx +598 -0
  122. package/components/newsroom-topics-view.tsx +425 -0
  123. package/components/papyrus-console-shell.tsx +1912 -0
  124. package/components/pictogram-figure.tsx +65 -0
  125. package/components/presentation-footer.tsx +146 -0
  126. package/components/reader-auth-control.tsx +162 -0
  127. package/components/reader-auth-state.ts +122 -0
  128. package/components/reader-settings.ts +332 -0
  129. package/components/reader-theme-control.tsx +85 -0
  130. package/components/reference-source-preview.tsx +163 -0
  131. package/components/settings-page.tsx +239 -0
  132. package/components/topic-steering-workspace.tsx +18063 -0
  133. package/components/ui/badge.tsx +30 -0
  134. package/components/ui/button-group.tsx +87 -0
  135. package/components/ui/button.tsx +58 -0
  136. package/components/ui/card.tsx +32 -0
  137. package/components/ui/command.tsx +194 -0
  138. package/components/ui/dialog.tsx +158 -0
  139. package/components/ui/dropdown-menu.tsx +266 -0
  140. package/components/ui/hover-card.tsx +51 -0
  141. package/components/ui/input-group.tsx +158 -0
  142. package/components/ui/input.tsx +20 -0
  143. package/components/ui/scroll-area.tsx +16 -0
  144. package/components/ui/select.tsx +199 -0
  145. package/components/ui/separator.tsx +23 -0
  146. package/components/ui/sheet.tsx +92 -0
  147. package/components/ui/spinner.tsx +10 -0
  148. package/components/ui/tabs.tsx +113 -0
  149. package/components/ui/textarea.tsx +18 -0
  150. package/components/ui/tooltip.tsx +66 -0
  151. package/components/use-resolved-papyrus-theme.ts +47 -0
  152. package/infra/amplify-app-shell.js +109 -0
  153. package/infra/amplify-app-shell.ts +155 -0
  154. package/infra/index.js +12 -0
  155. package/lib/amplify-outputs-guard.ts +24 -0
  156. package/lib/amplify-outputs-path.ts +16 -0
  157. package/lib/amplify-server-runtime.ts +63 -0
  158. package/lib/archive-data.ts +30 -0
  159. package/lib/archive-types.ts +15 -0
  160. package/lib/articles.ts +320 -0
  161. package/lib/cached-content-repository.ts +89 -0
  162. package/lib/category-dashboard.ts +37 -0
  163. package/lib/category-repository.ts +1907 -0
  164. package/lib/console-chat-client.ts +1316 -0
  165. package/lib/console-web-context.ts +118 -0
  166. package/lib/content-load-timing.ts +23 -0
  167. package/lib/content-repository.ts +40 -0
  168. package/lib/content-types.ts +104 -0
  169. package/lib/define-site.js +3 -0
  170. package/lib/define-site.ts +21 -0
  171. package/lib/demo-amplify-outputs.ts +15 -0
  172. package/lib/dev-sandbox-editor-auth.ts +18 -0
  173. package/lib/doctrine.ts +128 -0
  174. package/lib/edition-routes.ts +155 -0
  175. package/lib/edition-sections.ts +111 -0
  176. package/lib/empty-edition-layout-plan.ts +109 -0
  177. package/lib/excerpts.ts +8 -0
  178. package/lib/graphql-content-repository.ts +986 -0
  179. package/lib/image-url.ts +10 -0
  180. package/lib/layout-plan.ts +981 -0
  181. package/lib/layout-scenarios.ts +564 -0
  182. package/lib/markus-body.ts +168 -0
  183. package/lib/markus-ir.ts +235 -0
  184. package/lib/markus-projection.ts +243 -0
  185. package/lib/markus-to-article.ts +93 -0
  186. package/lib/news-desk-session.ts +160 -0
  187. package/lib/newspaper-layout.ts +3459 -0
  188. package/lib/newsroom-assignments.ts +173 -0
  189. package/lib/newsroom-base-path.ts +48 -0
  190. package/lib/newsroom-card-layout.ts +102 -0
  191. package/lib/newsroom-category-drilldown.ts +133 -0
  192. package/lib/newsroom-demo-dashboard.ts +267 -0
  193. package/lib/newsroom-demo-profile.ts +46 -0
  194. package/lib/newsroom-forum-routes.ts +153 -0
  195. package/lib/newsroom-index-filters.ts +202 -0
  196. package/lib/newsroom-knowledge-query-request.ts +51 -0
  197. package/lib/newsroom-list-selection.ts +42 -0
  198. package/lib/newsroom-nav.ts +42 -0
  199. package/lib/newsroom-references.ts +116 -0
  200. package/lib/newsroom-sections.ts +52 -0
  201. package/lib/newsroom-topics.ts +118 -0
  202. package/lib/papyrus-object-kinds.ts +24 -0
  203. package/lib/papyrus-web-locations.ts +480 -0
  204. package/lib/pilobol-sample.ts +167 -0
  205. package/lib/presentation-footer.ts +92 -0
  206. package/lib/pretext-layout.ts +179 -0
  207. package/lib/public-placeholder-config.ts +102 -0
  208. package/lib/publication-items.ts +181 -0
  209. package/lib/reader-route-config.ts +1 -0
  210. package/lib/reader-static-params.ts +56 -0
  211. package/lib/reader-storage-url.ts +63 -0
  212. package/lib/reference-display.ts +74 -0
  213. package/lib/reference-policy.ts +97 -0
  214. package/lib/reference-source-preview.ts +175 -0
  215. package/lib/renderer-config.ts +50 -0
  216. package/lib/renderer.ts +87 -0
  217. package/lib/reporting-story-budget.ts +638 -0
  218. package/lib/semantic-graph.ts +554 -0
  219. package/lib/site-brand-tenant.ts +41 -0
  220. package/lib/site-brand.ts +179 -0
  221. package/lib/site-renderer.ts +17 -0
  222. package/lib/site-stack.ts +195 -0
  223. package/lib/themed-image.ts +30 -0
  224. package/lib/utils.ts +6 -0
  225. package/middleware.ts +78 -0
  226. package/package.json +296 -1
  227. package/publications/anth_us/brand.ts +68 -0
  228. package/publications/anth_us/theme.css +66 -0
  229. package/publications/papyrus/theme.css +11 -0
  230. package/publications/pilobol_us/brand.ts +37 -0
  231. package/publications/pilobol_us/theme.css +140 -0
  232. package/publications/threat_intelligence/blog-defense/graph.ts +636 -0
  233. package/publications/threat_intelligence/blog-defense/layout.ts +934 -0
  234. package/publications/threat_intelligence/blog-defense/page-background.tsx +590 -0
  235. package/publications/threat_intelligence/brand.ts +40 -0
  236. package/publications/threat_intelligence/pictograms/art.tsx +608 -0
  237. package/publications/threat_intelligence/pictograms/figure.tsx +82 -0
  238. package/publications/threat_intelligence/pictograms/registry.ts +30 -0
  239. package/publications/threat_intelligence/pictograms/system.tsx +305 -0
  240. package/publications/threat_intelligence/seed/seed-edition-content.json +629 -0
  241. package/publications/threat_intelligence/theme.css +1440 -0
  242. package/renderers/markus/index.ts +25 -0
  243. package/renderers/markus/stubs.tsx +33 -0
  244. package/renderers/pretext/article-page.tsx +174 -0
  245. package/renderers/pretext/index.ts +30 -0
  246. package/renderers/pretext/layouts.ts +16 -0
  247. package/renderers/pretext/presentation-shell.tsx +477 -0
  248. package/routes.manifest.json +182 -0
  249. package/scripts/fixtures/pilobol-sample.ast.json +234 -0
  250. package/src/backend.js +1 -0
  251. package/src/backend.ts +3 -0
  252. package/src/sync.mjs +87 -0
  253. package/src/with-papyrus.d.mts +2 -0
  254. package/src/with-papyrus.mjs +47 -0
@@ -0,0 +1,199 @@
1
+ import * as React from "react"
2
+ import { Select as SelectPrimitive } from "@base-ui/react/select"
3
+
4
+ import { cn } from "../../lib/utils"
5
+ import { ChevronDownIcon, CheckIcon, ChevronUpIcon } from "lucide-react"
6
+
7
+ const Select = SelectPrimitive.Root
8
+
9
+ function SelectGroup({ className, ...props }: SelectPrimitive.Group.Props) {
10
+ return (
11
+ <SelectPrimitive.Group
12
+ data-slot="select-group"
13
+ className={cn("scroll-my-1 p-1", className)}
14
+ {...props}
15
+ />
16
+ )
17
+ }
18
+
19
+ function SelectValue({ className, ...props }: SelectPrimitive.Value.Props) {
20
+ return (
21
+ <SelectPrimitive.Value
22
+ data-slot="select-value"
23
+ className={cn("flex flex-1 text-left", className)}
24
+ {...props}
25
+ />
26
+ )
27
+ }
28
+
29
+ function SelectTrigger({
30
+ className,
31
+ size = "default",
32
+ children,
33
+ ...props
34
+ }: SelectPrimitive.Trigger.Props & {
35
+ size?: "sm" | "default"
36
+ }) {
37
+ return (
38
+ <SelectPrimitive.Trigger
39
+ data-slot="select-trigger"
40
+ data-size={size}
41
+ className={cn(
42
+ "flex w-fit items-center justify-between gap-1.5 rounded-lg border border-input bg-transparent py-2 pr-2 pl-2.5 text-sm whitespace-nowrap transition-colors outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),10px)] *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
43
+ className
44
+ )}
45
+ {...props}
46
+ >
47
+ {children}
48
+ <SelectPrimitive.Icon
49
+ render={
50
+ <ChevronDownIcon className="pointer-events-none size-4 text-muted-foreground" />
51
+ }
52
+ />
53
+ </SelectPrimitive.Trigger>
54
+ )
55
+ }
56
+
57
+ function SelectContent({
58
+ className,
59
+ children,
60
+ side = "bottom",
61
+ sideOffset = 4,
62
+ align = "center",
63
+ alignOffset = 0,
64
+ alignItemWithTrigger = true,
65
+ ...props
66
+ }: SelectPrimitive.Popup.Props &
67
+ Pick<
68
+ SelectPrimitive.Positioner.Props,
69
+ "align" | "alignOffset" | "side" | "sideOffset" | "alignItemWithTrigger"
70
+ >) {
71
+ return (
72
+ <SelectPrimitive.Portal>
73
+ <SelectPrimitive.Positioner
74
+ side={side}
75
+ sideOffset={sideOffset}
76
+ align={align}
77
+ alignOffset={alignOffset}
78
+ alignItemWithTrigger={alignItemWithTrigger}
79
+ className="isolate z-50"
80
+ >
81
+ <SelectPrimitive.Popup
82
+ data-slot="select-content"
83
+ data-align-trigger={alignItemWithTrigger}
84
+ className={cn("relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )}
85
+ {...props}
86
+ >
87
+ <SelectScrollUpButton />
88
+ <SelectPrimitive.List>{children}</SelectPrimitive.List>
89
+ <SelectScrollDownButton />
90
+ </SelectPrimitive.Popup>
91
+ </SelectPrimitive.Positioner>
92
+ </SelectPrimitive.Portal>
93
+ )
94
+ }
95
+
96
+ function SelectLabel({
97
+ className,
98
+ ...props
99
+ }: SelectPrimitive.GroupLabel.Props) {
100
+ return (
101
+ <SelectPrimitive.GroupLabel
102
+ data-slot="select-label"
103
+ className={cn("px-1.5 py-1 text-xs text-muted-foreground", className)}
104
+ {...props}
105
+ />
106
+ )
107
+ }
108
+
109
+ function SelectItem({
110
+ className,
111
+ children,
112
+ ...props
113
+ }: SelectPrimitive.Item.Props) {
114
+ return (
115
+ <SelectPrimitive.Item
116
+ data-slot="select-item"
117
+ className={cn(
118
+ "relative flex w-full cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
119
+ className
120
+ )}
121
+ {...props}
122
+ >
123
+ <SelectPrimitive.ItemText className="flex flex-1 shrink-0 gap-2 whitespace-nowrap">
124
+ {children}
125
+ </SelectPrimitive.ItemText>
126
+ <SelectPrimitive.ItemIndicator
127
+ render={
128
+ <span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center" />
129
+ }
130
+ >
131
+ <CheckIcon className="pointer-events-none" />
132
+ </SelectPrimitive.ItemIndicator>
133
+ </SelectPrimitive.Item>
134
+ )
135
+ }
136
+
137
+ function SelectSeparator({
138
+ className,
139
+ ...props
140
+ }: SelectPrimitive.Separator.Props) {
141
+ return (
142
+ <SelectPrimitive.Separator
143
+ data-slot="select-separator"
144
+ className={cn("pointer-events-none -mx-1 my-1 h-px bg-border", className)}
145
+ {...props}
146
+ />
147
+ )
148
+ }
149
+
150
+ function SelectScrollUpButton({
151
+ className,
152
+ ...props
153
+ }: React.ComponentProps<typeof SelectPrimitive.ScrollUpArrow>) {
154
+ return (
155
+ <SelectPrimitive.ScrollUpArrow
156
+ data-slot="select-scroll-up-button"
157
+ className={cn(
158
+ "top-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
159
+ className
160
+ )}
161
+ {...props}
162
+ >
163
+ <ChevronUpIcon
164
+ />
165
+ </SelectPrimitive.ScrollUpArrow>
166
+ )
167
+ }
168
+
169
+ function SelectScrollDownButton({
170
+ className,
171
+ ...props
172
+ }: React.ComponentProps<typeof SelectPrimitive.ScrollDownArrow>) {
173
+ return (
174
+ <SelectPrimitive.ScrollDownArrow
175
+ data-slot="select-scroll-down-button"
176
+ className={cn(
177
+ "bottom-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
178
+ className
179
+ )}
180
+ {...props}
181
+ >
182
+ <ChevronDownIcon
183
+ />
184
+ </SelectPrimitive.ScrollDownArrow>
185
+ )
186
+ }
187
+
188
+ export {
189
+ Select,
190
+ SelectContent,
191
+ SelectGroup,
192
+ SelectItem,
193
+ SelectLabel,
194
+ SelectScrollDownButton,
195
+ SelectScrollUpButton,
196
+ SelectSeparator,
197
+ SelectTrigger,
198
+ SelectValue,
199
+ }
@@ -0,0 +1,23 @@
1
+ import { Separator as SeparatorPrimitive } from "@base-ui/react/separator"
2
+
3
+ import { cn } from "../../lib/utils"
4
+
5
+ function Separator({
6
+ className,
7
+ orientation = "horizontal",
8
+ ...props
9
+ }: SeparatorPrimitive.Props) {
10
+ return (
11
+ <SeparatorPrimitive
12
+ data-slot="separator"
13
+ orientation={orientation}
14
+ className={cn(
15
+ "shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch",
16
+ className
17
+ )}
18
+ {...props}
19
+ />
20
+ )
21
+ }
22
+
23
+ export { Separator }
@@ -0,0 +1,92 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { Dialog as DialogPrimitive } from "@base-ui/react/dialog";
5
+ import { XIcon } from "lucide-react";
6
+
7
+ import { cn } from "../../lib/utils";
8
+ import { Button } from "./button";
9
+
10
+ type SheetSide = "bottom" | "left" | "right";
11
+
12
+ const sideClasses: Record<SheetSide, string> = {
13
+ bottom:
14
+ "inset-x-0 bottom-0 top-auto max-h-[85dvh] w-full rounded-t-2xl border-t data-open:slide-in-from-bottom data-closed:slide-out-to-bottom",
15
+ left: "inset-y-0 left-0 h-full w-[min(100vw,20rem)] rounded-r-2xl border-r data-open:slide-in-from-left data-closed:slide-out-to-left",
16
+ right: "inset-y-0 right-0 h-full w-[min(100vw,20rem)] rounded-l-2xl border-l data-open:slide-in-from-right data-closed:slide-out-to-right",
17
+ };
18
+
19
+ function Sheet({ ...props }: DialogPrimitive.Root.Props) {
20
+ return <DialogPrimitive.Root data-slot="sheet" {...props} />;
21
+ }
22
+
23
+ function SheetTrigger({ ...props }: DialogPrimitive.Trigger.Props) {
24
+ return <DialogPrimitive.Trigger data-slot="sheet-trigger" {...props} />;
25
+ }
26
+
27
+ function SheetClose({ ...props }: DialogPrimitive.Close.Props) {
28
+ return <DialogPrimitive.Close data-slot="sheet-close" {...props} />;
29
+ }
30
+
31
+ function SheetContent({
32
+ children,
33
+ className,
34
+ showCloseButton = true,
35
+ side = "bottom",
36
+ ...props
37
+ }: DialogPrimitive.Popup.Props & {
38
+ showCloseButton?: boolean;
39
+ side?: SheetSide;
40
+ }) {
41
+ return (
42
+ <DialogPrimitive.Portal>
43
+ <DialogPrimitive.Backdrop
44
+ className="fixed inset-0 z-50 bg-black/40 data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0"
45
+ />
46
+ <DialogPrimitive.Popup
47
+ className={cn(
48
+ "fixed z-50 grid gap-4 bg-background p-4 text-foreground shadow-lg outline-none duration-200 data-open:animate-in data-closed:animate-out",
49
+ sideClasses[side],
50
+ className,
51
+ )}
52
+ {...props}
53
+ >
54
+ {children}
55
+ {showCloseButton ? (
56
+ <DialogPrimitive.Close
57
+ render={
58
+ <Button className="absolute top-3 right-3" size="icon-sm" variant="ghost" />
59
+ }
60
+ >
61
+ <XIcon className="size-4" />
62
+ <span className="sr-only">Close</span>
63
+ </DialogPrimitive.Close>
64
+ ) : null}
65
+ </DialogPrimitive.Popup>
66
+ </DialogPrimitive.Portal>
67
+ );
68
+ }
69
+
70
+ function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
71
+ return <div className={cn("flex flex-col gap-1 pr-8", className)} {...props} />;
72
+ }
73
+
74
+ function SheetTitle({ className, ...props }: DialogPrimitive.Title.Props) {
75
+ return (
76
+ <DialogPrimitive.Title
77
+ className={cn("text-base font-semibold tracking-tight", className)}
78
+ {...props}
79
+ />
80
+ );
81
+ }
82
+
83
+ function SheetDescription({ className, ...props }: DialogPrimitive.Description.Props) {
84
+ return (
85
+ <DialogPrimitive.Description
86
+ className={cn("text-sm text-muted-foreground", className)}
87
+ {...props}
88
+ />
89
+ );
90
+ }
91
+
92
+ export { Sheet, SheetClose, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger };
@@ -0,0 +1,10 @@
1
+ import { cn } from "../../lib/utils"
2
+ import { Loader2Icon } from "lucide-react"
3
+
4
+ function Spinner({ className, ...props }: React.ComponentProps<"svg">) {
5
+ return (
6
+ <Loader2Icon role="status" aria-label="Loading" className={cn("size-4 animate-spin", className)} {...props} />
7
+ )
8
+ }
9
+
10
+ export { Spinner }
@@ -0,0 +1,113 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+
5
+ import { cn } from "../../lib/utils";
6
+
7
+ type TabsContextValue = {
8
+ value: string;
9
+ setValue: (value: string) => void;
10
+ };
11
+
12
+ const TabsContext = React.createContext<TabsContextValue | null>(null);
13
+
14
+ function useTabsContext(componentName: string): TabsContextValue {
15
+ const context = React.useContext(TabsContext);
16
+ if (!context) {
17
+ throw new Error(`${componentName} must be used within <Tabs>.`);
18
+ }
19
+ return context;
20
+ }
21
+
22
+ function Tabs({
23
+ children,
24
+ className,
25
+ defaultValue,
26
+ onValueChange,
27
+ value,
28
+ }: React.PropsWithChildren<{
29
+ className?: string;
30
+ defaultValue: string;
31
+ onValueChange?: (value: string) => void;
32
+ value?: string;
33
+ }>) {
34
+ const [uncontrolledValue, setUncontrolledValue] = React.useState(defaultValue);
35
+ const activeValue = value ?? uncontrolledValue;
36
+ const setValue = React.useCallback(
37
+ (nextValue: string) => {
38
+ if (value === undefined) {
39
+ setUncontrolledValue(nextValue);
40
+ }
41
+ onValueChange?.(nextValue);
42
+ },
43
+ [onValueChange, value],
44
+ );
45
+
46
+ return (
47
+ <TabsContext.Provider value={{ value: activeValue, setValue }}>
48
+ <div className={cn("flex flex-col gap-3", className)}>{children}</div>
49
+ </TabsContext.Provider>
50
+ );
51
+ }
52
+
53
+ function TabsList({
54
+ children,
55
+ className,
56
+ }: React.PropsWithChildren<{ className?: string }>) {
57
+ return (
58
+ <div
59
+ aria-orientation="horizontal"
60
+ className={cn(
61
+ "inline-flex h-9 w-fit items-center rounded-lg bg-muted/50 p-1 text-muted-foreground",
62
+ className,
63
+ )}
64
+ role="tablist"
65
+ >
66
+ {children}
67
+ </div>
68
+ );
69
+ }
70
+
71
+ function TabsTrigger({
72
+ children,
73
+ className,
74
+ value,
75
+ }: React.PropsWithChildren<{ className?: string; value: string }>) {
76
+ const context = useTabsContext("TabsTrigger");
77
+ const active = context.value === value;
78
+ return (
79
+ <button
80
+ aria-selected={active}
81
+ className={cn(
82
+ "inline-flex min-w-[7rem] items-center justify-center rounded-md px-3 py-1.5 text-sm font-medium transition-all",
83
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[rgb(var(--ink-solid-rgb)_/_0.18)]",
84
+ active
85
+ ? "bg-primary/10 text-foreground shadow-none ring-1 ring-primary/15"
86
+ : "text-muted-foreground hover:bg-primary/5 hover:text-foreground",
87
+ className,
88
+ )}
89
+ data-state={active ? "active" : "inactive"}
90
+ onClick={() => context.setValue(value)}
91
+ role="tab"
92
+ type="button"
93
+ >
94
+ {children}
95
+ </button>
96
+ );
97
+ }
98
+
99
+ function TabsContent({
100
+ children,
101
+ className,
102
+ value,
103
+ }: React.PropsWithChildren<{ className?: string; value: string }>) {
104
+ const context = useTabsContext("TabsContent");
105
+ if (context.value !== value) return null;
106
+ return (
107
+ <div className={className} data-state="active" role="tabpanel">
108
+ {children}
109
+ </div>
110
+ );
111
+ }
112
+
113
+ export { Tabs, TabsContent, TabsList, TabsTrigger };
@@ -0,0 +1,18 @@
1
+ import * as React from "react"
2
+
3
+ import { cn } from "../../lib/utils"
4
+
5
+ function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
6
+ return (
7
+ <textarea
8
+ data-slot="textarea"
9
+ className={cn(
10
+ "flex field-sizing-content min-h-16 w-full rounded-lg border border-input bg-transparent px-2.5 py-2 text-base transition-colors outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
11
+ className
12
+ )}
13
+ {...props}
14
+ />
15
+ )
16
+ }
17
+
18
+ export { Textarea }
@@ -0,0 +1,66 @@
1
+ "use client"
2
+
3
+ import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip"
4
+
5
+ import { cn } from "../../lib/utils"
6
+
7
+ function TooltipProvider({
8
+ delay = 0,
9
+ ...props
10
+ }: TooltipPrimitive.Provider.Props) {
11
+ return (
12
+ <TooltipPrimitive.Provider
13
+ data-slot="tooltip-provider"
14
+ delay={delay}
15
+ {...props}
16
+ />
17
+ )
18
+ }
19
+
20
+ function Tooltip({ ...props }: TooltipPrimitive.Root.Props) {
21
+ return <TooltipPrimitive.Root data-slot="tooltip" {...props} />
22
+ }
23
+
24
+ function TooltipTrigger({ ...props }: TooltipPrimitive.Trigger.Props) {
25
+ return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
26
+ }
27
+
28
+ function TooltipContent({
29
+ className,
30
+ side = "top",
31
+ sideOffset = 4,
32
+ align = "center",
33
+ alignOffset = 0,
34
+ children,
35
+ ...props
36
+ }: TooltipPrimitive.Popup.Props &
37
+ Pick<
38
+ TooltipPrimitive.Positioner.Props,
39
+ "align" | "alignOffset" | "side" | "sideOffset"
40
+ >) {
41
+ return (
42
+ <TooltipPrimitive.Portal>
43
+ <TooltipPrimitive.Positioner
44
+ align={align}
45
+ alignOffset={alignOffset}
46
+ side={side}
47
+ sideOffset={sideOffset}
48
+ className="isolate z-50"
49
+ >
50
+ <TooltipPrimitive.Popup
51
+ data-slot="tooltip-content"
52
+ className={cn(
53
+ "z-50 inline-flex w-fit max-w-xs origin-(--transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
54
+ className
55
+ )}
56
+ {...props}
57
+ >
58
+ {children}
59
+ <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground data-[side=bottom]:top-1 data-[side=inline-end]:top-1/2! data-[side=inline-end]:-left-1 data-[side=inline-end]:-translate-y-1/2 data-[side=inline-start]:top-1/2! data-[side=inline-start]:-right-1 data-[side=inline-start]:-translate-y-1/2 data-[side=left]:top-1/2! data-[side=left]:-right-1 data-[side=left]:-translate-y-1/2 data-[side=right]:top-1/2! data-[side=right]:-left-1 data-[side=right]:-translate-y-1/2 data-[side=top]:-bottom-2.5" />
60
+ </TooltipPrimitive.Popup>
61
+ </TooltipPrimitive.Positioner>
62
+ </TooltipPrimitive.Portal>
63
+ )
64
+ }
65
+
66
+ export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
@@ -0,0 +1,47 @@
1
+ "use client";
2
+
3
+ import { useEffect, useState } from "react";
4
+ import type { ResolvedTheme } from "../lib/themed-image";
5
+
6
+ const SETTINGS_EVENT = "papyrus:settings-changed";
7
+
8
+ function readResolvedTheme(): ResolvedTheme {
9
+ if (typeof document === "undefined" || typeof window === "undefined") return "light";
10
+ const theme = document.documentElement.dataset.papyrusTheme;
11
+ if (theme === "dark") return "dark";
12
+ if (theme === "light") return "light";
13
+ return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
14
+ }
15
+
16
+ export function useResolvedPapyrusTheme(): ResolvedTheme {
17
+ const [resolvedTheme, setResolvedTheme] = useState<ResolvedTheme>("light");
18
+
19
+ useEffect(() => {
20
+ const media = window.matchMedia("(prefers-color-scheme: dark)");
21
+ const refresh = () => setResolvedTheme(readResolvedTheme());
22
+ refresh();
23
+ media.addEventListener("change", refresh);
24
+ window.addEventListener(SETTINGS_EVENT, refresh);
25
+ window.addEventListener("storage", refresh);
26
+ const observer = new MutationObserver((mutations) => {
27
+ for (const mutation of mutations) {
28
+ if (mutation.type === "attributes" && mutation.attributeName === "data-papyrus-theme") {
29
+ refresh();
30
+ break;
31
+ }
32
+ }
33
+ });
34
+ observer.observe(document.documentElement, {
35
+ attributes: true,
36
+ attributeFilter: ["data-papyrus-theme"],
37
+ });
38
+ return () => {
39
+ media.removeEventListener("change", refresh);
40
+ window.removeEventListener(SETTINGS_EVENT, refresh);
41
+ window.removeEventListener("storage", refresh);
42
+ observer.disconnect();
43
+ };
44
+ }, []);
45
+
46
+ return resolvedTheme;
47
+ }
@@ -0,0 +1,109 @@
1
+ import { Stack, SecretValue } from "aws-cdk-lib";
2
+ import * as iam from "aws-cdk-lib/aws-iam";
3
+ import * as amplify from "aws-cdk-lib/aws-amplify";
4
+ /**
5
+ * Provisions the Amplify *app shell* for a Papyrus publication CMS:
6
+ *
7
+ * - AWS::Amplify::App (WEB_COMPUTE, GitHub repo connection via PAT in Secrets Manager)
8
+ * - AWS::Amplify::Branch (main, with PAPYRUS_SITE_BRAND env vars)
9
+ * - AWS::Amplify::Domain (newsroom.<site>.us on the site's Route 53 zone)
10
+ * - IAM service role (backend deploy) + compute role (SSR rendering)
11
+ *
12
+ * The Papyrus *backend* (AppSync, Storage, Lambda) is NOT created here. It
13
+ * deploys via `ampx pipeline-deploy` from the repo `amplify.yml` once Amplify
14
+ * CI runs a build on the branch. This stack only creates the container the
15
+ * backend deploys into, so the app is never "created manually" in the console.
16
+ *
17
+ * The GitHub access token is read from Secrets Manager by dynamic reference,
18
+ * so the token value never appears in the synthesized template.
19
+ */
20
+ export class AmplifyAppShellStack extends Stack {
21
+ appId;
22
+ defaultDomain;
23
+ constructor(scope, id, config, props) {
24
+ super(scope, id, props);
25
+ const githubToken = SecretValue.secretsManager(config.githubTokenSecretName).unsafeUnwrap();
26
+ // Service role Amplify assumes to deploy the backend (ampx pipeline-deploy
27
+ // creates AppSync, Storage, Lambda, etc., so it needs broad permissions).
28
+ const serviceRole = new iam.Role(this, "AmplifyServiceRole", {
29
+ assumedBy: new iam.ServicePrincipal("amplify.amazonaws.com"),
30
+ description: `Backend deploy service role for Amplify app ${config.appName}`,
31
+ managedPolicies: [iam.ManagedPolicy.fromAwsManagedPolicyName("AdministratorAccess")],
32
+ });
33
+ // WEB_COMPUTE (Next.js SSR) requires a compute role for the rendering server.
34
+ const computeRole = new iam.Role(this, "AmplifyComputeRole", {
35
+ assumedBy: new iam.ServicePrincipal("amplify.amazonaws.com"),
36
+ description: `SSR compute role for Amplify app ${config.appName}`,
37
+ managedPolicies: [
38
+ iam.ManagedPolicy.fromAwsManagedPolicyName("service-role/AWSLambdaBasicExecutionRole"),
39
+ iam.ManagedPolicy.fromAwsManagedPolicyName("CloudWatchLogsFullAccess"),
40
+ ],
41
+ });
42
+ const app = new amplify.CfnApp(this, "App", {
43
+ name: config.appName,
44
+ description: config.description,
45
+ platform: config.platform,
46
+ repository: config.repository,
47
+ accessToken: githubToken,
48
+ iamServiceRole: serviceRole.roleArn,
49
+ computeRoleArn: computeRole.roleArn,
50
+ buildSpec: this.buildSpec(),
51
+ customRules: [{ source: "/<*>", target: "/index.html", status: "404-200" }],
52
+ });
53
+ this.appId = app.attrAppId;
54
+ this.defaultDomain = `${app.attrAppId}.amplifyapp.com`;
55
+ const branch = new amplify.CfnBranch(this, "Branch", {
56
+ appId: app.attrAppId,
57
+ branchName: config.branchName,
58
+ stage: "PRODUCTION",
59
+ enableAutoBuild: config.enableAutoBuild ?? true,
60
+ enablePerformanceMode: false,
61
+ environmentVariables: Object.entries(config.environment).map(([key, value]) => ({
62
+ name: key,
63
+ value,
64
+ })),
65
+ });
66
+ branch.addResourceDependency(app);
67
+ const domain = new amplify.CfnDomain(this, "Domain", {
68
+ appId: app.attrAppId,
69
+ domainName: config.domainName,
70
+ subDomainSettings: [{ branchName: config.branchName, prefix: "" }],
71
+ });
72
+ domain.addResourceDependency(branch);
73
+ this.exportValue(app.attrAppId, { description: `Amplify app id for ${config.appName}` });
74
+ }
75
+ /**
76
+ * Build spec mirrors the repo root amplify.yml so the app deploys the full
77
+ * Papyrus backend + Next.js frontend on every build. Inlined because Amplify
78
+ * needs a buildSpec on the app for the first deploy before the repo is cloned.
79
+ */
80
+ buildSpec() {
81
+ return [
82
+ "version: 1",
83
+ "backend:",
84
+ " phases:",
85
+ " build:",
86
+ " commands:",
87
+ " - npm install --cache .npm --prefer-offline",
88
+ " - |",
89
+ " if [ -z \"${PAPYRUS_CONSOLE_RESPONDER_IMAGE_URI:-}\" ]; then",
90
+ " export PAPYRUS_CONSOLE_RESPONDER_ALLOW_LOCAL_BUILD=true",
91
+ " fi",
92
+ " - npx ampx pipeline-deploy --debug --branch $AWS_BRANCH --app-id $AWS_APP_ID",
93
+ "frontend:",
94
+ " phases:",
95
+ " build:",
96
+ " commands:",
97
+ " - env | grep -e '^PAPYRUS_CONTENT_SOURCE=' -e '^PAPYRUS_EDITION_SLUG=' -e '^PAPYRUS_REVALIDATE_SECRET=' >> .env.production",
98
+ " - npm run build",
99
+ " artifacts:",
100
+ " baseDirectory: .next",
101
+ " files:",
102
+ " - '**/*'",
103
+ " cache:",
104
+ " paths:",
105
+ " - .next/cache/**/*",
106
+ " - .npm/**/*",
107
+ ].join("\n");
108
+ }
109
+ }