@framed-dev/core 0.1.0

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,3314 @@
1
+ import { SupabaseClient } from '@supabase/supabase-js';
2
+ export { SupabaseClient } from '@supabase/supabase-js';
3
+
4
+ type LocalizedContent = Record<string, string>;
5
+ type LocalizableString = string | LocalizedContent;
6
+ declare const isLocalizedContent: (value: LocalizableString) => value is LocalizedContent;
7
+ declare const getLocalizedValue: (value: LocalizableString | undefined, language: string, primaryLanguage?: string) => string;
8
+ declare const toLocalizedContent: (value: LocalizableString, primaryLanguage: string) => LocalizedContent;
9
+ declare const ensureLocalizedContent: (value: LocalizableString | undefined, primaryLanguage: string) => LocalizedContent;
10
+ declare const str: (value: LocalizableString | undefined, lang?: string) => string;
11
+
12
+ /**
13
+ * i18n utilities - Stub implementations
14
+ *
15
+ * These are placeholder functions for internationalization.
16
+ * In the full SaaS app, this would use react-i18next.
17
+ * For the widget/SDK, translations should be provided by the host app.
18
+ */
19
+ type TranslationOptions = {
20
+ [key: string]: string | number;
21
+ };
22
+ /**
23
+ * Translation function type
24
+ * Returns the translation for a key, or the fallback if provided
25
+ */
26
+ type TFunction = (key: string, fallbackOrOptions?: string | TranslationOptions) => string;
27
+ /**
28
+ * Stub useTranslation hook
29
+ *
30
+ * Returns a simple t function that:
31
+ * - If second arg is a string, returns it as fallback
32
+ * - If second arg is an object with interpolation values, returns key with values interpolated
33
+ * - Otherwise returns the key
34
+ */
35
+ declare function useTranslation(_namespaces?: string[]): {
36
+ t: TFunction;
37
+ };
38
+
39
+ interface Phase2Data {
40
+ pages: PageDetail[];
41
+ }
42
+ interface PageDetail {
43
+ id?: string;
44
+ name: string;
45
+ layout_type: string;
46
+ sections: Section[];
47
+ features: string[];
48
+ }
49
+ interface Section {
50
+ id?: string;
51
+ type: string;
52
+ order: number;
53
+ custom_name?: string;
54
+ title: LocalizableString;
55
+ content: LocalizableString;
56
+ media: MediaInfo[];
57
+ ctas: CTAInfo[];
58
+ additional_content?: AdditionalContentItem[];
59
+ skip_section: boolean;
60
+ skip_reason?: string;
61
+ is_completed?: boolean;
62
+ is_cms_managed?: boolean;
63
+ cms_fields?: string[];
64
+ type_data?: SectionTypeData;
65
+ ai_layout_hints?: {
66
+ columns?: number;
67
+ mediaPosition?: 'left' | 'right' | 'top' | 'bottom' | 'background';
68
+ style?: 'minimal' | 'bold' | 'elegant' | 'playful';
69
+ };
70
+ layout_notes?: string;
71
+ }
72
+ type AdditionalContentType = 'text' | 'image' | 'button' | 'video' | 'heading' | 'list';
73
+ interface AdditionalContentItem {
74
+ id: string;
75
+ type: AdditionalContentType;
76
+ order: number;
77
+ data: {
78
+ text?: LocalizableString;
79
+ media?: MediaInfo;
80
+ cta?: CTAInfo;
81
+ heading?: LocalizableString;
82
+ heading_level?: 'h2' | 'h3' | 'h4';
83
+ list_items?: LocalizableString | string[];
84
+ list_style?: 'bullet' | 'numbered';
85
+ };
86
+ }
87
+ interface SectionTypeData {
88
+ map_address?: string;
89
+ map_zoom?: number;
90
+ form_fields?: string[];
91
+ form_submit_email?: string;
92
+ pricing_tiers?: PricingTier[];
93
+ stats?: Stat[];
94
+ faqs?: FAQ[];
95
+ }
96
+ interface PricingTier {
97
+ name: string;
98
+ price: string;
99
+ features: string[];
100
+ highlighted?: boolean;
101
+ }
102
+ interface Stat {
103
+ number: string;
104
+ label: string;
105
+ suffix?: string;
106
+ }
107
+ interface FAQ {
108
+ question: string;
109
+ answer: string;
110
+ }
111
+ interface MediaInfo {
112
+ type: 'image' | 'video' | 'illustration' | 'icon' | 'none';
113
+ description: LocalizableString;
114
+ alt?: string;
115
+ url: string | null;
116
+ uploaded_file?: {
117
+ name: string;
118
+ size: number;
119
+ type: string;
120
+ path: string;
121
+ };
122
+ }
123
+ interface CTAInfo {
124
+ text: LocalizableString;
125
+ action: string;
126
+ target: string;
127
+ }
128
+
129
+ type PlanTier = 'free' | 'pro' | 'team';
130
+ type AuthMode = 'dev' | 'local' | 'magic_link' | 'framed_login';
131
+ type TaskStatus = 'open' | 'in_progress' | 'done' | 'rejected';
132
+ type TaskType = 'text_change' | 'bug_report' | 'style_change' | 'feature_request' | 'content_update' | 'general_feedback';
133
+ interface FramedConfig {
134
+ projectId: string;
135
+ mode: 'local' | 'sync';
136
+ auth: AuthConfig;
137
+ ai?: AIConfig;
138
+ sync?: SyncConfig;
139
+ widget?: WidgetConfig;
140
+ features?: Partial<WidgetFeatures>;
141
+ }
142
+ interface AuthConfig {
143
+ mode: AuthMode;
144
+ loginRoute?: string;
145
+ allowedEmails?: string[];
146
+ }
147
+ interface AIConfig {
148
+ provider: 'openrouter' | 'anthropic';
149
+ apiKey: string;
150
+ model?: string;
151
+ }
152
+ interface SyncConfig {
153
+ apiKey: string;
154
+ supabaseUrl: string;
155
+ }
156
+ interface WidgetConfig {
157
+ position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
158
+ theme?: 'light' | 'dark' | 'auto';
159
+ hotkey?: string;
160
+ }
161
+ interface WidgetFeatures {
162
+ elementSelect: boolean;
163
+ textEdit: boolean;
164
+ annotate: boolean;
165
+ screenshot: boolean;
166
+ regionSelect: boolean;
167
+ screenRecording: boolean;
168
+ voiceInput: boolean;
169
+ mobilePreview: boolean;
170
+ feedbackDrawer: boolean;
171
+ multiplayer: boolean;
172
+ presence: boolean;
173
+ assignments: boolean;
174
+ mentions: boolean;
175
+ activityFeed: boolean;
176
+ teamRoles: boolean;
177
+ }
178
+ interface Session {
179
+ id: string;
180
+ user: {
181
+ id: string;
182
+ name: string;
183
+ email?: string;
184
+ };
185
+ permissions: {
186
+ canSubmitFeedback: boolean;
187
+ canViewTasks: boolean;
188
+ canEditTasks: boolean;
189
+ };
190
+ limits: UsageLimits;
191
+ expiresAt: string;
192
+ }
193
+ interface UsageLimits {
194
+ feedbackPerMonth: number;
195
+ feedbackUsed: number;
196
+ aiParsesPerMonth: number;
197
+ aiParsesUsed: number;
198
+ projects: number;
199
+ reviewersPerProject: number;
200
+ historyRetentionDays: number;
201
+ }
202
+ interface Task {
203
+ id: string;
204
+ type: TaskType;
205
+ status: TaskStatus;
206
+ element?: ElementInfo;
207
+ page: PageInfo;
208
+ feedback: FeedbackInfo;
209
+ task: TaskInfo;
210
+ attachments?: Attachment[];
211
+ assignment?: Assignment;
212
+ mentions?: Mention[];
213
+ comments?: Comment[];
214
+ meta: TaskMeta;
215
+ }
216
+ interface ElementInfo {
217
+ selector: string;
218
+ xpath: string;
219
+ tagName: string;
220
+ textContent?: string;
221
+ rect: DOMRect;
222
+ }
223
+ interface PageInfo {
224
+ url: string;
225
+ path: string;
226
+ title: string;
227
+ }
228
+ interface FeedbackInfo {
229
+ text: string;
230
+ originalText?: string;
231
+ voiceTranscript?: string;
232
+ }
233
+ interface TaskInfo {
234
+ title: string;
235
+ description: string;
236
+ suggestedChanges?: string;
237
+ }
238
+ interface Attachment {
239
+ id: string;
240
+ type: 'screenshot' | 'recording' | 'annotation';
241
+ url: string;
242
+ thumbnailUrl?: string;
243
+ mimeType: string;
244
+ size: number;
245
+ }
246
+ interface Assignment {
247
+ userId: string;
248
+ userName: string;
249
+ assignedAt: string;
250
+ }
251
+ interface Mention {
252
+ userId: string;
253
+ userName: string;
254
+ position: {
255
+ start: number;
256
+ end: number;
257
+ };
258
+ }
259
+ interface TaskComment {
260
+ id: string;
261
+ userId: string;
262
+ userName: string;
263
+ text: string;
264
+ createdAt: string;
265
+ }
266
+ /** @deprecated Use TaskComment instead */
267
+ type Comment = TaskComment;
268
+ interface TaskMeta {
269
+ createdAt: string;
270
+ updatedAt: string;
271
+ createdBy: string;
272
+ projectId: string;
273
+ }
274
+ interface WidgetDataLayer {
275
+ getSession(): Promise<Session | null>;
276
+ loadTasks(page?: string): Promise<Task[]>;
277
+ saveTask(task: Task): Promise<void>;
278
+ updateTask(id: string, updates: Partial<Task>): Promise<void>;
279
+ uploadFile(file: File): Promise<{
280
+ url: string;
281
+ path: string;
282
+ }>;
283
+ checkLimits(): Promise<LimitStatus>;
284
+ sync?(): Promise<SyncResult>;
285
+ }
286
+ interface LimitStatus {
287
+ canSubmitFeedback: boolean;
288
+ feedbackRemaining: number;
289
+ canUseAI: boolean;
290
+ aiParsesRemaining: number;
291
+ }
292
+ interface SyncResult {
293
+ success: boolean;
294
+ syncedTasks: number;
295
+ errors?: string[];
296
+ }
297
+ type AnnotationMode = 'select' | 'text-edit' | 'highlight' | 'underline' | 'strikethrough' | 'circle' | 'bracket' | 'draw' | null;
298
+ type ViewportMode = 'desktop' | 'tablet' | 'mobile';
299
+ interface Position {
300
+ x: number;
301
+ y: number;
302
+ }
303
+ interface AnnotationData {
304
+ type: string;
305
+ color: string;
306
+ }
307
+ interface PendingAttachment {
308
+ id: string;
309
+ file: File;
310
+ type: string;
311
+ previewUrl: string | null;
312
+ }
313
+ interface ElementHierarchy {
314
+ tagName: string;
315
+ id?: string;
316
+ className?: string;
317
+ }
318
+ interface FeedbackComment {
319
+ id: string;
320
+ content: string;
321
+ contentHtml?: string;
322
+ author: string;
323
+ createdAt: string;
324
+ resolved: boolean;
325
+ pageUrl?: string;
326
+ pageTitle?: string;
327
+ position?: Position;
328
+ selector?: string;
329
+ elementText?: string;
330
+ elementType?: string;
331
+ elementId?: string;
332
+ elementClassName?: string;
333
+ parentHierarchy?: ElementHierarchy[];
334
+ attachments?: {
335
+ url: string;
336
+ type: string;
337
+ fileName: string;
338
+ }[];
339
+ annotation?: AnnotationData;
340
+ textEdit?: {
341
+ originalText: string;
342
+ newText: string;
343
+ };
344
+ viewport?: {
345
+ width: number;
346
+ height: number;
347
+ type: ViewportMode;
348
+ };
349
+ }
350
+ interface WidgetUIState {
351
+ feedbackPanelOpen: boolean;
352
+ quickInputOpen: boolean;
353
+ chatModalOpen: boolean;
354
+ annotationMode: AnnotationMode;
355
+ annotationColor: string;
356
+ selectedElement: Element | null;
357
+ hoveredElement: Element | null;
358
+ originalText: string;
359
+ editedText: string;
360
+ isDrawing: boolean;
361
+ drawingPaths: {
362
+ points: Position[];
363
+ color: string;
364
+ }[];
365
+ comments: FeedbackComment[];
366
+ showResolved: boolean;
367
+ isCapturing: boolean;
368
+ isRegionSelectMode: boolean;
369
+ isRecording: boolean;
370
+ pendingAttachments: PendingAttachment[];
371
+ richTextMode: boolean;
372
+ viewportMode: ViewportMode;
373
+ currentPageUrl: string;
374
+ }
375
+ /**
376
+ * SDK Configuration for dual-client architecture.
377
+ * - User's Supabase → Data storage (projects, tasks, files)
378
+ * - Framed API → AI/premium features
379
+ */
380
+ interface SDKConfig {
381
+ supabaseUrl: string;
382
+ supabaseKey: string;
383
+ framedApiKey?: string;
384
+ projectId?: string;
385
+ theme?: 'light' | 'dark';
386
+ }
387
+ /**
388
+ * Result wrapper for AI-enhanced operations.
389
+ * Indicates whether AI was used or fallback occurred.
390
+ */
391
+ interface AIFeatureResult<T> {
392
+ data: T;
393
+ wasEnhanced: boolean;
394
+ fallbackReason?: string;
395
+ }
396
+ /**
397
+ * Generic upload result for file operations.
398
+ */
399
+ interface UploadResult {
400
+ url: string;
401
+ name: string;
402
+ type?: string;
403
+ size?: number;
404
+ format?: string;
405
+ }
406
+ /**
407
+ * Result from AI translation service.
408
+ */
409
+ interface TranslationResult {
410
+ translations: Record<string, string>;
411
+ errors: Record<string, string>;
412
+ }
413
+ /**
414
+ * Callback interface for wizard components.
415
+ * These are implemented by the SDK using the dual-client architecture.
416
+ */
417
+ interface WizardCallbacks {
418
+ onUploadFile: (file: File) => Promise<UploadResult>;
419
+ onUploadFont: (file: File) => Promise<UploadResult>;
420
+ onLogoUpload: (file: File) => Promise<void>;
421
+ onTranslateAll: (value: Record<string, string>, primaryLanguage: string, targetLanguages: string[]) => Promise<TranslationResult>;
422
+ }
423
+
424
+ /**
425
+ * Section completion status for Phase 2 navigation.
426
+ */
427
+ type SectionStatus = 'empty' | 'in_progress' | 'completed' | 'skipped';
428
+ /**
429
+ * Global subsection types for Phase 2 navigation.
430
+ */
431
+ type GlobalsSubType = 'navigation' | 'footer' | 'cta' | 'sitemap' | 'media' | 'review';
432
+ /**
433
+ * Phase 2 navigation state.
434
+ */
435
+ interface Phase2NavigationState {
436
+ activePageIndex: number;
437
+ activeSectionIndex: number;
438
+ activeGlobalsType: GlobalsSubType | null;
439
+ isGlobalsExpanded: boolean;
440
+ totalSections: number;
441
+ }
442
+ /**
443
+ * Phase 2 navigation actions.
444
+ */
445
+ interface Phase2NavigationActions {
446
+ setActivePageIndex: (index: number) => void;
447
+ setActiveSectionIndex: (index: number) => void;
448
+ setActiveGlobalsType: (type: GlobalsSubType | null) => void;
449
+ setIsGlobalsExpanded: (expanded: boolean) => void;
450
+ goToNextSection: () => void;
451
+ goToPreviousSection: () => void;
452
+ goToPage: (pageIndex: number, sectionIndex?: number) => void;
453
+ goToGlobals: (type: GlobalsSubType) => void;
454
+ }
455
+ /**
456
+ * Get page completion status based on sections.
457
+ */
458
+ declare const getPageStatus: (page: PageDetail) => SectionStatus;
459
+ /**
460
+ * Get section completion status.
461
+ */
462
+ declare const getSectionStatus: (section: Section) => SectionStatus;
463
+
464
+ declare const PLAN_LIMITS: Record<PlanTier, UsageLimits>;
465
+ declare const FEATURE_DEFAULTS: Record<PlanTier, WidgetFeatures>;
466
+ declare function canSubmitFeedback(limits: UsageLimits): boolean;
467
+ declare function canUseAI(limits: UsageLimits): boolean;
468
+ declare function getFeaturesForTier(tier: PlanTier): WidgetFeatures;
469
+ declare function mergeFeatures(tier: PlanTier, overrides?: Partial<WidgetFeatures>): WidgetFeatures;
470
+
471
+ /**
472
+ * Core modules in the Framed application
473
+ * - build: Discovery (Phase 1), Project Details (Phase 2), Overview & Export (Phase 3)
474
+ * - review: Feedback & Review (Phase 4)
475
+ * - manage: Manage & Maintain (Phase 5)
476
+ */
477
+ type ModuleType = 'build' | 'review' | 'manage';
478
+ /**
479
+ * Mapping of modules to their constituent phases
480
+ */
481
+ declare const MODULE_PHASES: Record<ModuleType, number[]>;
482
+ /**
483
+ * Mapping of phases to their parent module
484
+ */
485
+ declare const PHASE_TO_MODULE: Record<number, ModuleType>;
486
+ /**
487
+ * Module display information
488
+ */
489
+ interface ModuleInfo {
490
+ id: ModuleType;
491
+ name: string;
492
+ shortName: string;
493
+ description: string;
494
+ phases: number[];
495
+ icon: string;
496
+ }
497
+ declare const MODULES: ModuleInfo[];
498
+ /**
499
+ * Module limits per agency (null = unlimited)
500
+ */
501
+ interface ModuleLimits {
502
+ build: number | null;
503
+ review: number | null;
504
+ manage: number | null;
505
+ }
506
+ /**
507
+ * Module usage statistics for an agency
508
+ */
509
+ interface ModuleUsageStats {
510
+ module: ModuleType;
511
+ used: number;
512
+ limit: number | null;
513
+ remaining: number | null;
514
+ percentage: number | null;
515
+ }
516
+ /**
517
+ * Plan configuration with module limits
518
+ */
519
+ interface PlanConfiguration {
520
+ id: string;
521
+ plan_type: PlanType;
522
+ display_name: string;
523
+ description: string;
524
+ build_limit: number | null;
525
+ review_limit: number | null;
526
+ manage_limit: number | null;
527
+ total_project_limit: number | null;
528
+ included_features: string[];
529
+ price_monthly_cents?: number;
530
+ price_yearly_cents?: number;
531
+ currency: string;
532
+ is_active: boolean;
533
+ sort_order: number;
534
+ }
535
+ type PlanType = 'oneoff' | 'silver' | 'gold';
536
+ type SubscriptionStatus = 'active' | 'expired' | 'cancelled' | 'trialing';
537
+ interface ProjectLike {
538
+ project_type?: 'standard' | 'review_only' | 'manage_only';
539
+ enabled_modules?: ModuleType[] | null;
540
+ visible_modules?: ModuleType[] | null;
541
+ disabled_phases?: number[];
542
+ }
543
+ interface AgencyLike {
544
+ plan_type?: PlanType;
545
+ module_limits?: ModuleLimits;
546
+ }
547
+ /**
548
+ * Get default enabled modules based on project type
549
+ */
550
+ declare const getDefaultModules: (project: ProjectLike) => ModuleType[];
551
+ /**
552
+ * Check if a module is enabled for a project
553
+ * Enabled modules have the feature available (can be accessed)
554
+ */
555
+ declare const isModuleEnabled: (project: ProjectLike | null, module: ModuleType) => boolean;
556
+ /**
557
+ * Check if a module is visible in the UI for a project
558
+ * Visible modules show in navigation (enabled + not hidden)
559
+ */
560
+ declare const isModuleVisible: (project: ProjectLike | null, module: ModuleType) => boolean;
561
+ /**
562
+ * Check if a specific phase is accessible based on module configuration
563
+ */
564
+ declare const isPhaseAccessible: (project: ProjectLike | null, phase: number) => boolean;
565
+ /**
566
+ * Check if a specific phase should be shown in navigation
567
+ * Combines module visibility with legacy disabled_phases support
568
+ */
569
+ declare const isPhaseVisible: (project: ProjectLike | null, phase: number) => boolean;
570
+ /**
571
+ * Get the phases that are visible for a project (shown in navigation)
572
+ */
573
+ declare const getVisiblePhases: (project: ProjectLike | null) => number[];
574
+ /**
575
+ * Get the phases that are accessible for a project (can be navigated to, even if hidden from nav)
576
+ */
577
+ declare const getAccessiblePhases: (project: ProjectLike | null) => number[];
578
+ /**
579
+ * Get the first accessible phase for a project
580
+ * Uses accessible phases (not just visible) so hidden phases can still be accessed
581
+ */
582
+ declare const getFirstAccessiblePhase: (project: ProjectLike | null) => number;
583
+ /**
584
+ * Get the first visible phase for a project
585
+ * Used for dashboard navigation - respects hidden phases (e.g., completed review phase)
586
+ */
587
+ declare const getFirstVisiblePhase: (project: ProjectLike | null) => number;
588
+ /**
589
+ * Get module limits for an agency based on plan type or explicit limits
590
+ */
591
+ declare const getModuleLimits: (agency: AgencyLike | null) => ModuleLimits;
592
+ /**
593
+ * Check if a module limit is unlimited
594
+ */
595
+ declare const isModuleUnlimited: (agency: AgencyLike | null, module: ModuleType) => boolean;
596
+ /**
597
+ * Get display name for a module
598
+ */
599
+ declare const getModuleDisplayName: (module: ModuleType) => string;
600
+ /**
601
+ * Get module description
602
+ */
603
+ declare const getModuleDescription: (module: ModuleType) => string;
604
+ /**
605
+ * Get phases included in a module as a readable string
606
+ */
607
+ declare const getModulePhasesLabel: (module: ModuleType) => string;
608
+
609
+ /**
610
+ * Project categories - defines the type of digital product being specified
611
+ * @default 'website' for backwards compatibility
612
+ */
613
+ type ProjectCategory = 'website' | 'saas' | 'mobile_app' | 'ecommerce' | 'api_backend' | 'custom';
614
+ type MobilePlatform = 'ios' | 'android' | 'cross_platform' | 'pwa';
615
+ type SaasCategory = 'b2b' | 'b2c' | 'internal' | 'marketplace';
616
+ type ApiStyle = 'rest' | 'graphql' | 'grpc' | 'websocket';
617
+ /**
618
+ * Screen types - universal term for pages/screens/endpoints across project types
619
+ */
620
+ type ScreenType = 'page' | 'screen' | 'endpoint' | 'flow' | 'dashboard' | 'modal' | 'email';
621
+ /**
622
+ * Universal screen definition - replaces "pages" concept for multi-type support
623
+ */
624
+ interface ScreenDefinition {
625
+ id: string;
626
+ name: string;
627
+ type: ScreenType;
628
+ order: number;
629
+ description?: LocalizableString;
630
+ features: string[];
631
+ ai_generated?: boolean;
632
+ template_source?: string;
633
+ website_page?: WebsitePageConfig;
634
+ app_screen?: AppScreenConfig;
635
+ api_endpoint?: ApiEndpointConfig;
636
+ user_flow?: UserFlowConfig;
637
+ }
638
+ interface WebsitePageConfig {
639
+ is_landing_page?: boolean;
640
+ seo_priority?: 'high' | 'medium' | 'low';
641
+ content_type?: 'static' | 'dynamic' | 'cms_managed';
642
+ }
643
+ interface AppScreenConfig {
644
+ navigation_type: 'stack' | 'tab' | 'drawer' | 'modal';
645
+ has_header: boolean;
646
+ has_bottom_nav: boolean;
647
+ scroll_behavior: 'scroll' | 'fixed' | 'paginated';
648
+ gesture_interactions: string[];
649
+ }
650
+ interface ApiEndpointConfig {
651
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
652
+ path: string;
653
+ authentication_required: boolean;
654
+ rate_limited: boolean;
655
+ request_body?: DataModelReference;
656
+ response_body?: DataModelReference;
657
+ }
658
+ interface UserFlowConfig {
659
+ steps: FlowStep[];
660
+ entry_points: string[];
661
+ exit_points: string[];
662
+ branching_logic?: BranchingRule[];
663
+ }
664
+ interface FlowStep {
665
+ id: string;
666
+ name: string;
667
+ screen_id?: string;
668
+ description?: string;
669
+ order: number;
670
+ }
671
+ interface BranchingRule {
672
+ condition: string;
673
+ target_step_id: string;
674
+ }
675
+ interface DataModelReference {
676
+ model_name: string;
677
+ fields?: string[];
678
+ }
679
+ /**
680
+ * SaaS application specific details
681
+ */
682
+ interface SaasDetails {
683
+ category: SaasCategory;
684
+ has_user_accounts: boolean;
685
+ user_types: string[];
686
+ subscription_model: 'free' | 'freemium' | 'paid' | 'enterprise' | null;
687
+ key_features: string[];
688
+ integrations_needed: string[];
689
+ data_sensitivity: 'public' | 'private' | 'sensitive' | 'regulated';
690
+ estimated_users: 'small' | 'medium' | 'large' | 'enterprise';
691
+ real_time_features?: boolean;
692
+ collaboration_features?: boolean;
693
+ }
694
+ /**
695
+ * Mobile app specific details
696
+ */
697
+ interface MobileAppDetails {
698
+ platforms: MobilePlatform[];
699
+ distribution: 'app_store' | 'enterprise' | 'sideload';
700
+ offline_support: boolean;
701
+ push_notifications: boolean;
702
+ native_features: string[];
703
+ monetization: 'free' | 'paid' | 'freemium' | 'ads' | 'subscription';
704
+ minimum_os_versions?: {
705
+ ios?: string;
706
+ android?: string;
707
+ };
708
+ tablet_support?: boolean;
709
+ }
710
+ /**
711
+ * API/Backend specific details
712
+ */
713
+ interface ApiBackendDetails {
714
+ api_style: ApiStyle;
715
+ authentication: 'none' | 'api_key' | 'oauth' | 'jwt' | 'custom';
716
+ consumers: string[];
717
+ estimated_requests: 'low' | 'medium' | 'high' | 'enterprise';
718
+ data_models: DataModelDefinition[];
719
+ versioning_strategy?: 'url' | 'header' | 'query_param';
720
+ rate_limiting?: boolean;
721
+ webhooks?: boolean;
722
+ }
723
+ interface DataModelDefinition {
724
+ name: string;
725
+ description?: string;
726
+ fields: DataFieldDefinition[];
727
+ relationships?: DataRelationship[];
728
+ }
729
+ interface DataFieldDefinition {
730
+ name: string;
731
+ type: 'string' | 'number' | 'boolean' | 'date' | 'array' | 'object' | 'file';
732
+ required: boolean;
733
+ description?: string;
734
+ }
735
+ interface DataRelationship {
736
+ target_model: string;
737
+ type: 'one_to_one' | 'one_to_many' | 'many_to_many';
738
+ field_name: string;
739
+ }
740
+ /**
741
+ * Enhanced e-commerce details (upgraded from existing ecommerce_details)
742
+ */
743
+ interface EnhancedEcommerceDetails {
744
+ product_types: string;
745
+ payment_methods: string[];
746
+ shipping_info: string;
747
+ has_existing_products: boolean;
748
+ inventory_management: boolean;
749
+ multi_vendor: boolean;
750
+ subscription_products: boolean;
751
+ digital_products: boolean;
752
+ international_shipping: boolean;
753
+ tax_handling: 'simple' | 'complex' | 'automated';
754
+ product_variants: boolean;
755
+ reviews_enabled: boolean;
756
+ wishlist_enabled: boolean;
757
+ }
758
+ type QuestionType = 'text' | 'textarea' | 'select' | 'multiselect' | 'boolean' | 'scale' | 'conditional';
759
+ type QuestionCategory = 'core' | 'feature' | 'technical' | 'business' | 'ux' | 'integration';
760
+ /**
761
+ * AI-generated question for dynamic discovery
762
+ */
763
+ interface AIQuestion {
764
+ id: string;
765
+ question: LocalizableString;
766
+ type: QuestionType;
767
+ options?: QuestionOption[];
768
+ validation?: ValidationRule;
769
+ depends_on?: DependencyRule;
770
+ ai_generated: boolean;
771
+ priority: number;
772
+ category: QuestionCategory;
773
+ help_text?: LocalizableString;
774
+ placeholder?: LocalizableString;
775
+ }
776
+ interface QuestionOption {
777
+ value: string;
778
+ label: LocalizableString;
779
+ description?: LocalizableString;
780
+ icon?: string;
781
+ }
782
+ interface ValidationRule {
783
+ required?: boolean;
784
+ min_length?: number;
785
+ max_length?: number;
786
+ pattern?: string;
787
+ custom_message?: LocalizableString;
788
+ }
789
+ interface DependencyRule {
790
+ question_id: string;
791
+ operator: 'equals' | 'not_equals' | 'contains' | 'not_contains' | 'exists';
792
+ value: unknown;
793
+ }
794
+ /**
795
+ * AI conversation context for dynamic questioning
796
+ */
797
+ interface SuggestedPage {
798
+ id: string;
799
+ name: string;
800
+ reason: string;
801
+ }
802
+ interface AIConversation {
803
+ project_idea: string;
804
+ inferred_category?: ProjectCategory;
805
+ confidence_score?: number;
806
+ detected_features?: string[];
807
+ suggested_pages?: SuggestedPage[];
808
+ follow_up_questions: AIQuestion[];
809
+ answers: Record<string, unknown>;
810
+ conversation_history?: AIConversationTurn[];
811
+ analyzed_context?: {
812
+ url_insights?: string;
813
+ document_insights?: string;
814
+ };
815
+ }
816
+ /**
817
+ * Project context for AI analysis
818
+ * Allows users to provide additional context like existing websites, briefs, or documents
819
+ */
820
+ interface ProjectContextFile {
821
+ name: string;
822
+ url: string;
823
+ type: 'pdf' | 'doc' | 'image' | 'text';
824
+ size?: number;
825
+ extracted_text?: string;
826
+ }
827
+ interface ProjectContext {
828
+ existing_url?: string;
829
+ context_text?: string;
830
+ context_files?: ProjectContextFile[];
831
+ }
832
+ interface AIConversationTurn {
833
+ role: 'system' | 'user' | 'assistant';
834
+ content: string;
835
+ timestamp: string;
836
+ }
837
+ interface GlobalElements {
838
+ navigation: {
839
+ logo_position: 'left' | 'center' | 'right';
840
+ menu_style: 'horizontal' | 'hamburger' | 'sidebar';
841
+ menu_items: LocalizableString;
842
+ cta_button?: {
843
+ text: LocalizableString;
844
+ action: string;
845
+ style: 'primary' | 'secondary' | 'outline';
846
+ };
847
+ };
848
+ footer: {
849
+ columns: number;
850
+ content: LocalizableString;
851
+ show_social: boolean;
852
+ social_links?: string;
853
+ copyright: LocalizableString;
854
+ };
855
+ global_cta: {
856
+ primary_cta: LocalizableString;
857
+ secondary_cta?: LocalizableString;
858
+ };
859
+ }
860
+ interface PageContent {
861
+ description: LocalizableString;
862
+ features: string[];
863
+ }
864
+ interface Reference {
865
+ url: string;
866
+ notes: string;
867
+ }
868
+ /**
869
+ * AI-generated project overview shown between Phase 1 and Phase 2
870
+ * Allows users to review and edit AI's understanding before proceeding
871
+ */
872
+ interface ProjectOverview {
873
+ /** High-level project summary (2-3 sentences) */
874
+ project_summary: string;
875
+ /** Key features extracted from discovery (3-5 items) */
876
+ key_features: string[];
877
+ /** Target audience summary */
878
+ target_audience_summary: string;
879
+ /** Design direction based on style keywords */
880
+ design_direction: string;
881
+ /** Purpose of each page/screen */
882
+ recommended_pages: Array<{
883
+ name: string;
884
+ purpose: string;
885
+ }>;
886
+ /** When the overview was last edited by user */
887
+ edited_at?: string;
888
+ /** User's choice for content approach */
889
+ content_approach?: 'manual' | 'ai_generated' | 'scrape_existing';
890
+ /** When the overview was generated */
891
+ generated_at?: string;
892
+ /** Imported content from existing website (when content_approach is 'scrape_existing') */
893
+ imported_content?: ImportedContentData;
894
+ }
895
+ /**
896
+ * Data structure for content imported from an existing website
897
+ */
898
+ interface ImportedContentData {
899
+ /** Source URL that was crawled */
900
+ source_url: string;
901
+ /** When the content was imported */
902
+ imported_at: string;
903
+ /** Crawled pages with their content */
904
+ pages: ImportedPageData[];
905
+ /** Downloaded images */
906
+ images: ImportedImageData[];
907
+ }
908
+ interface ImportedPageData {
909
+ /** Page name/title */
910
+ name: string;
911
+ /** Original URL */
912
+ url: string;
913
+ /** Content sections */
914
+ sections: ImportedSectionData[];
915
+ }
916
+ interface ImportedSectionData {
917
+ id: string;
918
+ name: string;
919
+ order: number;
920
+ blocks: ImportedBlockData[];
921
+ }
922
+ interface ImportedBlockData {
923
+ id: string;
924
+ type: 'heading' | 'text' | 'list' | 'button';
925
+ tagName: string;
926
+ content: string;
927
+ order: number;
928
+ level?: number;
929
+ items?: string[];
930
+ href?: string;
931
+ }
932
+ interface ImportedImageData {
933
+ /** Original URL from source site */
934
+ originalUrl: string;
935
+ /** Stored URL in our bucket */
936
+ storedUrl: string;
937
+ /** Alt text */
938
+ alt?: string;
939
+ /** Source page URL */
940
+ pageUrl: string;
941
+ }
942
+ /**
943
+ * Phase 1 Data - Discovery phase
944
+ */
945
+ interface Phase1Data {
946
+ /**
947
+ * Project category - determines the type of digital product
948
+ * @default 'website' for backwards compatibility
949
+ */
950
+ project_category?: ProjectCategory;
951
+ /**
952
+ * AI conversation context for dynamic questioning
953
+ */
954
+ ai_conversation?: AIConversation;
955
+ /**
956
+ * Universal screens array - replaces pages for multi-type support
957
+ * For websites, these map to pages; for apps, to screens; for APIs, to endpoints
958
+ */
959
+ screens?: ScreenDefinition[];
960
+ /**
961
+ * Category-specific details
962
+ */
963
+ saas_details?: SaasDetails;
964
+ mobile_details?: MobileAppDetails;
965
+ api_details?: ApiBackendDetails;
966
+ enhanced_ecommerce_details?: EnhancedEcommerceDetails;
967
+ /**
968
+ * Project context for AI analysis
969
+ * Allows users to provide existing website URLs, briefs, or documents
970
+ */
971
+ project_context?: ProjectContext;
972
+ business_name: string;
973
+ industry: string;
974
+ goal: LocalizableString;
975
+ audience: LocalizableString;
976
+ primary_device: 'mobile' | 'desktop' | 'both';
977
+ /** @deprecated Use project_category === 'ecommerce' and enhanced_ecommerce_details instead */
978
+ is_ecommerce: boolean;
979
+ /** @deprecated Use enhanced_ecommerce_details instead */
980
+ ecommerce_details?: {
981
+ product_types: string;
982
+ payment_methods: string[];
983
+ shipping_info: string;
984
+ has_existing_products: boolean;
985
+ };
986
+ /** @deprecated Use screens array instead */
987
+ pages: string[];
988
+ /** @deprecated Use screens array instead */
989
+ custom_pages: string[];
990
+ page_names: Record<string, string>;
991
+ page_content: Record<string, PageContent>;
992
+ legal_content_preference: 'has_content' | 'needs_creation' | 'not_sure';
993
+ has_logo: boolean;
994
+ logo_url: string | null;
995
+ colors: {
996
+ primary: string | null;
997
+ secondary: string | null;
998
+ accent: string | null;
999
+ no_idea: boolean;
1000
+ };
1001
+ font: {
1002
+ primary: string | null;
1003
+ secondary: string | null;
1004
+ custom_fonts: string[];
1005
+ custom_font_files: {
1006
+ name: string;
1007
+ url: string;
1008
+ format: 'woff' | 'woff2' | 'ttf' | 'otf';
1009
+ }[];
1010
+ no_idea: boolean;
1011
+ };
1012
+ style_keywords: string[];
1013
+ references: Reference[];
1014
+ globals?: GlobalElements;
1015
+ functionality?: {
1016
+ cms: {
1017
+ type: 'none' | 'custom' | 'existing' | 'headless';
1018
+ existing_cms?: string;
1019
+ cms_features?: string[];
1020
+ };
1021
+ languages: {
1022
+ is_multilingual: boolean;
1023
+ primary_language: string;
1024
+ additional_languages: string[];
1025
+ translation_method?: 'manual' | 'automated' | 'hybrid';
1026
+ };
1027
+ integrations: {
1028
+ analytics: string[];
1029
+ marketing: string[];
1030
+ social: string[];
1031
+ payment: string[];
1032
+ other: string[];
1033
+ };
1034
+ authentication: {
1035
+ needs_auth: boolean;
1036
+ auth_type?: 'social' | 'email' | 'sso' | 'custom';
1037
+ user_roles?: string[];
1038
+ features?: string[];
1039
+ };
1040
+ technical: {
1041
+ hosting_preference?: 'managed' | 'self-hosted' | 'no_preference';
1042
+ performance_priority: 'speed' | 'features' | 'balanced';
1043
+ accessibility_level: 'basic' | 'wcag_aa' | 'wcag_aaa';
1044
+ seo_requirements?: string[];
1045
+ };
1046
+ additional: string[];
1047
+ };
1048
+ homepage_layout?: string;
1049
+ /**
1050
+ * Cached AI-enhanced prompt data to avoid re-generating
1051
+ */
1052
+ enhanced_prompts?: {
1053
+ enhanced_prompt?: string;
1054
+ planning_section?: string;
1055
+ project_instructions_md?: string;
1056
+ generated_at?: string;
1057
+ };
1058
+ /**
1059
+ * AI-generated project overview for Phase 1 → Phase 2 transition
1060
+ * Stores the overview summary and user's content approach choice
1061
+ */
1062
+ overview?: ProjectOverview;
1063
+ }
1064
+
1065
+ interface Phase3Data {
1066
+ generated_prompt: string;
1067
+ json_export: Record<string, unknown>;
1068
+ submitted_at?: string;
1069
+ submitted_by?: string;
1070
+ }
1071
+ /**
1072
+ * Supported AI tools for export generation
1073
+ */
1074
+ type AIToolType = 'bolt' | 'lovable' | 'v0' | 'cursor' | 'claude' | '10web' | 'windsurf' | 'notion';
1075
+ /**
1076
+ * Export format options
1077
+ */
1078
+ type ExportFormat = 'long_prompt' | 'short_prompt' | 'markdown_file';
1079
+ /**
1080
+ * AI tool configuration with metadata
1081
+ */
1082
+ interface AIToolConfig {
1083
+ id: AIToolType;
1084
+ name: string;
1085
+ description: string;
1086
+ icon?: string;
1087
+ supportedFormats: ExportFormat[];
1088
+ promptStyle?: 'detailed' | 'concise' | 'conversational';
1089
+ maxTokens?: number;
1090
+ }
1091
+ /**
1092
+ * Status of a build spec todo item
1093
+ */
1094
+ type BuildSpecItemStatus = 'pending' | 'in_progress' | 'completed' | 'skipped';
1095
+ /**
1096
+ * A single todo item in the build spec
1097
+ */
1098
+ interface BuildSpecItem {
1099
+ id: string;
1100
+ title: string;
1101
+ description?: string;
1102
+ status: BuildSpecItemStatus;
1103
+ order: number;
1104
+ pageName?: string;
1105
+ sectionId?: string;
1106
+ content?: {
1107
+ text?: string;
1108
+ images?: {
1109
+ url: string;
1110
+ alt?: string;
1111
+ caption?: string;
1112
+ }[];
1113
+ };
1114
+ createdAt: string;
1115
+ createdBy?: string;
1116
+ updatedAt?: string;
1117
+ updatedBy?: string;
1118
+ completedAt?: string;
1119
+ completedBy?: string;
1120
+ notes?: string;
1121
+ }
1122
+ /**
1123
+ * A section grouping related build spec items
1124
+ */
1125
+ interface BuildSpecSection {
1126
+ id: string;
1127
+ title: string;
1128
+ description?: string;
1129
+ order: number;
1130
+ type: 'overview' | 'page' | 'global' | 'technical';
1131
+ pageName?: string;
1132
+ items: BuildSpecItem[];
1133
+ collapsed?: boolean;
1134
+ }
1135
+ /**
1136
+ * Generated export for an AI tool
1137
+ */
1138
+ interface BuildSpecExport {
1139
+ id: string;
1140
+ toolType: AIToolType;
1141
+ format: ExportFormat;
1142
+ content: string;
1143
+ generatedAt: string;
1144
+ generatedBy: string;
1145
+ fileName?: string;
1146
+ fileSize?: number;
1147
+ }
1148
+ /**
1149
+ * Build spec phase data stored in project.data.buildSpec
1150
+ */
1151
+ interface BuildSpecData {
1152
+ sections: BuildSpecSection[];
1153
+ autoGeneratedAt?: string;
1154
+ autoGeneratedFrom?: 'phase1' | 'phase2' | 'both';
1155
+ lastManualEditAt?: string;
1156
+ lastManualEditBy?: string;
1157
+ exports?: BuildSpecExport[];
1158
+ lastExportAt?: string;
1159
+ preferredTool?: AIToolType;
1160
+ editorContent?: string;
1161
+ totalItems?: number;
1162
+ completedItems?: number;
1163
+ }
1164
+
1165
+ interface FeedbackRound {
1166
+ id: string;
1167
+ round_number: number;
1168
+ started_at: string;
1169
+ started_by: string;
1170
+ started_by_name: string;
1171
+ ended_at?: string;
1172
+ ended_by?: string;
1173
+ ended_by_name?: string;
1174
+ status: 'active' | 'feedback_complete' | 'closed';
1175
+ feedback_completed_at?: string;
1176
+ feedback_completed_by?: string;
1177
+ feedback_completed_by_name?: string;
1178
+ summary?: FeedbackRoundSummary;
1179
+ screenshot_version?: number;
1180
+ }
1181
+ interface FeedbackRoundSummary {
1182
+ total_items: number;
1183
+ resolved_in_round: number;
1184
+ carried_over: number;
1185
+ unable_to_resolve: number;
1186
+ new_items_added: number;
1187
+ }
1188
+ interface ResolutionImportResult {
1189
+ imported_at: string;
1190
+ imported_by: string;
1191
+ imported_by_name: string;
1192
+ round_number: number;
1193
+ matched_items: number;
1194
+ resolved_count: number;
1195
+ unable_count: number;
1196
+ unmatched_ids: string[];
1197
+ unmatched_notes: string;
1198
+ }
1199
+ type ResolutionStatus = 'resolved' | 'unable_to_resolve' | 'needs_clarification';
1200
+ interface ParsedResolutionItem {
1201
+ feedback_id: string;
1202
+ status: ResolutionStatus;
1203
+ description: string;
1204
+ question?: string;
1205
+ }
1206
+ type ImageStatus = 'pending' | 'approved' | 'changes_requested';
1207
+ interface PageImage {
1208
+ id: string;
1209
+ page_name: string;
1210
+ image_url: string;
1211
+ alt_text?: string;
1212
+ detected_from: 'crawl' | 'manual' | 'upload';
1213
+ dimensions?: {
1214
+ width: number;
1215
+ height: number;
1216
+ };
1217
+ file_size?: number;
1218
+ status: ImageStatus;
1219
+ approved_by?: string;
1220
+ approved_by_name?: string;
1221
+ approved_at?: string;
1222
+ suggested_replacement?: {
1223
+ url: string;
1224
+ uploaded_by: string;
1225
+ uploaded_by_name: string;
1226
+ uploaded_at: string;
1227
+ reason?: string;
1228
+ };
1229
+ comments?: ImageComment[];
1230
+ created_at: string;
1231
+ updated_at: string;
1232
+ }
1233
+ interface ImageComment {
1234
+ id: string;
1235
+ user_id: string;
1236
+ user_name: string;
1237
+ user_role: UserRole;
1238
+ comment: string;
1239
+ created_at: string;
1240
+ edited_at?: string;
1241
+ }
1242
+ type SectionSuggestionStatus = 'pending' | 'approved' | 'rejected';
1243
+ interface SectionSuggestion {
1244
+ id: string;
1245
+ page_name: string;
1246
+ type: string;
1247
+ title: LocalizableString;
1248
+ content: LocalizableString;
1249
+ media_type: 'image' | 'video' | 'illustration' | 'icon' | 'none';
1250
+ media_description?: LocalizableString;
1251
+ notes?: string;
1252
+ suggested_by: string;
1253
+ suggested_by_name: string;
1254
+ suggested_by_role: UserRole;
1255
+ suggested_at: string;
1256
+ status: SectionSuggestionStatus;
1257
+ reviewed_by?: string;
1258
+ reviewed_by_name?: string;
1259
+ reviewed_at?: string;
1260
+ review_notes?: string;
1261
+ }
1262
+ interface ContentBlock {
1263
+ id: string;
1264
+ type: 'heading' | 'text' | 'list';
1265
+ tagName: string;
1266
+ originalContent: string;
1267
+ currentContent: string;
1268
+ order: number;
1269
+ level?: number;
1270
+ items?: string[];
1271
+ currentItems?: string[];
1272
+ }
1273
+ interface ContentSection {
1274
+ id: string;
1275
+ name: string;
1276
+ order: number;
1277
+ blocks: ContentBlock[];
1278
+ collapsed?: boolean;
1279
+ }
1280
+ interface TextEdit {
1281
+ id: string;
1282
+ blockId: string;
1283
+ sectionId: string;
1284
+ previousContent: string;
1285
+ newContent: string;
1286
+ editedAt: string;
1287
+ editedBy: string;
1288
+ editedByName: string;
1289
+ editedByRole: UserRole;
1290
+ editType: 'modify' | 'restore';
1291
+ comment?: string;
1292
+ resolved?: boolean;
1293
+ resolvedAt?: string;
1294
+ resolvedBy?: string;
1295
+ resolvedByName?: string;
1296
+ }
1297
+ interface LanguageContent {
1298
+ language: string;
1299
+ pageTitle?: string;
1300
+ sections: ContentSection[];
1301
+ crawledAt: string;
1302
+ crawledBy: string;
1303
+ editHistory: TextEdit[];
1304
+ }
1305
+ interface PageTextContent {
1306
+ pageUrl: string;
1307
+ languages: Record<string, LanguageContent>;
1308
+ primaryLanguage: string;
1309
+ version: number;
1310
+ }
1311
+ interface CrawledTextElement {
1312
+ id: string;
1313
+ selector: string;
1314
+ tagName: string;
1315
+ elementType: CrawledElementType;
1316
+ originalText: string;
1317
+ currentText: string;
1318
+ order: number;
1319
+ parentSelector?: string;
1320
+ }
1321
+ type CrawledElementType = 'heading' | 'paragraph' | 'link' | 'button' | 'label' | 'list-item' | 'other';
1322
+ interface PageVersion {
1323
+ version_number: number;
1324
+ created_at: string;
1325
+ created_by: string;
1326
+ screenshot_desktop: ScreenshotInfo | null;
1327
+ screenshot_mobile: ScreenshotInfo | null;
1328
+ notes?: string;
1329
+ approved?: boolean;
1330
+ approved_by?: string;
1331
+ approved_by_name?: string;
1332
+ approved_at?: string;
1333
+ round_number?: number;
1334
+ }
1335
+ interface ScreenshotInfo {
1336
+ url: string;
1337
+ uploaded_at: string;
1338
+ uploaded_by: string;
1339
+ file_name: string;
1340
+ file_size: number;
1341
+ }
1342
+ interface AttachmentTranscription {
1343
+ text: string;
1344
+ language?: string;
1345
+ duration?: number;
1346
+ segments?: Array<{
1347
+ start: number;
1348
+ end: number;
1349
+ text: string;
1350
+ }>;
1351
+ }
1352
+ interface CommentAttachment {
1353
+ id: string;
1354
+ file_name: string;
1355
+ file_url: string;
1356
+ file_type: string;
1357
+ file_size: number;
1358
+ uploaded_at: string;
1359
+ transcription?: AttachmentTranscription;
1360
+ }
1361
+ interface PageComment {
1362
+ id: string;
1363
+ user_id: string;
1364
+ user_name: string;
1365
+ user_role: UserRole;
1366
+ version_number: number;
1367
+ screenshot_type: 'desktop' | 'mobile';
1368
+ comment: string;
1369
+ position: {
1370
+ x: number;
1371
+ y: number;
1372
+ };
1373
+ created_at: string;
1374
+ updated_at?: string;
1375
+ edited_at?: string;
1376
+ resolved?: boolean;
1377
+ parent_id?: string;
1378
+ replies?: PageComment[];
1379
+ mentions?: string[];
1380
+ attachments?: CommentAttachment[];
1381
+ metadata?: {
1382
+ reviewType?: 'screenshot' | 'live';
1383
+ viewMode?: 'desktop' | 'mobile';
1384
+ elementMetadata?: unknown;
1385
+ pageUrl?: string;
1386
+ pagePath?: string;
1387
+ cssSelector?: string;
1388
+ source?: 'widget' | 'screenshot' | 'app' | 'content_editor';
1389
+ changeType?: 'modified' | 'added' | 'deleted';
1390
+ originalContent?: string;
1391
+ newContent?: string;
1392
+ paragraphIndex?: number;
1393
+ hasScreenshot?: boolean;
1394
+ screenshotUrl?: string;
1395
+ screenshotType?: 'region' | 'full';
1396
+ annotationsCount?: number;
1397
+ };
1398
+ selector?: string;
1399
+ element_html?: string;
1400
+ element_text?: string;
1401
+ element_type?: string;
1402
+ element_styles?: Record<string, string>;
1403
+ viewport_size?: {
1404
+ width: number;
1405
+ height: number;
1406
+ };
1407
+ created_in_round?: number;
1408
+ resolved_in_round?: number;
1409
+ resolution_status?: ResolutionStatus;
1410
+ resolution_description?: string;
1411
+ needs_clarification?: boolean;
1412
+ clarification_suggestion?: string;
1413
+ }
1414
+ interface Phase4Page {
1415
+ page_name: string;
1416
+ page_url?: string;
1417
+ versions: PageVersion[];
1418
+ comments: PageComment[];
1419
+ textContent?: PageTextContent;
1420
+ is_virtual_view?: boolean;
1421
+ parent_url?: string;
1422
+ }
1423
+ interface Phase4Data {
1424
+ demo_url?: string;
1425
+ pages: Phase4Page[];
1426
+ suggestions?: SectionSuggestion[];
1427
+ rounds?: FeedbackRound[];
1428
+ current_round?: number;
1429
+ import_history?: ResolutionImportResult[];
1430
+ project_approved?: boolean;
1431
+ project_approved_at?: string;
1432
+ project_approved_by?: string;
1433
+ project_approved_by_name?: string;
1434
+ completed_at?: string;
1435
+ completed_by?: string;
1436
+ completed_by_name?: string;
1437
+ onboarding_completed?: boolean;
1438
+ page_images?: PageImage[];
1439
+ }
1440
+ interface BaseFeedItem {
1441
+ id: string;
1442
+ pageName: string;
1443
+ pageUrl?: string;
1444
+ timestamp: string;
1445
+ userId: string;
1446
+ userName: string;
1447
+ userRole: UserRole;
1448
+ }
1449
+ interface FeedbackFeedItem extends BaseFeedItem {
1450
+ type: 'feedback';
1451
+ comment: PageComment;
1452
+ screenshotUrl?: string;
1453
+ versionNumber: number;
1454
+ screenshotType: 'desktop' | 'mobile';
1455
+ position: {
1456
+ x: number;
1457
+ y: number;
1458
+ };
1459
+ createdInRound?: number;
1460
+ resolvedInRound?: number;
1461
+ resolutionStatus?: ResolutionStatus;
1462
+ resolutionDescription?: string;
1463
+ }
1464
+ interface TextEditFeedItem extends BaseFeedItem {
1465
+ type: 'text_edit';
1466
+ edit: TextEdit;
1467
+ language: string;
1468
+ sectionName: string;
1469
+ blockType: 'heading' | 'text' | 'list';
1470
+ tagName: string;
1471
+ originalContent: string;
1472
+ newContent: string;
1473
+ }
1474
+ interface ImageFeedItem extends BaseFeedItem {
1475
+ type: 'image_feedback';
1476
+ imageId: string;
1477
+ imageUrl: string;
1478
+ imageAltText?: string;
1479
+ imageStatus: ImageStatus;
1480
+ comment: ImageComment;
1481
+ usedOnPages: string[];
1482
+ replacementUrl?: string;
1483
+ }
1484
+ type FeedItem = FeedbackFeedItem | TextEditFeedItem | ImageFeedItem;
1485
+ declare const isFeedbackItem: (item: FeedItem) => item is FeedbackFeedItem;
1486
+ declare const isTextEditItem: (item: FeedItem) => item is TextEditFeedItem;
1487
+ declare const isImageFeedItem: (item: FeedItem) => item is ImageFeedItem;
1488
+ interface WidgetSession {
1489
+ id: string;
1490
+ project_id: string;
1491
+ token: string;
1492
+ created_by: string;
1493
+ created_at: string;
1494
+ expires_at: string;
1495
+ last_activity: string;
1496
+ guest_name?: string;
1497
+ page_url?: string;
1498
+ user_agent?: string;
1499
+ is_active: boolean;
1500
+ }
1501
+ interface WidgetSessionCreate {
1502
+ project_id: string;
1503
+ page_url?: string;
1504
+ guest_name?: string;
1505
+ }
1506
+ interface WidgetSessionValidation {
1507
+ valid: boolean;
1508
+ error?: string;
1509
+ session?: {
1510
+ id: string;
1511
+ project_id: string;
1512
+ expires_at: string;
1513
+ guest_name?: string;
1514
+ };
1515
+ project?: {
1516
+ id: string;
1517
+ name: string;
1518
+ status: ProjectStatus;
1519
+ pages: {
1520
+ page_name: string;
1521
+ page_url: string;
1522
+ comment_count: number;
1523
+ has_approved_version: boolean;
1524
+ }[];
1525
+ };
1526
+ }
1527
+
1528
+ /**
1529
+ * Categories for maintenance items
1530
+ */
1531
+ type MaintenanceCategory = 'bug_fix' | 'performance' | 'security' | 'content_update' | 'design_tweak' | 'new_feature' | 'new_section' | 'new_page' | 'campaign' | 'seo' | 'accessibility' | 'technical_debt' | 'other';
1532
+ /**
1533
+ * Priority levels for maintenance items
1534
+ */
1535
+ type MaintenancePriority = 'critical' | 'high' | 'medium' | 'low';
1536
+ /**
1537
+ * Status workflow for maintenance items
1538
+ * backlog → scheduled → in_progress → review → completed
1539
+ * ↓
1540
+ * wont_fix
1541
+ */
1542
+ type MaintenanceItemStatus = 'backlog' | 'scheduled' | 'in_progress' | 'review' | 'completed' | 'wont_fix';
1543
+ /**
1544
+ * Configuration for recurring maintenance tasks
1545
+ */
1546
+ interface RecurringConfig {
1547
+ enabled: boolean;
1548
+ frequency: 'weekly' | 'monthly' | 'quarterly';
1549
+ next_due: string;
1550
+ last_completed?: string;
1551
+ }
1552
+ /**
1553
+ * Comment on a maintenance item
1554
+ */
1555
+ interface MaintenanceComment {
1556
+ id: string;
1557
+ user_id: string;
1558
+ user_name: string;
1559
+ user_role?: UserRole;
1560
+ comment: string;
1561
+ created_at: string;
1562
+ edited_at?: string;
1563
+ attachments?: CommentAttachment[];
1564
+ parent_id?: string;
1565
+ replies?: MaintenanceComment[];
1566
+ is_internal?: boolean;
1567
+ mentions?: string[];
1568
+ }
1569
+ /**
1570
+ * Status transition history for maintenance items
1571
+ */
1572
+ interface StatusTransition {
1573
+ from_status: MaintenanceItemStatus;
1574
+ to_status: MaintenanceItemStatus;
1575
+ changed_at: string;
1576
+ changed_by: string;
1577
+ changed_by_name: string;
1578
+ }
1579
+ /**
1580
+ * A single maintenance item (bug, feature request, improvement, etc.)
1581
+ */
1582
+ interface MaintenanceItem {
1583
+ id: string;
1584
+ title: string;
1585
+ description: string;
1586
+ category: MaintenanceCategory;
1587
+ priority: MaintenancePriority;
1588
+ status: MaintenanceItemStatus;
1589
+ page_name?: string;
1590
+ page_url?: string;
1591
+ element_selector?: string;
1592
+ screenshot_url?: string;
1593
+ position?: {
1594
+ x: number;
1595
+ y: number;
1596
+ };
1597
+ scheduled_for?: string;
1598
+ due_date?: string;
1599
+ estimated_hours?: number;
1600
+ recurring?: RecurringConfig;
1601
+ source?: 'widget' | 'manual' | string;
1602
+ created_at: string;
1603
+ created_by: string;
1604
+ created_by_name: string;
1605
+ created_by_role?: UserRole;
1606
+ updated_at?: string;
1607
+ resolved_at?: string;
1608
+ resolved_by?: string;
1609
+ resolved_by_name?: string;
1610
+ resolution_notes?: string;
1611
+ comments?: MaintenanceComment[];
1612
+ attachments?: CommentAttachment[];
1613
+ related_items?: string[];
1614
+ blocks?: string[];
1615
+ blocked_by?: string[];
1616
+ status_history?: StatusTransition[];
1617
+ total_time_minutes?: number;
1618
+ metadata?: {
1619
+ type?: 'text-edit' | 'general-feedback' | 'element-feedback';
1620
+ originalContent?: string;
1621
+ newContent?: string;
1622
+ source?: 'widget' | 'manual' | 'phase4';
1623
+ aiValidation?: {
1624
+ clarityScore?: number;
1625
+ conversationThread?: Array<{
1626
+ role: 'user' | 'ai';
1627
+ content: string;
1628
+ timestamp: string;
1629
+ type?: string;
1630
+ }>;
1631
+ summary?: {
1632
+ clarificationQuestions?: string[];
1633
+ finalClarityScore?: number;
1634
+ wasInitiallyClear?: boolean;
1635
+ };
1636
+ };
1637
+ [key: string]: unknown;
1638
+ };
1639
+ }
1640
+ /**
1641
+ * Summary stats for a maintenance cycle
1642
+ */
1643
+ interface MaintenanceCycleSummary {
1644
+ total_items: number;
1645
+ completed: number;
1646
+ carried_over: number;
1647
+ new_items_added: number;
1648
+ hours_spent?: number;
1649
+ }
1650
+ /**
1651
+ * A maintenance cycle (similar to FeedbackRound in Phase 4)
1652
+ */
1653
+ interface MaintenanceCycle {
1654
+ id: string;
1655
+ cycle_number: number;
1656
+ name: string;
1657
+ type: 'sprint' | 'monthly' | 'quarterly' | 'ad_hoc';
1658
+ started_at: string;
1659
+ started_by: string;
1660
+ started_by_name: string;
1661
+ ended_at?: string;
1662
+ ended_by?: string;
1663
+ ended_by_name?: string;
1664
+ status: 'active' | 'completed' | 'cancelled';
1665
+ summary?: MaintenanceCycleSummary;
1666
+ }
1667
+ /**
1668
+ * Quick stats cache for Phase 5 dashboard
1669
+ */
1670
+ interface MaintenanceStats {
1671
+ total_items: number;
1672
+ open_items: number;
1673
+ completed_items: number;
1674
+ critical_count: number;
1675
+ high_count: number;
1676
+ by_category: Record<MaintenanceCategory, number>;
1677
+ last_updated: string;
1678
+ }
1679
+ /**
1680
+ * Credential type for categorization
1681
+ */
1682
+ type CredentialType = 'hosting' | 'cms' | 'ftp' | 'database' | 'email' | 'api' | 'other';
1683
+ /**
1684
+ * Stored credential for accessing client sites
1685
+ * Used in Phase 5 for maintenance access
1686
+ */
1687
+ interface StoredCredential {
1688
+ id: string;
1689
+ name: string;
1690
+ type?: CredentialType;
1691
+ url?: string;
1692
+ username?: string;
1693
+ password?: string;
1694
+ notes?: string;
1695
+ created_at: string;
1696
+ created_by: string;
1697
+ created_by_name: string;
1698
+ updated_at?: string;
1699
+ updated_by?: string;
1700
+ updated_by_name?: string;
1701
+ }
1702
+ /**
1703
+ * Quote status workflow
1704
+ * draft → pending_review → quoted → approved/rejected → in_progress → completed
1705
+ */
1706
+ type QuoteStatus = 'draft' | 'pending_review' | 'quoted' | 'approved' | 'rejected' | 'in_progress' | 'completed';
1707
+ /**
1708
+ * A quote/estimate for a maintenance item
1709
+ */
1710
+ interface MaintenanceQuote {
1711
+ id: string;
1712
+ status: QuoteStatus;
1713
+ estimated_hours?: number;
1714
+ hourly_rate?: number;
1715
+ fixed_price?: number;
1716
+ currency: 'EUR' | 'USD' | 'GBP';
1717
+ line_items?: {
1718
+ description: string;
1719
+ hours?: number;
1720
+ amount: number;
1721
+ }[];
1722
+ quote_notes?: string;
1723
+ valid_until?: string;
1724
+ client_response_notes?: string;
1725
+ responded_at?: string;
1726
+ responded_by?: string;
1727
+ responded_by_name?: string;
1728
+ quoted_at?: string;
1729
+ quoted_by?: string;
1730
+ quoted_by_name?: string;
1731
+ revision_number: number;
1732
+ previous_quotes?: MaintenanceQuote[];
1733
+ }
1734
+ /**
1735
+ * Activity log entry for maintenance items
1736
+ */
1737
+ interface MaintenanceActivity {
1738
+ id: string;
1739
+ item_id: string;
1740
+ type: 'created' | 'status_changed' | 'priority_changed' | 'assigned' | 'comment_added' | 'quote_requested' | 'quote_submitted' | 'quote_approved' | 'quote_rejected' | 'attachment_added' | 'completed';
1741
+ user_id: string;
1742
+ user_name: string;
1743
+ user_role?: UserRole;
1744
+ timestamp: string;
1745
+ details?: {
1746
+ from?: string;
1747
+ to?: string;
1748
+ comment?: string;
1749
+ amount?: number;
1750
+ };
1751
+ }
1752
+ /**
1753
+ * Extended maintenance item with quote support
1754
+ */
1755
+ interface MaintenanceItemExtended extends MaintenanceItem {
1756
+ quote?: MaintenanceQuote;
1757
+ requires_quote?: boolean;
1758
+ assigned_to?: string;
1759
+ assigned_to_name?: string;
1760
+ activity?: MaintenanceActivity[];
1761
+ is_urgent?: boolean;
1762
+ is_pinned?: boolean;
1763
+ client_visible?: boolean;
1764
+ }
1765
+ /**
1766
+ * Phase 5: Manage & Maintain data structure
1767
+ * For ongoing post-launch maintenance and feedback
1768
+ */
1769
+ interface Phase5Data {
1770
+ live_url?: string;
1771
+ staging_url?: string;
1772
+ items: MaintenanceItem[];
1773
+ cycles?: MaintenanceCycle[];
1774
+ current_cycle?: number;
1775
+ stats?: MaintenanceStats;
1776
+ sla?: {
1777
+ critical_response_hours: number;
1778
+ high_response_hours: number;
1779
+ medium_response_hours: number;
1780
+ low_response_hours: number;
1781
+ };
1782
+ credentials?: StoredCredential[];
1783
+ pricing?: {
1784
+ default_hourly_rate: number;
1785
+ currency: 'EUR' | 'USD' | 'GBP';
1786
+ };
1787
+ }
1788
+ /**
1789
+ * Time entry for logging work done on maintenance items
1790
+ */
1791
+ interface TimeEntry {
1792
+ id: string;
1793
+ project_id: string;
1794
+ maintenance_item_id?: string;
1795
+ user_id: string;
1796
+ agency_id?: string;
1797
+ logged_date: string;
1798
+ duration_minutes: number;
1799
+ description?: string;
1800
+ billable: boolean;
1801
+ user_name?: string;
1802
+ created_at: string;
1803
+ updated_at: string;
1804
+ }
1805
+ /**
1806
+ * Hour package status
1807
+ */
1808
+ type HourPackageStatus = 'active' | 'expired' | 'depleted';
1809
+ /**
1810
+ * Hour package (strippenkaart) for a project
1811
+ */
1812
+ interface HourPackage {
1813
+ id: string;
1814
+ project_id: string;
1815
+ agency_id?: string;
1816
+ name: string;
1817
+ total_minutes: number;
1818
+ used_minutes: number;
1819
+ remaining_minutes: number;
1820
+ purchased_at: string;
1821
+ expires_at?: string;
1822
+ status: HourPackageStatus;
1823
+ price_cents?: number;
1824
+ currency: 'EUR' | 'USD' | 'GBP';
1825
+ stripe_payment_id?: string;
1826
+ notes?: string;
1827
+ created_at: string;
1828
+ updated_at: string;
1829
+ }
1830
+ /**
1831
+ * Internal note for agency team only
1832
+ */
1833
+ interface InternalNote {
1834
+ id: string;
1835
+ notable_type: 'project' | 'maintenance_item' | 'meeting';
1836
+ notable_id: string;
1837
+ project_id: string;
1838
+ user_id: string;
1839
+ user_name: string;
1840
+ body: string;
1841
+ is_pinned: boolean;
1842
+ title?: string;
1843
+ meeting_date?: string;
1844
+ content_html?: string;
1845
+ created_at: string;
1846
+ updated_at: string;
1847
+ }
1848
+ /**
1849
+ * Domain monitor check status
1850
+ */
1851
+ type DomainCheckStatus = 'pending' | 'ok' | 'warning' | 'error';
1852
+ /**
1853
+ * Domain/SSL monitor for a project
1854
+ */
1855
+ interface DomainMonitor {
1856
+ id: string;
1857
+ project_id: string;
1858
+ agency_id?: string;
1859
+ domain: string;
1860
+ ssl_expiry?: string;
1861
+ domain_expiry?: string;
1862
+ last_checked?: string;
1863
+ check_status: DomainCheckStatus;
1864
+ check_error?: string;
1865
+ alerts_enabled: boolean;
1866
+ alert_days: number[];
1867
+ created_at: string;
1868
+ updated_at: string;
1869
+ }
1870
+ /**
1871
+ * Uptime monitor for a project
1872
+ */
1873
+ interface UptimeMonitor {
1874
+ id: string;
1875
+ project_id: string;
1876
+ agency_id?: string;
1877
+ url: string;
1878
+ name?: string;
1879
+ method: 'GET' | 'HEAD';
1880
+ expected_status_code: number;
1881
+ timeout_ms: number;
1882
+ check_interval_minutes: number;
1883
+ is_active: boolean;
1884
+ is_up: boolean;
1885
+ last_status_code?: number;
1886
+ last_response_time_ms?: number;
1887
+ last_checked_at?: string;
1888
+ last_error?: string;
1889
+ consecutive_failures: number;
1890
+ uptime_percentage?: number;
1891
+ avg_response_time_ms?: number;
1892
+ total_checks_30d: number;
1893
+ failed_checks_30d: number;
1894
+ last_down_at?: string;
1895
+ last_up_at?: string;
1896
+ down_since?: string;
1897
+ alerts_enabled: boolean;
1898
+ alert_threshold: number;
1899
+ created_at: string;
1900
+ updated_at: string;
1901
+ }
1902
+ /**
1903
+ * Individual uptime check result
1904
+ */
1905
+ interface UptimeCheck {
1906
+ id: string;
1907
+ monitor_id: string;
1908
+ checked_at: string;
1909
+ is_up: boolean;
1910
+ status_code?: number;
1911
+ response_time_ms?: number;
1912
+ error?: string;
1913
+ }
1914
+ /**
1915
+ * Uptime incident (downtime period)
1916
+ */
1917
+ interface UptimeIncident {
1918
+ id: string;
1919
+ monitor_id: string;
1920
+ project_id: string;
1921
+ started_at: string;
1922
+ ended_at?: string;
1923
+ duration_minutes?: number;
1924
+ error_type?: 'timeout' | 'connection_refused' | 'dns_error' | 'ssl_error' | 'http_error' | 'unknown';
1925
+ last_error?: string;
1926
+ checks_failed: number;
1927
+ is_resolved: boolean;
1928
+ created_at: string;
1929
+ }
1930
+ /**
1931
+ * Changelog entry category
1932
+ */
1933
+ type ChangelogCategory = 'bugfix' | 'improvement' | 'feature' | 'content' | 'security';
1934
+ /**
1935
+ * Changelog entry for a project
1936
+ */
1937
+ interface ChangelogEntry {
1938
+ id: string;
1939
+ project_id: string;
1940
+ maintenance_item_id?: string;
1941
+ title: string;
1942
+ description?: string;
1943
+ category: ChangelogCategory;
1944
+ is_public: boolean;
1945
+ published_at?: string;
1946
+ created_by: string;
1947
+ created_by_name: string;
1948
+ created_at: string;
1949
+ }
1950
+ /**
1951
+ * Monthly report data structure
1952
+ */
1953
+ interface MonthlyReportData {
1954
+ period: {
1955
+ start: string;
1956
+ end: string;
1957
+ };
1958
+ summary: {
1959
+ tickets_completed: number;
1960
+ tickets_created: number;
1961
+ hours_logged: number;
1962
+ hours_remaining: number;
1963
+ };
1964
+ tickets_by_category: Record<MaintenanceCategory, number>;
1965
+ time_breakdown: Array<{
1966
+ description: string;
1967
+ minutes: number;
1968
+ user_name?: string;
1969
+ }>;
1970
+ open_items: Array<{
1971
+ title: string;
1972
+ priority: MaintenancePriority;
1973
+ status: MaintenanceItemStatus;
1974
+ category: MaintenanceCategory;
1975
+ }>;
1976
+ changelog: Array<{
1977
+ title: string;
1978
+ category: ChangelogCategory;
1979
+ date: string;
1980
+ }>;
1981
+ }
1982
+ /**
1983
+ * Monthly report for a project
1984
+ */
1985
+ interface MonthlyReport {
1986
+ id: string;
1987
+ project_id: string;
1988
+ agency_id?: string;
1989
+ report_month: string;
1990
+ data: MonthlyReportData;
1991
+ pdf_url?: string;
1992
+ sent_to_client: boolean;
1993
+ sent_at?: string;
1994
+ created_at: string;
1995
+ }
1996
+ /**
1997
+ * Improvement suggestion category
1998
+ */
1999
+ type SuggestionCategory = 'performance' | 'ux' | 'seo' | 'design' | 'functionality' | 'security' | 'accessibility';
2000
+ /**
2001
+ * Improvement suggestion impact level
2002
+ */
2003
+ type SuggestionImpact = 'low' | 'medium' | 'high';
2004
+ /**
2005
+ * Improvement suggestion status
2006
+ */
2007
+ type SuggestionStatus = 'draft' | 'proposed' | 'accepted' | 'rejected' | 'converted';
2008
+ /**
2009
+ * Improvement suggestion for proactive agency recommendations
2010
+ */
2011
+ interface ImprovementSuggestion {
2012
+ id: string;
2013
+ project_id: string;
2014
+ title: string;
2015
+ description: string;
2016
+ category: SuggestionCategory;
2017
+ impact: SuggestionImpact;
2018
+ effort_hours?: number;
2019
+ estimated_cost_cents?: number;
2020
+ currency: 'EUR' | 'USD' | 'GBP';
2021
+ status: SuggestionStatus;
2022
+ proposed_at?: string;
2023
+ client_response_at?: string;
2024
+ client_response_notes?: string;
2025
+ converted_item_id?: string;
2026
+ created_by: string;
2027
+ created_by_name: string;
2028
+ created_at: string;
2029
+ updated_at: string;
2030
+ }
2031
+ /**
2032
+ * Extended Phase 5 notification types
2033
+ */
2034
+ type Phase5NotificationType = 'maintenance_item_created' | 'maintenance_item_completed' | 'maintenance_priority_alert' | 'maintenance_cycle_started' | 'maintenance_cycle_closed' | 'hour_package_low' | 'time_entry_added' | 'suggestion_proposed' | 'suggestion_accepted' | 'suggestion_rejected' | 'domain_expiry_warning' | 'monthly_report_ready';
2035
+
2036
+ type UserRole = 'super_admin' | 'admin' | 'manager' | 'client';
2037
+ interface User {
2038
+ id: string;
2039
+ email: string;
2040
+ name: string;
2041
+ role: UserRole;
2042
+ agency_id?: string | null;
2043
+ preferred_language?: 'nl' | 'en' | 'es';
2044
+ avatar_url?: string | null;
2045
+ preferences?: UserPreferences | null;
2046
+ created_at?: string;
2047
+ }
2048
+ interface UserPreferences {
2049
+ language: string;
2050
+ email_notifications: {
2051
+ project_updates: boolean;
2052
+ project_status_changes: boolean;
2053
+ comments: boolean;
2054
+ mentions: boolean;
2055
+ weekly_summary: boolean;
2056
+ };
2057
+ }
2058
+ interface Agency {
2059
+ id: string;
2060
+ name: string;
2061
+ slug: string;
2062
+ project_limit?: number | null;
2063
+ logo_url?: string | null;
2064
+ created_at?: string;
2065
+ created_by?: string | null;
2066
+ is_active?: boolean;
2067
+ plan_type?: PlanType;
2068
+ subscription_status?: SubscriptionStatus;
2069
+ subscription_start_date?: string;
2070
+ subscription_end_date?: string | null;
2071
+ features?: {
2072
+ performance_analysis?: boolean;
2073
+ seo_audit?: boolean;
2074
+ uptime_monitoring?: boolean;
2075
+ [key: string]: boolean | undefined;
2076
+ };
2077
+ seo_credits_limit?: number;
2078
+ seo_credits_used?: number;
2079
+ module_limits?: ModuleLimits;
2080
+ stripe_customer_id?: string | null;
2081
+ stripe_subscription_id?: string | null;
2082
+ stripe_subscription_status?: 'active' | 'past_due' | 'canceled' | 'incomplete' | 'trialing' | null;
2083
+ stripe_current_period_end?: string | null;
2084
+ billing_email?: string | null;
2085
+ }
2086
+ type ProjectStatus = 'draft' | 'submitted' | 'in_development' | 'feedback' | 'in_management' | 'completed' | 'archived';
2087
+ type ProjectType = 'standard' | 'review_only' | 'manage_only';
2088
+ /**
2089
+ * Project-level monitoring feature flags
2090
+ * These can only be enabled if the agency has the feature available
2091
+ */
2092
+ interface ProjectMonitoringFeatures {
2093
+ seo_enabled?: boolean;
2094
+ performance_enabled?: boolean;
2095
+ uptime_enabled?: boolean;
2096
+ analytics_enabled?: boolean;
2097
+ }
2098
+ interface Project {
2099
+ id: string;
2100
+ agency_id?: string;
2101
+ created_by?: string;
2102
+ user_id?: string;
2103
+ name: string;
2104
+ status?: ProjectStatus;
2105
+ project_type?: ProjectType;
2106
+ source_url?: string | null;
2107
+ current_step?: number;
2108
+ locked_phases?: number[];
2109
+ unlocked_phases?: number[];
2110
+ disabled_phases?: number[];
2111
+ enabled_modules?: ModuleType[];
2112
+ visible_modules?: ModuleType[];
2113
+ data?: ProjectData;
2114
+ created_at?: string;
2115
+ updated_at?: string;
2116
+ enabled_features?: string[];
2117
+ monitoring_features?: ProjectMonitoringFeatures;
2118
+ analytics_enabled?: boolean;
2119
+ agency?: Agency;
2120
+ budget_hours_per_month?: number | null;
2121
+ budget_hourly_rate_cents?: number | null;
2122
+ agency_notes?: string | null;
2123
+ }
2124
+ interface ProjectMember {
2125
+ id: string;
2126
+ project_id: string;
2127
+ user_id: string;
2128
+ role: 'manager' | 'client';
2129
+ created_at?: string;
2130
+ user?: User;
2131
+ }
2132
+ interface ProjectData {
2133
+ phase1?: Phase1Data;
2134
+ phase2?: Phase2Data;
2135
+ phase3?: BuildSpecData;
2136
+ phase4?: Phase4Data;
2137
+ phase5?: Phase5Data;
2138
+ buildSpec?: BuildSpecData;
2139
+ }
2140
+ interface ProjectShare {
2141
+ id: string;
2142
+ project_id: string;
2143
+ share_token: string;
2144
+ created_by: string;
2145
+ expires_at?: string | null;
2146
+ access_level: 'view_only';
2147
+ created_at?: string;
2148
+ updated_at?: string;
2149
+ }
2150
+ interface GeneralComment {
2151
+ id: string;
2152
+ project_id: string;
2153
+ phase?: number | null;
2154
+ page_name?: string | null;
2155
+ section_id?: string | null;
2156
+ author_id?: string | null;
2157
+ guest_name?: string | null;
2158
+ content: string;
2159
+ mentions?: string[];
2160
+ parent_id?: string | null;
2161
+ is_resolved?: boolean;
2162
+ created_at?: string;
2163
+ updated_at?: string;
2164
+ author?: User;
2165
+ replies?: GeneralComment[];
2166
+ }
2167
+ interface PerformanceMetrics {
2168
+ performance: number;
2169
+ accessibility: number;
2170
+ bestPractices: number;
2171
+ seo: number;
2172
+ firstContentfulPaint: number;
2173
+ largestContentfulPaint: number;
2174
+ totalBlockingTime: number;
2175
+ cumulativeLayoutShift: number;
2176
+ speedIndex: number;
2177
+ }
2178
+ interface PerformanceAudit {
2179
+ id: string;
2180
+ title: string;
2181
+ description: string;
2182
+ score: number | null;
2183
+ scoreDisplayMode?: 'binary' | 'numeric' | 'informative' | 'notApplicable';
2184
+ displayValue?: string;
2185
+ numericValue?: number;
2186
+ numericUnit?: string;
2187
+ details?: unknown;
2188
+ }
2189
+ interface PerformanceOpportunity extends PerformanceAudit {
2190
+ overallSavingsMs?: number;
2191
+ overallSavingsBytes?: number;
2192
+ }
2193
+ interface PerformanceReport {
2194
+ id: string;
2195
+ projectId: string;
2196
+ pageName: string;
2197
+ pageUrl: string;
2198
+ strategy: 'mobile' | 'desktop';
2199
+ metrics: PerformanceMetrics;
2200
+ audits: PerformanceAudit[];
2201
+ opportunities: PerformanceOpportunity[];
2202
+ diagnostics: PerformanceAudit[];
2203
+ fetchTime: string;
2204
+ lighthouseVersion: string;
2205
+ rawData?: unknown;
2206
+ createdAt?: string;
2207
+ }
2208
+ interface ProjectPerformanceUsage {
2209
+ projectId: string;
2210
+ checksUsed: number;
2211
+ checksLimit: number;
2212
+ lastCheckAt?: string;
2213
+ reports: PerformanceReport[];
2214
+ }
2215
+ interface ProjectFeature {
2216
+ id: string;
2217
+ projectId: string;
2218
+ featureName: string;
2219
+ enabled: boolean;
2220
+ enabledBy?: string;
2221
+ enabledAt?: string;
2222
+ createdAt?: string;
2223
+ }
2224
+ interface SeoAuditIssue {
2225
+ type: 'error' | 'warning' | 'info';
2226
+ category: string;
2227
+ title: string;
2228
+ description: string;
2229
+ url?: string;
2230
+ selector?: string;
2231
+ impact?: 'critical' | 'serious' | 'moderate' | 'minor';
2232
+ }
2233
+ interface SeoAuditResult {
2234
+ url: string;
2235
+ title?: string;
2236
+ metaDescription?: string;
2237
+ h1?: string[];
2238
+ canonicalUrl?: string;
2239
+ robotsDirectives?: string[];
2240
+ issues: SeoAuditIssue[];
2241
+ scores?: {
2242
+ overall?: number;
2243
+ content?: number;
2244
+ technical?: number;
2245
+ mobile?: number;
2246
+ };
2247
+ pageData?: Record<string, unknown>;
2248
+ }
2249
+ interface SeoReport {
2250
+ id: string;
2251
+ projectId: string;
2252
+ pageName: string;
2253
+ pageUrl: string;
2254
+ apifyRunId: string;
2255
+ apifyDatasetId: string;
2256
+ results: SeoAuditResult[];
2257
+ createdAt: string;
2258
+ createdBy: string;
2259
+ }
2260
+
2261
+ interface ElementMetadata {
2262
+ tagName: string;
2263
+ id: string | null;
2264
+ classes: string[];
2265
+ text: string;
2266
+ bbox: {
2267
+ x: number;
2268
+ y: number;
2269
+ width: number;
2270
+ height: number;
2271
+ };
2272
+ }
2273
+ interface AnnotationBody {
2274
+ type: 'TextualBody';
2275
+ text: string;
2276
+ format: 'text/plain';
2277
+ language: string;
2278
+ }
2279
+ interface AnnotationTarget {
2280
+ type: 'SpecificResource';
2281
+ source: string;
2282
+ selector: {
2283
+ type: 'CssSelector';
2284
+ value: string;
2285
+ }[];
2286
+ }
2287
+ interface Annotation {
2288
+ type: 'Annotation';
2289
+ id?: string;
2290
+ wizardPhase: 4;
2291
+ projectId: string;
2292
+ pageUrl: string;
2293
+ body: AnnotationBody;
2294
+ target: AnnotationTarget;
2295
+ elementMetadata: ElementMetadata;
2296
+ created_at: string;
2297
+ created_by: string;
2298
+ }
2299
+
2300
+ /**
2301
+ * Framed API Client
2302
+ *
2303
+ * Handles communication with Framed's AI services (premium features).
2304
+ * Uses the customer's API key for authentication.
2305
+ */
2306
+ interface FramedApiError {
2307
+ message: string;
2308
+ code?: string;
2309
+ status?: number;
2310
+ }
2311
+ declare class FramedApiClient {
2312
+ private apiKey;
2313
+ constructor(apiKey?: string);
2314
+ /**
2315
+ * Check if the client is configured with an API key.
2316
+ */
2317
+ get isEnabled(): boolean;
2318
+ /**
2319
+ * Make a POST request to a Framed API endpoint.
2320
+ *
2321
+ * @param endpoint - The endpoint path (e.g., 'sdk-ai-translate')
2322
+ * @param data - The request payload
2323
+ * @returns The response data
2324
+ * @throws Error if API key is not configured or request fails
2325
+ */
2326
+ post<T>(endpoint: string, data: unknown): Promise<T>;
2327
+ /**
2328
+ * Make a GET request to a Framed API endpoint.
2329
+ *
2330
+ * @param endpoint - The endpoint path
2331
+ * @param params - Optional query parameters
2332
+ * @returns The response data
2333
+ */
2334
+ get<T>(endpoint: string, params?: Record<string, string>): Promise<T>;
2335
+ }
2336
+
2337
+ /**
2338
+ * User Supabase Client Factory
2339
+ *
2340
+ * Creates and manages a Supabase client for the customer's own database.
2341
+ * This is where project data, tasks, and file uploads are stored.
2342
+ */
2343
+
2344
+ /**
2345
+ * Initialize the user's Supabase client.
2346
+ *
2347
+ * @param url - The customer's Supabase URL (e.g., https://xxx.supabase.co)
2348
+ * @param key - The customer's Supabase anon key
2349
+ * @returns The initialized Supabase client
2350
+ */
2351
+ declare function initUserSupabase(url: string, key: string): SupabaseClient;
2352
+ /**
2353
+ * Get the current user Supabase client.
2354
+ *
2355
+ * @returns The Supabase client
2356
+ * @throws Error if client has not been initialized
2357
+ */
2358
+ declare function getUserSupabase(): SupabaseClient;
2359
+ /**
2360
+ * Check if the Supabase client has been initialized.
2361
+ */
2362
+ declare function isSupabaseInitialized(): boolean;
2363
+ /**
2364
+ * Reset the Supabase client (useful for testing or logout).
2365
+ */
2366
+ declare function resetUserSupabase(): void;
2367
+
2368
+ /**
2369
+ * Callback Implementations
2370
+ *
2371
+ * These functions implement the callback interfaces used by wizard components.
2372
+ * They use the dual-client architecture:
2373
+ * - File uploads → User's Supabase storage
2374
+ * - AI features → Framed API
2375
+ */
2376
+
2377
+ /**
2378
+ * Upload a file to the user's Supabase storage.
2379
+ *
2380
+ * @param file - The file to upload
2381
+ * @param bucket - The storage bucket name (default: 'uploads')
2382
+ * @param path - Optional custom path for the file
2383
+ * @returns Upload result with URL and metadata
2384
+ */
2385
+ declare function uploadFile(file: File, bucket?: string, path?: string): Promise<UploadResult>;
2386
+ /**
2387
+ * Upload a font file to the user's Supabase storage.
2388
+ *
2389
+ * @param file - The font file to upload
2390
+ * @returns Upload result with font-specific metadata
2391
+ */
2392
+ declare function uploadFont(file: File): Promise<UploadResult>;
2393
+ /**
2394
+ * Upload a logo file to the user's Supabase storage.
2395
+ *
2396
+ * @param file - The logo image file to upload
2397
+ * @returns Upload result with URL
2398
+ */
2399
+ declare function uploadLogo(file: File): Promise<UploadResult>;
2400
+ /**
2401
+ * Translate content using the Framed AI API.
2402
+ *
2403
+ * @param framedApi - The Framed API client instance
2404
+ * @param value - The content to translate (language code to text mapping)
2405
+ * @param primaryLanguage - The source language code
2406
+ * @param targetLanguages - Array of target language codes
2407
+ * @returns Translation result with translations and any errors
2408
+ */
2409
+ declare function translateContent(framedApi: FramedApiClient, value: Record<string, string>, primaryLanguage: string, targetLanguages: string[]): Promise<TranslationResult>;
2410
+ /**
2411
+ * Create the complete callbacks object for wizard components.
2412
+ *
2413
+ * @param framedApi - The Framed API client instance
2414
+ * @returns Object with all wizard callback implementations
2415
+ */
2416
+ declare function createWizardCallbacks(framedApi: FramedApiClient): {
2417
+ onUploadFile: (file: File) => Promise<UploadResult>;
2418
+ onUploadFont: (file: File) => Promise<UploadResult>;
2419
+ onLogoUpload: (file: File) => Promise<void>;
2420
+ onTranslateAll: (value: Record<string, string>, primaryLanguage: string, targetLanguages: string[]) => Promise<TranslationResult>;
2421
+ };
2422
+
2423
+ interface LayoutOption {
2424
+ id: string;
2425
+ name: string;
2426
+ description: string;
2427
+ sections: {
2428
+ type: string;
2429
+ title: string;
2430
+ requiresMedia: boolean;
2431
+ requiresCTA: boolean;
2432
+ }[];
2433
+ }
2434
+ interface PageTypeLayouts {
2435
+ [key: string]: LayoutOption[];
2436
+ }
2437
+ declare const PAGE_LAYOUTS: PageTypeLayouts;
2438
+ declare const getLayoutsForPage: (pageId: string) => LayoutOption[];
2439
+ declare const getPageDisplayName: (pageId: string) => string;
2440
+
2441
+ /**
2442
+ * Date filtering utilities for Phase 4 feedback views
2443
+ */
2444
+ interface DateRange {
2445
+ startDate: Date | null;
2446
+ endDate: Date | null;
2447
+ }
2448
+ interface DatePreset {
2449
+ id: string;
2450
+ label: string;
2451
+ getRange: () => DateRange;
2452
+ }
2453
+ declare const DATE_PRESETS: DatePreset[];
2454
+ /**
2455
+ * Check if a date string is within the given date range
2456
+ */
2457
+ declare function isWithinDateRange(dateString: string, range: DateRange): boolean;
2458
+ /**
2459
+ * Format a date range for display in the filter button
2460
+ */
2461
+ declare function formatDateRangeLabel(range: DateRange): string;
2462
+ /**
2463
+ * Check if a date range filter is active (has any date set)
2464
+ */
2465
+ declare function isDateRangeActive(range: DateRange): boolean;
2466
+ /**
2467
+ * Get an empty date range
2468
+ */
2469
+ declare function getEmptyDateRange(): DateRange;
2470
+
2471
+ /**
2472
+ * Permission utilities for multi-tenant SaaS
2473
+ * Source: framed-app/src/utils/permissions.ts
2474
+ */
2475
+
2476
+ /**
2477
+ * Check if an agency's subscription is currently valid
2478
+ */
2479
+ declare const isSubscriptionValid: (agency: Agency | null) => boolean;
2480
+ /**
2481
+ * Get the effective project limit based on the agency's plan type
2482
+ */
2483
+ declare const getEffectiveProjectLimit: (agency: Agency | null) => number | null;
2484
+ declare const canCreateProject: (user: User | null, agency: Agency | null, projectCount: number) => boolean;
2485
+ declare const canViewProject: (user: User | null, project: Project | null, members?: ProjectMember[]) => boolean;
2486
+ declare const canEditProject: (user: User | null, project: Project | null, members?: ProjectMember[]) => boolean;
2487
+ declare const canDeleteProject: (user: User | null, project: Project | null) => boolean;
2488
+ declare const canLockPhase: (user: User | null, project: Project | null) => boolean;
2489
+ /**
2490
+ * Check if user can disable/enable phases (hide from navigation)
2491
+ * Same permissions as phase locking - admins can control phase visibility
2492
+ */
2493
+ declare const canDisablePhase: (user: User | null, project: Project | null) => boolean;
2494
+ /**
2495
+ * Check if a phase is disabled (hidden from navigation)
2496
+ */
2497
+ declare const isPhaseDisabled: (phase: number, project: Project | null) => boolean;
2498
+ /**
2499
+ * Check if a phase is soft-locked (automatically disabled during review)
2500
+ * Soft-lock applies to Phase 1 and 2 when project is in Phase 3+ or not in draft status
2501
+ * Admins can override by adding phase to unlocked_phases
2502
+ */
2503
+ declare const isSoftLocked: (phase: number, project: Project | null) => boolean;
2504
+ /**
2505
+ * Check if user can unlock a soft-locked phase
2506
+ * Same permissions as hard lock
2507
+ */
2508
+ declare const canUnlockSoftLock: (user: User | null, project: Project | null) => boolean;
2509
+ /**
2510
+ * Check if user can approve/reject section suggestions
2511
+ * Only admins and super_admins can approve suggestions
2512
+ */
2513
+ declare const canApproveSuggestion: (user: User | null, project: Project | null) => boolean;
2514
+ declare const canInviteUsers: (user: User | null) => boolean;
2515
+ declare const canManageProjectMembers: (user: User | null, project: Project | null) => boolean;
2516
+ declare const canRemoveUser: (actor: User | null, targetUser: User | null) => boolean;
2517
+ declare const canManageAgency: (user: User | null, agencyId: string) => boolean;
2518
+ declare const canCreateAgency: (user: User | null) => boolean;
2519
+ declare const canDeleteAgency: (user: User | null) => boolean;
2520
+ declare const canSetProjectLimit: (user: User | null) => boolean;
2521
+ declare const isSuperAdmin: (user: User | null) => boolean;
2522
+ declare const isAdmin: (user: User | null) => boolean;
2523
+ declare const isManager: (user: User | null) => boolean;
2524
+ declare const isClient: (user: User | null) => boolean;
2525
+ declare const hasRole: (user: User | null, role: User["role"]) => boolean;
2526
+ declare const hasHigherRole: (user: User | null, compareRole: User["role"]) => boolean;
2527
+ declare const hasEqualOrHigherRole: (user: User | null, compareRole: User["role"]) => boolean;
2528
+ declare const getRoleBadgeColor: (role: User["role"]) => string;
2529
+ declare const getRoleDisplayName: (role: User["role"]) => string;
2530
+ declare const getRoleBadge: (role: User["role"]) => string;
2531
+ declare const canManageTeam: (user: User | null) => boolean;
2532
+ /**
2533
+ * Check if user has admin-level privileges (admin or super_admin)
2534
+ * Use this for features that should be available to both admins and super admins
2535
+ * Examples: export functionality, viewing admin-only UI elements
2536
+ */
2537
+ declare const hasAdminPrivileges: (user: User | null) => boolean;
2538
+ /**
2539
+ * Get display name for plan type
2540
+ */
2541
+ declare const getPlanDisplayName: (planType: Agency["plan_type"]) => string;
2542
+ /**
2543
+ * Get display name for subscription status
2544
+ */
2545
+ declare const getSubscriptionStatusDisplayName: (status: Agency["subscription_status"]) => string;
2546
+ /**
2547
+ * Get badge color for subscription status
2548
+ */
2549
+ declare const getSubscriptionStatusBadgeColor: (status: Agency["subscription_status"]) => string;
2550
+ /**
2551
+ * Get badge color for plan type
2552
+ */
2553
+ declare const getPlanBadgeColor: (planType: Agency["plan_type"]) => string;
2554
+ /**
2555
+ * Format date for display
2556
+ */
2557
+ declare const formatSubscriptionDate: (dateString: string | null) => string;
2558
+ /**
2559
+ * Get days remaining until subscription expires
2560
+ */
2561
+ declare const getDaysRemaining: (endDate: string | null) => number | null;
2562
+ /**
2563
+ * Check if user can configure modules for a project
2564
+ * Only admins and super_admins can change module settings
2565
+ */
2566
+ declare const canConfigureModules: (user: User | null, project: Project | null) => boolean;
2567
+ /**
2568
+ * Check if user can access a specific phase based on module configuration
2569
+ * Combines module availability with existing project permissions
2570
+ */
2571
+ declare const canAccessPhase: (user: User | null, project: Project | null, phase: number, members?: ProjectMember[]) => boolean;
2572
+ /**
2573
+ * Check if a module is available for a project (enabled)
2574
+ */
2575
+ declare const isModuleAvailable: (project: Project | null, module: ModuleType) => boolean;
2576
+
2577
+ /**
2578
+ * Comment utilities - Pure functions for mention handling
2579
+ * Source: framed-app/src/utils/commentUtils.ts
2580
+ *
2581
+ * Note: Upload functions are NOT included here as they require runtime
2582
+ * dependencies (supabase). Only pure utility functions are exported.
2583
+ *
2584
+ * For UploadResult type, use the one from @framed/core types.
2585
+ */
2586
+ /**
2587
+ * Extract user IDs from @mentions in text
2588
+ * Format: @[userId:userName] or @userName (if we have user list)
2589
+ */
2590
+ declare function extractMentions(text: string, availableUsers?: Array<{
2591
+ id: string;
2592
+ name: string;
2593
+ }>): string[];
2594
+ /**
2595
+ * Format text with @mentions highlighted
2596
+ * Converts @[userId:userName] to styled mentions
2597
+ */
2598
+ declare function formatMentions(text: string): string;
2599
+
2600
+ /**
2601
+ * Localized content utilities
2602
+ * Source: framed-app/src/utils/localizedContent.ts
2603
+ */
2604
+
2605
+ /**
2606
+ * Get language settings from Phase 1 functionality data
2607
+ */
2608
+ interface LanguageSettings {
2609
+ isMultilingual: boolean;
2610
+ primaryLanguage: string;
2611
+ additionalLanguages: string[];
2612
+ allLanguages: string[];
2613
+ }
2614
+ declare const getLanguageSettings: (functionality?: {
2615
+ languages?: {
2616
+ is_multilingual: boolean;
2617
+ primary_language: string;
2618
+ additional_languages: string[];
2619
+ };
2620
+ }) => LanguageSettings;
2621
+ /**
2622
+ * Get display value for a LocalizableString
2623
+ * Always returns a string, handling both old (string) and new (LocalizedContent) formats
2624
+ */
2625
+ declare const getDisplayValue: (value: LocalizableString | undefined, languageSettings: LanguageSettings, displayLanguage?: string) => string;
2626
+ /**
2627
+ * Update a LocalizableString value for a specific language
2628
+ * Handles conversion from string to LocalizedContent if needed
2629
+ */
2630
+ declare const updateLocalizedValue: (currentValue: LocalizableString | undefined, newValue: string, language: string, languageSettings: LanguageSettings) => LocalizableString;
2631
+ /**
2632
+ * Convert form data to the appropriate format based on language settings
2633
+ * Used when saving data - ensures format matches multilingual setting
2634
+ */
2635
+ declare const normalizeLocalizedField: (value: LocalizableString | undefined, languageSettings: LanguageSettings) => LocalizableString;
2636
+ /**
2637
+ * Check if a localized field has content in all required languages
2638
+ */
2639
+ declare const isLocalizedFieldComplete: (value: LocalizableString | undefined, languageSettings: LanguageSettings, requireAllLanguages?: boolean) => boolean;
2640
+ /**
2641
+ * Create initial localized content with empty values for all languages
2642
+ */
2643
+ declare const createEmptyLocalizedContent: (languageSettings: LanguageSettings) => LocalizedContent;
2644
+ /**
2645
+ * Migrate a single string value to localized content format
2646
+ * Useful for database migrations or data upgrades
2647
+ */
2648
+ declare const migrateToLocalized: (value: string, sourceLanguage: string, languageSettings: LanguageSettings) => LocalizedContent;
2649
+
2650
+ /**
2651
+ * Format file size for display
2652
+ * Source: framed-app/src/utils/storage.ts (pure function extracted)
2653
+ */
2654
+ declare const formatFileSize: (bytes: number) => string;
2655
+
2656
+ /**
2657
+ * Storage utilities - Stub implementations
2658
+ *
2659
+ * These are placeholder functions that log warnings.
2660
+ * Actual storage operations should be handled by the parent app
2661
+ * via FramedProvider callbacks (onUploadFile, onDeleteFile).
2662
+ */
2663
+ interface UploadMediaOptions {
2664
+ file: File;
2665
+ projectId: string;
2666
+ path?: string;
2667
+ }
2668
+ interface UploadMediaResult {
2669
+ url: string;
2670
+ path: string;
2671
+ }
2672
+ /**
2673
+ * Upload media file to storage
2674
+ * Stub - logs warning, actual upload should use FramedProvider callbacks
2675
+ */
2676
+ declare function uploadMedia(_options: UploadMediaOptions): Promise<UploadMediaResult | null>;
2677
+ /**
2678
+ * Delete media file from storage
2679
+ * Stub - logs warning, actual delete should use FramedProvider callbacks
2680
+ */
2681
+ declare function deleteMedia(_path: string): Promise<boolean>;
2682
+
2683
+ /**
2684
+ * Feature flag utilities
2685
+ * Source: framed-app/src/utils/featureFlags.ts
2686
+ */
2687
+
2688
+ /**
2689
+ * Check if a user/project has access to a specific feature
2690
+ */
2691
+ declare const hasFeature: (user: User | null, project: Project | null, featureName: string) => boolean;
2692
+ /**
2693
+ * Check if agency has a monitoring feature available
2694
+ * This is the "capability" check - can the agency use this feature at all?
2695
+ */
2696
+ declare const agencyHasMonitoringFeature: (project: Project | null, feature: "seo" | "performance" | "uptime" | "analytics") => boolean;
2697
+ /**
2698
+ * Check if a monitoring feature is enabled for a specific project
2699
+ * Returns true if:
2700
+ * 1. Agency has the feature available AND
2701
+ * 2. Project has the feature enabled (or no explicit setting = enabled by default)
2702
+ *
2703
+ * This allows agencies to selectively enable/disable features per project for billing
2704
+ */
2705
+ declare const projectHasMonitoringFeature: (user: User | null, project: Project | null, feature: "seo" | "performance" | "uptime" | "analytics") => boolean;
2706
+ /**
2707
+ * Check if performance analysis feature is available
2708
+ * Available to:
2709
+ * - Super admins (always)
2710
+ * - Gold plan agencies (automatically)
2711
+ * - Agencies with feature explicitly enabled
2712
+ */
2713
+ declare const hasPerformanceAnalysis: (user: User | null, project: Project | null) => boolean;
2714
+ /**
2715
+ * Check if SEO audit feature is available
2716
+ * Available to:
2717
+ * - Super admins (always)
2718
+ * - Gold plan agencies (automatically)
2719
+ * - Agencies with feature explicitly enabled
2720
+ */
2721
+ declare const hasSeoAudit: (user: User | null, project: Project | null) => boolean;
2722
+ /**
2723
+ * Get SEO audit credits info for an agency
2724
+ */
2725
+ declare const getSeoCreditsInfo: (project: Project | null) => {
2726
+ used: number;
2727
+ limit: number;
2728
+ remaining: number;
2729
+ };
2730
+ /**
2731
+ * Check if AI content generation feature is available
2732
+ * This includes:
2733
+ * - AI content suggestions for sections
2734
+ * - AI translation for multilingual content
2735
+ * - Auto-generate all content feature
2736
+ *
2737
+ * Available to:
2738
+ * - Super admins (always)
2739
+ * - Gold plan agencies (automatically)
2740
+ * - Silver plan agencies (automatically)
2741
+ * - Agencies with feature explicitly enabled
2742
+ */
2743
+ declare const hasAIContentGeneration: (user: User | null, project: Project | null) => boolean;
2744
+ /**
2745
+ * Get all available features for a project
2746
+ */
2747
+ declare const getAvailableFeatures: (user: User | null, project: Project | null) => string[];
2748
+ /**
2749
+ * Feature display names for UI
2750
+ */
2751
+ declare const FEATURE_NAMES: Record<string, string>;
2752
+ /**
2753
+ * Feature descriptions for UI
2754
+ */
2755
+ declare const FEATURE_DESCRIPTIONS: Record<string, string>;
2756
+ /**
2757
+ * Monitoring feature display names for Phase 5
2758
+ */
2759
+ declare const MONITORING_FEATURE_NAMES: Record<'seo' | 'performance' | 'uptime' | 'analytics', string>;
2760
+ /**
2761
+ * Monitoring feature descriptions for Phase 5
2762
+ */
2763
+ declare const MONITORING_FEATURE_DESCRIPTIONS: Record<'seo' | 'performance' | 'uptime' | 'analytics', string>;
2764
+
2765
+ /**
2766
+ * Shared comment factory utility
2767
+ * Source: framed-app/src/utils/createComment.ts
2768
+ *
2769
+ * Ensures consistent comment structure across all views:
2770
+ * - Page-level feedback (Phase4ReviewPageNew)
2771
+ * - All feedback view (AllFeedbackView)
2772
+ * - Mobile view (Phase4MobileView)
2773
+ */
2774
+
2775
+ interface CreateCommentOptions {
2776
+ comment: string;
2777
+ userId: string;
2778
+ userName: string;
2779
+ userRole?: UserRole;
2780
+ attachments?: CommentAttachment[];
2781
+ versionNumber?: number;
2782
+ position?: {
2783
+ x: number;
2784
+ y: number;
2785
+ };
2786
+ screenshotType?: 'desktop' | 'mobile';
2787
+ source?: 'app' | 'widget' | 'screenshot' | 'content_editor';
2788
+ currentRound?: number;
2789
+ }
2790
+ /**
2791
+ * Creates a standardized PageComment object with all required fields populated
2792
+ */
2793
+ declare function createComment(options: CreateCommentOptions): PageComment;
2794
+
2795
+ /**
2796
+ * Backwards Compatibility Utilities
2797
+ * Source: framed-app/src/utils/compatibility.ts
2798
+ *
2799
+ * Helper functions to bridge the old website-only data model with the new
2800
+ * multi-project-type model. These ensure existing projects continue to work
2801
+ * while new projects can use the enhanced features.
2802
+ */
2803
+
2804
+ /**
2805
+ * Get the project category from Phase1Data, defaulting to 'website' for backwards compatibility
2806
+ */
2807
+ declare function getProjectCategory(phase1: Phase1Data | undefined): ProjectCategory;
2808
+ /**
2809
+ * Check if a project is e-commerce (supports both old and new data models)
2810
+ */
2811
+ declare function isEcommerceProject(phase1: Phase1Data | undefined): boolean;
2812
+ /**
2813
+ * Check if a project is a website (includes e-commerce as a website subtype for UI purposes)
2814
+ */
2815
+ declare function isWebsiteBasedProject(phase1: Phase1Data | undefined): boolean;
2816
+ /**
2817
+ * Check if a project category should show the traditional page/section wizard (Phase 2)
2818
+ */
2819
+ declare function shouldShowTraditionalWizard(phase1: Phase1Data | undefined): boolean;
2820
+ /**
2821
+ * Get pages from Phase1Data - supports both old (pages array) and new (screens array) formats
2822
+ * Returns an array of page IDs for backwards compatibility
2823
+ */
2824
+ declare function getPages(phase1: Phase1Data | undefined): string[];
2825
+ /**
2826
+ * Get custom pages from Phase1Data - supports both old and new formats
2827
+ */
2828
+ declare function getCustomPages(phase1: Phase1Data | undefined): string[];
2829
+ /**
2830
+ * Get all screens from Phase1Data - returns screens array or converts pages to screens
2831
+ */
2832
+ declare function getScreens(phase1: Phase1Data | undefined): ScreenDefinition[];
2833
+ declare function isDefaultPage(pageId: string): boolean;
2834
+ /**
2835
+ * Get the default display name for a page ID
2836
+ */
2837
+ declare function getDefaultPageName(pageId: string): string;
2838
+ /**
2839
+ * Get the display name for a screen/page
2840
+ */
2841
+ declare function getScreenDisplayName(screen: ScreenDefinition | string, phase1?: Phase1Data): string;
2842
+ /**
2843
+ * Get the appropriate screen type label for a project category
2844
+ */
2845
+ declare function getScreenTypeLabel(category: ProjectCategory, plural?: boolean): string;
2846
+ /**
2847
+ * Get the default screen type for a project category
2848
+ */
2849
+ declare function getDefaultScreenType(category: ProjectCategory): ScreenType;
2850
+ /**
2851
+ * Migrate Phase1Data from old format to new format
2852
+ * This is a non-destructive operation that adds new fields without removing old ones
2853
+ */
2854
+ declare function migratePhase1Data(phase1: Phase1Data): Phase1Data;
2855
+ /**
2856
+ * Check if Phase1Data needs migration
2857
+ */
2858
+ declare function needsMigration(phase1: Phase1Data | undefined): boolean;
2859
+ interface ProjectCategoryInfo {
2860
+ id: ProjectCategory;
2861
+ name: string;
2862
+ description: string;
2863
+ icon: string;
2864
+ examples: string[];
2865
+ }
2866
+ /**
2867
+ * Get display information for project categories
2868
+ */
2869
+ declare function getProjectCategoryInfo(category: ProjectCategory): ProjectCategoryInfo;
2870
+ /**
2871
+ * Get all project categories with their display info
2872
+ */
2873
+ declare function getAllProjectCategories(): ProjectCategoryInfo[];
2874
+
2875
+ /**
2876
+ * Utilities for collecting and filtering unified feed items
2877
+ * Source: framed-app/src/utils/feedUtils.ts
2878
+ *
2879
+ * Combines feedback comments, text edits, and image feedback into a single chronological feed
2880
+ */
2881
+
2882
+ /**
2883
+ * Get screenshot URL for a comment based on version and screenshot type
2884
+ * For region captures from the widget, the screenshot is stored in comment.metadata.screenshotUrl
2885
+ */
2886
+ declare function getScreenshotUrl(page: Phase4Page, comment: PageComment): string | undefined;
2887
+ /**
2888
+ * Collect all feed items from Phase 4 pages and images
2889
+ * Combines feedback comments, text edits, and image feedback into a single array
2890
+ */
2891
+ declare function collectFeedItems(pages: Phase4Page[], pageImages?: PageImage[]): FeedItem[];
2892
+ type FeedTypeFilter = 'all' | 'feedback' | 'text_edits' | 'image_feedback';
2893
+ type FeedStatusFilter = 'all' | 'open' | 'resolved';
2894
+ interface FeedFilters {
2895
+ type: FeedTypeFilter;
2896
+ status: FeedStatusFilter;
2897
+ page?: string;
2898
+ language?: string;
2899
+ round?: number;
2900
+ }
2901
+ /**
2902
+ * Filter feed items based on selected filters
2903
+ */
2904
+ declare function filterFeedItems(items: FeedItem[], filters: FeedFilters): FeedItem[];
2905
+ interface GroupedFeedItems {
2906
+ [pageName: string]: FeedItem[];
2907
+ }
2908
+ /**
2909
+ * Group feed items by page name
2910
+ */
2911
+ declare function groupFeedItemsByPage(items: FeedItem[]): GroupedFeedItems;
2912
+ interface FeedStats {
2913
+ total: number;
2914
+ feedbackCount: number;
2915
+ textEditCount: number;
2916
+ imageFeedbackCount: number;
2917
+ openFeedback: number;
2918
+ resolvedFeedback: number;
2919
+ openTextEdits: number;
2920
+ resolvedTextEdits: number;
2921
+ openImageFeedback: number;
2922
+ resolvedImageFeedback: number;
2923
+ pagesAffected: number;
2924
+ languagesUsed: string[];
2925
+ }
2926
+ /**
2927
+ * Calculate statistics for feed items
2928
+ */
2929
+ declare function calculateFeedStats(items: FeedItem[]): FeedStats;
2930
+ /**
2931
+ * Get available languages from feed items
2932
+ */
2933
+ declare function getAvailableLanguages(items: FeedItem[]): string[];
2934
+ /**
2935
+ * Get available pages from feed items
2936
+ */
2937
+ declare function getAvailablePages(items: FeedItem[]): string[];
2938
+
2939
+ /**
2940
+ * Resolution Report Parser
2941
+ * Source: framed-app/src/utils/resolutionParser.ts
2942
+ *
2943
+ * Parses markdown resolution reports from AI agents and applies changes to feedback
2944
+ *
2945
+ * Supports multiple formats:
2946
+ * 1. Checkbox format: **ID:** `feedback-id` with **Status:** [X] DONE / [ ] MANUAL / [ ] SKIPPED
2947
+ * 2. Simple format: ## Resolved Items / ## Unable to Resolve with - [id]: description
2948
+ * 3. Page-grouped format: ### Home Page (X items) with - [id]: description
2949
+ * 4. Mixed format with Notes, Clarification sections, etc.
2950
+ */
2951
+
2952
+ interface ResolutionParseResult {
2953
+ success: boolean;
2954
+ resolved: ParsedResolutionItem[];
2955
+ unable_to_resolve: ParsedResolutionItem[];
2956
+ needs_clarification: ParsedResolutionItem[];
2957
+ unmatched_ids: string[];
2958
+ total_parsed: number;
2959
+ errors: string[];
2960
+ notes?: string;
2961
+ }
2962
+ interface ApplyResolutionOptions {
2963
+ user: User;
2964
+ roundNumber: number;
2965
+ createNotesForUnmatched?: boolean;
2966
+ }
2967
+ /**
2968
+ * Parse a resolution markdown report
2969
+ * Handles various AI response formats including page-grouped items
2970
+ */
2971
+ declare function parseResolutionReport(markdown: string, existingIds: string[]): ResolutionParseResult;
2972
+ /**
2973
+ * Apply parsed resolutions to Phase4 data
2974
+ * For "needs_clarification" items, creates a reply asking for clarification
2975
+ */
2976
+ declare function applyResolutions(phase4Data: Phase4Data, parsed: ResolutionParseResult, options: ApplyResolutionOptions): {
2977
+ updatedData: Phase4Data;
2978
+ importResult: ResolutionImportResult;
2979
+ };
2980
+ /**
2981
+ * Get all feedback IDs from Phase4 data
2982
+ */
2983
+ declare function getAllFeedbackIds(phase4Data: Phase4Data): string[];
2984
+ /**
2985
+ * Count feedback statistics for a round
2986
+ */
2987
+ declare function countRoundStats(phase4Data: Phase4Data, roundNumber: number): {
2988
+ total: number;
2989
+ open: number;
2990
+ resolved: number;
2991
+ unable: number;
2992
+ };
2993
+ /**
2994
+ * Calculate summary when closing a round
2995
+ */
2996
+ declare function calculateRoundSummary(phase4Data: Phase4Data, roundNumber: number): {
2997
+ total_items: number;
2998
+ resolved_in_round: number;
2999
+ carried_over: number;
3000
+ unable_to_resolve: number;
3001
+ new_items_added: number;
3002
+ };
3003
+ /**
3004
+ * Migrate existing comments to Round 1
3005
+ * Called when first round is started on a project with existing comments
3006
+ */
3007
+ declare function migrateToRounds(phase4Data: Phase4Data, userId: string, userName: string): Phase4Data;
3008
+
3009
+ /**
3010
+ * Prompt Generator Utilities
3011
+ * Source: framed-app/src/utils/promptGenerator.ts
3012
+ *
3013
+ * Generates AI prompts and exports for project data
3014
+ */
3015
+
3016
+ /**
3017
+ * Generate a natural language prompt for AI website builders
3018
+ */
3019
+ declare const generateNaturalLanguagePrompt: (data: ProjectData) => string;
3020
+ /**
3021
+ * Generate JSON export for programmatic use
3022
+ */
3023
+ declare const generateJSONExport: (data: ProjectData) => Record<string, unknown>;
3024
+ /**
3025
+ * Generate a short prompt (max 1000 characters) for AI builders with limited input
3026
+ */
3027
+ declare const generateShortPrompt: (data: Phase1Data, isNl?: boolean) => string;
3028
+ /**
3029
+ * Collect all media URLs from project
3030
+ */
3031
+ declare const collectProjectMedia: (data: ProjectData) => string[];
3032
+ /**
3033
+ * Generate planning section for project
3034
+ * Stub - full implementation in framed-app
3035
+ */
3036
+ declare const generatePlanningSection: (data: Phase1Data, isNl?: boolean) => string;
3037
+ /**
3038
+ * Generate project instructions markdown
3039
+ * Stub - full implementation in framed-app
3040
+ */
3041
+ declare const generateProjectInstructionsMd: (data: Phase1Data, isNl?: boolean) => string;
3042
+ /**
3043
+ * Enhanced prompt response from AI enhancement
3044
+ */
3045
+ interface EnhancedPromptResponse {
3046
+ enhanced_prompt: string;
3047
+ planning_section: string;
3048
+ project_instructions_md: string;
3049
+ error?: string;
3050
+ }
3051
+
3052
+ /**
3053
+ * Text diff utilities
3054
+ *
3055
+ * Provides word-level diff computation for text comparison.
3056
+ * Used by CompactDiff component and text editing features.
3057
+ */
3058
+ interface DiffPart {
3059
+ value: string;
3060
+ added?: boolean;
3061
+ removed?: boolean;
3062
+ }
3063
+ /**
3064
+ * Simple word-level diff implementation
3065
+ * For production, consider using the 'diff' npm package
3066
+ */
3067
+ declare function computeTextDiff(original: string, current: string): DiffPart[];
3068
+
3069
+ /**
3070
+ * Text content utilities for managing page text edits
3071
+ * Pure functions for content manipulation - no API calls
3072
+ *
3073
+ * Source: framed-app/src/services/textContentService.ts (pure functions only)
3074
+ */
3075
+
3076
+ declare const LANGUAGE_LABELS: Record<string, string>;
3077
+ interface ExportedChange {
3078
+ sectionName: string;
3079
+ blockType: string;
3080
+ tagName: string;
3081
+ original: string;
3082
+ updated: string;
3083
+ editCount: number;
3084
+ lastEditedBy: string;
3085
+ lastEditedAt: string;
3086
+ }
3087
+ interface TextChangesExport {
3088
+ pageUrl: string;
3089
+ language: string;
3090
+ crawledAt: string;
3091
+ exportedAt: string;
3092
+ totalBlocks: number;
3093
+ changedBlocks: number;
3094
+ changes: ExportedChange[];
3095
+ }
3096
+ /**
3097
+ * Add or update language content in PageTextContent
3098
+ */
3099
+ declare function addLanguageContent(textContent: PageTextContent | undefined, pageUrl: string, languageContent: LanguageContent): PageTextContent;
3100
+ /**
3101
+ * Apply a text edit to a block and return updated content
3102
+ */
3103
+ declare function applyBlockEdit(textContent: PageTextContent, language: string, sectionId: string, blockId: string, newContent: string, userId: string, userName: string, userRole: UserRole, comment?: string): PageTextContent;
3104
+ /**
3105
+ * Apply list items edit
3106
+ */
3107
+ declare function applyListEdit(textContent: PageTextContent, language: string, sectionId: string, blockId: string, newItems: string[], userId: string, userName: string, userRole: UserRole, comment?: string): PageTextContent;
3108
+ /**
3109
+ * Restore block to original content
3110
+ */
3111
+ declare function restoreBlockToOriginal(textContent: PageTextContent, language: string, sectionId: string, blockId: string, userId: string, userName: string, userRole: UserRole): PageTextContent;
3112
+ /**
3113
+ * Check if block has been modified
3114
+ */
3115
+ declare function isBlockModified(block: ContentBlock): boolean;
3116
+ /**
3117
+ * Get count of modified blocks in a language
3118
+ */
3119
+ declare function getModifiedCount(langContent: LanguageContent): number;
3120
+ /**
3121
+ * Get total block count
3122
+ */
3123
+ declare function getTotalBlockCount(langContent: LanguageContent): number;
3124
+ /**
3125
+ * Get edit history for specific block
3126
+ */
3127
+ declare function getBlockHistory(langContent: LanguageContent, blockId: string): TextEdit[];
3128
+ /**
3129
+ * Get recent edits across all blocks
3130
+ */
3131
+ declare function getRecentEdits(langContent: LanguageContent, limit?: number): TextEdit[];
3132
+ /**
3133
+ * Export changes for developer handoff
3134
+ */
3135
+ declare function exportTextChanges(textContent: PageTextContent, language: string): TextChangesExport | null;
3136
+ /**
3137
+ * Get available languages for a page's text content
3138
+ */
3139
+ declare function getTextContentLanguages(textContent: PageTextContent | undefined): string[];
3140
+ /**
3141
+ * Get language label for display
3142
+ */
3143
+ declare function getLanguageLabel(code: string): string;
3144
+ /**
3145
+ * Format relative time (e.g., "2 hours ago")
3146
+ */
3147
+ declare function formatRelativeTime(dateString: string): string;
3148
+
3149
+ /**
3150
+ * Screenshot/scraping utility functions
3151
+ * Pure functions only - no API calls
3152
+ *
3153
+ * Source: framed-app/src/services/screenshotService.ts (pure functions only)
3154
+ */
3155
+ /**
3156
+ * Convert URL to friendly page name (fallback when title can't be fetched)
3157
+ * Example: https://example.com/about-us -> About Us
3158
+ */
3159
+ declare function generatePageName(url: string): string;
3160
+ /**
3161
+ * Parse sitemap XML and extract page URLs
3162
+ * Note: Uses browser DOMParser API
3163
+ */
3164
+ declare function parseSitemapXML(xmlText: string): string[];
3165
+ /**
3166
+ * Generate minimal Phase 1 data for review-only projects
3167
+ */
3168
+ declare function generateMinimalPhase1Data(businessName: string, pageNames: string[]): {
3169
+ business_name: string;
3170
+ industry: string;
3171
+ goal: string;
3172
+ audience: string;
3173
+ is_ecommerce: boolean;
3174
+ pages: string[];
3175
+ custom_pages: string[];
3176
+ page_content: {};
3177
+ legal_content_preference: "not_sure";
3178
+ has_logo: boolean;
3179
+ logo_url: null;
3180
+ colors: {
3181
+ primary: null;
3182
+ secondary: null;
3183
+ accent: null;
3184
+ no_idea: boolean;
3185
+ };
3186
+ font: {
3187
+ primary: null;
3188
+ secondary: null;
3189
+ custom_fonts: never[];
3190
+ no_idea: boolean;
3191
+ };
3192
+ style_keywords: never[];
3193
+ references: never[];
3194
+ };
3195
+
3196
+ /**
3197
+ * SEO audit utility functions
3198
+ * Pure functions for parsing and scoring - no API calls
3199
+ *
3200
+ * Source: framed-app/src/services/seoService.ts (pure functions only)
3201
+ */
3202
+
3203
+ /**
3204
+ * Parse Apify SEO audit results into our format
3205
+ */
3206
+ declare const parseApifyResults: (apifyResults: any[]) => SeoAuditResult[];
3207
+ /**
3208
+ * Format issue title from camelCase key
3209
+ */
3210
+ declare const formatIssueTitle: (key: string) => string;
3211
+ /**
3212
+ * Calculate SEO scores based on issues
3213
+ */
3214
+ declare const calculateScores: (issues: SeoAuditResult["issues"], rawData: any) => SeoAuditResult["scores"];
3215
+
3216
+ /**
3217
+ * Performance analysis utility functions
3218
+ * Pure functions for parsing and formatting - no API calls
3219
+ *
3220
+ * Source: framed-app/src/services/performanceService.ts (pure functions only)
3221
+ */
3222
+
3223
+ interface PageSpeedResponse {
3224
+ lighthouseResult: {
3225
+ categories: {
3226
+ performance: {
3227
+ score: number;
3228
+ };
3229
+ accessibility: {
3230
+ score: number;
3231
+ };
3232
+ 'best-practices': {
3233
+ score: number;
3234
+ };
3235
+ seo: {
3236
+ score: number;
3237
+ };
3238
+ };
3239
+ audits: Record<string, any>;
3240
+ lighthouseVersion: string;
3241
+ };
3242
+ analysisUTCTimestamp: string;
3243
+ }
3244
+ /**
3245
+ * Parse PageSpeed Insights response into our format
3246
+ */
3247
+ declare const parsePageSpeedResponse: (data: PageSpeedResponse, url: string, strategy: "mobile" | "desktop") => PerformanceReport;
3248
+ /**
3249
+ * Format milliseconds to human-readable time
3250
+ */
3251
+ declare const formatMs: (ms: number) => string;
3252
+ /**
3253
+ * Format bytes to human-readable size
3254
+ */
3255
+ declare const formatBytes: (bytes: number) => string;
3256
+ /**
3257
+ * Get color class based on score
3258
+ */
3259
+ declare const getScoreColor: (score: number) => string;
3260
+ /**
3261
+ * Get background color class based on score
3262
+ */
3263
+ declare const getScoreBgColor: (score: number) => string;
3264
+
3265
+ /**
3266
+ * Generate instruction text for maintenance items
3267
+ *
3268
+ * Creates concise, AI-friendly instructions that can be copied to clipboard
3269
+ * for use with tools like Claude, Cursor, etc.
3270
+ */
3271
+
3272
+ interface GenerateInstructionOptions {
3273
+ item: MaintenanceItem & {
3274
+ quote?: MaintenanceQuote;
3275
+ };
3276
+ projectName?: string;
3277
+ liveUrl?: string;
3278
+ }
3279
+ /**
3280
+ * Generate a short, copyable instruction for a maintenance item
3281
+ */
3282
+ declare function generateItemInstruction({ item, projectName, liveUrl }: GenerateInstructionOptions): string;
3283
+ /**
3284
+ * Generate instruction for a page comment (Phase 4)
3285
+ */
3286
+ interface PageCommentLike {
3287
+ comment: string;
3288
+ resolved?: boolean;
3289
+ element_text?: string;
3290
+ element_type?: string;
3291
+ user_name?: string;
3292
+ screenshot_type?: string;
3293
+ attachments?: unknown[];
3294
+ metadata?: {
3295
+ source?: string;
3296
+ originalContent?: string;
3297
+ newContent?: string;
3298
+ cssSelector?: string;
3299
+ pageUrl?: string;
3300
+ pagePath?: string;
3301
+ };
3302
+ }
3303
+ interface GenerateCommentInstructionOptions {
3304
+ comment: PageCommentLike;
3305
+ pageName?: string;
3306
+ projectName?: string;
3307
+ demoUrl?: string;
3308
+ }
3309
+ /**
3310
+ * Generate a short, copyable instruction for a page comment (Phase 4)
3311
+ */
3312
+ declare function generateCommentInstruction({ comment, pageName, projectName, demoUrl }: GenerateCommentInstructionOptions): string;
3313
+
3314
+ export { type AIConfig, type AIConversation, type AIConversationTurn, type AIFeatureResult, type AIQuestion, type AIToolConfig, type AIToolType, type AdditionalContentItem, type AdditionalContentType, type Agency, type Annotation, type AnnotationBody, type AnnotationData, type AnnotationMode, type AnnotationTarget, type ApiBackendDetails, type ApiEndpointConfig, type ApiStyle, type AppScreenConfig, type ApplyResolutionOptions, type Assignment, type Attachment, type AttachmentTranscription, type AuthConfig, type AuthMode, type BaseFeedItem, type BranchingRule, type BuildSpecData, type BuildSpecExport, type BuildSpecItem, type BuildSpecItemStatus, type BuildSpecSection, type CTAInfo, type ChangelogCategory, type ChangelogEntry, type Comment, type CommentAttachment, type ContentBlock, type ContentSection, type CrawledElementType, type CrawledTextElement, type CreateCommentOptions, type CredentialType, DATE_PRESETS, type DataFieldDefinition, type DataModelDefinition, type DataModelReference, type DataRelationship, type DatePreset, type DateRange, type DependencyRule, type DiffPart, type DomainCheckStatus, type DomainMonitor, type ElementHierarchy, type ElementInfo, type ElementMetadata, type EnhancedEcommerceDetails, type EnhancedPromptResponse, type ExportFormat, type ExportedChange, type FAQ, FEATURE_DEFAULTS, FEATURE_DESCRIPTIONS, FEATURE_NAMES, type FeedFilters, type FeedItem, type FeedStats, type FeedStatusFilter, type FeedTypeFilter, type FeedbackComment, type FeedbackFeedItem, type FeedbackInfo, type FeedbackRound, type FeedbackRoundSummary, type FlowStep, FramedApiClient, type FramedApiError, type FramedConfig, type GeneralComment, type GenerateCommentInstructionOptions, type GenerateInstructionOptions, type GlobalElements, type GlobalsSubType, type GroupedFeedItems, type HourPackage, type HourPackageStatus, type ImageComment, type ImageFeedItem, type ImageStatus, type ImportedBlockData, type ImportedContentData, type ImportedImageData, type ImportedPageData, type ImportedSectionData, type ImprovementSuggestion, type InternalNote, LANGUAGE_LABELS, type LanguageContent, type LanguageSettings, type LayoutOption, type LimitStatus, type LocalizableString, type LocalizedContent, MODULES, MODULE_PHASES, MONITORING_FEATURE_DESCRIPTIONS, MONITORING_FEATURE_NAMES, type MaintenanceActivity, type MaintenanceCategory, type MaintenanceComment, type MaintenanceCycle, type MaintenanceCycleSummary, type MaintenanceItem, type MaintenanceItemExtended, type MaintenanceItemStatus, type MaintenancePriority, type MaintenanceQuote, type MaintenanceStats, type MediaInfo, type Mention, type MobileAppDetails, type MobilePlatform, type ModuleInfo, type ModuleLimits, type ModuleType, type ModuleUsageStats, type MonthlyReport, type MonthlyReportData, PAGE_LAYOUTS, PHASE_TO_MODULE, PLAN_LIMITS, type PageComment, type PageCommentLike, type PageContent, type PageDetail, type PageImage, type PageInfo, type PageTextContent, type PageTypeLayouts, type PageVersion, type ParsedResolutionItem, type PendingAttachment, type PerformanceAudit, type PerformanceMetrics, type PerformanceOpportunity, type PerformanceReport, type Phase1Data, type Phase2Data, type Phase2NavigationActions, type Phase2NavigationState, type Phase3Data, type Phase4Data, type Phase4Page, type Phase5Data, type Phase5NotificationType, type PlanConfiguration, type PlanTier, type PlanType, type Position, type PricingTier, type Project, type ProjectCategory, type ProjectCategoryInfo, type ProjectContext, type ProjectContextFile, type ProjectData, type ProjectFeature, type ProjectMember, type ProjectMonitoringFeatures, type ProjectOverview, type ProjectPerformanceUsage, type ProjectShare, type ProjectStatus, type ProjectType, type QuestionCategory, type QuestionOption, type QuestionType, type QuoteStatus, type RecurringConfig, type Reference, type ResolutionImportResult, type ResolutionParseResult, type ResolutionStatus, type SDKConfig, type SaasCategory, type SaasDetails, type ScreenDefinition, type ScreenType, type ScreenshotInfo, type Section, type SectionStatus, type SectionSuggestion, type SectionSuggestionStatus, type SectionTypeData, type SeoAuditIssue, type SeoAuditResult, type SeoReport, type Session, type Stat, type StatusTransition, type StoredCredential, type SubscriptionStatus, type SuggestedPage, type SuggestionCategory, type SuggestionImpact, type SuggestionStatus, type SyncConfig, type SyncResult, type Task, type TaskComment, type TaskInfo, type TaskMeta, type TaskStatus, type TaskType, type TextChangesExport, type TextEdit, type TextEditFeedItem, type TimeEntry, type TranslationResult, type UploadMediaOptions, type UploadMediaResult, type UploadResult, type UptimeCheck, type UptimeIncident, type UptimeMonitor, type UsageLimits, type User, type UserFlowConfig, type UserPreferences, type UserRole, type ValidationRule, type ViewportMode, type WebsitePageConfig, type WidgetConfig, type WidgetDataLayer, type WidgetFeatures, type WidgetSession, type WidgetSessionCreate, type WidgetSessionValidation, type WidgetUIState, type WizardCallbacks, addLanguageContent, agencyHasMonitoringFeature, applyBlockEdit, applyListEdit, applyResolutions, calculateFeedStats, calculateRoundSummary, calculateScores, canAccessPhase, canApproveSuggestion, canConfigureModules, canCreateAgency, canCreateProject, canDeleteAgency, canDeleteProject, canDisablePhase, canEditProject, canInviteUsers, canLockPhase, canManageAgency, canManageProjectMembers, canManageTeam, canRemoveUser, canSetProjectLimit, canSubmitFeedback, canUnlockSoftLock, canUseAI, canViewProject, collectFeedItems, collectProjectMedia, computeTextDiff, countRoundStats, createComment, createEmptyLocalizedContent, createWizardCallbacks, deleteMedia, ensureLocalizedContent, exportTextChanges, extractMentions, filterFeedItems, formatBytes, formatDateRangeLabel, formatFileSize, formatIssueTitle, formatMentions, formatMs, formatRelativeTime, formatSubscriptionDate, generateCommentInstruction, generateItemInstruction, generateJSONExport, generateMinimalPhase1Data, generateNaturalLanguagePrompt, generatePageName, generatePlanningSection, generateProjectInstructionsMd, generateShortPrompt, getAccessiblePhases, getAllFeedbackIds, getAllProjectCategories, getAvailableFeatures, getAvailableLanguages, getAvailablePages, getBlockHistory, getCustomPages, getDaysRemaining, getDefaultModules, getDefaultPageName, getDefaultScreenType, getDisplayValue, getEffectiveProjectLimit, getEmptyDateRange, getFeaturesForTier, getFirstAccessiblePhase, getFirstVisiblePhase, getLanguageLabel, getLanguageSettings, getLayoutsForPage, getLocalizedValue, getModifiedCount, getModuleDescription, getModuleDisplayName, getModuleLimits, getModulePhasesLabel, getPageDisplayName, getPageStatus, getPages, getPlanBadgeColor, getPlanDisplayName, getProjectCategory, getProjectCategoryInfo, getRecentEdits, getRoleBadge, getRoleBadgeColor, getRoleDisplayName, getScoreBgColor, getScoreColor, getScreenDisplayName, getScreenTypeLabel, getScreens, getScreenshotUrl, getSectionStatus, getSeoCreditsInfo, getSubscriptionStatusBadgeColor, getSubscriptionStatusDisplayName, getTextContentLanguages, getTotalBlockCount, getUserSupabase, getVisiblePhases, groupFeedItemsByPage, hasAIContentGeneration, hasAdminPrivileges, hasEqualOrHigherRole, hasFeature, hasHigherRole, hasPerformanceAnalysis, hasRole, hasSeoAudit, initUserSupabase, isAdmin, isBlockModified, isClient, isDateRangeActive, isDefaultPage, isEcommerceProject, isFeedbackItem, isImageFeedItem, isLocalizedContent, isLocalizedFieldComplete, isManager, isModuleAvailable, isModuleEnabled, isModuleUnlimited, isModuleVisible, isPhaseAccessible, isPhaseDisabled, isPhaseVisible, isSoftLocked, isSubscriptionValid, isSupabaseInitialized, isSuperAdmin, isTextEditItem, isWebsiteBasedProject, isWithinDateRange, mergeFeatures, migratePhase1Data, migrateToLocalized, migrateToRounds, needsMigration, normalizeLocalizedField, parseApifyResults, parsePageSpeedResponse, parseResolutionReport, parseSitemapXML, projectHasMonitoringFeature, resetUserSupabase, restoreBlockToOriginal, shouldShowTraditionalWizard, str, toLocalizedContent, translateContent, updateLocalizedValue, uploadFile, uploadFont, uploadLogo, uploadMedia, useTranslation };