@innovastudio/contentbuilder 1.5.189 → 1.5.191
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +2 -0
- package/package.json +1 -1
- package/public/contentbuilder/contentbuilder.css +0 -1
- package/public/contentbuilder/contentbuilder.esm.js +796 -11
- package/public/contentbuilder/contentbuilder.min.js +7 -7
- package/public/contentbuilder/lang/en.js +2 -2
- package/public/contentbuilder/lang/fr.js +2 -2
|
@@ -88119,6 +88119,10 @@ class CodeChat {
|
|
|
88119
88119
|
this.builder = builder;
|
|
88120
88120
|
const builderStuff = this.builder.builderStuff;
|
|
88121
88121
|
this.builderStuff = builderStuff;
|
|
88122
|
+
|
|
88123
|
+
// Check if demo mode is enabled
|
|
88124
|
+
this.isDemoMode = this.builder.demoMode || false;
|
|
88125
|
+
this.demoConversations = this.builder.demoConversations || [];
|
|
88122
88126
|
const out = s => this.out(s);
|
|
88123
88127
|
|
|
88124
88128
|
// Load saved settings or use defaults
|
|
@@ -88128,6 +88132,9 @@ class CodeChat {
|
|
|
88128
88132
|
this.systemModel = this.builder.systemModel;
|
|
88129
88133
|
}
|
|
88130
88134
|
this.codeModels = [{
|
|
88135
|
+
id: 'anthropic/claude-opus-4.5',
|
|
88136
|
+
label: 'Claude Opus 4.5'
|
|
88137
|
+
}, {
|
|
88131
88138
|
id: 'google/gemini-3-pro-preview',
|
|
88132
88139
|
label: 'Google Gemini 3 Pro Preview'
|
|
88133
88140
|
}, {
|
|
@@ -88455,6 +88462,12 @@ class CodeChat {
|
|
|
88455
88462
|
// backward
|
|
88456
88463
|
this.imageModels = this.builder.imageGenerationModels;
|
|
88457
88464
|
}
|
|
88465
|
+
let inputPlaceholderText;
|
|
88466
|
+
if (this.builder.editor) {
|
|
88467
|
+
inputPlaceholderText = out('e.g., Create a landing page for a creative studio');
|
|
88468
|
+
} else {
|
|
88469
|
+
inputPlaceholderText = out('e.g., Create an article about a productive home workspace');
|
|
88470
|
+
}
|
|
88458
88471
|
let html = `
|
|
88459
88472
|
<style>
|
|
88460
88473
|
#chatPanel {
|
|
@@ -88786,10 +88799,12 @@ class CodeChat {
|
|
|
88786
88799
|
box-shadow: 6px 14px 20px 0px rgba(95, 95, 95, 0.11);
|
|
88787
88800
|
z-index: 10005;
|
|
88788
88801
|
display: none;
|
|
88802
|
+
pointer-events: auto; /* Reset cursor to default to prevent it from getting stuck */
|
|
88789
88803
|
}
|
|
88790
88804
|
|
|
88791
88805
|
#settingsDialog.open {
|
|
88792
88806
|
display: block;
|
|
88807
|
+
pointer-events: auto; /* Reset cursor to default to prevent it from getting stuck */
|
|
88793
88808
|
}
|
|
88794
88809
|
|
|
88795
88810
|
#settingsDialog .settings-header {
|
|
@@ -89050,9 +89065,118 @@ class CodeChat {
|
|
|
89050
89065
|
body.dark #chatPanel .copy-button:hover {
|
|
89051
89066
|
background: rgba(255, 255, 255, 0.2);
|
|
89052
89067
|
}
|
|
89068
|
+
|
|
89069
|
+
|
|
89070
|
+
/* Quick Image Size Button */
|
|
89071
|
+
#chatPanel .quick-image-size-button {
|
|
89072
|
+
width: 48px;
|
|
89073
|
+
height: 48px;
|
|
89074
|
+
border: none;
|
|
89075
|
+
border-radius: 8px;
|
|
89076
|
+
font-size: 13px;
|
|
89077
|
+
font-weight: 500;
|
|
89078
|
+
cursor: pointer;
|
|
89079
|
+
transition: background 0.2s;
|
|
89080
|
+
display: flex;
|
|
89081
|
+
align-items: center;
|
|
89082
|
+
justify-content: center;
|
|
89083
|
+
}
|
|
89084
|
+
|
|
89085
|
+
#chatPanel .quick-image-size-button:hover:not(:disabled) {
|
|
89086
|
+
background: rgba(0, 0, 0, 0.05);
|
|
89087
|
+
}
|
|
89088
|
+
|
|
89089
|
+
#chatPanel .quick-image-size-button:disabled {
|
|
89090
|
+
opacity: 0.6;
|
|
89091
|
+
cursor: not-allowed;
|
|
89092
|
+
}
|
|
89093
|
+
|
|
89094
|
+
body.dark #chatPanel .quick-image-size-button {
|
|
89095
|
+
background: #484848;
|
|
89096
|
+
}
|
|
89097
|
+
|
|
89098
|
+
body.dark #chatPanel .quick-image-size-button:hover:not(:disabled) {
|
|
89099
|
+
background: #4c4c4c;
|
|
89100
|
+
}
|
|
89101
|
+
|
|
89102
|
+
/* Image Size Popover */
|
|
89103
|
+
#chatPanel .image-size-popover {
|
|
89104
|
+
position: absolute;
|
|
89105
|
+
bottom: 72px;
|
|
89106
|
+
right: 68px;
|
|
89107
|
+
background: white;
|
|
89108
|
+
border: 1px solid #e5e5e5;
|
|
89109
|
+
border-radius: 8px;
|
|
89110
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
89111
|
+
z-index: 10006;
|
|
89112
|
+
min-width: 160px;
|
|
89113
|
+
max-height: 300px;
|
|
89114
|
+
overflow-y: auto;
|
|
89115
|
+
}
|
|
89116
|
+
|
|
89117
|
+
body.dark #chatPanel .image-size-popover {
|
|
89118
|
+
background: #3a3a3a;
|
|
89119
|
+
border-color: #555;
|
|
89120
|
+
}
|
|
89121
|
+
|
|
89122
|
+
#chatPanel .size-options {
|
|
89123
|
+
padding: 8px;
|
|
89124
|
+
}
|
|
89125
|
+
|
|
89126
|
+
#chatPanel .size-option {
|
|
89127
|
+
display: flex;
|
|
89128
|
+
align-items: center;
|
|
89129
|
+
padding: 10px 12px;
|
|
89130
|
+
cursor: pointer;
|
|
89131
|
+
border-radius: 6px;
|
|
89132
|
+
transition: background 0.2s;
|
|
89133
|
+
font-size: 14px;
|
|
89134
|
+
}
|
|
89135
|
+
#chatPanel .size-option label,
|
|
89136
|
+
#chatPanel .size-option input {
|
|
89137
|
+
cursor: pointer;
|
|
89138
|
+
}
|
|
89139
|
+
|
|
89140
|
+
#chatPanel .size-option:hover {
|
|
89141
|
+
background: rgba(0, 0, 0, 0.05);
|
|
89142
|
+
}
|
|
89143
|
+
|
|
89144
|
+
#chatPanel .size-option.selected {
|
|
89145
|
+
background: rgba(62, 147, 247, 0.1);
|
|
89146
|
+
font-weight: 500;
|
|
89147
|
+
}
|
|
89148
|
+
|
|
89149
|
+
#chatPanel .size-option input[type="radio"] {
|
|
89150
|
+
margin-right: 8px;
|
|
89151
|
+
}
|
|
89152
|
+
|
|
89153
|
+
body.dark #chatPanel .size-option:hover {
|
|
89154
|
+
background: rgba(255, 255, 255, 0.1);
|
|
89155
|
+
}
|
|
89156
|
+
|
|
89157
|
+
body.dark #chatPanel .size-option.selected {
|
|
89158
|
+
background: rgba(62, 147, 247, 0.2);
|
|
89159
|
+
}
|
|
89160
|
+
|
|
89161
|
+
#chatPanel .image-size-popover::-webkit-scrollbar {
|
|
89162
|
+
width: 6px;
|
|
89163
|
+
}
|
|
89164
|
+
|
|
89165
|
+
#chatPanel .image-size-popover::-webkit-scrollbar-track {
|
|
89166
|
+
background: transparent;
|
|
89167
|
+
}
|
|
89168
|
+
|
|
89169
|
+
#chatPanel .image-size-popover::-webkit-scrollbar-thumb {
|
|
89170
|
+
background: #e5e5e5;
|
|
89171
|
+
border-radius: 3px;
|
|
89172
|
+
}
|
|
89173
|
+
|
|
89174
|
+
body.dark #chatPanel .image-size-popover::-webkit-scrollbar-thumb {
|
|
89175
|
+
background: #555;
|
|
89176
|
+
}
|
|
89053
89177
|
</style>
|
|
89054
89178
|
<div
|
|
89055
|
-
class="hidden"
|
|
89179
|
+
class="hidden keep-selection"
|
|
89056
89180
|
id="chatPanel"
|
|
89057
89181
|
role="dialog"
|
|
89058
89182
|
aria-labelledby="chatPanelTitle"
|
|
@@ -89102,10 +89226,24 @@ class CodeChat {
|
|
|
89102
89226
|
<label for="promptInput" class="sr-only">${out('Message input')}</label>
|
|
89103
89227
|
<textarea
|
|
89104
89228
|
id="promptInput"
|
|
89105
|
-
placeholder="${
|
|
89229
|
+
placeholder="${inputPlaceholderText}"
|
|
89106
89230
|
rows="1"
|
|
89107
89231
|
aria-label="${out('Type your message')}"
|
|
89108
89232
|
></textarea>
|
|
89233
|
+
|
|
89234
|
+
<!-- NEW: Quick image size button -->
|
|
89235
|
+
<button
|
|
89236
|
+
id="quickImageSizeButton"
|
|
89237
|
+
type="button"
|
|
89238
|
+
class="quick-image-size-button"
|
|
89239
|
+
aria-label="${out('Select image size')}"
|
|
89240
|
+
aria-haspopup="true"
|
|
89241
|
+
aria-expanded="false"
|
|
89242
|
+
style="display: none;"
|
|
89243
|
+
>
|
|
89244
|
+
□
|
|
89245
|
+
</button>
|
|
89246
|
+
|
|
89109
89247
|
<button
|
|
89110
89248
|
id="sendButton"
|
|
89111
89249
|
type="button"
|
|
@@ -89119,15 +89257,30 @@ class CodeChat {
|
|
|
89119
89257
|
</svg>
|
|
89120
89258
|
</button>
|
|
89121
89259
|
</div>
|
|
89260
|
+
|
|
89261
|
+
<!-- NEW: Size selection popover -->
|
|
89262
|
+
<div
|
|
89263
|
+
id="imageSizePopover"
|
|
89264
|
+
class="image-size-popover"
|
|
89265
|
+
role="menu"
|
|
89266
|
+
aria-hidden="true"
|
|
89267
|
+
style="display: none;"
|
|
89268
|
+
>
|
|
89269
|
+
<div class="size-options" id="sizeOptionsContainer">
|
|
89270
|
+
<!-- Options populated dynamically -->
|
|
89271
|
+
</div>
|
|
89272
|
+
</div>
|
|
89273
|
+
|
|
89122
89274
|
</div>
|
|
89123
89275
|
</div>
|
|
89124
89276
|
|
|
89125
89277
|
<!-- Settings Dialog Overlay -->
|
|
89126
|
-
<div id="settingsOverlay" aria-hidden="true"></div>
|
|
89278
|
+
<div id="settingsOverlay" class="keep-selection" aria-hidden="true"></div>
|
|
89127
89279
|
|
|
89128
89280
|
<!-- Settings Dialog -->
|
|
89129
89281
|
<div
|
|
89130
89282
|
id="settingsDialog"
|
|
89283
|
+
class="keep-selection"
|
|
89131
89284
|
role="dialog"
|
|
89132
89285
|
aria-labelledby="settingsTitle"
|
|
89133
89286
|
aria-modal="true"
|
|
@@ -89261,9 +89414,16 @@ class CodeChat {
|
|
|
89261
89414
|
this.chatModelSelect.value = this.settings.chatModel;
|
|
89262
89415
|
this.imageModelSelect.value = this.settings.imageModel;
|
|
89263
89416
|
this.imageSizeSelect.value = this.settings.imageSize;
|
|
89264
|
-
|
|
89417
|
+
|
|
89418
|
+
// Check saved state - default to closed if never set
|
|
89419
|
+
const savedState = localStorage.getItem('chatPanelVisible');
|
|
89420
|
+
let isChatVisible = savedState === 'true'; // Only open if explicitly set to 'true'
|
|
89265
89421
|
if (!isChatVisible) {
|
|
89266
89422
|
modal.classList.add('hidden');
|
|
89423
|
+
modal.setAttribute('aria-hidden', 'true');
|
|
89424
|
+
} else {
|
|
89425
|
+
modal.classList.remove('hidden');
|
|
89426
|
+
modal.removeAttribute('aria-hidden');
|
|
89267
89427
|
}
|
|
89268
89428
|
const btnClose = modal.querySelector('.close-button');
|
|
89269
89429
|
btnClose.addEventListener('click', () => {
|
|
@@ -89346,7 +89506,45 @@ class CodeChat {
|
|
|
89346
89506
|
this.closeChatButton = closeChatButton;
|
|
89347
89507
|
this.sendButton = sendButton;
|
|
89348
89508
|
this.messagesContainer = messagesContainer;
|
|
89509
|
+
|
|
89510
|
+
// NEW: Quick image size button elements
|
|
89511
|
+
this.quickImageSizeButton = builderStuff.querySelector('#quickImageSizeButton');
|
|
89512
|
+
this.imageSizePopover = builderStuff.querySelector('#imageSizePopover');
|
|
89513
|
+
this.sizeOptionsContainer = builderStuff.querySelector('#sizeOptionsContainer');
|
|
89514
|
+
|
|
89515
|
+
// Quick Image Size Button Handlers
|
|
89516
|
+
this.quickImageSizeButton.addEventListener('click', e => {
|
|
89517
|
+
e.stopPropagation();
|
|
89518
|
+
this.toggleImageSizePopover();
|
|
89519
|
+
});
|
|
89520
|
+
|
|
89521
|
+
// Close popover when clicking outside
|
|
89522
|
+
document.addEventListener('click', e => {
|
|
89523
|
+
if (!this.imageSizePopover.contains(e.target) && !this.quickImageSizeButton.contains(e.target)) {
|
|
89524
|
+
this.closeImageSizePopover();
|
|
89525
|
+
}
|
|
89526
|
+
});
|
|
89527
|
+
|
|
89528
|
+
// Close popover on Escape
|
|
89529
|
+
this.imageSizePopover.addEventListener('keydown', e => {
|
|
89530
|
+
if (e.key === 'Escape') {
|
|
89531
|
+
this.closeImageSizePopover();
|
|
89532
|
+
this.quickImageSizeButton.focus();
|
|
89533
|
+
}
|
|
89534
|
+
});
|
|
89349
89535
|
this.renderImageOptions();
|
|
89536
|
+
if (this.demoConversations) {
|
|
89537
|
+
this.loadConversations();
|
|
89538
|
+
}
|
|
89539
|
+
if (this.isDemoMode) {
|
|
89540
|
+
this.addDemoBanner();
|
|
89541
|
+
|
|
89542
|
+
// Disable input in demo mode
|
|
89543
|
+
this.promptInput.disabled = true;
|
|
89544
|
+
// this.promptInput.placeholder = out('Demo mode - Chat is read-only');
|
|
89545
|
+
this.sendButton.disabled = true;
|
|
89546
|
+
this.messagesContainer.scrollTop = this.messagesContainer.scrollHeight;
|
|
89547
|
+
}
|
|
89350
89548
|
}
|
|
89351
89549
|
renderImageOptions() {
|
|
89352
89550
|
const out = s => this.out(s);
|
|
@@ -89453,6 +89651,9 @@ class CodeChat {
|
|
|
89453
89651
|
// Trigger initial render with defaults
|
|
89454
89652
|
modelSelect.value = defaultModelId;
|
|
89455
89653
|
renderSizes(defaultModelId, false);
|
|
89654
|
+
|
|
89655
|
+
// NEW: Initialize quick size button
|
|
89656
|
+
this.updateQuickImageSizeButton();
|
|
89456
89657
|
}
|
|
89457
89658
|
|
|
89458
89659
|
/**
|
|
@@ -89518,6 +89719,9 @@ class CodeChat {
|
|
|
89518
89719
|
this.settingsOverlay.setAttribute('aria-hidden', 'true');
|
|
89519
89720
|
this.settingsDialog.classList.remove('open');
|
|
89520
89721
|
this.settingsDialog.setAttribute('aria-hidden', 'true');
|
|
89722
|
+
|
|
89723
|
+
// Reset cursor to default to prevent it from getting stuck
|
|
89724
|
+
document.body.style.cursor = '';
|
|
89521
89725
|
if (this.settingsLastFocusedElement) {
|
|
89522
89726
|
this.settingsLastFocusedElement.focus();
|
|
89523
89727
|
}
|
|
@@ -89529,6 +89733,9 @@ class CodeChat {
|
|
|
89529
89733
|
this.settings.imageModel = this.imageModelSelect.value;
|
|
89530
89734
|
this.settings.imageSize = this.imageSizeSelect.value;
|
|
89531
89735
|
this.saveSettingsToStorage();
|
|
89736
|
+
|
|
89737
|
+
// NEW: Update quick button when settings change
|
|
89738
|
+
this.updateQuickImageSizeButton();
|
|
89532
89739
|
this.closeSettings();
|
|
89533
89740
|
}
|
|
89534
89741
|
|
|
@@ -89562,6 +89769,106 @@ Your job:
|
|
|
89562
89769
|
3. Determine what each image should depict based on surrounding content and context
|
|
89563
89770
|
4. Create detailed image generation prompts for each image
|
|
89564
89771
|
|
|
89772
|
+
📸 EDITORIAL STYLE GUIDE - Apply to all image prompts:
|
|
89773
|
+
|
|
89774
|
+
- Default aesthetic: Minimalist magazine-style (Kinfolk, Cereal, Vogue Living)
|
|
89775
|
+
- Composition: Clean, lots of negative space, well-balanced
|
|
89776
|
+
|
|
89777
|
+
|
|
89778
|
+
CREATIVE PROMPT ENHANCEMENT:
|
|
89779
|
+
When constructing image prompts, intelligently enrich basic descriptions using these proven cinematic stylers:
|
|
89780
|
+
|
|
89781
|
+
1. **Golden Hour Cinematic**
|
|
89782
|
+
"Warm sunlight, golden-hour lighting. Low contrast with lifted shadows, ethereal glow throughout. Overall vibe: relaxed, coastal, sun-kissed, carefree, West Coast lifestyle, evoking warmth and endless summer."
|
|
89783
|
+
→ Best for: Landscapes, architecture, outdoor scenes, lifestyle content
|
|
89784
|
+
|
|
89785
|
+
2. **Morning Halo Effect**
|
|
89786
|
+
"The lighting is soft, morning sunlight coming, creating a 'halo' effect. Vibrant cinematic shot"
|
|
89787
|
+
→ Best for: Portraits, people in action, outdoor activities
|
|
89788
|
+
|
|
89789
|
+
3. **Natural Forest Serenity**
|
|
89790
|
+
"in a lush, green forest. The sunlight should be filtering through the trees, creating a serene and natural atmosphere"
|
|
89791
|
+
→ Best for: Nature scenes, outdoor activities, wellness content
|
|
89792
|
+
|
|
89793
|
+
4. **Coastal Summer Drama**
|
|
89794
|
+
"on a dramatic windswept coastline. The overall atmosphere is cheerful, breezy, and full of summer warmth"
|
|
89795
|
+
→ Best for: Beach/coastal scenes, adventure, travel content
|
|
89796
|
+
|
|
89797
|
+
5. **Modern Interior Elegance**
|
|
89798
|
+
"illuminated by warm, natural lighting. The background features a softly blurred modern interior with subtle lights and cool tones, adding depth without distraction"
|
|
89799
|
+
→ Best for: Indoor portraits, product shots, professional/business settings
|
|
89800
|
+
|
|
89801
|
+
ENHANCEMENT STRATEGY:
|
|
89802
|
+
- Analyze the subject matter and context from the HTML/request
|
|
89803
|
+
- Select the most appropriate styler that matches the scene type
|
|
89804
|
+
- Check if user has specified lighting/time → DO NOT apply contradicting styler elements
|
|
89805
|
+
* Example: User says "daylight" → DO NOT add "golden-hour" or "sunset"
|
|
89806
|
+
- Aim for creative elevation while maintaining the core subject integrity
|
|
89807
|
+
- Blend the styler naturally into the prompt (adapt grammar, pronouns, context)
|
|
89808
|
+
- IF user is vague (no specific lighting/mood) → Apply full styler enhancement
|
|
89809
|
+
- User's explicit details ALWAYS take precedence over styler recommendations
|
|
89810
|
+
` +
|
|
89811
|
+
/*
|
|
89812
|
+
ENHANCEMENT STRATEGY:
|
|
89813
|
+
- Analyze the subject matter and context from the HTML/request
|
|
89814
|
+
- Select the most appropriate styler that matches the scene type
|
|
89815
|
+
- Blend the styler naturally into the prompt (adapt grammar, pronouns, context)
|
|
89816
|
+
- If the subject already has specific lighting/atmosphere details, complement rather than override
|
|
89817
|
+
- Aim for creative elevation while maintaining the core subject integrity
|
|
89818
|
+
*/
|
|
89819
|
+
`
|
|
89820
|
+
Examples of enriched prompts:
|
|
89821
|
+
- Basic: "a house with mountain view"
|
|
89822
|
+
→ Enhanced: "a house with beautiful mountain view. Warm sunlight, golden-hour lighting. Low contrast with lifted shadows, ethereal glow throughout. Overall vibe: relaxed, coastal, sun-kissed, carefree, West Coast lifestyle, evoking warmth and endless summer."
|
|
89823
|
+
|
|
89824
|
+
- Basic: "person hiking"
|
|
89825
|
+
→ Enhanced: "a person hiking in nature. The lighting is soft, morning sunlight coming, creating a 'halo' effect. Vibrant cinematic shot"
|
|
89826
|
+
|
|
89827
|
+
- Basic: "woman reading"
|
|
89828
|
+
→ Enhanced: "a woman sitting comfortably and reading. Her face is illuminated by warm, natural lighting. The background features a softly blurred modern interior with subtle lights and cool tones, adding depth without distraction"
|
|
89829
|
+
|
|
89830
|
+
- Already detailed: "dramatic headshot with bokeh and rim lighting"
|
|
89831
|
+
→ No enhancement needed (preserve user's vision)
|
|
89832
|
+
|
|
89833
|
+
|
|
89834
|
+
CONTEXT-SPECIFIC GUIDELINES:
|
|
89835
|
+
|
|
89836
|
+
Interior/Indoor scenes:
|
|
89837
|
+
- Lighting: Soft natural window light
|
|
89838
|
+
- Backdrop: white walls or neutral tones
|
|
89839
|
+
- Mood: Calm, serene, inviting, uncluttered
|
|
89840
|
+
- Reference: "Kinfolk interior photography"
|
|
89841
|
+
|
|
89842
|
+
People/Portraits/Activities:
|
|
89843
|
+
- Lighting: Natural diffused light, flattering and soft
|
|
89844
|
+
- Styling: Effortlessly elegant, contemporary casual
|
|
89845
|
+
- Pose: Candid, authentic moments, relaxed
|
|
89846
|
+
- Reference: "modern lifestyle editorial photography"
|
|
89847
|
+
|
|
89848
|
+
Products/Objects:
|
|
89849
|
+
- Lighting: Clean studio or soft natural light
|
|
89850
|
+
- Backdrop: Minimalist neutral, lots of breathing room
|
|
89851
|
+
- Mood: Refined simplicity, premium feel
|
|
89852
|
+
- Reference: "high-end editorial product photography"
|
|
89853
|
+
|
|
89854
|
+
Food/Culinary:
|
|
89855
|
+
- Lighting: Soft daylight, gentle overhead or 45° angle
|
|
89856
|
+
- Styling: Minimal props, artisan ceramics, linen
|
|
89857
|
+
- Mood: Fresh, appetizing, rustic-modern
|
|
89858
|
+
- Reference: "Bon Appétit editorial food styling"
|
|
89859
|
+
|
|
89860
|
+
Outdoor/Landscape:
|
|
89861
|
+
- Composition: Serene, contemplative, minimal
|
|
89862
|
+
- Mood: Calm atmosphere, breathing space, lifted brightness
|
|
89863
|
+
- Colors: Vibrant cinematic shot.
|
|
89864
|
+
- Reference: "editorial travel photography"
|
|
89865
|
+
|
|
89866
|
+
CRITICAL RULES:
|
|
89867
|
+
1. If user specifies a style (cinematic, vintage, vibrant, etc.) → RESPECT IT, only add quality markers
|
|
89868
|
+
2. If user is vague → APPLY FULL EDITORIAL TREATMENT based on subject category
|
|
89869
|
+
3. Always maintain cohesive aesthetic across all images
|
|
89870
|
+
4. Every prompt should feel like it belongs in a high-end lifestyle magazine
|
|
89871
|
+
|
|
89565
89872
|
Respond with a JSON array with one entry per image to generate:
|
|
89566
89873
|
[
|
|
89567
89874
|
{
|
|
@@ -89672,6 +89979,11 @@ Response: [
|
|
|
89672
89979
|
*/
|
|
89673
89980
|
async sendMessage() {
|
|
89674
89981
|
const out = s => this.out(s);
|
|
89982
|
+
|
|
89983
|
+
// Prevent sending messages in demo mode
|
|
89984
|
+
if (this.isDemoMode) {
|
|
89985
|
+
return;
|
|
89986
|
+
}
|
|
89675
89987
|
const prompt = this.promptInput.value.trim();
|
|
89676
89988
|
if (!prompt) return;
|
|
89677
89989
|
this.addMessage('user', prompt);
|
|
@@ -89739,14 +90051,42 @@ Response: [
|
|
|
89739
90051
|
} else if (result.type === 'image') {
|
|
89740
90052
|
if (result.imageDetails && result.imageDetails.length > 1) {
|
|
89741
90053
|
this.addMessage('assistant', `✓ Generated ${result.imageDetails.length} images successfully`);
|
|
90054
|
+
// this.addMessage('assistant', `✓ ${out('Generated {count} images successfully').replace('{count}', result.imageDetails.length)}`);
|
|
89742
90055
|
this.addImagePreview(result.imageDetails);
|
|
89743
90056
|
} else {
|
|
89744
|
-
this.addMessage('assistant', '✓
|
|
90057
|
+
this.addMessage('assistant', '✓ Image generated successfully');
|
|
90058
|
+
// this.addMessage('assistant', `✓ ${out('Image generated successfully')}`);
|
|
89745
90059
|
if (result.generatedUrls && result.generatedUrls[0]) {
|
|
89746
90060
|
this.addImagePreview([{
|
|
89747
90061
|
url: result.generatedUrls[0],
|
|
89748
90062
|
context: result.description
|
|
89749
90063
|
}]);
|
|
90064
|
+
|
|
90065
|
+
// If there is a selected image, replace with the new image
|
|
90066
|
+
const url = result.generatedUrls[0];
|
|
90067
|
+
if (this.builder.editor) {
|
|
90068
|
+
// ContentBox
|
|
90069
|
+
const img = this.builder.editor.activeImage;
|
|
90070
|
+
if (img) {
|
|
90071
|
+
this.builder.editor.saveForUndo();
|
|
90072
|
+
img.addEventListener('load', () => {
|
|
90073
|
+
this.builder.editor.element.image.repositionImageTool();
|
|
90074
|
+
this.builder.editor.elmTool.repositionElementTool();
|
|
90075
|
+
});
|
|
90076
|
+
this.builder.editor.activeImage.setAttribute('src', url);
|
|
90077
|
+
}
|
|
90078
|
+
} else {
|
|
90079
|
+
// ContentBuilder
|
|
90080
|
+
const img = this.builder.activeImage;
|
|
90081
|
+
if (img) {
|
|
90082
|
+
this.builder.uo.saveForUndo();
|
|
90083
|
+
img.addEventListener('load', () => {
|
|
90084
|
+
this.builder.element.image.repositionImageTool();
|
|
90085
|
+
this.builder.elmTool.repositionElementTool();
|
|
90086
|
+
});
|
|
90087
|
+
this.builder.activeImage.setAttribute('src', url);
|
|
90088
|
+
}
|
|
90089
|
+
}
|
|
89750
90090
|
}
|
|
89751
90091
|
}
|
|
89752
90092
|
}
|
|
@@ -89800,15 +90140,16 @@ Response: [
|
|
|
89800
90140
|
`;
|
|
89801
90141
|
images.forEach(img => {
|
|
89802
90142
|
previewHTML += `
|
|
89803
|
-
<div style="border-radius:
|
|
89804
|
-
<img src="${img.url}" alt="${img.context || 'Generated image'}" style="width: 100%;
|
|
90143
|
+
<div style="border-radius: 6px; overflow: hidden; background: rgba(255,255,255,0.05);">
|
|
90144
|
+
<img src="${img.url}" alt="${img.context || 'Generated image'}" style="width: 100%;" />
|
|
89805
90145
|
<div style="padding: 8px; font-size: 11px; opacity: 0.8;">
|
|
89806
90146
|
${img.context || ''}
|
|
89807
90147
|
<a href="${img.url}" target="_blank" rel="noopener noreferrer" style="display: block; margin-top: 4px;">View</a>
|
|
89808
90148
|
</div>
|
|
89809
90149
|
</div>
|
|
89810
|
-
`;
|
|
90150
|
+
`; // height: 150px; object-fit: cover;
|
|
89811
90151
|
});
|
|
90152
|
+
|
|
89812
90153
|
previewHTML += '</div>';
|
|
89813
90154
|
previewDiv.innerHTML = previewHTML;
|
|
89814
90155
|
this.messagesContainer.appendChild(previewDiv);
|
|
@@ -89829,6 +90170,32 @@ Response: [
|
|
|
89829
90170
|
|
|
89830
90171
|
// Check if image generation is enabled
|
|
89831
90172
|
const imageGenEnabled = this.builder.generateMediaUrl_Fal ? true : false;
|
|
90173
|
+
|
|
90174
|
+
// Build context about previously generated images
|
|
90175
|
+
let imageHistoryContext = '';
|
|
90176
|
+
const previousImageResults = this.conversationResults.filter(r => r.type === 'image' && r.generatedUrls && r.generatedUrls.length > 0);
|
|
90177
|
+
if (previousImageResults.length > 0) {
|
|
90178
|
+
imageHistoryContext = '\n\n=== PREVIOUSLY GENERATED IMAGES IN THIS CONVERSATION ===\n';
|
|
90179
|
+
imageHistoryContext += 'The following images were generated earlier in this conversation:\n\n';
|
|
90180
|
+
previousImageResults.forEach((result, idx) => {
|
|
90181
|
+
if (result.imageDetails && result.imageDetails.length > 0) {
|
|
90182
|
+
result.imageDetails.forEach((img, imgIdx) => {
|
|
90183
|
+
imageHistoryContext += `Generated Image ${idx + 1}.${imgIdx + 1}:\n`;
|
|
90184
|
+
imageHistoryContext += ` URL: ${img.url}\n`;
|
|
90185
|
+
imageHistoryContext += ` Context: ${img.context}\n`;
|
|
90186
|
+
imageHistoryContext += ` Description: ${result.description}\n\n`;
|
|
90187
|
+
});
|
|
90188
|
+
} else {
|
|
90189
|
+
result.generatedUrls.forEach((url, urlIdx) => {
|
|
90190
|
+
imageHistoryContext += `Generated Image ${idx + 1}.${urlIdx + 1}:\n`;
|
|
90191
|
+
imageHistoryContext += ` URL: ${url}\n`;
|
|
90192
|
+
imageHistoryContext += ` Description: ${result.description}\n\n`;
|
|
90193
|
+
});
|
|
90194
|
+
}
|
|
90195
|
+
});
|
|
90196
|
+
imageHistoryContext += '⚠️ IMPORTANT: If the user refers to "the generated image", "this image", "the image", or "previous image", they are referring to these images above.\n';
|
|
90197
|
+
imageHistoryContext += '⚠️ DO NOT create a new image task. Instead, create a CODE task that uses these existing URLs.\n\n';
|
|
90198
|
+
}
|
|
89832
90199
|
const classificationPrompt = `Analyze this user message and break it down into tasks.
|
|
89833
90200
|
|
|
89834
90201
|
CURRENT HTML CONTEXT (use this to understand the page structure):
|
|
@@ -89836,11 +90203,19 @@ CURRENT HTML CONTEXT (use this to understand the page structure):
|
|
|
89836
90203
|
${this.builder.html()}
|
|
89837
90204
|
\`\`\`
|
|
89838
90205
|
|
|
90206
|
+
${imageHistoryContext}
|
|
90207
|
+
|
|
89839
90208
|
IMPORTANT: This is a WEB BUILDER tool with chat capabilities. Valid requests are:
|
|
89840
90209
|
- Creating/editing/removing HTML content (code tasks)
|
|
89841
90210
|
${imageGenEnabled ? '- Generating images for the webpage (image tasks)' : ''}
|
|
89842
90211
|
- Asking questions or having conversations (chat tasks) - THIS CAN BE ABOUT ANYTHING
|
|
89843
90212
|
|
|
90213
|
+
CRITICAL CONTEXT RULE:
|
|
90214
|
+
- In a web builder, "create an article/blog/story/content" means CREATE A WEBPAGE (CODE task)
|
|
90215
|
+
- Only use CHAT task for questions, explanations, or requests for advice
|
|
90216
|
+
- If user says "create", "write", "make", "build" followed by content → CODE task
|
|
90217
|
+
- If user says "explain", "how do I", "what is", "tell me about" → CHAT task
|
|
90218
|
+
|
|
89844
90219
|
${imageGenEnabled ? '' : `
|
|
89845
90220
|
⚠️ AI IMAGE GENERATION IS CURRENTLY DISABLED
|
|
89846
90221
|
- If user requests AI image generation/creation, explain that it's disabled
|
|
@@ -89910,6 +90285,18 @@ IMAGE TASK RULES:
|
|
|
89910
90285
|
- IMPORTANT: Look at the HTML context to understand if multiple images are involved
|
|
89911
90286
|
- If the target section has multiple images, indicate this in targetElement (e.g., "all 3 images in Culinary Delights section")
|
|
89912
90287
|
- Image tasks should come BEFORE code tasks that depend on them
|
|
90288
|
+
|
|
90289
|
+
- CRITICAL: DO NOT create image task for these phrases (they mean REUSE existing):
|
|
90290
|
+
* "use the generated image" / "use the image"
|
|
90291
|
+
* "use this image" / "use existing image"
|
|
90292
|
+
* "with the generated image" / "with this image"
|
|
90293
|
+
* "with the previous image" / "with the last image"
|
|
90294
|
+
* "using the image I just created/generated"
|
|
90295
|
+
* Any reference to a PREVIOUS image from conversation history
|
|
90296
|
+
- Decision logic:
|
|
90297
|
+
* "Generate a new image of X" → CREATE image task (new generation)
|
|
90298
|
+
* "Create article using the generated image" → NO image task (reuse existing)
|
|
90299
|
+
|
|
89913
90300
|
` : ''}
|
|
89914
90301
|
|
|
89915
90302
|
Examples:
|
|
@@ -89947,6 +90334,17 @@ Input: "Create a landing page about wood furniture workshop"
|
|
|
89947
90334
|
Output: {"is_valid": true, "tasks": [{"type": "code", "description": "Create a landing page for a wood furniture workshop", "order": 1}], "is_mixed": false}
|
|
89948
90335
|
Note: No image task created because user did not explicitly request AI image generation
|
|
89949
90336
|
|
|
90337
|
+
Input: "Create an inspiring article and use the generated image"
|
|
90338
|
+
Output: {"is_valid": true, "tasks": [{"type": "code", "description": "Create article using previously generated image from conversation history", "order": 1}], "is_mixed": false}
|
|
90339
|
+
Note: No image task because user wants to REUSE existing image
|
|
90340
|
+
|
|
90341
|
+
Input: "Write a blog post about productivity and use this image"
|
|
90342
|
+
Output: {"is_valid": true, "tasks": [{"type": "code", "description": "Create blog post using previously generated image", "order": 1}], "is_mixed": false}
|
|
90343
|
+
|
|
90344
|
+
Input: "Generate another mountain image and create a landing page"
|
|
90345
|
+
Output: {"tasks": [{"type": "image", "description": "Generate new mountain image", "imagePrompt": "...", "order": 1}, {"type": "code", "description": "Create landing page with new image", "order": 2}], "is_mixed": true}
|
|
90346
|
+
Note: "another" and "generate" indicate NEW image generation
|
|
90347
|
+
|
|
89950
90348
|
` : `
|
|
89951
90349
|
Input: "Generate an AI image of a mountain"
|
|
89952
90350
|
Output: {"is_valid": false, "reason": "AI image generation is currently disabled.", "tasks": [], "is_mixed": false}
|
|
@@ -90030,7 +90428,9 @@ Output: {"is_valid": false, "reason": "AI image generation is currently disabled
|
|
|
90030
90428
|
if (imageTasksFromThisRequest.length > 0) {
|
|
90031
90429
|
hasGeneratedImages = true;
|
|
90032
90430
|
imageContext = '\n\n=== GENERATED IMAGE URLS (USE THESE FOR YOUR TASK) ===\n';
|
|
90033
|
-
|
|
90431
|
+
|
|
90432
|
+
// ⭐ REVERSE to show most recent first
|
|
90433
|
+
imageTasksFromThisRequest.reverse().forEach(imageTask => {
|
|
90034
90434
|
if (imageTask.imageDetails && imageTask.imageDetails.length > 0) {
|
|
90035
90435
|
// Multiple images with context
|
|
90036
90436
|
imageTask.imageDetails.forEach((img, idx) => {
|
|
@@ -90045,7 +90445,8 @@ Output: {"is_valid": false, "reason": "AI image generation is currently disabled
|
|
|
90045
90445
|
});
|
|
90046
90446
|
}
|
|
90047
90447
|
});
|
|
90048
|
-
imageContext += '\n⚠️ IMPORTANT: Use
|
|
90448
|
+
imageContext += '\n⚠️ IMPORTANT: Use the FIRST image URL listed above (most recent) unless the task specifically asks for a different image.\n';
|
|
90449
|
+
imageContext += '⚠️ The first image is the MOST RECENTLY GENERATED and should be used by default.\n';
|
|
90049
90450
|
imageContext += '⚠️ Your task description specifies WHICH images to update - follow it precisely.\n';
|
|
90050
90451
|
}
|
|
90051
90452
|
|
|
@@ -90142,6 +90543,10 @@ ${this.builder.html()}
|
|
|
90142
90543
|
TASK: ${task.description}
|
|
90143
90544
|
|
|
90144
90545
|
IMPORTANT: Follow the Best Practices in Content framework.
|
|
90546
|
+
${this.builder.editor ? `CRITICAL: This project uses the Content.css and Box framework, NOT Tailwind. ONLY use classes explicitly documented in this framework guide.
|
|
90547
|
+
DO NOT use Tailwind classes like gap-4, w-1/2, top-4, hover:scale-120, etc.` : `CRITICAL: This project uses the Content.css framework, NOT Tailwind. ONLY use classes explicitly documented in this framework guide.
|
|
90548
|
+
DO NOT use Tailwind classes like gap-4, w-1/2, top-4, hover:scale-120, etc.`}
|
|
90549
|
+
For flexibility, you can use inline styles or embedded <style> at the end of the generated HTML.
|
|
90145
90550
|
|
|
90146
90551
|
${imageContext}
|
|
90147
90552
|
${chatContext}
|
|
@@ -90594,6 +90999,319 @@ ${this.builder.html()}
|
|
|
90594
90999
|
document.body.removeChild(announcement);
|
|
90595
91000
|
}, 1000);
|
|
90596
91001
|
}
|
|
91002
|
+
|
|
91003
|
+
/**
|
|
91004
|
+
* ============================================================================
|
|
91005
|
+
* DEMO MODE
|
|
91006
|
+
* ============================================================================
|
|
91007
|
+
*/
|
|
91008
|
+
loadConversations() {
|
|
91009
|
+
// Load demo conversations
|
|
91010
|
+
if (this.demoConversations && this.demoConversations.length > 0) {
|
|
91011
|
+
this.demoConversations.forEach(msg => {
|
|
91012
|
+
if (msg.role === 'user') {
|
|
91013
|
+
this.addMessage('user', msg.content);
|
|
91014
|
+
} else if (msg.role === 'assistant') {
|
|
91015
|
+
this.addMessage('assistant', msg.content, true);
|
|
91016
|
+
|
|
91017
|
+
// Add image preview if available
|
|
91018
|
+
if (msg.imagePreview && msg.imagePreview.length > 0) {
|
|
91019
|
+
this.addImagePreview(msg.imagePreview);
|
|
91020
|
+
|
|
91021
|
+
// Include in conversationResults with correct structure
|
|
91022
|
+
this.conversationResults.push({
|
|
91023
|
+
type: 'image',
|
|
91024
|
+
description: 'Previously generated image from demo',
|
|
91025
|
+
content: `Generated ${msg.imagePreview.length} images`,
|
|
91026
|
+
generatedUrls: msg.imagePreview.map(img => img.url),
|
|
91027
|
+
// Array of URLs
|
|
91028
|
+
imageDetails: msg.imagePreview.map(img => ({
|
|
91029
|
+
// Array of objects
|
|
91030
|
+
url: img.url,
|
|
91031
|
+
context: img.context,
|
|
91032
|
+
prompt: img.context || '' // Use context as prompt if no prompt available
|
|
91033
|
+
})),
|
|
91034
|
+
|
|
91035
|
+
targetElement: 'demo images'
|
|
91036
|
+
});
|
|
91037
|
+
}
|
|
91038
|
+
}
|
|
91039
|
+
});
|
|
91040
|
+
}
|
|
91041
|
+
}
|
|
91042
|
+
addDemoBanner() {
|
|
91043
|
+
const out = s => this.out(s);
|
|
91044
|
+
const banner = document.createElement('div');
|
|
91045
|
+
banner.className = 'demo-banner';
|
|
91046
|
+
banner.innerHTML = `
|
|
91047
|
+
<div style="
|
|
91048
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
91049
|
+
color: white;
|
|
91050
|
+
padding: 12px 16px;
|
|
91051
|
+
border-radius: 8px;
|
|
91052
|
+
font-size: 13px;
|
|
91053
|
+
display: flex;
|
|
91054
|
+
align-items: center;
|
|
91055
|
+
gap: 8px;
|
|
91056
|
+
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
|
|
91057
|
+
">
|
|
91058
|
+
<span style="font-size: 18px;">📖</span>
|
|
91059
|
+
<div>
|
|
91060
|
+
<strong>${out('Read-Only Demo')}</strong>
|
|
91061
|
+
<div style="font-size: 11px; opacity: 0.9; margin-top: 2px;">
|
|
91062
|
+
${out('Full AI chat available in the complete version.')}
|
|
91063
|
+
</div>
|
|
91064
|
+
</div>
|
|
91065
|
+
</div>
|
|
91066
|
+
`;
|
|
91067
|
+
|
|
91068
|
+
// Insert banner at the top of messages container
|
|
91069
|
+
// this.messagesContainer.insertBefore(banner, this.messagesContainer.firstChild);
|
|
91070
|
+
this.messagesContainer.appendChild(banner);
|
|
91071
|
+
}
|
|
91072
|
+
|
|
91073
|
+
/**
|
|
91074
|
+
* Update the quick image size button based on current model and settings
|
|
91075
|
+
*/
|
|
91076
|
+
updateQuickImageSizeButton() {
|
|
91077
|
+
const imageGenEnabled = this.builder.generateMediaUrl_Fal ? true : false;
|
|
91078
|
+
if (!imageGenEnabled) {
|
|
91079
|
+
this.quickImageSizeButton.style.display = 'none';
|
|
91080
|
+
return;
|
|
91081
|
+
}
|
|
91082
|
+
const currentModelId = this.settings.imageModel;
|
|
91083
|
+
const availableSizes = this.getSizesForModel(currentModelId);
|
|
91084
|
+
|
|
91085
|
+
// Hide button if model has no size options
|
|
91086
|
+
if (!availableSizes || availableSizes.length === 0) {
|
|
91087
|
+
this.quickImageSizeButton.style.display = 'none';
|
|
91088
|
+
return;
|
|
91089
|
+
}
|
|
91090
|
+
|
|
91091
|
+
// Show button
|
|
91092
|
+
this.quickImageSizeButton.style.display = 'flex';
|
|
91093
|
+
|
|
91094
|
+
// Smart selection: check if current size is available
|
|
91095
|
+
let selectedSize = this.settings.imageSize;
|
|
91096
|
+
if (!availableSizes.includes(selectedSize)) {
|
|
91097
|
+
// Current size not available, find best fallback
|
|
91098
|
+
selectedSize = this.findBestFallback(selectedSize, availableSizes);
|
|
91099
|
+
this.settings.imageSize = selectedSize;
|
|
91100
|
+
|
|
91101
|
+
// Sync with settings dialog
|
|
91102
|
+
this.imageSizeSelect.value = selectedSize;
|
|
91103
|
+
|
|
91104
|
+
// Save to storage
|
|
91105
|
+
this.saveSettingsToStorage();
|
|
91106
|
+
}
|
|
91107
|
+
|
|
91108
|
+
// Update button label
|
|
91109
|
+
this.updateButtonLabel(selectedSize);
|
|
91110
|
+
|
|
91111
|
+
// Populate popover options
|
|
91112
|
+
this.populateSizePopover(availableSizes, selectedSize);
|
|
91113
|
+
}
|
|
91114
|
+
|
|
91115
|
+
/**
|
|
91116
|
+
* Get available sizes for a model (reuse existing logic)
|
|
91117
|
+
*/
|
|
91118
|
+
getSizesForModel(modelId) {
|
|
91119
|
+
const model = this.imageModels.find(m => m.id === modelId);
|
|
91120
|
+
if (!model) return null;
|
|
91121
|
+
|
|
91122
|
+
// if sizes explicitly empty array → means no size options
|
|
91123
|
+
if (Array.isArray(model.sizes) && model.sizes.length === 0) {
|
|
91124
|
+
return null;
|
|
91125
|
+
}
|
|
91126
|
+
const defaultSizes = ['square', 'square_hd', 'landscape_4_3', 'landscape_16_9', 'portrait_4_3', 'portrait_16_9'];
|
|
91127
|
+
return model.sizes || defaultSizes;
|
|
91128
|
+
}
|
|
91129
|
+
|
|
91130
|
+
/**
|
|
91131
|
+
* Find best fallback size when current selection is not available
|
|
91132
|
+
*/
|
|
91133
|
+
findBestFallback(preferredSize, availableSizes) {
|
|
91134
|
+
// Strategy 1: Try to match orientation
|
|
91135
|
+
const orientation = this.getOrientation(preferredSize);
|
|
91136
|
+
const matchingOrientation = availableSizes.find(size => this.getOrientation(size) === orientation);
|
|
91137
|
+
if (matchingOrientation) return matchingOrientation;
|
|
91138
|
+
|
|
91139
|
+
// Strategy 2: Common defaults
|
|
91140
|
+
if (availableSizes.includes('16:9')) return '16:9';
|
|
91141
|
+
if (availableSizes.includes('landscape_16_9')) return 'landscape_16_9';
|
|
91142
|
+
if (availableSizes.includes('1:1')) return '1:1';
|
|
91143
|
+
if (availableSizes.includes('square')) return 'square';
|
|
91144
|
+
|
|
91145
|
+
// Strategy 3: First available
|
|
91146
|
+
return availableSizes[0];
|
|
91147
|
+
}
|
|
91148
|
+
|
|
91149
|
+
/**
|
|
91150
|
+
* Determine orientation from size value
|
|
91151
|
+
*/
|
|
91152
|
+
getOrientation(size) {
|
|
91153
|
+
const landscape = ['16:9', '21:9', '4:3', '3:2', '5:4', 'landscape_16_9', 'landscape_4_3'];
|
|
91154
|
+
const portrait = ['9:16', '9:21', '3:4', '2:3', '4:5', 'portrait_16_9', 'portrait_4_3'];
|
|
91155
|
+
if (landscape.includes(size)) return 'landscape';
|
|
91156
|
+
if (portrait.includes(size)) return 'portrait';
|
|
91157
|
+
return 'square';
|
|
91158
|
+
}
|
|
91159
|
+
|
|
91160
|
+
/**
|
|
91161
|
+
* Update button label to show current size
|
|
91162
|
+
*/
|
|
91163
|
+
updateButtonLabel(sizeValue) {
|
|
91164
|
+
if (!sizeValue) {
|
|
91165
|
+
this.quickImageSizeButton.textContent = '□';
|
|
91166
|
+
this.quickImageSizeButton.setAttribute('aria-label', this.out('Select image size'));
|
|
91167
|
+
return;
|
|
91168
|
+
}
|
|
91169
|
+
|
|
91170
|
+
// Convert verbose labels to short form
|
|
91171
|
+
const labelMap = {
|
|
91172
|
+
'landscape_16_9': '16:9',
|
|
91173
|
+
'landscape_4_3': '4:3',
|
|
91174
|
+
'portrait_16_9': '9:16',
|
|
91175
|
+
'portrait_4_3': '3:4',
|
|
91176
|
+
'square': '1:1',
|
|
91177
|
+
'square_hd': '1:1 HD'
|
|
91178
|
+
};
|
|
91179
|
+
const displayLabel = labelMap[sizeValue] || sizeValue;
|
|
91180
|
+
this.quickImageSizeButton.textContent = displayLabel;
|
|
91181
|
+
this.quickImageSizeButton.setAttribute('aria-label', `${this.out('Image size')}: ${displayLabel}`);
|
|
91182
|
+
}
|
|
91183
|
+
|
|
91184
|
+
/**
|
|
91185
|
+
* Populate the size popover with available options
|
|
91186
|
+
*/
|
|
91187
|
+
populateSizePopover(sizes, selectedSize) {
|
|
91188
|
+
const out = s => this.out(s);
|
|
91189
|
+
const sizeDefs = {
|
|
91190
|
+
'square_hd': out('Square HD'),
|
|
91191
|
+
'square': out('Square'),
|
|
91192
|
+
'landscape_4_3': out('Landscape 4x3'),
|
|
91193
|
+
'landscape_16_9': out('Landscape 16x9'),
|
|
91194
|
+
'portrait_4_3': out('Portrait 3x4'),
|
|
91195
|
+
'portrait_16_9': out('Portrait 9x16'),
|
|
91196
|
+
'1:1': out('Square'),
|
|
91197
|
+
'3:2': out('Landscape 3x2'),
|
|
91198
|
+
'4:3': out('Landscape 4x3'),
|
|
91199
|
+
'5:4': out('Landscape 5x4'),
|
|
91200
|
+
'16:9': out('Landscape 16x9'),
|
|
91201
|
+
'21:9': out('Landscape 21:9'),
|
|
91202
|
+
'2:3': out('Portrait 2x3'),
|
|
91203
|
+
'3:4': out('Portrait 3x4'),
|
|
91204
|
+
'4:5': out('Portrait 4x5'),
|
|
91205
|
+
'9:16': out('Portrait 9x16'),
|
|
91206
|
+
'9:21': out('Portrait 9:21')
|
|
91207
|
+
};
|
|
91208
|
+
this.sizeOptionsContainer.innerHTML = '';
|
|
91209
|
+
sizes.forEach(size => {
|
|
91210
|
+
const option = document.createElement('div');
|
|
91211
|
+
option.className = 'size-option' + (size === selectedSize ? ' selected' : '');
|
|
91212
|
+
option.setAttribute('role', 'menuitem');
|
|
91213
|
+
option.setAttribute('tabindex', '0');
|
|
91214
|
+
const radio = document.createElement('input');
|
|
91215
|
+
radio.type = 'radio';
|
|
91216
|
+
radio.name = 'quickImageSize';
|
|
91217
|
+
radio.value = size;
|
|
91218
|
+
radio.checked = size === selectedSize;
|
|
91219
|
+
radio.id = `quick-size-${size}`;
|
|
91220
|
+
const label = document.createElement('label');
|
|
91221
|
+
label.htmlFor = `quick-size-${size}`;
|
|
91222
|
+
label.textContent = sizeDefs[size] || size;
|
|
91223
|
+
// label.style.cursor = 'pointer';
|
|
91224
|
+
label.style.flex = '1';
|
|
91225
|
+
option.appendChild(radio);
|
|
91226
|
+
option.appendChild(label);
|
|
91227
|
+
|
|
91228
|
+
// Click handler
|
|
91229
|
+
option.addEventListener('click', () => {
|
|
91230
|
+
this.selectImageSize(size);
|
|
91231
|
+
});
|
|
91232
|
+
|
|
91233
|
+
// Keyboard handler
|
|
91234
|
+
option.addEventListener('keydown', e => {
|
|
91235
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
91236
|
+
e.preventDefault();
|
|
91237
|
+
this.selectImageSize(size);
|
|
91238
|
+
}
|
|
91239
|
+
});
|
|
91240
|
+
this.sizeOptionsContainer.appendChild(option);
|
|
91241
|
+
});
|
|
91242
|
+
}
|
|
91243
|
+
|
|
91244
|
+
/**
|
|
91245
|
+
* Handle size selection
|
|
91246
|
+
*/
|
|
91247
|
+
selectImageSize(size) {
|
|
91248
|
+
// Update settings
|
|
91249
|
+
this.settings.imageSize = size;
|
|
91250
|
+
|
|
91251
|
+
// Sync with settings dialog
|
|
91252
|
+
this.imageSizeSelect.value = size;
|
|
91253
|
+
|
|
91254
|
+
// Update button label
|
|
91255
|
+
this.updateButtonLabel(size);
|
|
91256
|
+
|
|
91257
|
+
// Update popover selection
|
|
91258
|
+
this.sizeOptionsContainer.querySelectorAll('.size-option').forEach(opt => {
|
|
91259
|
+
opt.classList.remove('selected');
|
|
91260
|
+
opt.querySelector('input[type="radio"]').checked = false;
|
|
91261
|
+
});
|
|
91262
|
+
|
|
91263
|
+
// const selectedOption = this.sizeOptionsContainer.querySelector(`#quick-size-${size}`);
|
|
91264
|
+
const selectedOption = Array.from(this.sizeOptionsContainer.querySelectorAll('input[type="radio"]')).find(radio => radio.value === size);
|
|
91265
|
+
if (selectedOption) {
|
|
91266
|
+
selectedOption.closest('.size-option').classList.add('selected');
|
|
91267
|
+
selectedOption.checked = true;
|
|
91268
|
+
}
|
|
91269
|
+
|
|
91270
|
+
// Save to storage
|
|
91271
|
+
this.saveSettingsToStorage();
|
|
91272
|
+
|
|
91273
|
+
// Close popover
|
|
91274
|
+
this.closeImageSizePopover();
|
|
91275
|
+
|
|
91276
|
+
// Focus back to input
|
|
91277
|
+
this.promptInput.focus();
|
|
91278
|
+
}
|
|
91279
|
+
|
|
91280
|
+
/**
|
|
91281
|
+
* Toggle popover visibility
|
|
91282
|
+
*/
|
|
91283
|
+
toggleImageSizePopover() {
|
|
91284
|
+
const isVisible = this.imageSizePopover.style.display !== 'none';
|
|
91285
|
+
if (isVisible) {
|
|
91286
|
+
this.closeImageSizePopover();
|
|
91287
|
+
} else {
|
|
91288
|
+
this.openImageSizePopover();
|
|
91289
|
+
}
|
|
91290
|
+
}
|
|
91291
|
+
|
|
91292
|
+
/**
|
|
91293
|
+
* Open the size popover
|
|
91294
|
+
*/
|
|
91295
|
+
openImageSizePopover() {
|
|
91296
|
+
this.imageSizePopover.style.display = 'block';
|
|
91297
|
+
this.imageSizePopover.setAttribute('aria-hidden', 'false');
|
|
91298
|
+
this.quickImageSizeButton.setAttribute('aria-expanded', 'true');
|
|
91299
|
+
|
|
91300
|
+
// Focus first option
|
|
91301
|
+
const firstOption = this.sizeOptionsContainer.querySelector('.size-option');
|
|
91302
|
+
if (firstOption) {
|
|
91303
|
+
firstOption.focus();
|
|
91304
|
+
}
|
|
91305
|
+
}
|
|
91306
|
+
|
|
91307
|
+
/**
|
|
91308
|
+
* Close the size popover
|
|
91309
|
+
*/
|
|
91310
|
+
closeImageSizePopover() {
|
|
91311
|
+
this.imageSizePopover.style.display = 'none';
|
|
91312
|
+
this.imageSizePopover.setAttribute('aria-hidden', 'true');
|
|
91313
|
+
this.quickImageSizeButton.setAttribute('aria-expanded', 'false');
|
|
91314
|
+
}
|
|
90597
91315
|
out(s) {
|
|
90598
91316
|
let result = this.builder.lang[s];
|
|
90599
91317
|
if (result) return result;else return s;
|
|
@@ -91019,7 +91737,7 @@ Classes: transition-none, transition, transition-colors, transition-opacity, tra
|
|
|
91019
91737
|
|
|
91020
91738
|
**Duration**
|
|
91021
91739
|
|
|
91022
|
-
Classes: duration-75, duration-100, duration-150, duration-200, duration-300, duration-500, duration-700, duration-1000
|
|
91740
|
+
Classes: duration-75, duration-100, duration-150, duration-200, duration-300, duration-500, duration-700, duration-1000, duration-1500
|
|
91023
91741
|
|
|
91024
91742
|
**Timing**
|
|
91025
91743
|
|
|
@@ -91033,6 +91751,10 @@ Classes: delay-75, delay-100, delay-150, delay-200, delay-300, delay-500
|
|
|
91033
91751
|
|
|
91034
91752
|
Classes: scale-0, scale-50, scale-75, scale-90, scale-95, scale-100, scale-105, scale-110, scale-125, scale-150
|
|
91035
91753
|
|
|
91754
|
+
**Hover Effect**
|
|
91755
|
+
|
|
91756
|
+
Classes: hover:scale-105
|
|
91757
|
+
|
|
91036
91758
|
**Rotate**
|
|
91037
91759
|
|
|
91038
91760
|
Classes: rotate-0, rotate-45, rotate-90, rotate-180
|
|
@@ -91366,6 +92088,31 @@ When creating icon-based features, benefits, or service sections: use Bootstrap
|
|
|
91366
92088
|
<h4 class="size-18 font-medium text-center pb-3">Lightning Fast</h4>
|
|
91367
92089
|
<p class="size-14 leading-16 text-gray-600 text-center">Description...</p>
|
|
91368
92090
|
|
|
92091
|
+
### 9. Image Layout with Hover Effect
|
|
92092
|
+
|
|
92093
|
+
Use this pattern for all images (galleries, featured images, portfolio items):
|
|
92094
|
+
|
|
92095
|
+
<div class="w-full overflow-hidden relative bg-gray-50" style="aspect-ratio:16/9">
|
|
92096
|
+
<img src="..." alt="..." class="w-full h-full object-cover transition-transform duration-1500 hover:scale-105">
|
|
92097
|
+
</div>
|
|
92098
|
+
|
|
92099
|
+
**Key elements:**
|
|
92100
|
+
- 'aspect-ratio:16/9' - maintains proportions (adjust as needed: 1/1, 4/3, 3/2, 16/9, etc.)
|
|
92101
|
+
- 'overflow-hidden' - keeps scaled image contained
|
|
92102
|
+
- 'bg-gray-50' - placeholder while image loads
|
|
92103
|
+
- 'transition-transform duration-1000' - smooth 1-second animation
|
|
92104
|
+
- 'hover:scale-105' - subtle 5% scale on hover
|
|
92105
|
+
- 'object-cover' - ensures image fills container
|
|
92106
|
+
|
|
92107
|
+
**Common aspect ratios:**
|
|
92108
|
+
- Square: 'aspect-ratio:1/1'
|
|
92109
|
+
- Portrait: 'aspect-ratio:3/4' or 'aspect-ratio:2/3'
|
|
92110
|
+
- Landscape: 'aspect-ratio:4/3' or 'aspect-ratio:3/2'
|
|
92111
|
+
- Wide: 'aspect-ratio:16/9' or 'aspect-ratio:21/9'
|
|
92112
|
+
- Tall portrait: 'aspect-ratio:9/16'
|
|
92113
|
+
|
|
92114
|
+
Always include this hover effect for a polished, interactive feel.
|
|
92115
|
+
|
|
91369
92116
|
`;
|
|
91370
92117
|
|
|
91371
92118
|
const contextCodeBlock = `
|
|
@@ -91565,6 +92312,17 @@ The frameworks provide utility classes for structure and typography. To create r
|
|
|
91565
92312
|
|
|
91566
92313
|
**Example <style>: When needed for animations, transitions, or reusable patterns**
|
|
91567
92314
|
|
|
92315
|
+
<div class="row">
|
|
92316
|
+
<div class="column">
|
|
92317
|
+
|
|
92318
|
+
<!-- GUIDANCE: Use this layout to show images at any aspect ratio with a hover scale effect -->
|
|
92319
|
+
<div class="w-full overflow-hidden relative bg-gray-50" style="aspect-ratio:16/9">
|
|
92320
|
+
<img src="https://placehold.co/1200x1200/f5f5f5/999?text=Featured" alt="Featured Project" class="w-full h-full object-cover transition-transform duration-1500 hover:scale-105">
|
|
92321
|
+
</div>
|
|
92322
|
+
|
|
92323
|
+
</div>
|
|
92324
|
+
</div>
|
|
92325
|
+
|
|
91568
92326
|
<div class="row">
|
|
91569
92327
|
<div class="column">
|
|
91570
92328
|
|
|
@@ -91739,6 +92497,33 @@ class ContentBuilder {
|
|
|
91739
92497
|
'snippets': []
|
|
91740
92498
|
},
|
|
91741
92499
|
screenMode: 'desktop',
|
|
92500
|
+
demoMode: false,
|
|
92501
|
+
/*
|
|
92502
|
+
demoConversations: [
|
|
92503
|
+
{
|
|
92504
|
+
role: 'user',
|
|
92505
|
+
content: 'Generate an image: "a house with beautiful mountain view. warm sunlight, golden-hour lighting, filmic teal-orange color grade".',
|
|
92506
|
+
// timestamp: Date.now() - 180000 // 3 minutes ago
|
|
92507
|
+
},
|
|
92508
|
+
{
|
|
92509
|
+
role: 'assistant',
|
|
92510
|
+
content: '✓ Image generated successfully',
|
|
92511
|
+
imagePreview: [
|
|
92512
|
+
{ url: 'uploads/ai-8vnqv.png', context: 'Generate image of a house with a beautiful mountain view' },
|
|
92513
|
+
]
|
|
92514
|
+
},
|
|
92515
|
+
{
|
|
92516
|
+
role: 'user',
|
|
92517
|
+
content: `Create a thoughtful article about finding home in extraordinary landscapes. Write vivid prose about mountain dwellings and how our environments shape the way we live.
|
|
92518
|
+
Use the generated image for this article, and present it as a premium design magazine feature.`,
|
|
92519
|
+
},
|
|
92520
|
+
{
|
|
92521
|
+
role: 'assistant',
|
|
92522
|
+
content: '✓ Create a thoughtful article about finding home in extraordinary landscapes with vivid prose about mountain dwellings and their impact on our lives, formatted as a premium design magazine feature.',
|
|
92523
|
+
}
|
|
92524
|
+
],
|
|
92525
|
+
*/
|
|
92526
|
+
|
|
91742
92527
|
// Live Preview
|
|
91743
92528
|
// previewURL: 'preview.html',
|
|
91744
92529
|
onPreviewOpen: () => {
|