@ossy/package-catalog 1.0.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.
@@ -0,0 +1,197 @@
1
+ import React from 'react'
2
+ import { Icon, Page, ContentHeader, Text, View } from '@ossy/design-system'
3
+ import { useApp } from '@ossy/app/shell'
4
+ import { useRouter } from '@ossy/router-react'
5
+ import { countPackageCapabilities } from './formatPaths.js'
6
+ import { ManifestSection } from './ManifestSection.jsx'
7
+ import { PackageHero } from './PackageHero.jsx'
8
+ import {
9
+ enrichActionWithSchema,
10
+ capabilitySchemaMapFromApp,
11
+ } from './ActionCapabilityCard.jsx'
12
+ import { PackageActionsSection } from './PackageActionsSection.jsx'
13
+ import { PackageHowToSection } from './PackageHowToSection.jsx'
14
+ import { PKG_DETAIL_SECTION_IDS } from './packageDetailSections.js'
15
+ import { PackageServiceToggle } from './PackageServiceToggle.jsx'
16
+ import { WidgetCapabilityCard } from './WidgetCapabilityCard.jsx'
17
+ import { pkgStoreEnvelopedSurfaceProps } from './packageStoreStyles.js'
18
+ import { CatalogPageStyles } from './CatalogPageStyles.jsx'
19
+ import {
20
+ filterBackgroundTasks,
21
+ getPackageIcon,
22
+ getPackagePitch,
23
+ slugToDisplayName,
24
+ } from './packageCopy.js'
25
+
26
+ export const metadata = {
27
+ id: 'packages/detail',
28
+ title: 'Capability overview',
29
+ path: {
30
+ en: '/packages/:packageSlug',
31
+ sv: '/paket/:packageSlug',
32
+ },
33
+ }
34
+
35
+ export default function PackageDetailPage () {
36
+ const app = useApp()
37
+ const router = useRouter()
38
+ const slug = router.params.packageSlug
39
+ const pkg = app?.manifestSummary?.packages?.find((p) => p.slug === slug)
40
+
41
+ if (!pkg) {
42
+ return (
43
+ <Page maxWidth="l" gap="m" className="pkg-store-root">
44
+ <Text variant="heading-secondary" as="h2" text="package-catalog.detail.notFound.title" />
45
+ <Text text="package-catalog.detail.notFound.body" params={{ slug: slug || '—' }} />
46
+ </Page>
47
+ )
48
+ }
49
+
50
+ const displayName = slugToDisplayName(pkg.slug)
51
+ const pitch = getPackagePitch(pkg)
52
+ const packageIcon = getPackageIcon(pkg)
53
+ const packageDefinition = app?.manifestSummary?.definitions?.[pkg.slug]
54
+ const totalCapabilities = countPackageCapabilities(pkg)
55
+ const backgroundTasks = filterBackgroundTasks(pkg.tasks)
56
+
57
+ const capabilitySchemas = capabilitySchemaMapFromApp(app?.capabilities)
58
+
59
+ const documentedActions = (pkg.actions || []).map((action) =>
60
+ enrichActionWithSchema(action, capabilitySchemas),
61
+ )
62
+
63
+ const manifestSectionProps = {
64
+ titleVariant: 'heading-tertiary',
65
+ compact: true,
66
+ enveloped: true,
67
+ }
68
+
69
+ return (
70
+ <>
71
+ <CatalogPageStyles />
72
+
73
+ <Page maxWidth="xl" gap="l" className="pkg-store-root">
74
+ <PackageHero
75
+ eyebrow={pkg.package}
76
+ title={displayName}
77
+ text={pitch}
78
+ meta="package-catalog.detail.meta.capabilities"
79
+ metaParams={{
80
+ count: totalCapabilities,
81
+ countSuffix: totalCapabilities === 1 ? 'y' : 'ies',
82
+ }}
83
+ maxWidth="l"
84
+ />
85
+
86
+ <ContentHeader
87
+ inset="none"
88
+ title={displayName}
89
+ descriptionMaxWidth="m"
90
+ lead={<Text maxWidth="m">{pitch}</Text>}
91
+ far={(
92
+ <PackageServiceToggle
93
+ packageName={pkg.package}
94
+ entitlementRequired={packageDefinition?.entitlementRequired}
95
+ />
96
+ )}
97
+ />
98
+
99
+ <PackageHowToSection pkg={pkg} backgroundTasks={backgroundTasks} />
100
+
101
+ {pkg.components?.length > 0 && (
102
+ <View id={PKG_DETAIL_SECTION_IDS.widgets} {...pkgStoreEnvelopedSurfaceProps}>
103
+ <View gap="xs">
104
+ <View layout="row" gap="s" alignItems="center">
105
+ <Icon name="layout-grid" size="m" />
106
+ <Text as="h2" variant="heading-secondary" text="package-catalog.detail.section.widgets.title" />
107
+ </View>
108
+ <Text
109
+ text="package-catalog.detail.section.widgets.subtitle"
110
+ className="pkg-store-prose--narrow"
111
+ />
112
+ </View>
113
+ <View gap="m" layout="row-wrap">
114
+ {pkg.components.map((component) => (
115
+ <WidgetCapabilityCard key={component.id} component={component} icon={packageIcon} />
116
+ ))}
117
+ </View>
118
+ </View>
119
+ )}
120
+
121
+ {documentedActions.length > 0 && (
122
+ <PackageActionsSection far={documentedActions} />
123
+ )}
124
+
125
+ {backgroundTasks.length > 0 && (
126
+ <ManifestSection
127
+ sectionId={PKG_DETAIL_SECTION_IDS.tasks}
128
+ title="package-catalog.detail.section.tasks.title"
129
+ subtitle="package-catalog.detail.section.tasks.subtitle"
130
+ items={backgroundTasks}
131
+ variant="backgroundTasks"
132
+ icon="timer"
133
+ enveloped
134
+ {...manifestSectionProps}
135
+ />
136
+ )}
137
+
138
+ <ManifestSection
139
+ sectionId={PKG_DETAIL_SECTION_IDS.aggregates}
140
+ title="package-catalog.detail.section.aggregates.title"
141
+ subtitle="package-catalog.detail.section.aggregates.subtitle"
142
+ items={pkg.aggregates}
143
+ variant="entities"
144
+ icon="stack"
145
+ enveloped
146
+ {...manifestSectionProps}
147
+ />
148
+
149
+ <ManifestSection
150
+ title="package-catalog.detail.section.pages.title"
151
+ subtitle="package-catalog.detail.section.pages.subtitle"
152
+ items={pkg.pages}
153
+ variant="pages"
154
+ linkPages
155
+ icon="file-document"
156
+ {...manifestSectionProps}
157
+ />
158
+
159
+ <ManifestSection
160
+ title="package-catalog.detail.section.resources.title"
161
+ subtitle="package-catalog.detail.section.resources.subtitle"
162
+ items={pkg.schemas}
163
+ variant="entities"
164
+ icon="database"
165
+ {...manifestSectionProps}
166
+ />
167
+
168
+ <ManifestSection
169
+ title="package-catalog.detail.section.apis.title"
170
+ subtitle="package-catalog.detail.section.apis.subtitle"
171
+ items={pkg.apis}
172
+ variant="secondary"
173
+ icon="code"
174
+ {...manifestSectionProps}
175
+ />
176
+
177
+ <ManifestSection
178
+ title="package-catalog.detail.section.integrations.title"
179
+ subtitle="package-catalog.detail.section.integrations.subtitle"
180
+ items={pkg.integrations}
181
+ variant="entities"
182
+ icon="link"
183
+ {...manifestSectionProps}
184
+ />
185
+
186
+ <ManifestSection
187
+ title="package-catalog.detail.section.emails.title"
188
+ subtitle="package-catalog.detail.section.emails.subtitle"
189
+ items={pkg.emails}
190
+ variant="secondary"
191
+ icon="email"
192
+ {...manifestSectionProps}
193
+ />
194
+ </Page>
195
+ </>
196
+ )
197
+ }
@@ -0,0 +1,194 @@
1
+ /** @type {Record<string, string>} */
2
+ const ICON_BY_SLUG = {
3
+ analytics: 'chart',
4
+ apps: 'layout-grid',
5
+ authentication: 'keyhole',
6
+ automation: 'attribution',
7
+ booking: 'calendar',
8
+ calendar: 'calendar',
9
+ consulting: 'briefcase',
10
+ consultancy: 'briefcase',
11
+ contracts: 'file-document',
12
+ distribution: 'share',
13
+ domains: 'globe',
14
+ email: 'email',
15
+ employees: 'users',
16
+ forecasts: 'chart',
17
+ gmail: 'email',
18
+ home: 'home',
19
+ 'integration-stripe': 'credit-card',
20
+ 'media-tasks': 'image',
21
+ observability: 'activity',
22
+ pages: 'file-document',
23
+ policies: 'shield',
24
+ profile: 'user',
25
+ resources: 'database',
26
+ resumes: 'file-document',
27
+ storage: 'software-upload',
28
+ tasks: 'check-circle',
29
+ themes: 'palette',
30
+ timesheets: 'clock',
31
+ tokens: 'palette',
32
+ tools: 'wrench',
33
+ users: 'users',
34
+ web: 'globe',
35
+ workspaces: 'building',
36
+ }
37
+
38
+ /** @type {Record<string, string>} */
39
+ const PITCH_BY_SLUG = {
40
+ 'app-test': 'Interactive demo shell for exploring Ossy feature packages',
41
+ analytics: 'Product analytics and usage insights',
42
+ apps: 'Multi-app workspace management',
43
+ authentication: 'Sign-in, sessions, and account security',
44
+ automation: 'Workflow automation and triggers',
45
+ booking: 'Booking & scheduling for service providers',
46
+ calendar: 'Calendars, availability, and time slots',
47
+ consulting: 'Consulting profiles and client-facing pages',
48
+ consultancy: 'Consultancy operations and delivery',
49
+ contracts: 'Contracts and agreement workflows',
50
+ distribution: 'Content distribution and publishing',
51
+ domains: 'Custom domains and DNS',
52
+ email: 'Transactional email and templates',
53
+ employees: 'Team members and org structure',
54
+ forecasts: 'Revenue and capacity forecasting',
55
+ gmail: 'Gmail integration and inbox sync',
56
+ home: 'Workspace home and navigation',
57
+ 'integration-stripe': 'Stripe payments and billing',
58
+ 'media-tasks': 'Media processing background jobs',
59
+ observability: 'Logging, metrics, and diagnostics',
60
+ pages: 'Page builder and site structure',
61
+ policies: 'Policies, roles, and permissions',
62
+ profile: 'User profiles and public identity',
63
+ resources: 'Structured resources and content types',
64
+ resumes: 'Résumés and professional portfolios',
65
+ storage: 'Files, folders, and workspace storage',
66
+ tasks: 'Tasks, checklists, and async work',
67
+ themes: 'Themes, branding, and appearance',
68
+ timesheets: 'Time tracking and billable hours',
69
+ tokens: 'Design tokens and theme variables',
70
+ tools: 'Developer tools and utilities',
71
+ users: 'Users, accounts, and membership',
72
+ web: 'Public web surfaces and marketing',
73
+ workspaces: 'Workspaces, teams, and tenancy',
74
+ }
75
+
76
+ /**
77
+ * @param {string} slug
78
+ * @returns {string}
79
+ */
80
+ export function slugToDisplayName (slug) {
81
+ if (!slug) return 'Package'
82
+ return slug
83
+ .split('-')
84
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
85
+ .join(' ')
86
+ }
87
+
88
+ /**
89
+ * @param {{ slug?: string }} pkg
90
+ * @returns {string}
91
+ */
92
+ export function getPackageIcon (pkg) {
93
+ const slug = pkg?.slug
94
+ if (slug && ICON_BY_SLUG[slug]) return ICON_BY_SLUG[slug]
95
+ const highlights = getPackageHighlights(pkg)
96
+ return highlights[0]?.icon || 'package'
97
+ }
98
+
99
+ /**
100
+ * @param {{ slug?: string, package?: string }} pkg
101
+ * @returns {string}
102
+ */
103
+ export function getPackagePitch (pkg) {
104
+ const slug = pkg?.slug
105
+ if (slug && PITCH_BY_SLUG[slug]) return PITCH_BY_SLUG[slug]
106
+ const name = slugToDisplayName(slug || '')
107
+ return `${name} — Ossy feature package`
108
+ }
109
+
110
+ /** Known background task descriptions for package detail showcase. */
111
+ export const SHOWCASE_TASK_DESCRIPTIONS = {
112
+ '@ossy/booking/tasks/send-reminder': 'Hourly cron — emails clients ~24h before their booking.',
113
+ '@ossy/workspaces/tasks/sync-workspace-membership': 'Event-driven — syncs membership when workspace users change.',
114
+ '@ossy/media-tasks/tasks/resize-common-web': 'On image upload — generates thumbnails and gallery sizes.',
115
+ '@ossy/media-tasks/tasks/visual-content-descriptors': 'On image upload — extracts visual descriptors for search.',
116
+ '@ossy/event-store/tasks/aggregate-rebuild': 'Rebuilds aggregate projections from the event stream.',
117
+ }
118
+
119
+ /**
120
+ * Primary invoke tasks use `{feature}/tasks/{intent}` derived from `{feature}/actions/{intent}`; background tasks use other names under `…/tasks/…`.
121
+ *
122
+ * @param {string} id
123
+ * @returns {boolean}
124
+ */
125
+ export function isBackgroundTaskId (id) {
126
+ return typeof id === 'string' && id.includes('/tasks/')
127
+ }
128
+
129
+ /**
130
+ * @param {Array<{ id: string, title?: string, description?: string }> | undefined} tasks
131
+ * @returns {Array<{ id: string, title?: string, description?: string }>}
132
+ */
133
+ export function filterBackgroundTasks (tasks) {
134
+ return (tasks || []).filter((task) => isBackgroundTaskId(task.id))
135
+ }
136
+
137
+ /**
138
+ * @param {{ id: string, title?: string, description?: string }} task
139
+ * @returns {string}
140
+ */
141
+ export function getTaskDisplayTitle (task) {
142
+ if (task.title) return task.title
143
+ const segment = task.id.split('/tasks/').pop()
144
+ return humanizeCapabilityId(segment || task.id)
145
+ }
146
+
147
+ /**
148
+ * @param {{ id: string, description?: string }} task
149
+ * @returns {string | undefined}
150
+ */
151
+ export function getTaskDisplayDescription (task) {
152
+ return task.description || SHOWCASE_TASK_DESCRIPTIONS[task.id]
153
+ }
154
+
155
+ /**
156
+ * @param {string} id
157
+ * @returns {string}
158
+ */
159
+ export function humanizeCapabilityId (id) {
160
+ if (!id) return ''
161
+ const segment = id.includes('/') ? id.split('/').pop() : id
162
+ return segment
163
+ .replace(/[-_]/g, ' ')
164
+ .replace(/\b\w/g, (c) => c.toUpperCase())
165
+ }
166
+
167
+ /**
168
+ * @param {object} pkg
169
+ * @returns {{ key: string, label: string, count: number, icon: string }[]}
170
+ */
171
+ export function getPackageHighlights (pkg) {
172
+ const rows = [
173
+ { key: 'pages', label: 'Pages', count: pkg.pages?.length || 0, icon: 'file-document' },
174
+ { key: 'actions', label: 'Actions', count: pkg.actions?.length || 0, icon: 'bolt' },
175
+ {
176
+ key: 'schemas',
177
+ label: 'Resource types',
178
+ count: pkg.schemas?.length || 0,
179
+ icon: 'database',
180
+ },
181
+ { key: 'apis', label: 'APIs', count: pkg.apis?.length || 0, icon: 'code' },
182
+ { key: 'components', label: 'Components', count: pkg.components?.length || 0, icon: 'layout-grid' },
183
+ { key: 'tasks', label: 'Tasks', count: pkg.tasks?.length || 0, icon: 'check-circle' },
184
+ {
185
+ key: 'integrations',
186
+ label: 'Integrations',
187
+ count: pkg.integrations?.length || 0,
188
+ icon: 'link',
189
+ },
190
+ { key: 'emails', label: 'Emails', count: pkg.emails?.length || 0, icon: 'email' },
191
+ { key: 'aggregates', label: 'Aggregates', count: pkg.aggregates?.length || 0, icon: 'stack' },
192
+ ]
193
+ return rows.filter((r) => r.count > 0)
194
+ }
@@ -0,0 +1,17 @@
1
+ /** Stable anchor ids for package detail capability sections. */
2
+ export const PKG_DETAIL_SECTION_IDS = {
3
+ widgets: 'pkg-section-widgets',
4
+ actions: 'pkg-section-actions',
5
+ tasks: 'pkg-section-tasks',
6
+ aggregates: 'pkg-section-aggregates',
7
+ }
8
+
9
+ /**
10
+ * @param {string} sectionId
11
+ */
12
+ export function scrollToPackageSection (sectionId) {
13
+ const el = document.getElementById(sectionId)
14
+ if (!el) return
15
+ el.scrollIntoView({ behavior: 'smooth', block: 'start' })
16
+ window.history.replaceState(null, '', `#${sectionId}`)
17
+ }
@@ -0,0 +1,305 @@
1
+ /** Enveloping surface wrapper shared by How-to, Actions, tasks, and aggregates sections. */
2
+ export const pkgStoreEnvelopedSurfaceProps = {
3
+ gap: 'm',
4
+ roundness: 'm',
5
+ inset: 'l',
6
+ className: 'pkg-store-envelope',
7
+ }
8
+
9
+ /** Shared storefront styles for package list and detail pages. */
10
+ export const packageStoreStyles = `
11
+ .pkg-store-root {
12
+ height: 100%;
13
+ overflow-y: auto;
14
+ }
15
+
16
+ .pkg-store-scroll-panel {
17
+ padding: var(--space-m) var(--space-l);
18
+ height: 100%;
19
+ overflow-y: auto;
20
+ }
21
+
22
+ .pkg-store-envelope {
23
+ border: 1px solid var(--separator-primary);
24
+ }
25
+
26
+ .pkg-store-prose {
27
+ max-width: 640px;
28
+ }
29
+
30
+ .pkg-store-prose--muted {
31
+ max-width: 640px;
32
+ opacity: 0.85;
33
+ }
34
+
35
+ .pkg-store-prose--narrow {
36
+ max-width: 560px;
37
+ opacity: 0.75;
38
+ }
39
+
40
+ .pkg-store-text-strong {
41
+ font-weight: 600;
42
+ }
43
+
44
+ .pkg-store-text-bold {
45
+ font-weight: 700;
46
+ }
47
+
48
+ .pkg-store-text-center {
49
+ text-align: center;
50
+ }
51
+
52
+ .pkg-store-text-muted {
53
+ opacity: 0.8;
54
+ }
55
+
56
+ .pkg-store-text-dim {
57
+ opacity: 0.85;
58
+ }
59
+
60
+ .pkg-store-mono {
61
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
62
+ font-size: 0.8125rem;
63
+ line-height: 1.4;
64
+ word-break: break-all;
65
+ }
66
+
67
+ .pkg-store-mono--sm {
68
+ font-size: 0.75rem;
69
+ word-break: break-word;
70
+ }
71
+
72
+ .pkg-store-mono--xs {
73
+ font-size: 0.7rem;
74
+ }
75
+
76
+ .pkg-store-card {
77
+ border: 1px solid var(--separator-primary);
78
+ }
79
+
80
+ .pkg-store-howto-card {
81
+ min-width: 280px;
82
+ flex: 1 1 280px;
83
+ max-width: 360px;
84
+ min-height: 160px;
85
+ }
86
+
87
+ .pkg-store-howto-card--link {
88
+ cursor: pointer;
89
+ }
90
+
91
+ .pkg-store-widget-card {
92
+ min-width: 280px;
93
+ flex: 1 1 280px;
94
+ max-width: 360px;
95
+ min-height: 140px;
96
+ border: 1px solid var(--separator-primary);
97
+ }
98
+
99
+ .pkg-store-action-card {
100
+ min-width: 200px;
101
+ flex: 1 1 200px;
102
+ max-width: 280px;
103
+ border: 1px solid var(--separator-primary);
104
+ cursor: pointer;
105
+ text-align: left;
106
+ overflow: visible;
107
+ }
108
+
109
+ .pkg-store-action-card[aria-pressed="true"] {
110
+ border-color: color-mix(in srgb, var(--foreground) 28%, var(--separator-primary));
111
+ background: color-mix(in srgb, var(--foreground) 4%, var(--surface-primary));
112
+ }
113
+
114
+ .pkg-store-action-examples {
115
+ border: 1px solid var(--separator-primary);
116
+ min-width: 0;
117
+ }
118
+
119
+ .pkg-store-package-link {
120
+ width: 200px;
121
+ border: 1px solid var(--separator-primary);
122
+ text-decoration: none;
123
+ color: inherit;
124
+ }
125
+
126
+ .pkg-store-section-link {
127
+ align-self: center;
128
+ opacity: 0.65;
129
+ font-size: 0.8125rem;
130
+ }
131
+
132
+ .pkg-store-hero-cover {
133
+ height: 100%;
134
+ border-radius: var(--space-m);
135
+ padding: var(--space-xl) var(--space-s);
136
+ }
137
+
138
+ .pkg-store-hero-eyebrow {
139
+ opacity: 0.75;
140
+ text-transform: uppercase;
141
+ letter-spacing: 0.06em;
142
+ }
143
+
144
+ .pkg-store-hero-title {
145
+ text-wrap: balance;
146
+ }
147
+
148
+ .pkg-store-hero-subtitle {
149
+ max-width: 800px;
150
+ }
151
+
152
+ .pkg-store-hero-meta {
153
+ opacity: 0.65;
154
+ }
155
+
156
+ .pkg-store-hero-actions {
157
+ flex-wrap: wrap;
158
+ }
159
+
160
+ .pkg-store-search {
161
+ width: 100%;
162
+ max-width: 420px;
163
+ margin: 0 auto;
164
+ }
165
+
166
+ .pkg-store-search [data-component="input"] {
167
+ width: 100%;
168
+ border-radius: var(--space-s);
169
+ padding: var(--space-s) var(--space-m);
170
+ font-size: 1rem;
171
+ line-height: 1.4;
172
+ }
173
+
174
+ .pkg-store-grid-meta {
175
+ opacity: 0.72;
176
+ font-size: 0.9375rem;
177
+ color: var(--text-default-color, var(--foreground));
178
+ }
179
+
180
+ .pkg-store-pages-grid {
181
+ display: grid;
182
+ grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
183
+ gap: var(--space-s);
184
+ }
185
+
186
+ .pkg-store-pages-list {
187
+ display: flex;
188
+ flex-direction: column;
189
+ gap: var(--space-s);
190
+ }
191
+
192
+ .pkg-store-page-row [data-button] {
193
+ padding: var(--space-xs);
194
+ }
195
+
196
+ .pkg-store-actions-split {
197
+ display: grid;
198
+ grid-template-columns: minmax(200px, 1fr) minmax(280px, 1.2fr);
199
+ gap: var(--space-m);
200
+ align-items: start;
201
+ }
202
+
203
+ .pkg-store-actions-list {
204
+ min-width: 0;
205
+ }
206
+
207
+ .pkg-store-action-card:focus-visible {
208
+ outline: 2px solid color-mix(in srgb, var(--foreground) 35%, transparent);
209
+ outline-offset: 2px;
210
+ }
211
+
212
+ @media (max-width: 720px) {
213
+ .pkg-store-actions-split {
214
+ grid-template-columns: 1fr;
215
+ }
216
+ }
217
+
218
+ .pkg-store-entity {
219
+ border: 1px solid var(--separator-primary);
220
+ }
221
+
222
+ .pkg-store-entities-grid,
223
+ .pkg-store-secondary-grid {
224
+ display: grid;
225
+ grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
226
+ gap: var(--space-s);
227
+ }
228
+
229
+ .pkg-store-secondary-item {
230
+ border: 1px solid var(--separator-primary);
231
+ background: color-mix(in srgb, var(--foreground) 4%, var(--surface-primary));
232
+ }
233
+
234
+ .pkg-store-footer {
235
+ padding: var(--space-l) var(--space-m);
236
+ border-radius: var(--space-m);
237
+ border: 1px solid var(--separator-primary);
238
+ background: color-mix(in srgb, var(--foreground) 4%, var(--surface-primary));
239
+ text-align: center;
240
+ }
241
+
242
+ .pkg-store-howto-card,
243
+ .pkg-store-action-card,
244
+ .pkg-store-widget-card {
245
+ overflow: visible;
246
+ }
247
+
248
+ .pkg-store-howto-card--link {
249
+ transition: border-color 0.15s ease, background 0.15s ease;
250
+ }
251
+
252
+ .pkg-store-howto-card--link:hover,
253
+ .pkg-store-howto-card--link:focus-visible {
254
+ border-color: color-mix(in srgb, var(--foreground) 22%, var(--separator-primary));
255
+ background: color-mix(in srgb, var(--foreground) 3%, var(--surface-primary));
256
+ outline: none;
257
+ }
258
+
259
+ #pkg-section-widgets,
260
+ #pkg-section-actions,
261
+ #pkg-section-tasks,
262
+ #pkg-section-aggregates {
263
+ scroll-margin-top: var(--space-l);
264
+ }
265
+
266
+ .pkg-store-card-body,
267
+ .pkg-store-action-card [data-component="text"] {
268
+ overflow-wrap: anywhere;
269
+ word-break: break-word;
270
+ white-space: normal;
271
+ }
272
+
273
+ .pkg-store-search--inline {
274
+ width: 100%;
275
+ max-width: 100%;
276
+ margin: 0;
277
+ }
278
+
279
+ .pkg-store-search--inline [data-component="input"] {
280
+ width: 100%;
281
+ border-radius: var(--space-s);
282
+ padding: var(--space-s) var(--space-m);
283
+ font-size: 1rem;
284
+ line-height: 1.4;
285
+ }
286
+
287
+ [data-component="@ossy/package-catalog/package-hero"] {
288
+ gap: var(--space-m);
289
+ min-height: min(42vh, 420px);
290
+ height: auto;
291
+ background-attachment: scroll;
292
+ box-shadow: inset 0 1px 0 color-mix(in srgb, var(--foreground) 6%, transparent);
293
+ }
294
+
295
+ [data-rounded] {
296
+ border-radius: var(--space-m);
297
+ overflow: hidden;
298
+ }
299
+
300
+ @media (min-width: 900px) {
301
+ [data-component="@ossy/package-catalog/package-hero"] {
302
+ min-height: min(46vh, 520px);
303
+ }
304
+ }
305
+ `