@girardmedia/bootspring 1.2.0 → 2.0.3
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.
- package/README.md +107 -14
- package/bin/bootspring.js +166 -27
- package/cli/agent.js +189 -17
- package/cli/analyze.js +499 -0
- package/cli/audit.js +557 -0
- package/cli/auth.js +495 -38
- package/cli/billing.js +302 -0
- package/cli/build.js +695 -0
- package/cli/business.js +109 -26
- package/cli/checkpoint-utils.js +168 -0
- package/cli/checkpoint.js +639 -0
- package/cli/cloud-sync.js +447 -0
- package/cli/content.js +198 -0
- package/cli/context.js +1 -1
- package/cli/deploy.js +543 -0
- package/cli/fundraise.js +112 -50
- package/cli/github-cmd.js +435 -0
- package/cli/health.js +477 -0
- package/cli/init.js +84 -13
- package/cli/legal.js +107 -95
- package/cli/log.js +2 -2
- package/cli/loop.js +976 -73
- package/cli/manager.js +711 -0
- package/cli/metrics.js +480 -0
- package/cli/monitor.js +812 -0
- package/cli/onboard.js +521 -0
- package/cli/orchestrator.js +12 -24
- package/cli/prd.js +594 -0
- package/cli/preseed-start.js +1483 -0
- package/cli/preseed.js +2302 -0
- package/cli/project.js +436 -0
- package/cli/quality.js +233 -0
- package/cli/security.js +913 -0
- package/cli/seed.js +1441 -5
- package/cli/skill.js +273 -211
- package/cli/suggest.js +989 -0
- package/cli/switch.js +453 -0
- package/cli/visualize.js +527 -0
- package/cli/watch.js +769 -0
- package/cli/workspace.js +607 -0
- package/core/analyze-workflow.js +1134 -0
- package/core/api-client.js +535 -22
- package/core/audit-workflow.js +1350 -0
- package/core/build-orchestrator.js +480 -0
- package/core/build-state.js +577 -0
- package/core/checkpoint-engine.js +408 -0
- package/core/config.js +1109 -26
- package/core/context-loader.js +21 -1
- package/core/deploy-workflow.js +836 -0
- package/core/entitlements.js +93 -22
- package/core/github-sync.js +610 -0
- package/core/index.js +8 -1
- package/core/ingest.js +1111 -0
- package/core/metrics-engine.js +768 -0
- package/core/onboard-workflow.js +1007 -0
- package/core/preseed-workflow.js +934 -0
- package/core/preseed.js +1617 -0
- package/core/project-context.js +325 -0
- package/core/project-state.js +694 -0
- package/core/r2-sync.js +583 -0
- package/core/scaffold.js +525 -7
- package/core/session.js +258 -0
- package/core/task-extractor.js +758 -0
- package/core/telemetry.js +28 -6
- package/core/tier-enforcement.js +737 -0
- package/core/utils.js +38 -14
- package/generators/questionnaire.js +15 -12
- package/generators/sections/ai.js +7 -7
- package/generators/sections/content.js +300 -0
- package/generators/sections/index.js +3 -0
- package/generators/sections/plugins.js +7 -6
- package/generators/templates/build-planning.template.js +596 -0
- package/generators/templates/content.template.js +819 -0
- package/generators/templates/index.js +2 -1
- package/hooks/git-autopilot.js +1250 -0
- package/hooks/index.js +9 -0
- package/intelligence/agent-collab.js +2057 -0
- package/intelligence/auto-suggest.js +634 -0
- package/intelligence/content-gen.js +1589 -0
- package/intelligence/cross-project.js +1647 -0
- package/intelligence/index.js +184 -0
- package/intelligence/learning/insights.json +517 -7
- package/intelligence/learning/pattern-learner.js +1008 -14
- package/intelligence/memory/decision-tracker.js +1431 -31
- package/intelligence/memory/decisions.jsonl +0 -0
- package/intelligence/orchestrator.js +2896 -1
- package/intelligence/prd.js +92 -1
- package/intelligence/recommendation-weights.json +14 -2
- package/intelligence/recommendations.js +463 -9
- package/intelligence/workflow-composer.js +1451 -0
- package/marketplace/index.d.ts +324 -0
- package/marketplace/index.js +1921 -0
- package/mcp/contracts/mcp-contract.v1.json +342 -4
- package/mcp/registry.js +680 -3
- package/mcp/response-formatter.js +23 -0
- package/mcp/tools/assist-tool.js +78 -4
- package/mcp/tools/autopilot-tool.js +408 -0
- package/mcp/tools/content-tool.js +571 -0
- package/mcp/tools/dashboard-tool.js +251 -5
- package/mcp/tools/mvp-tool.js +344 -0
- package/mcp/tools/plugin-tool.js +23 -1
- package/mcp/tools/prd-tool.js +579 -0
- package/mcp/tools/seed-tool.js +447 -0
- package/mcp/tools/skill-tool.js +43 -14
- package/mcp/tools/suggest-tool.js +147 -0
- package/package.json +15 -6
- package/agents/README.md +0 -93
- package/agents/ai-integration-expert/context.md +0 -386
- package/agents/api-expert/context.md +0 -416
- package/agents/architecture-expert/context.md +0 -454
- package/agents/auth-expert/context.md +0 -399
- package/agents/backend-expert/context.md +0 -483
- package/agents/business-strategy-expert/context.md +0 -180
- package/agents/code-review-expert/context.md +0 -365
- package/agents/competitive-analysis-expert/context.md +0 -239
- package/agents/data-modeling-expert/context.md +0 -352
- package/agents/database-expert/context.md +0 -250
- package/agents/devops-expert/context.md +0 -446
- package/agents/email-expert/context.md +0 -379
- package/agents/financial-expert/context.md +0 -213
- package/agents/frontend-expert/context.md +0 -364
- package/agents/fundraising-expert/context.md +0 -257
- package/agents/growth-expert/context.md +0 -249
- package/agents/index.js +0 -140
- package/agents/investor-relations-expert/context.md +0 -266
- package/agents/legal-expert/context.md +0 -284
- package/agents/marketing-expert/context.md +0 -236
- package/agents/monitoring-expert/context.md +0 -362
- package/agents/operations-expert/context.md +0 -279
- package/agents/partnerships-expert/context.md +0 -286
- package/agents/payment-expert/context.md +0 -340
- package/agents/performance-expert/context.md +0 -377
- package/agents/private-equity-expert/context.md +0 -246
- package/agents/railway-expert/context.md +0 -284
- package/agents/research-expert/context.md +0 -245
- package/agents/sales-expert/context.md +0 -241
- package/agents/security-expert/context.md +0 -343
- package/agents/testing-expert/context.md +0 -414
- package/agents/ui-ux-expert/context.md +0 -448
- package/agents/vercel-expert/context.md +0 -426
- package/skills/index.js +0 -787
- package/skills/patterns/README.md +0 -163
- package/skills/patterns/ai/agents.md +0 -281
- package/skills/patterns/ai/claude.md +0 -138
- package/skills/patterns/ai/embeddings.md +0 -150
- package/skills/patterns/ai/rag.md +0 -266
- package/skills/patterns/ai/streaming.md +0 -170
- package/skills/patterns/ai/structured-output.md +0 -162
- package/skills/patterns/ai/tools.md +0 -154
- package/skills/patterns/analytics/tracking.md +0 -220
- package/skills/patterns/api/errors.md +0 -296
- package/skills/patterns/api/graphql.md +0 -440
- package/skills/patterns/api/middleware.md +0 -279
- package/skills/patterns/api/openapi.md +0 -285
- package/skills/patterns/api/rate-limiting.md +0 -231
- package/skills/patterns/api/route-handler.md +0 -217
- package/skills/patterns/api/server-action.md +0 -249
- package/skills/patterns/api/versioning.md +0 -443
- package/skills/patterns/api/webhooks.md +0 -247
- package/skills/patterns/auth/clerk.md +0 -132
- package/skills/patterns/auth/mfa.md +0 -313
- package/skills/patterns/auth/nextauth.md +0 -140
- package/skills/patterns/auth/oauth.md +0 -237
- package/skills/patterns/auth/rbac.md +0 -152
- package/skills/patterns/auth/session-management.md +0 -367
- package/skills/patterns/auth/session.md +0 -120
- package/skills/patterns/database/audit.md +0 -177
- package/skills/patterns/database/migrations.md +0 -177
- package/skills/patterns/database/pagination.md +0 -230
- package/skills/patterns/database/pooling.md +0 -357
- package/skills/patterns/database/prisma.md +0 -180
- package/skills/patterns/database/relations.md +0 -187
- package/skills/patterns/database/seeding.md +0 -246
- package/skills/patterns/database/soft-delete.md +0 -153
- package/skills/patterns/database/transactions.md +0 -162
- package/skills/patterns/deployment/ci-cd.md +0 -231
- package/skills/patterns/deployment/docker.md +0 -188
- package/skills/patterns/deployment/monitoring.md +0 -387
- package/skills/patterns/deployment/vercel.md +0 -160
- package/skills/patterns/email/resend.md +0 -143
- package/skills/patterns/email/templates.md +0 -245
- package/skills/patterns/email/transactional.md +0 -503
- package/skills/patterns/email/verification.md +0 -176
- package/skills/patterns/files/download.md +0 -243
- package/skills/patterns/files/upload.md +0 -239
- package/skills/patterns/i18n/nextintl.md +0 -188
- package/skills/patterns/logging/structured.md +0 -292
- package/skills/patterns/notifications/email-queue.md +0 -248
- package/skills/patterns/notifications/push.md +0 -279
- package/skills/patterns/payments/checkout.md +0 -303
- package/skills/patterns/payments/invoices.md +0 -287
- package/skills/patterns/payments/portal.md +0 -245
- package/skills/patterns/payments/stripe.md +0 -272
- package/skills/patterns/payments/subscriptions.md +0 -300
- package/skills/patterns/payments/usage.md +0 -279
- package/skills/patterns/performance/caching.md +0 -276
- package/skills/patterns/performance/code-splitting.md +0 -233
- package/skills/patterns/performance/edge.md +0 -254
- package/skills/patterns/performance/isr.md +0 -266
- package/skills/patterns/performance/lazy-loading.md +0 -281
- package/skills/patterns/realtime/sse.md +0 -327
- package/skills/patterns/realtime/websockets.md +0 -336
- package/skills/patterns/search/filtering.md +0 -329
- package/skills/patterns/search/fulltext.md +0 -260
- package/skills/patterns/security/audit-logging.md +0 -444
- package/skills/patterns/security/csrf.md +0 -234
- package/skills/patterns/security/headers.md +0 -252
- package/skills/patterns/security/sanitization.md +0 -258
- package/skills/patterns/security/secrets.md +0 -261
- package/skills/patterns/security/validation.md +0 -268
- package/skills/patterns/security/xss.md +0 -229
- package/skills/patterns/seo/metadata.md +0 -252
- package/skills/patterns/state/context.md +0 -349
- package/skills/patterns/state/react-query.md +0 -313
- package/skills/patterns/state/url-state.md +0 -482
- package/skills/patterns/state/zustand.md +0 -262
- package/skills/patterns/testing/api.md +0 -259
- package/skills/patterns/testing/component.md +0 -233
- package/skills/patterns/testing/coverage.md +0 -207
- package/skills/patterns/testing/fixtures.md +0 -225
- package/skills/patterns/testing/integration.md +0 -436
- package/skills/patterns/testing/mocking.md +0 -177
- package/skills/patterns/testing/playwright.md +0 -162
- package/skills/patterns/testing/snapshot.md +0 -175
- package/skills/patterns/testing/vitest.md +0 -307
- package/skills/patterns/ui/accordions.md +0 -395
- package/skills/patterns/ui/cards.md +0 -299
- package/skills/patterns/ui/dropdowns.md +0 -476
- package/skills/patterns/ui/empty-states.md +0 -320
- package/skills/patterns/ui/forms.md +0 -405
- package/skills/patterns/ui/inputs.md +0 -319
- package/skills/patterns/ui/layouts.md +0 -282
- package/skills/patterns/ui/loading.md +0 -291
- package/skills/patterns/ui/modals.md +0 -338
- package/skills/patterns/ui/navigation.md +0 -374
- package/skills/patterns/ui/tables.md +0 -407
- package/skills/patterns/ui/toasts.md +0 -300
- package/skills/patterns/ui/tooltips.md +0 -396
- package/skills/patterns/utils/dates.md +0 -435
- package/skills/patterns/utils/errors.md +0 -451
- package/skills/patterns/utils/formatting.md +0 -345
- package/skills/patterns/utils/validation.md +0 -434
- package/templates/bootspring.config.js +0 -83
- package/templates/business/business-model-canvas.md +0 -246
- package/templates/business/business-plan.md +0 -266
- package/templates/business/competitive-analysis.md +0 -312
- package/templates/fundraising/data-room-checklist.md +0 -300
- package/templates/fundraising/investor-research.md +0 -243
- package/templates/fundraising/pitch-deck-outline.md +0 -253
- package/templates/legal/gdpr-checklist.md +0 -339
- package/templates/legal/privacy-policy.md +0 -285
- package/templates/legal/terms-of-service.md +0 -222
- package/templates/mcp.json +0 -9
|
@@ -1,345 +0,0 @@
|
|
|
1
|
-
# Formatting Patterns
|
|
2
|
-
|
|
3
|
-
Patterns for formatting numbers, currency, and text.
|
|
4
|
-
|
|
5
|
-
## Number Formatting
|
|
6
|
-
|
|
7
|
-
```typescript
|
|
8
|
-
// lib/format/numbers.ts
|
|
9
|
-
|
|
10
|
-
// Format number with locale
|
|
11
|
-
export function formatNumber(
|
|
12
|
-
value: number,
|
|
13
|
-
options?: Intl.NumberFormatOptions
|
|
14
|
-
): string {
|
|
15
|
-
return new Intl.NumberFormat('en-US', options).format(value)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Compact number (1K, 1M, etc.)
|
|
19
|
-
export function formatCompact(value: number): string {
|
|
20
|
-
return new Intl.NumberFormat('en-US', {
|
|
21
|
-
notation: 'compact',
|
|
22
|
-
maximumFractionDigits: 1
|
|
23
|
-
}).format(value)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Percentage
|
|
27
|
-
export function formatPercent(value: number, decimals = 1): string {
|
|
28
|
-
return new Intl.NumberFormat('en-US', {
|
|
29
|
-
style: 'percent',
|
|
30
|
-
minimumFractionDigits: decimals,
|
|
31
|
-
maximumFractionDigits: decimals
|
|
32
|
-
}).format(value)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Ordinal (1st, 2nd, 3rd, etc.)
|
|
36
|
-
export function formatOrdinal(value: number): string {
|
|
37
|
-
const pr = new Intl.PluralRules('en-US', { type: 'ordinal' })
|
|
38
|
-
const suffixes: Record<string, string> = {
|
|
39
|
-
one: 'st',
|
|
40
|
-
two: 'nd',
|
|
41
|
-
few: 'rd',
|
|
42
|
-
other: 'th'
|
|
43
|
-
}
|
|
44
|
-
return `${value}${suffixes[pr.select(value)]}`
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Decimal places
|
|
48
|
-
export function formatDecimal(value: number, places = 2): string {
|
|
49
|
-
return value.toFixed(places)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// With units
|
|
53
|
-
export function formatWithUnit(
|
|
54
|
-
value: number,
|
|
55
|
-
unit: string,
|
|
56
|
-
options?: { compact?: boolean }
|
|
57
|
-
): string {
|
|
58
|
-
const formatted = options?.compact ? formatCompact(value) : formatNumber(value)
|
|
59
|
-
return `${formatted} ${unit}${value !== 1 ? 's' : ''}`
|
|
60
|
-
}
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
## Currency Formatting
|
|
64
|
-
|
|
65
|
-
```typescript
|
|
66
|
-
// lib/format/currency.ts
|
|
67
|
-
|
|
68
|
-
interface CurrencyOptions {
|
|
69
|
-
currency?: string
|
|
70
|
-
locale?: string
|
|
71
|
-
compact?: boolean
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export function formatCurrency(
|
|
75
|
-
amount: number,
|
|
76
|
-
options: CurrencyOptions = {}
|
|
77
|
-
): string {
|
|
78
|
-
const { currency = 'USD', locale = 'en-US', compact = false } = options
|
|
79
|
-
|
|
80
|
-
return new Intl.NumberFormat(locale, {
|
|
81
|
-
style: 'currency',
|
|
82
|
-
currency,
|
|
83
|
-
notation: compact ? 'compact' : 'standard',
|
|
84
|
-
maximumFractionDigits: compact ? 1 : 2
|
|
85
|
-
}).format(amount)
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Format cents to dollars
|
|
89
|
-
export function formatCents(cents: number, options?: CurrencyOptions): string {
|
|
90
|
-
return formatCurrency(cents / 100, options)
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Parse currency string to number
|
|
94
|
-
export function parseCurrency(value: string): number {
|
|
95
|
-
const cleaned = value.replace(/[^0-9.-]+/g, '')
|
|
96
|
-
return parseFloat(cleaned) || 0
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Currency input formatter
|
|
100
|
-
export function formatCurrencyInput(value: string): string {
|
|
101
|
-
const num = value.replace(/[^0-9]/g, '')
|
|
102
|
-
if (!num) return ''
|
|
103
|
-
const cents = parseInt(num, 10)
|
|
104
|
-
return formatCents(cents)
|
|
105
|
-
}
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
## Text Formatting
|
|
109
|
-
|
|
110
|
-
```typescript
|
|
111
|
-
// lib/format/text.ts
|
|
112
|
-
|
|
113
|
-
// Truncate with ellipsis
|
|
114
|
-
export function truncate(text: string, maxLength: number): string {
|
|
115
|
-
if (text.length <= maxLength) return text
|
|
116
|
-
return text.slice(0, maxLength - 3) + '...'
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Truncate words
|
|
120
|
-
export function truncateWords(text: string, maxWords: number): string {
|
|
121
|
-
const words = text.split(/\s+/)
|
|
122
|
-
if (words.length <= maxWords) return text
|
|
123
|
-
return words.slice(0, maxWords).join(' ') + '...'
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Capitalize first letter
|
|
127
|
-
export function capitalize(text: string): string {
|
|
128
|
-
return text.charAt(0).toUpperCase() + text.slice(1).toLowerCase()
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Title case
|
|
132
|
-
export function titleCase(text: string): string {
|
|
133
|
-
return text
|
|
134
|
-
.toLowerCase()
|
|
135
|
-
.split(' ')
|
|
136
|
-
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
137
|
-
.join(' ')
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Sentence case
|
|
141
|
-
export function sentenceCase(text: string): string {
|
|
142
|
-
return text.charAt(0).toUpperCase() + text.slice(1).toLowerCase()
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Slugify
|
|
146
|
-
export function slugify(text: string): string {
|
|
147
|
-
return text
|
|
148
|
-
.toLowerCase()
|
|
149
|
-
.trim()
|
|
150
|
-
.replace(/[^\w\s-]/g, '')
|
|
151
|
-
.replace(/[\s_-]+/g, '-')
|
|
152
|
-
.replace(/^-+|-+$/g, '')
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Pluralize
|
|
156
|
-
export function pluralize(
|
|
157
|
-
count: number,
|
|
158
|
-
singular: string,
|
|
159
|
-
plural?: string
|
|
160
|
-
): string {
|
|
161
|
-
if (count === 1) return singular
|
|
162
|
-
return plural ?? `${singular}s`
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Add "a" or "an"
|
|
166
|
-
export function addArticle(word: string): string {
|
|
167
|
-
const vowels = ['a', 'e', 'i', 'o', 'u']
|
|
168
|
-
const article = vowels.includes(word[0].toLowerCase()) ? 'an' : 'a'
|
|
169
|
-
return `${article} ${word}`
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Highlight text
|
|
173
|
-
export function highlightText(
|
|
174
|
-
text: string,
|
|
175
|
-
query: string
|
|
176
|
-
): { text: string; highlighted: boolean }[] {
|
|
177
|
-
if (!query) return [{ text, highlighted: false }]
|
|
178
|
-
|
|
179
|
-
const regex = new RegExp(`(${escapeRegex(query)})`, 'gi')
|
|
180
|
-
const parts = text.split(regex)
|
|
181
|
-
|
|
182
|
-
return parts.map(part => ({
|
|
183
|
-
text: part,
|
|
184
|
-
highlighted: part.toLowerCase() === query.toLowerCase()
|
|
185
|
-
}))
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
function escapeRegex(str: string): string {
|
|
189
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
190
|
-
}
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
## File Size Formatting
|
|
194
|
-
|
|
195
|
-
```typescript
|
|
196
|
-
// lib/format/filesize.ts
|
|
197
|
-
|
|
198
|
-
const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
|
|
199
|
-
|
|
200
|
-
export function formatFileSize(bytes: number, decimals = 2): string {
|
|
201
|
-
if (bytes === 0) return '0 B'
|
|
202
|
-
|
|
203
|
-
const k = 1024
|
|
204
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
|
205
|
-
const size = bytes / Math.pow(k, i)
|
|
206
|
-
|
|
207
|
-
return `${size.toFixed(decimals)} ${units[i]}`
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
export function parseFileSize(sizeString: string): number {
|
|
211
|
-
const match = sizeString.match(/^([\d.]+)\s*([A-Z]+)$/i)
|
|
212
|
-
if (!match) return 0
|
|
213
|
-
|
|
214
|
-
const [, value, unit] = match
|
|
215
|
-
const unitIndex = units.findIndex(u => u.toLowerCase() === unit.toLowerCase())
|
|
216
|
-
if (unitIndex === -1) return 0
|
|
217
|
-
|
|
218
|
-
return parseFloat(value) * Math.pow(1024, unitIndex)
|
|
219
|
-
}
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
## Duration Formatting
|
|
223
|
-
|
|
224
|
-
```typescript
|
|
225
|
-
// lib/format/duration.ts
|
|
226
|
-
|
|
227
|
-
interface DurationParts {
|
|
228
|
-
days: number
|
|
229
|
-
hours: number
|
|
230
|
-
minutes: number
|
|
231
|
-
seconds: number
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
export function parseDuration(ms: number): DurationParts {
|
|
235
|
-
const seconds = Math.floor(ms / 1000) % 60
|
|
236
|
-
const minutes = Math.floor(ms / (1000 * 60)) % 60
|
|
237
|
-
const hours = Math.floor(ms / (1000 * 60 * 60)) % 24
|
|
238
|
-
const days = Math.floor(ms / (1000 * 60 * 60 * 24))
|
|
239
|
-
|
|
240
|
-
return { days, hours, minutes, seconds }
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
export function formatDuration(ms: number, options?: { short?: boolean }): string {
|
|
244
|
-
const { days, hours, minutes, seconds } = parseDuration(ms)
|
|
245
|
-
const parts: string[] = []
|
|
246
|
-
|
|
247
|
-
if (options?.short) {
|
|
248
|
-
if (days > 0) parts.push(`${days}d`)
|
|
249
|
-
if (hours > 0) parts.push(`${hours}h`)
|
|
250
|
-
if (minutes > 0) parts.push(`${minutes}m`)
|
|
251
|
-
if (seconds > 0 || parts.length === 0) parts.push(`${seconds}s`)
|
|
252
|
-
return parts.join(' ')
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if (days > 0) parts.push(`${days} day${days !== 1 ? 's' : ''}`)
|
|
256
|
-
if (hours > 0) parts.push(`${hours} hour${hours !== 1 ? 's' : ''}`)
|
|
257
|
-
if (minutes > 0) parts.push(`${minutes} minute${minutes !== 1 ? 's' : ''}`)
|
|
258
|
-
if (seconds > 0 || parts.length === 0) {
|
|
259
|
-
parts.push(`${seconds} second${seconds !== 1 ? 's' : ''}`)
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
return parts.join(', ')
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// Timer format (HH:MM:SS)
|
|
266
|
-
export function formatTimer(ms: number): string {
|
|
267
|
-
const { hours, minutes, seconds } = parseDuration(ms)
|
|
268
|
-
const pad = (n: number) => n.toString().padStart(2, '0')
|
|
269
|
-
|
|
270
|
-
if (hours > 0) {
|
|
271
|
-
return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`
|
|
272
|
-
}
|
|
273
|
-
return `${pad(minutes)}:${pad(seconds)}`
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// Video duration format
|
|
277
|
-
export function formatVideoDuration(seconds: number): string {
|
|
278
|
-
const mins = Math.floor(seconds / 60)
|
|
279
|
-
const secs = Math.floor(seconds % 60)
|
|
280
|
-
return `${mins}:${secs.toString().padStart(2, '0')}`
|
|
281
|
-
}
|
|
282
|
-
```
|
|
283
|
-
|
|
284
|
-
## React Formatting Components
|
|
285
|
-
|
|
286
|
-
```tsx
|
|
287
|
-
// components/format/Currency.tsx
|
|
288
|
-
interface CurrencyProps {
|
|
289
|
-
value: number
|
|
290
|
-
currency?: string
|
|
291
|
-
compact?: boolean
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
export function Currency({
|
|
295
|
-
value,
|
|
296
|
-
currency = 'USD',
|
|
297
|
-
compact = false
|
|
298
|
-
}: CurrencyProps) {
|
|
299
|
-
const formatted = formatCurrency(value, { currency, compact })
|
|
300
|
-
return <span>{formatted}</span>
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// components/format/Number.tsx
|
|
304
|
-
interface NumberProps {
|
|
305
|
-
value: number
|
|
306
|
-
compact?: boolean
|
|
307
|
-
percent?: boolean
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
export function Number({ value, compact, percent }: NumberProps) {
|
|
311
|
-
if (percent) return <span>{formatPercent(value)}</span>
|
|
312
|
-
if (compact) return <span>{formatCompact(value)}</span>
|
|
313
|
-
return <span>{formatNumber(value)}</span>
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
// components/format/Highlight.tsx
|
|
317
|
-
interface HighlightProps {
|
|
318
|
-
text: string
|
|
319
|
-
query: string
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
export function Highlight({ text, query }: HighlightProps) {
|
|
323
|
-
const parts = highlightText(text, query)
|
|
324
|
-
|
|
325
|
-
return (
|
|
326
|
-
<>
|
|
327
|
-
{parts.map((part, i) => (
|
|
328
|
-
<span
|
|
329
|
-
key={i}
|
|
330
|
-
className={part.highlighted ? 'bg-yellow-200' : undefined}
|
|
331
|
-
>
|
|
332
|
-
{part.text}
|
|
333
|
-
</span>
|
|
334
|
-
))}
|
|
335
|
-
</>
|
|
336
|
-
)
|
|
337
|
-
}
|
|
338
|
-
```
|
|
339
|
-
|
|
340
|
-
## When to Use
|
|
341
|
-
|
|
342
|
-
- Display formatting
|
|
343
|
-
- Data presentation
|
|
344
|
-
- Input formatting
|
|
345
|
-
- Search highlighting
|