diff --git a/internal/content/assets/insertr.css b/internal/content/assets/insertr.css index cc07563..4862303 100644 --- a/internal/content/assets/insertr.css +++ b/internal/content/assets/insertr.css @@ -321,88 +321,105 @@ body:not(.insertr-edit-mode) .insertr-editing-hover::after { } /* ================================================================= - EDIT FORM STYLES + STYLE-AWARE EDITOR STYLES + Modern interface for content editing with style preservation ================================================================= */ -.insertr-edit-form { - padding: var(--insertr-spacing-lg); - margin: 0; - font-family: var(--insertr-font-family); - font-size: var(--insertr-font-size-base); - line-height: var(--insertr-line-height); - color: var(--insertr-text-primary); +/* Main editor container */ +.insertr-style-aware-editor { background: var(--insertr-bg-primary); -} - -.insertr-form-header { - margin: 0 0 var(--insertr-spacing-lg) 0; - padding: 0 0 var(--insertr-spacing-md) 0; - border-bottom: 1px solid var(--insertr-border-color); -} - -.insertr-form-title { - margin: 0 0 var(--insertr-spacing-sm) 0; - padding: 0; - font-size: var(--insertr-font-size-lg); - font-weight: 600; + border: 1px solid var(--insertr-border-color); + border-radius: var(--insertr-border-radius); + padding: var(--insertr-spacing-lg); + min-width: 400px; + max-width: 600px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + font-family: var(--insertr-font-family); color: var(--insertr-text-primary); } -.insertr-form-help { - margin: 0; - padding: 0; +/* Style toolbar */ +.insertr-style-toolbar { + display: flex; + align-items: center; + gap: var(--insertr-spacing-xs); + padding: var(--insertr-spacing-sm); + background: var(--insertr-bg-secondary); + border: 1px solid var(--insertr-border-color); + border-radius: var(--insertr-border-radius); + margin-bottom: var(--insertr-spacing-md); + flex-wrap: wrap; +} + +.insertr-toolbar-title { font-size: var(--insertr-font-size-sm); + font-weight: 500; color: var(--insertr-text-muted); + margin-right: var(--insertr-spacing-xs); } -.insertr-form-body { - margin: 0 0 var(--insertr-spacing-lg) 0; - padding: 0; -} - -.insertr-form-group { - margin: 0 0 var(--insertr-spacing-md) 0; - padding: 0; -} - -.insertr-form-label { - display: block; - margin: 0 0 var(--insertr-spacing-xs) 0; - padding: 0; +.insertr-style-btn { + background: var(--insertr-bg-primary); + border: 1px solid var(--insertr-border-color); + border-radius: var(--insertr-border-radius); + padding: var(--insertr-spacing-xs) var(--insertr-spacing-sm); + font-size: var(--insertr-font-size-sm); font-weight: 500; color: var(--insertr-text-primary); + cursor: pointer; + transition: var(--insertr-transition); } -.insertr-form-input, -.insertr-form-textarea, -.insertr-form-select { +.insertr-style-btn:hover { + background: var(--insertr-bg-secondary); + border-color: var(--insertr-text-muted); +} + +.insertr-style-btn:active { + background: var(--insertr-border-color); + transform: translateY(1px); +} + +/* Editor components */ +.insertr-simple-editor, +.insertr-rich-editor, +.insertr-fallback-textarea { width: 100%; - padding: var(--insertr-input-padding); - margin: 0; border: var(--insertr-input-border); border-radius: var(--insertr-border-radius); - background: var(--insertr-input-bg); - font-family: var(--insertr-font-family); + padding: var(--insertr-input-padding); font-size: var(--insertr-font-size-base); line-height: var(--insertr-line-height); + font-family: var(--insertr-font-family); color: var(--insertr-text-primary); + background: var(--insertr-input-bg); + margin-bottom: var(--insertr-spacing-md); transition: var(--insertr-transition); box-sizing: border-box; } -.insertr-form-input:focus, -.insertr-form-textarea:focus, -.insertr-form-select:focus { +.insertr-simple-editor:focus, +.insertr-rich-editor:focus, +.insertr-fallback-textarea:focus { outline: none; border: var(--insertr-input-border-focus); box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); } -.insertr-form-textarea { - min-height: 120px; +.insertr-simple-editor, +.insertr-fallback-textarea { resize: vertical; + min-height: 120px; } +.insertr-rich-editor { + min-height: 100px; + overflow-y: auto; +} + + + +/* Form actions */ .insertr-form-actions { display: flex; gap: var(--insertr-spacing-sm); @@ -466,6 +483,19 @@ body:not(.insertr-edit-mode) .insertr-editing-hover::after { outline-offset: 2px; } +/* Fallback editor */ +.insertr-fallback-editor { + background: var(--insertr-bg-primary); + border: 1px solid var(--insertr-border-color); + border-radius: var(--insertr-border-radius); + padding: var(--insertr-spacing-lg); + min-width: 400px; + max-width: 500px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + font-family: var(--insertr-font-family); + color: var(--insertr-text-primary); +} + /* ================================================================= VERSION HISTORY MODAL ================================================================= */ @@ -694,10 +724,33 @@ body:not(.insertr-edit-mode) .insertr-editing-hover::after { top: -25px; } + /* StyleAware Editor responsive adjustments */ + .insertr-style-aware-editor, + .insertr-fallback-editor { + min-width: 300px; + max-width: calc(100vw - 2rem); + padding: var(--insertr-spacing-md); + } + + .insertr-style-toolbar { + padding: var(--insertr-spacing-xs); + gap: var(--insertr-spacing-xs); + } + + .insertr-style-btn { + padding: var(--insertr-spacing-xs); + font-size: var(--insertr-font-size-sm); + } + .insertr-form-actions { flex-direction: column; } + .insertr-btn-save, + .insertr-btn-cancel { + width: 100%; + } + .insertr-btn-history { margin-right: 0; order: -1; diff --git a/lib/src/styles/insertr.css b/lib/src/styles/insertr.css index bfcefbf..4862303 100644 --- a/lib/src/styles/insertr.css +++ b/lib/src/styles/insertr.css @@ -321,88 +321,105 @@ body:not(.insertr-edit-mode) .insertr-editing-hover::after { } /* ================================================================= - EDIT FORM STYLES + STYLE-AWARE EDITOR STYLES + Modern interface for content editing with style preservation ================================================================= */ -.insertr-edit-form { - padding: var(--insertr-spacing-lg); - margin: 0; - font-family: var(--insertr-font-family); - font-size: var(--insertr-font-size-base); - line-height: var(--insertr-line-height); - color: var(--insertr-text-primary); +/* Main editor container */ +.insertr-style-aware-editor { background: var(--insertr-bg-primary); -} - -.insertr-form-header { - margin: 0 0 var(--insertr-spacing-lg) 0; - padding: 0 0 var(--insertr-spacing-md) 0; - border-bottom: 1px solid var(--insertr-border-color); -} - -.insertr-form-title { - margin: 0 0 var(--insertr-spacing-sm) 0; - padding: 0; - font-size: var(--insertr-font-size-lg); - font-weight: 600; + border: 1px solid var(--insertr-border-color); + border-radius: var(--insertr-border-radius); + padding: var(--insertr-spacing-lg); + min-width: 400px; + max-width: 600px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + font-family: var(--insertr-font-family); color: var(--insertr-text-primary); } -.insertr-form-help { - margin: 0; - padding: 0; +/* Style toolbar */ +.insertr-style-toolbar { + display: flex; + align-items: center; + gap: var(--insertr-spacing-xs); + padding: var(--insertr-spacing-sm); + background: var(--insertr-bg-secondary); + border: 1px solid var(--insertr-border-color); + border-radius: var(--insertr-border-radius); + margin-bottom: var(--insertr-spacing-md); + flex-wrap: wrap; +} + +.insertr-toolbar-title { font-size: var(--insertr-font-size-sm); + font-weight: 500; color: var(--insertr-text-muted); + margin-right: var(--insertr-spacing-xs); } -.insertr-form-body { - margin: 0 0 var(--insertr-spacing-lg) 0; - padding: 0; -} - -.insertr-form-group { - margin: 0 0 var(--insertr-spacing-md) 0; - padding: 0; -} - -.insertr-form-label { - display: block; - margin: 0 0 var(--insertr-spacing-xs) 0; - padding: 0; +.insertr-style-btn { + background: var(--insertr-bg-primary); + border: 1px solid var(--insertr-border-color); + border-radius: var(--insertr-border-radius); + padding: var(--insertr-spacing-xs) var(--insertr-spacing-sm); + font-size: var(--insertr-font-size-sm); font-weight: 500; color: var(--insertr-text-primary); + cursor: pointer; + transition: var(--insertr-transition); } -.insertr-form-input, -.insertr-form-textarea, -.insertr-form-select { +.insertr-style-btn:hover { + background: var(--insertr-bg-secondary); + border-color: var(--insertr-text-muted); +} + +.insertr-style-btn:active { + background: var(--insertr-border-color); + transform: translateY(1px); +} + +/* Editor components */ +.insertr-simple-editor, +.insertr-rich-editor, +.insertr-fallback-textarea { width: 100%; - padding: var(--insertr-input-padding); - margin: 0; border: var(--insertr-input-border); border-radius: var(--insertr-border-radius); - background: var(--insertr-input-bg); - font-family: var(--insertr-font-family); + padding: var(--insertr-input-padding); font-size: var(--insertr-font-size-base); line-height: var(--insertr-line-height); + font-family: var(--insertr-font-family); color: var(--insertr-text-primary); + background: var(--insertr-input-bg); + margin-bottom: var(--insertr-spacing-md); transition: var(--insertr-transition); box-sizing: border-box; } -.insertr-form-input:focus, -.insertr-form-textarea:focus, -.insertr-form-select:focus { +.insertr-simple-editor:focus, +.insertr-rich-editor:focus, +.insertr-fallback-textarea:focus { outline: none; border: var(--insertr-input-border-focus); box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); } -.insertr-form-textarea { - min-height: 120px; +.insertr-simple-editor, +.insertr-fallback-textarea { resize: vertical; + min-height: 120px; } +.insertr-rich-editor { + min-height: 100px; + overflow-y: auto; +} + + + +/* Form actions */ .insertr-form-actions { display: flex; gap: var(--insertr-spacing-sm); @@ -466,6 +483,19 @@ body:not(.insertr-edit-mode) .insertr-editing-hover::after { outline-offset: 2px; } +/* Fallback editor */ +.insertr-fallback-editor { + background: var(--insertr-bg-primary); + border: 1px solid var(--insertr-border-color); + border-radius: var(--insertr-border-radius); + padding: var(--insertr-spacing-lg); + min-width: 400px; + max-width: 500px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + font-family: var(--insertr-font-family); + color: var(--insertr-text-primary); +} + /* ================================================================= VERSION HISTORY MODAL ================================================================= */ @@ -694,318 +724,35 @@ body:not(.insertr-edit-mode) .insertr-editing-hover::after { top: -25px; } + /* StyleAware Editor responsive adjustments */ + .insertr-style-aware-editor, + .insertr-fallback-editor { + min-width: 300px; + max-width: calc(100vw - 2rem); + padding: var(--insertr-spacing-md); + } + + .insertr-style-toolbar { + padding: var(--insertr-spacing-xs); + gap: var(--insertr-spacing-xs); + } + + .insertr-style-btn { + padding: var(--insertr-spacing-xs); + font-size: var(--insertr-font-size-sm); + } + .insertr-form-actions { flex-direction: column; } + .insertr-btn-save, + .insertr-btn-cancel { + width: 100%; + } + .insertr-btn-history { margin-right: 0; order: -1; } -}/** - * Styles for StyleAwareEditor - * Clean, modern interface that integrates with existing Insertr styling - */ - -/* Main editor container */ -.insertr-style-aware-editor { - background: white; - border: 1px solid #d1d5db; - border-radius: 8px; - padding: 1rem; - min-width: 400px; - max-width: 600px; - box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1); - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; -} - -/* Style toolbar */ -.insertr-style-toolbar { - display: flex; - align-items: center; - gap: 0.5rem; - padding: 0.75rem; - background: #f9fafb; - border: 1px solid #e5e7eb; - border-radius: 6px; - margin-bottom: 1rem; - flex-wrap: wrap; -} - -.insertr-toolbar-title { - font-size: 0.875rem; - font-weight: 500; - color: #6b7280; - margin-right: 0.5rem; -} - -.insertr-style-btn { - background: white; - border: 1px solid #d1d5db; - border-radius: 4px; - padding: 0.375rem 0.75rem; - font-size: 0.875rem; - font-weight: 500; - color: #374151; - cursor: pointer; - transition: all 0.2s ease; -} - -.insertr-style-btn:hover { - background: #f3f4f6; - border-color: #9ca3af; -} - -.insertr-style-btn:active { - background: #e5e7eb; - transform: translateY(1px); -} - -/* Simple text editor */ -.insertr-simple-editor { - width: 100%; - border: 1px solid #d1d5db; - border-radius: 6px; - padding: 0.75rem; - font-size: 0.875rem; - line-height: 1.5; - resize: vertical; - font-family: inherit; - margin-bottom: 1rem; -} - -.insertr-simple-editor:focus { - outline: none; - border-color: #3b82f6; - box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); -} - -/* Rich text editor */ -.insertr-rich-editor { - width: 100%; - min-height: 100px; - border: 1px solid #d1d5db; - border-radius: 6px; - padding: 0.75rem; - font-size: 0.875rem; - line-height: 1.5; - margin-bottom: 1rem; - overflow-y: auto; -} - -.insertr-rich-editor:focus { - outline: none; - border-color: #3b82f6; - box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); -} - -/* Multi-property editor */ -.insertr-multi-property-editor { - margin-bottom: 1rem; -} - -.insertr-styled-element-form { - background: #f9fafb; - border: 1px solid #e5e7eb; - border-radius: 6px; - padding: 1rem; - margin-bottom: 1rem; -} - -.insertr-styled-element-form:last-child { - margin-bottom: 0; -} - -.insertr-form-header { - margin: 0 0 0.75rem 0; - font-size: 1rem; - font-weight: 600; - color: #374151; -} - -/* Property inputs */ -.insertr-property-input, -.insertr-text-input { - margin-bottom: 0.75rem; -} - -.insertr-property-input:last-child, -.insertr-text-input:last-child { - margin-bottom: 0; -} - -.insertr-property-label, -.insertr-input-label { - display: block; - font-size: 0.875rem; - font-weight: 500; - color: #374151; - margin-bottom: 0.25rem; -} - -.insertr-property-field, -.insertr-text-field { - width: 100%; - border: 1px solid #d1d5db; - border-radius: 4px; - padding: 0.5rem; - font-size: 0.875rem; - line-height: 1.4; - font-family: inherit; - transition: border-color 0.2s ease; -} - -.insertr-property-field:focus, -.insertr-text-field:focus { - outline: none; - border-color: #3b82f6; - box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); -} - -/* URL inputs */ -.insertr-property-field[type="url"] { - font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; - font-size: 0.8125rem; -} - -/* Form actions */ -.insertr-form-actions { - display: flex; - gap: 0.75rem; - justify-content: flex-end; - padding-top: 1rem; - border-top: 1px solid #e5e7eb; -} - -.insertr-btn-save, -.insertr-btn-cancel { - padding: 0.5rem 1rem; - font-size: 0.875rem; - font-weight: 500; - border-radius: 6px; - cursor: pointer; - transition: all 0.2s ease; - border: 1px solid; -} - -.insertr-btn-save { - background: #3b82f6; - border-color: #3b82f6; - color: white; -} - -.insertr-btn-save:hover { - background: #2563eb; - border-color: #2563eb; -} - -.insertr-btn-cancel { - background: white; - border-color: #d1d5db; - color: #374151; -} - -.insertr-btn-cancel:hover { - background: #f9fafb; - border-color: #9ca3af; -} - -/* Fallback editor */ -.insertr-fallback-editor { - background: white; - border: 1px solid #d1d5db; - border-radius: 8px; - padding: 1rem; - min-width: 400px; - max-width: 500px; - box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1); -} - -.insertr-fallback-textarea { - width: 100%; - border: 1px solid #d1d5db; - border-radius: 6px; - padding: 0.75rem; - font-size: 0.875rem; - line-height: 1.5; - resize: vertical; - font-family: inherit; - margin-bottom: 1rem; -} - -.insertr-fallback-textarea:focus { - outline: none; - border-color: #3b82f6; - box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); -} - -/* Responsive adjustments */ -@media (max-width: 640px) { - .insertr-style-aware-editor, - .insertr-fallback-editor { - min-width: 300px; - max-width: calc(100vw - 2rem); - margin: 1rem; - } - - .insertr-style-toolbar { - padding: 0.5rem; - gap: 0.375rem; - } - - .insertr-style-btn { - padding: 0.25rem 0.5rem; - font-size: 0.8125rem; - } - - .insertr-form-actions { - flex-direction: column; - } - - .insertr-btn-save, - .insertr-btn-cancel { - width: 100%; - } -} - -/* Dark mode support (if needed) */ -@media (prefers-color-scheme: dark) { - .insertr-style-aware-editor, - .insertr-fallback-editor { - background: #1f2937; - border-color: #374151; - color: #f9fafb; - } - - .insertr-style-toolbar, - .insertr-styled-element-form { - background: #111827; - border-color: #374151; - } - - .insertr-style-btn { - background: #374151; - border-color: #4b5563; - color: #f9fafb; - } - - .insertr-style-btn:hover { - background: #4b5563; - border-color: #6b7280; - } - - .insertr-property-field, - .insertr-text-field, - .insertr-simple-editor, - .insertr-fallback-textarea { - background: #374151; - border-color: #4b5563; - color: #f9fafb; - } - - .insertr-rich-editor { - background: #374151; - border-color: #4b5563; - color: #f9fafb; - } } \ No newline at end of file diff --git a/lib/src/styles/style-aware-editor.css b/lib/src/styles/style-aware-editor.css deleted file mode 100644 index 7bccba3..0000000 --- a/lib/src/styles/style-aware-editor.css +++ /dev/null @@ -1,307 +0,0 @@ -/** - * Styles for StyleAwareEditor - * Clean, modern interface that integrates with existing Insertr styling - */ - -/* Main editor container */ -.insertr-style-aware-editor { - background: white; - border: 1px solid #d1d5db; - border-radius: 8px; - padding: 1rem; - min-width: 400px; - max-width: 600px; - box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1); - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; -} - -/* Style toolbar */ -.insertr-style-toolbar { - display: flex; - align-items: center; - gap: 0.5rem; - padding: 0.75rem; - background: #f9fafb; - border: 1px solid #e5e7eb; - border-radius: 6px; - margin-bottom: 1rem; - flex-wrap: wrap; -} - -.insertr-toolbar-title { - font-size: 0.875rem; - font-weight: 500; - color: #6b7280; - margin-right: 0.5rem; -} - -.insertr-style-btn { - background: white; - border: 1px solid #d1d5db; - border-radius: 4px; - padding: 0.375rem 0.75rem; - font-size: 0.875rem; - font-weight: 500; - color: #374151; - cursor: pointer; - transition: all 0.2s ease; -} - -.insertr-style-btn:hover { - background: #f3f4f6; - border-color: #9ca3af; -} - -.insertr-style-btn:active { - background: #e5e7eb; - transform: translateY(1px); -} - -/* Simple text editor */ -.insertr-simple-editor { - width: 100%; - border: 1px solid #d1d5db; - border-radius: 6px; - padding: 0.75rem; - font-size: 0.875rem; - line-height: 1.5; - resize: vertical; - font-family: inherit; - margin-bottom: 1rem; -} - -.insertr-simple-editor:focus { - outline: none; - border-color: #3b82f6; - box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); -} - -/* Rich text editor */ -.insertr-rich-editor { - width: 100%; - min-height: 100px; - border: 1px solid #d1d5db; - border-radius: 6px; - padding: 0.75rem; - font-size: 0.875rem; - line-height: 1.5; - margin-bottom: 1rem; - overflow-y: auto; -} - -.insertr-rich-editor:focus { - outline: none; - border-color: #3b82f6; - box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); -} - -/* Multi-property editor */ -.insertr-multi-property-editor { - margin-bottom: 1rem; -} - -.insertr-styled-element-form { - background: #f9fafb; - border: 1px solid #e5e7eb; - border-radius: 6px; - padding: 1rem; - margin-bottom: 1rem; -} - -.insertr-styled-element-form:last-child { - margin-bottom: 0; -} - -.insertr-form-header { - margin: 0 0 0.75rem 0; - font-size: 1rem; - font-weight: 600; - color: #374151; -} - -/* Property inputs */ -.insertr-property-input, -.insertr-text-input { - margin-bottom: 0.75rem; -} - -.insertr-property-input:last-child, -.insertr-text-input:last-child { - margin-bottom: 0; -} - -.insertr-property-label, -.insertr-input-label { - display: block; - font-size: 0.875rem; - font-weight: 500; - color: #374151; - margin-bottom: 0.25rem; -} - -.insertr-property-field, -.insertr-text-field { - width: 100%; - border: 1px solid #d1d5db; - border-radius: 4px; - padding: 0.5rem; - font-size: 0.875rem; - line-height: 1.4; - font-family: inherit; - transition: border-color 0.2s ease; -} - -.insertr-property-field:focus, -.insertr-text-field:focus { - outline: none; - border-color: #3b82f6; - box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); -} - -/* URL inputs */ -.insertr-property-field[type="url"] { - font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; - font-size: 0.8125rem; -} - -/* Form actions */ -.insertr-form-actions { - display: flex; - gap: 0.75rem; - justify-content: flex-end; - padding-top: 1rem; - border-top: 1px solid #e5e7eb; -} - -.insertr-btn-save, -.insertr-btn-cancel { - padding: 0.5rem 1rem; - font-size: 0.875rem; - font-weight: 500; - border-radius: 6px; - cursor: pointer; - transition: all 0.2s ease; - border: 1px solid; -} - -.insertr-btn-save { - background: #3b82f6; - border-color: #3b82f6; - color: white; -} - -.insertr-btn-save:hover { - background: #2563eb; - border-color: #2563eb; -} - -.insertr-btn-cancel { - background: white; - border-color: #d1d5db; - color: #374151; -} - -.insertr-btn-cancel:hover { - background: #f9fafb; - border-color: #9ca3af; -} - -/* Fallback editor */ -.insertr-fallback-editor { - background: white; - border: 1px solid #d1d5db; - border-radius: 8px; - padding: 1rem; - min-width: 400px; - max-width: 500px; - box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1); -} - -.insertr-fallback-textarea { - width: 100%; - border: 1px solid #d1d5db; - border-radius: 6px; - padding: 0.75rem; - font-size: 0.875rem; - line-height: 1.5; - resize: vertical; - font-family: inherit; - margin-bottom: 1rem; -} - -.insertr-fallback-textarea:focus { - outline: none; - border-color: #3b82f6; - box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); -} - -/* Responsive adjustments */ -@media (max-width: 640px) { - .insertr-style-aware-editor, - .insertr-fallback-editor { - min-width: 300px; - max-width: calc(100vw - 2rem); - margin: 1rem; - } - - .insertr-style-toolbar { - padding: 0.5rem; - gap: 0.375rem; - } - - .insertr-style-btn { - padding: 0.25rem 0.5rem; - font-size: 0.8125rem; - } - - .insertr-form-actions { - flex-direction: column; - } - - .insertr-btn-save, - .insertr-btn-cancel { - width: 100%; - } -} - -/* Dark mode support (if needed) */ -@media (prefers-color-scheme: dark) { - .insertr-style-aware-editor, - .insertr-fallback-editor { - background: #1f2937; - border-color: #374151; - color: #f9fafb; - } - - .insertr-style-toolbar, - .insertr-styled-element-form { - background: #111827; - border-color: #374151; - } - - .insertr-style-btn { - background: #374151; - border-color: #4b5563; - color: #f9fafb; - } - - .insertr-style-btn:hover { - background: #4b5563; - border-color: #6b7280; - } - - .insertr-property-field, - .insertr-text-field, - .insertr-simple-editor, - .insertr-fallback-textarea { - background: #374151; - border-color: #4b5563; - color: #f9fafb; - } - - .insertr-rich-editor { - background: #374151; - border-color: #4b5563; - color: #f9fafb; - } -} \ No newline at end of file diff --git a/lib/src/ui/style-aware-editor.js b/lib/src/ui/style-aware-editor.js index cc864a0..2620a0a 100644 --- a/lib/src/ui/style-aware-editor.js +++ b/lib/src/ui/style-aware-editor.js @@ -104,12 +104,9 @@ export class StyleAwareEditor { } const hasStyledElements = detection.structure.some(piece => piece.type === 'styled'); - const hasMultiProperty = this.hasMultiPropertyElements(detection.structure); - if (hasMultiProperty) { - return 'multi-property'; // Complex elements with multiple editable properties - } else if (hasStyledElements) { - return 'rich'; // Rich text with styling + if (hasStyledElements) { + return 'rich'; // Rich text with styling (handles all styled content including links) } else { return 'simple'; // Plain text } @@ -147,14 +144,11 @@ export class StyleAwareEditor { case 'rich': this.createRichEditor(analysis); break; - case 'multi-property': - this.createMultiPropertyEditor(analysis); - break; } - // Add toolbar if enabled and we have detected styles - if (this.options.showToolbar && analysis.styles.size > 0) { - this.createStyleToolbar(analysis.styles); + // Add toolbar if enabled and we have any styled content + if (this.options.showToolbar && (analysis.styles.size > 0 || analysis.hasMultiPropertyElements)) { + this.createStyleToolbar(analysis.styles, analysis.structure); } // Add form actions @@ -191,173 +185,19 @@ export class StyleAwareEditor { this.editorContainer.appendChild(editor); } - /** - * Create multi-property editor for complex elements - * - * @param {Object} analysis - Analysis results - */ - createMultiPropertyEditor(analysis) { - const container = document.createElement('div'); - container.className = 'insertr-multi-property-editor'; - - // Create fields for each structure piece - analysis.structure.forEach((piece, index) => { - if (piece.type === 'text') { - // Simple text input - const input = this.createTextInput(piece.content, `Text ${index + 1}`); - input.dataset.structureIndex = index; - container.appendChild(input); - } else if (piece.type === 'styled') { - // Multi-property form for styled element - const form = this.createStyledElementForm(piece, index); - container.appendChild(form); - } - }); - - this.contentEditor = container; - this.editorContainer.appendChild(container); - } + + + + + /** - * Create form for editing styled element with multiple properties - * - * @param {Object} piece - Structure piece for styled element - * @param {number} index - Index in structure array - * @returns {HTMLElement} - Form element - */ - createStyledElementForm(piece, index) { - const form = document.createElement('div'); - form.className = 'insertr-styled-element-form'; - form.dataset.structureIndex = index; - form.dataset.styleId = piece.styleId; - - const style = this.detectedStyles.get(piece.styleId); - if (!style) { - return form; - } - - // Form header - const header = document.createElement('h4'); - header.textContent = style.name; - header.className = 'insertr-form-header'; - form.appendChild(header); - - // Create input for each editable property - Object.entries(piece.properties).forEach(([property, value]) => { - const input = this.createPropertyInput(property, value, style.tagName); - form.appendChild(input); - }); - - return form; - } - - /** - * Create input field for a specific property - * - * @param {string} property - Property name (content, href, src, etc.) - * @param {string} value - Current property value - * @param {string} tagName - Element tag name for context - * @returns {HTMLElement} - Input field container - */ - createPropertyInput(property, value, tagName) { - const container = document.createElement('div'); - container.className = 'insertr-property-input'; - - const label = document.createElement('label'); - label.textContent = this.getPropertyLabel(property, tagName); - label.className = 'insertr-property-label'; - - let input; - - switch (property) { - case 'content': - input = document.createElement('textarea'); - input.rows = 2; - break; - case 'href': - input = document.createElement('input'); - input.type = 'url'; - input.placeholder = 'https://example.com'; - break; - case 'src': - input = document.createElement('input'); - input.type = 'url'; - input.placeholder = 'https://example.com/image.jpg'; - break; - case 'alt': - case 'title': - input = document.createElement('input'); - input.type = 'text'; - break; - default: - input = document.createElement('input'); - input.type = 'text'; - break; - } - - input.className = 'insertr-property-field'; - input.name = property; - input.value = value || ''; - - container.appendChild(label); - container.appendChild(input); - - return container; - } - - /** - * Get human-readable label for property - * - * @param {string} property - Property name - * @param {string} tagName - Element tag name - * @returns {string} - Human-readable label - */ - getPropertyLabel(property, tagName) { - const labels = { - content: tagName === 'img' ? 'Description' : 'Text', - href: 'URL', - src: 'Image URL', - alt: 'Alt Text', - title: 'Title', - target: 'Target', - placeholder: 'Placeholder' - }; - - return labels[property] || property.charAt(0).toUpperCase() + property.slice(1); - } - - /** - * Create simple text input - * - * @param {string} content - Current content - * @param {string} label - Input label - * @returns {HTMLElement} - Input container - */ - createTextInput(content, label) { - const container = document.createElement('div'); - container.className = 'insertr-text-input'; - - const labelEl = document.createElement('label'); - labelEl.textContent = label; - labelEl.className = 'insertr-input-label'; - - const input = document.createElement('textarea'); - input.className = 'insertr-text-field'; - input.value = content; - input.rows = 2; - - container.appendChild(labelEl); - container.appendChild(input); - - return container; - } - - /** - * Create formatting toolbar with detected style buttons + * Create formatting toolbar with detected style buttons and link button * * @param {Map} styles - Detected styles + * @param {Array} structure - Content structure (to detect links) */ - createStyleToolbar(styles) { + createStyleToolbar(styles, structure = []) { const toolbar = document.createElement('div'); toolbar.className = 'insertr-style-toolbar'; @@ -366,12 +206,27 @@ export class StyleAwareEditor { title.className = 'insertr-toolbar-title'; toolbar.appendChild(title); - // Add button for each detected style + // Add button for each detected style (except links - those use the link popup) for (const [styleId, styleInfo] of styles) { + // Skip link styles - they should be handled by the link popup, not toolbar buttons + if (styleInfo.tagName.toLowerCase() === 'a') { + continue; + } + const button = this.createStyleButton(styleId, styleInfo); toolbar.appendChild(button); } + // Add link button if we have links in content or always for rich editor + const hasLinks = structure.some(piece => + piece.type === 'styled' && piece.element && piece.element.tagName.toLowerCase() === 'a' + ); + + if (hasLinks || styles.size > 0) { + const linkButton = this.createLinkButton(); + toolbar.appendChild(linkButton); + } + this.toolbar = toolbar; this.editorContainer.insertBefore(toolbar, this.contentEditor); } @@ -400,6 +255,27 @@ export class StyleAwareEditor { return button; } + /** + * Create link button for opening link configuration popup + * + * @returns {HTMLElement} - Link button + */ + createLinkButton() { + const button = document.createElement('button'); + button.type = 'button'; + button.className = 'insertr-style-btn'; + button.textContent = '🔗 Link'; + button.title = 'Add/Edit Link'; + + // Add click handler for link configuration + button.addEventListener('click', (e) => { + e.preventDefault(); + this.openLinkPopup(); + }); + + return button; + } + /** * Create form action buttons (Save, Cancel) */ @@ -553,63 +429,12 @@ export class StyleAwareEditor { type: 'html', content: this.contentEditor.innerHTML }; - } else if (this.contentEditor.className.includes('multi-property-editor')) { - // Multi-property editor - return this.extractMultiPropertyContent(); } return null; } - /** - * Extract content from multi-property editor - * - * @returns {Object} - Structured content with updated properties - */ - extractMultiPropertyContent() { - const updatedStructure = [...this.contentStructure]; - const updatedProperties = {}; - - // Extract text inputs - const textInputs = this.contentEditor.querySelectorAll('.insertr-text-input textarea'); - textInputs.forEach(input => { - const index = parseInt(input.closest('.insertr-text-input').dataset.structureIndex); - if (!isNaN(index)) { - updatedStructure[index] = { - ...updatedStructure[index], - content: input.value - }; - } - }); - - // Extract styled element forms - const styledForms = this.contentEditor.querySelectorAll('.insertr-styled-element-form'); - styledForms.forEach(form => { - const index = parseInt(form.dataset.structureIndex); - const styleId = form.dataset.styleId; - - if (!isNaN(index)) { - const properties = {}; - const propertyFields = form.querySelectorAll('.insertr-property-field'); - - propertyFields.forEach(field => { - properties[field.name] = field.value; - }); - - updatedProperties[index] = { properties }; - updatedStructure[index] = { - ...updatedStructure[index], - properties - }; - } - }); - - return { - type: 'structured', - structure: updatedStructure, - updatedProperties: updatedProperties - }; - } + /** * Apply extracted content to the original element @@ -657,6 +482,230 @@ export class StyleAwareEditor { return this.editorContainer; } + /** + * Open link configuration popup for selected text + */ + openLinkPopup() { + // Get current selection + const selection = window.getSelection(); + + // Check if we have a valid selection in our editor + if (!selection.rangeCount || !this.contentEditor.contains(selection.anchorNode)) { + alert('Please select some text to create a link'); + return; + } + + const range = selection.getRangeAt(0); + const selectedText = range.toString().trim(); + + if (!selectedText) { + alert('Please select some text to create a link'); + return; + } + + // Check if selection is inside an existing link + let existingLink = null; + let currentNode = range.commonAncestorContainer; + + // Walk up the DOM to find if we're inside a link + while (currentNode && currentNode !== this.contentEditor) { + if (currentNode.nodeType === Node.ELEMENT_NODE && currentNode.tagName.toLowerCase() === 'a') { + existingLink = currentNode; + break; + } + currentNode = currentNode.parentNode; + } + + // Get existing URL if editing a link + const currentUrl = existingLink ? existingLink.href : ''; + const currentTarget = existingLink ? existingLink.target : ''; + + // Show popup + this.showLinkConfigPopup(selectedText, currentUrl, currentTarget, (url, target) => { + this.applyLinkToSelection(range, selectedText, url, target, existingLink); + }); + } + + /** + * Show link configuration popup + * + * @param {string} text - Selected text + * @param {string} currentUrl - Current URL (if editing) + * @param {string} currentTarget - Current target (if editing) + * @param {Function} onSave - Callback when user saves + */ + showLinkConfigPopup(text, currentUrl, currentTarget, onSave) { + // Create popup overlay + const overlay = document.createElement('div'); + overlay.className = 'insertr-modal-overlay'; + overlay.style.zIndex = '999999'; + + // Create popup container + const popup = document.createElement('div'); + popup.className = 'insertr-modal-container'; + popup.style.maxWidth = '400px'; + + // Create form + const form = document.createElement('div'); + form.className = 'insertr-edit-form'; + + // Header + const header = document.createElement('div'); + header.className = 'insertr-form-header'; + header.innerHTML = ` +
Configure link for: "${text}"
+ `; + + // Form body + const body = document.createElement('div'); + body.className = 'insertr-form-body'; + + // URL input + const urlGroup = document.createElement('div'); + urlGroup.className = 'insertr-form-group'; + urlGroup.innerHTML = ` + + + `; + + // Target input + const targetGroup = document.createElement('div'); + targetGroup.className = 'insertr-form-group'; + targetGroup.innerHTML = ` + + + `; + + body.appendChild(urlGroup); + body.appendChild(targetGroup); + + // Actions + const actions = document.createElement('div'); + actions.className = 'insertr-form-actions'; + + const saveBtn = document.createElement('button'); + saveBtn.className = 'insertr-btn-save'; + saveBtn.textContent = 'Save Link'; + + const cancelBtn = document.createElement('button'); + cancelBtn.className = 'insertr-btn-cancel'; + cancelBtn.textContent = 'Cancel'; + + // Remove link button (if editing existing link) + if (currentUrl) { + const removeBtn = document.createElement('button'); + removeBtn.className = 'insertr-btn-cancel'; + removeBtn.textContent = 'Remove Link'; + removeBtn.style.marginRight = 'auto'; + actions.appendChild(removeBtn); + + removeBtn.addEventListener('click', () => { + onSave('', ''); // Empty URL removes the link + document.body.removeChild(overlay); + }); + } + + actions.appendChild(cancelBtn); + actions.appendChild(saveBtn); + + // Assemble popup + form.appendChild(header); + form.appendChild(body); + form.appendChild(actions); + popup.appendChild(form); + overlay.appendChild(popup); + + // Event handlers + saveBtn.addEventListener('click', () => { + const url = document.getElementById('link-url').value.trim(); + const target = document.getElementById('link-target').value; + + if (url) { + onSave(url, target); + document.body.removeChild(overlay); + } else { + alert('Please enter a valid URL'); + } + }); + + cancelBtn.addEventListener('click', () => { + document.body.removeChild(overlay); + }); + + overlay.addEventListener('click', (e) => { + if (e.target === overlay) { + document.body.removeChild(overlay); + } + }); + + // Show popup + document.body.appendChild(overlay); + + // Focus URL input + setTimeout(() => { + document.getElementById('link-url').focus(); + }, 100); + } + + /** + * Apply link to the current selection + * + * @param {Range} range - Selection range + * @param {string} text - Selected text + * @param {string} url - Link URL + * @param {string} target - Link target + * @param {HTMLElement} existingLink - Existing link element (if editing) + */ + applyLinkToSelection(range, text, url, target, existingLink) { + if (!url) { + // Remove link if no URL provided + if (existingLink) { + const parent = existingLink.parentNode; + while (existingLink.firstChild) { + parent.insertBefore(existingLink.firstChild, existingLink); + } + parent.removeChild(existingLink); + } + return; + } + + if (existingLink) { + // Update existing link + existingLink.href = url; + if (target) { + existingLink.target = target; + } else { + existingLink.removeAttribute('target'); + } + } else { + // Create new link + const link = document.createElement('a'); + link.href = url; + if (target) { + link.target = target; + } + + try { + range.surroundContents(link); + } catch (e) { + // Fallback for complex selections + link.textContent = text; + range.deleteContents(); + range.insertNode(link); + } + } + + // Clear selection + window.getSelection().removeAllRanges(); + + // Trigger change event + this.onChange(); + } + /** * Destroy the editor and clean up */