feat: phase 5 of tailwind migration
This commit is contained in:
@@ -9,18 +9,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.chat-panel {
|
.chat-panel {
|
||||||
display: flex;
|
|
||||||
min-height: 0;
|
|
||||||
flex-direction: column;
|
|
||||||
color: var(--vscode-editor-foreground);
|
color: var(--vscode-editor-foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-panel-header {
|
.chat-panel-header {
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 16px;
|
|
||||||
padding: 12px 16px;
|
|
||||||
border-bottom: 1px solid var(--vscode-panel-border);
|
border-bottom: 1px solid var(--vscode-panel-border);
|
||||||
background: var(--vscode-sideBar-background);
|
background: var(--vscode-sideBar-background);
|
||||||
}
|
}
|
||||||
@@ -28,8 +20,6 @@
|
|||||||
.chat-panel-title {
|
.chat-panel-title {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
@@ -91,10 +81,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.chat-messages {
|
.chat-messages {
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 16px;
|
|
||||||
padding: 16px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-message {
|
.chat-message {
|
||||||
@@ -152,9 +138,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.chat-panel .chat-input-wrapper {
|
.chat-panel .chat-input-wrapper {
|
||||||
display: flex;
|
|
||||||
align-items: flex-end;
|
|
||||||
gap: 8px;
|
|
||||||
min-height: 30px;
|
min-height: 30px;
|
||||||
border: 1px solid var(--vscode-input-border);
|
border: 1px solid var(--vscode-input-border);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
@@ -204,3 +187,37 @@
|
|||||||
.chat-panel .chat-send-button:disabled {
|
.chat-panel .chat-send-button:disabled {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 720px) {
|
||||||
|
.chat-panel-header {
|
||||||
|
align-items: stretch;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 10px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-panel-title {
|
||||||
|
width: 100%;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-model-selector-wrap {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-panel .chat-model-selector-button.chat-model-selector-inline {
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-messages {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-message-content {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-panel .chat-input-container {
|
||||||
|
padding: 8px 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -321,58 +321,6 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-editor .editor-header,
|
|
||||||
.scripts-view-shell.editor .editor-header,
|
|
||||||
.templates-view-shell.editor .editor-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 12px;
|
|
||||||
padding: 0 12px;
|
|
||||||
min-height: 35px;
|
|
||||||
background-color: var(--vscode-tab-activeBackground);
|
|
||||||
border-bottom: 1px solid var(--vscode-panel-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-editor .editor-tabs,
|
|
||||||
.scripts-view-shell.editor .editor-tabs,
|
|
||||||
.templates-view-shell.editor .editor-tabs {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 2px;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-editor .editor-tab,
|
|
||||||
.scripts-view-shell.editor .editor-tab,
|
|
||||||
.templates-view-shell.editor .editor-tab {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
min-width: 0;
|
|
||||||
padding: 6px 12px;
|
|
||||||
background-color: var(--vscode-tab-inactiveBackground);
|
|
||||||
color: var(--vscode-tab-inactiveForeground);
|
|
||||||
font-size: 13px;
|
|
||||||
border-radius: 4px 4px 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-editor .editor-tab.active,
|
|
||||||
.scripts-view-shell.editor .editor-tab.active,
|
|
||||||
.templates-view-shell.editor .editor-tab.active {
|
|
||||||
background-color: var(--vscode-tab-activeBackground);
|
|
||||||
color: var(--vscode-tab-activeForeground);
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-editor .editor-tab-title,
|
|
||||||
.scripts-view-shell.editor .editor-tab-title,
|
|
||||||
.templates-view-shell.editor .editor-tab-title {
|
|
||||||
min-width: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-editor .editor-tab-dirty {
|
.post-editor .editor-tab-dirty {
|
||||||
color: var(--vscode-notificationsWarningIcon-foreground, var(--vscode-editorWarning-foreground));
|
color: var(--vscode-notificationsWarningIcon-foreground, var(--vscode-editorWarning-foreground));
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
@@ -384,14 +332,6 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-editor .editor-actions,
|
|
||||||
.scripts-view-shell.editor .editor-actions,
|
|
||||||
.templates-view-shell.editor .editor-actions {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-editor .quick-actions-wrapper {
|
.post-editor .quick-actions-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@@ -409,60 +349,17 @@
|
|||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-editor .quick-actions-menu {
|
|
||||||
position: absolute;
|
|
||||||
top: 100%;
|
|
||||||
right: 0;
|
|
||||||
margin-top: 4px;
|
|
||||||
min-width: 280px;
|
|
||||||
background: var(--vscode-dropdown-background, #3c3c3c);
|
|
||||||
border: 1px solid var(--vscode-dropdown-border, #454545);
|
|
||||||
border-radius: 6px;
|
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
|
||||||
z-index: 1000;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-editor .quick-actions-divider {
|
.post-editor .quick-actions-divider {
|
||||||
height: 1px;
|
height: 1px;
|
||||||
background: var(--vscode-dropdown-border, #454545);
|
background: var(--vscode-dropdown-border, #454545);
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-editor .quick-action-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
gap: 10px;
|
|
||||||
width: 100%;
|
|
||||||
padding: 10px 12px;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
color: var(--vscode-dropdown-foreground, #ccc);
|
|
||||||
cursor: pointer;
|
|
||||||
text-align: left;
|
|
||||||
transition: background 0.1s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-editor .quick-action-item:hover:not(:disabled) {
|
|
||||||
background: var(--vscode-list-hoverBackground, #2a2d2e);
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-editor .quick-action-item:disabled {
|
|
||||||
opacity: 0.5;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-editor .quick-action-icon {
|
.post-editor .quick-action-icon {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-editor .quick-action-text {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-editor .quick-action-text strong {
|
.post-editor .quick-action-text strong {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
@@ -510,19 +407,7 @@
|
|||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-editor .editor-content {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 16px;
|
|
||||||
padding: 16px;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-editor .metadata-toggle-header {
|
.post-editor .metadata-toggle-header {
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-editor .metadata-toggle {
|
.post-editor .metadata-toggle {
|
||||||
@@ -550,37 +435,15 @@
|
|||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-editor .editor-header-row {
|
|
||||||
display: flex;
|
|
||||||
gap: 16px;
|
|
||||||
align-items: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-editor .editor-header-row.is-collapsed {
|
.post-editor .editor-header-row.is-collapsed {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-editor .editor-meta {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 8px;
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-editor .editor-media-panel {
|
.post-editor .editor-media-panel {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-editor .editor-field {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 4px;
|
|
||||||
flex: 1;
|
|
||||||
min-width: 200px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-editor .editor-field label,
|
.post-editor .editor-field label,
|
||||||
.post-editor .editor-body label,
|
.post-editor .editor-body label,
|
||||||
.post-editor .post-editor-links-label {
|
.post-editor .post-editor-links-label {
|
||||||
@@ -600,27 +463,11 @@
|
|||||||
color: var(--vscode-foreground);
|
color: var(--vscode-foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-editor .post-editor-input,
|
|
||||||
.post-editor .post-editor-textarea {
|
|
||||||
width: 100%;
|
|
||||||
padding: 8px 10px;
|
|
||||||
border: 1px solid var(--vscode-input-border, var(--vscode-panel-border));
|
|
||||||
border-radius: 4px;
|
|
||||||
background: var(--vscode-input-background, rgba(255, 255, 255, 0.06));
|
|
||||||
color: var(--vscode-input-foreground, var(--vscode-foreground));
|
|
||||||
font: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-editor .post-editor-input.is-readonly {
|
.post-editor .post-editor-input.is-readonly {
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-editor .post-editor-textarea {
|
|
||||||
line-height: 1.5;
|
|
||||||
resize: vertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-editor .post-editor-excerpt {
|
.post-editor .post-editor-excerpt {
|
||||||
min-height: 96px;
|
min-height: 96px;
|
||||||
}
|
}
|
||||||
@@ -796,33 +643,11 @@
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-editor .editor-field-row {
|
|
||||||
display: flex;
|
|
||||||
gap: 12px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-editor .editor-language-row {
|
|
||||||
display: flex;
|
|
||||||
gap: 6px;
|
|
||||||
align-items: center;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-editor .editor-language-row select {
|
.post-editor .editor-language-row select {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post-editor .editor-translations-flags {
|
|
||||||
display: flex;
|
|
||||||
gap: 4px;
|
|
||||||
align-items: center;
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
overflow-x: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-editor .editor-translation-flag {
|
.post-editor .editor-translation-flag {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -1,64 +1,8 @@
|
|||||||
[data-testid="media-editor"] {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
background-color: var(--vscode-editor-background);
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-testid="media-editor"] .editor-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 12px;
|
|
||||||
padding: 0 12px;
|
|
||||||
min-height: 35px;
|
|
||||||
background-color: var(--vscode-tab-activeBackground);
|
|
||||||
border-bottom: 1px solid var(--vscode-panel-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-testid="media-editor"] .editor-tabs {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 2px;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-testid="media-editor"] .editor-tab {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 6px;
|
|
||||||
min-width: 0;
|
|
||||||
padding: 6px 12px;
|
|
||||||
background-color: var(--vscode-tab-inactiveBackground);
|
|
||||||
color: var(--vscode-tab-inactiveForeground);
|
|
||||||
font-size: 13px;
|
|
||||||
border-radius: 4px 4px 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-testid="media-editor"] .editor-tab.active {
|
|
||||||
background-color: var(--vscode-tab-activeBackground);
|
|
||||||
color: var(--vscode-tab-activeForeground);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-testid="media-editor"] .editor-tab-title {
|
|
||||||
min-width: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-testid="media-editor"] .editor-tab-dirty {
|
[data-testid="media-editor"] .editor-tab-dirty {
|
||||||
color: var(--vscode-notificationsWarningIcon-foreground, var(--vscode-editorWarning-foreground));
|
color: var(--vscode-notificationsWarningIcon-foreground, var(--vscode-editorWarning-foreground));
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-testid="media-editor"] .editor-actions {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-testid="media-editor"] .editor-actions button {
|
[data-testid="media-editor"] .editor-actions button {
|
||||||
padding: 4px 10px;
|
padding: 4px 10px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
@@ -89,62 +33,17 @@
|
|||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-testid="media-editor"] .quick-actions-menu {
|
|
||||||
position: absolute;
|
|
||||||
top: calc(100% + 4px);
|
|
||||||
right: 0;
|
|
||||||
width: 280px;
|
|
||||||
background: var(--vscode-dropdown-background, #252526);
|
|
||||||
border: 1px solid var(--vscode-dropdown-border, #454545);
|
|
||||||
border-radius: 6px;
|
|
||||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
|
|
||||||
overflow: hidden;
|
|
||||||
z-index: 30;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-testid="media-editor"] .quick-actions-divider {
|
[data-testid="media-editor"] .quick-actions-divider {
|
||||||
height: 1px;
|
height: 1px;
|
||||||
background: var(--vscode-dropdown-border, #454545);
|
background: var(--vscode-dropdown-border, #454545);
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-testid="media-editor"] .quick-action-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 10px;
|
|
||||||
width: 100%;
|
|
||||||
padding: 10px 12px;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
color: var(--vscode-dropdown-foreground, #ccc);
|
|
||||||
cursor: pointer;
|
|
||||||
text-align: left;
|
|
||||||
transition: background 0.1s;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-testid="media-editor"] .quick-action-item:hover:not(:disabled) {
|
|
||||||
background: var(--vscode-list-hoverBackground, #2a2d2e);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-testid="media-editor"] .quick-action-item:disabled {
|
|
||||||
opacity: 0.5;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-testid="media-editor"] .quick-action-icon {
|
[data-testid="media-editor"] .quick-action-icon {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-testid="media-editor"] .quick-action-text {
|
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 2px;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-testid="media-editor"] .quick-action-text strong {
|
[data-testid="media-editor"] .quick-action-text strong {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
@@ -155,29 +54,12 @@
|
|||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-testid="media-editor"] .editor-content {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 16px;
|
|
||||||
overflow-y: auto;
|
|
||||||
gap: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-testid="media-editor"] > .editor-content.media-editor {
|
[data-testid="media-editor"] > .editor-content.media-editor {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
gap: 24px;
|
gap: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-testid="media-editor"] .editor-field {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 4px;
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-testid="media-editor"] .editor-field label {
|
[data-testid="media-editor"] .editor-field label {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
@@ -186,35 +68,12 @@
|
|||||||
letter-spacing: 0.5px;
|
letter-spacing: 0.5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-testid="media-editor"] .editor-field-row {
|
|
||||||
display: flex;
|
|
||||||
gap: 12px;
|
|
||||||
width: 100%;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-testid="media-editor"] .post-editor-input,
|
|
||||||
[data-testid="media-editor"] .post-editor-textarea {
|
|
||||||
width: 100%;
|
|
||||||
padding: 8px 10px;
|
|
||||||
border: 1px solid var(--vscode-input-border, var(--vscode-panel-border));
|
|
||||||
border-radius: 4px;
|
|
||||||
background: var(--vscode-input-background, rgba(255, 255, 255, 0.06));
|
|
||||||
color: var(--vscode-input-foreground, var(--vscode-foreground));
|
|
||||||
font: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-testid="media-editor"] .post-editor-input.disabled,
|
[data-testid="media-editor"] .post-editor-input.disabled,
|
||||||
[data-testid="media-editor"] .post-editor-input:disabled {
|
[data-testid="media-editor"] .post-editor-input:disabled {
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-testid="media-editor"] .post-editor-textarea {
|
|
||||||
line-height: 1.5;
|
|
||||||
resize: vertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-testid="media-editor"] .media-preview {
|
[data-testid="media-editor"] .media-preview {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -255,18 +114,10 @@
|
|||||||
|
|
||||||
[data-testid="media-editor"] .media-details {
|
[data-testid="media-editor"] .media-details {
|
||||||
width: 320px;
|
width: 320px;
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-testid="media-editor"] .media-editor-details-form {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-testid="media-editor"] .media-details textarea {
|
[data-testid="media-editor"] .media-details textarea {
|
||||||
resize: vertical;
|
resize: vertical;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,4 @@
|
|||||||
.menu-editor-view {
|
|
||||||
padding: 1rem;
|
|
||||||
height: 100%;
|
|
||||||
flex: 1;
|
|
||||||
min-height: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 0.75rem;
|
|
||||||
overflow: hidden;
|
|
||||||
background: var(--vscode-editor-background);
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-editor-header {
|
.menu-editor-header {
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-editor-header h2 {
|
.menu-editor-header h2 {
|
||||||
@@ -26,18 +10,7 @@
|
|||||||
color: var(--vscode-descriptionForeground);
|
color: var(--vscode-descriptionForeground);
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu-editor-main {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 0;
|
|
||||||
flex: 1;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-editor-tree-wrap {
|
.menu-editor-tree-wrap {
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
flex: 1;
|
|
||||||
border: 1px solid var(--vscode-panel-border);
|
border: 1px solid var(--vscode-panel-border);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
background: var(--vscode-editor-background);
|
background: var(--vscode-editor-background);
|
||||||
@@ -46,9 +19,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.menu-editor-toolbar {
|
.menu-editor-toolbar {
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.2rem;
|
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
padding-bottom: 0.4rem;
|
padding-bottom: 0.4rem;
|
||||||
border-bottom: 1px solid var(--vscode-panel-border);
|
border-bottom: 1px solid var(--vscode-panel-border);
|
||||||
|
|||||||
@@ -130,6 +130,119 @@
|
|||||||
color: var(--vscode-descriptionForeground);
|
color: var(--vscode-descriptionForeground);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ui-editor-shell {
|
||||||
|
height: 100%;
|
||||||
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
background: var(--vscode-editor-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-editor-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
min-height: 35px;
|
||||||
|
padding: 0 12px;
|
||||||
|
border-bottom: 1px solid var(--vscode-panel-border);
|
||||||
|
background: var(--vscode-tab-activeBackground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-editor-tab-current {
|
||||||
|
display: inline-flex;
|
||||||
|
max-width: 100%;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 4px 4px 0 0;
|
||||||
|
background: var(--vscode-tab-activeBackground);
|
||||||
|
color: var(--vscode-tab-activeForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-editor-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-toolbar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
min-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-toolbar-group {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-field-stack {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-field-stack > label,
|
||||||
|
.ui-field-label {
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-field-grid-2,
|
||||||
|
.ui-field-grid-3 {
|
||||||
|
display: grid;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-dropdown-menu {
|
||||||
|
background: var(--vscode-dropdown-background, var(--vscode-sideBar-background));
|
||||||
|
border: 1px solid var(--vscode-dropdown-border, var(--vscode-panel-border));
|
||||||
|
border-radius: 6px;
|
||||||
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-dropdown-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--vscode-dropdown-foreground, var(--vscode-foreground));
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: left;
|
||||||
|
transition: background 0.1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-dropdown-item:hover:not(:disabled) {
|
||||||
|
background: var(--vscode-list-hoverBackground, #2a2d2e);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-dropdown-item:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-section-card {
|
||||||
|
border: 1px solid var(--vscode-panel-border);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: color-mix(in srgb, var(--vscode-editor-background) 84%, var(--vscode-input-background));
|
||||||
|
}
|
||||||
|
|
||||||
.btn-base {
|
.btn-base {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -176,3 +289,13 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.ui-field-grid-2 {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-field-grid-3 {
|
||||||
|
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<div id={"chat-editor-#{@chat_editor.id}"} class="chat-panel flex h-full min-h-0 flex-col" data-testid="chat-editor" phx-hook="ChatSurface">
|
<div id={"chat-editor-#{@chat_editor.id}"} class="chat-panel ui-editor-shell flex h-full min-h-0 flex-col" data-testid="chat-editor" phx-hook="ChatSurface">
|
||||||
<div class="chat-panel-header flex shrink-0 items-center justify-between gap-3">
|
<div class="chat-panel-header flex shrink-0 items-center justify-between gap-3 px-4 py-3">
|
||||||
<div class="chat-panel-title flex min-w-0 flex-1 items-center justify-between gap-3">
|
<div class="chat-panel-title flex min-w-0 flex-1 items-center justify-between gap-3">
|
||||||
<span class="chat-panel-title-main">
|
<span class="chat-panel-title-main">
|
||||||
<%= if @chat_editor.needs_api_key? do %>
|
<%= if @chat_editor.needs_api_key? do %>
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<%= if @chat_editor.model_selector_open? and @chat_editor.available_models != [] do %>
|
<%= if @chat_editor.model_selector_open? and @chat_editor.available_models != [] do %>
|
||||||
<div class="chat-model-selector-menu absolute right-0 top-full z-10 mt-2 flex min-w-56 flex-col">
|
<div class="chat-model-selector-menu ui-dropdown-menu absolute right-0 top-full z-10 mt-2 flex min-w-56 flex-col">
|
||||||
<%= for group <- @chat_editor.available_model_groups do %>
|
<%= for group <- @chat_editor.available_model_groups do %>
|
||||||
<section class="chat-model-provider-group" data-testid="chat-model-provider-group" data-provider={group.provider}>
|
<section class="chat-model-provider-group" data-testid="chat-model-provider-group" data-provider={group.provider}>
|
||||||
<%= if length(@chat_editor.available_model_groups) > 1 do %>
|
<%= if length(@chat_editor.available_model_groups) > 1 do %>
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
<%= for model <- group.models do %>
|
<%= for model <- group.models do %>
|
||||||
<button
|
<button
|
||||||
class={[
|
class={[
|
||||||
"chat-model-selector-option flex items-center justify-between gap-2 text-left",
|
"chat-model-selector-option ui-dropdown-item flex items-center justify-between gap-2 text-left",
|
||||||
if(model.id == @chat_editor.effective_model, do: "active")
|
if(model.id == @chat_editor.effective_model, do: "active")
|
||||||
]}
|
]}
|
||||||
type="button"
|
type="button"
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
|
|
||||||
<div class="chat-messages chat-surface-scroll min-h-0 flex-1 overflow-auto">
|
<div class="chat-messages chat-surface-scroll min-h-0 flex-1 overflow-auto">
|
||||||
<%= if @chat_editor.needs_api_key? do %>
|
<%= if @chat_editor.needs_api_key? do %>
|
||||||
<div class="chat-welcome chat-api-key-state flex flex-col items-start gap-3" data-testid="chat-api-key-required">
|
<div class="chat-welcome chat-api-key-state ui-section-card flex flex-col items-start gap-3 p-4" data-testid="chat-api-key-required">
|
||||||
<div class="chat-welcome-icon">🔑</div>
|
<div class="chat-welcome-icon">🔑</div>
|
||||||
<h2><%= dgettext("ui", "API Key Required") %></h2>
|
<h2><%= dgettext("ui", "API Key Required") %></h2>
|
||||||
<p><%= dgettext("ui", "Configure an API key in Settings to enable AI chat.") %></p>
|
<p><%= dgettext("ui", "Configure an API key in Settings to enable AI chat.") %></p>
|
||||||
@@ -67,7 +67,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= if Enum.empty?(@chat_editor.messages) and not @chat_editor.is_streaming do %>
|
<%= if Enum.empty?(@chat_editor.messages) and not @chat_editor.is_streaming do %>
|
||||||
<div class="chat-welcome flex flex-col items-start gap-3">
|
<div class="chat-welcome ui-section-card flex flex-col items-start gap-3 p-4">
|
||||||
<div class="chat-welcome-icon">🤖</div>
|
<div class="chat-welcome-icon">🤖</div>
|
||||||
<h2><%= dgettext("ui", "Welcome to the AI Assistant") %></h2>
|
<h2><%= dgettext("ui", "Welcome to the AI Assistant") %></h2>
|
||||||
<p><%= dgettext("ui", "I can help you manage your blog with rich visualizations. Try asking me to:") %></p>
|
<p><%= dgettext("ui", "I can help you manage your blog with rich visualizations. Try asking me to:") %></p>
|
||||||
@@ -83,7 +83,7 @@
|
|||||||
<%= for message <- @chat_editor.messages do %>
|
<%= for message <- @chat_editor.messages do %>
|
||||||
<div class={["chat-message flex items-start gap-3", to_string(message.role || "assistant")]}>
|
<div class={["chat-message flex items-start gap-3", to_string(message.role || "assistant")]}>
|
||||||
<div class="chat-message-avatar"><%= if message.role == :user, do: "👤", else: "🤖" %></div>
|
<div class="chat-message-avatar"><%= if message.role == :user, do: "👤", else: "🤖" %></div>
|
||||||
<div class="chat-message-content">
|
<div class="chat-message-content ui-section-card">
|
||||||
<div class="chat-message-header"><span class="chat-message-role"><%= message_role_label(message.role) %></span></div>
|
<div class="chat-message-header"><span class="chat-message-role"><%= message_role_label(message.role) %></span></div>
|
||||||
<.chat_tool_markers markers={message.tool_markers} />
|
<.chat_tool_markers markers={message.tool_markers} />
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@
|
|||||||
<%= if @chat_editor.pending_user_message do %>
|
<%= if @chat_editor.pending_user_message do %>
|
||||||
<div class="chat-message user pending flex items-start gap-3" data-testid="chat-pending-user-message">
|
<div class="chat-message user pending flex items-start gap-3" data-testid="chat-pending-user-message">
|
||||||
<div class="chat-message-avatar">👤</div>
|
<div class="chat-message-avatar">👤</div>
|
||||||
<div class="chat-message-content">
|
<div class="chat-message-content ui-section-card">
|
||||||
<div class="chat-message-header">
|
<div class="chat-message-header">
|
||||||
<span class="chat-message-role"><%= message_role_label(:user) %></span>
|
<span class="chat-message-role"><%= message_role_label(:user) %></span>
|
||||||
</div>
|
</div>
|
||||||
@@ -115,7 +115,7 @@
|
|||||||
<%= if @chat_editor.is_streaming and (@chat_editor.streaming_content != "" or @chat_editor.streaming_tool_markers != []) do %>
|
<%= if @chat_editor.is_streaming and (@chat_editor.streaming_content != "" or @chat_editor.streaming_tool_markers != []) do %>
|
||||||
<div class="chat-message assistant streaming flex items-start gap-3" data-testid="chat-streaming-message">
|
<div class="chat-message assistant streaming flex items-start gap-3" data-testid="chat-streaming-message">
|
||||||
<div class="chat-message-avatar">🤖</div>
|
<div class="chat-message-avatar">🤖</div>
|
||||||
<div class="chat-message-content">
|
<div class="chat-message-content ui-section-card">
|
||||||
<div class="chat-message-header">
|
<div class="chat-message-header">
|
||||||
<span class="chat-message-role"><%= message_role_label(:assistant) %></span>
|
<span class="chat-message-role"><%= message_role_label(:assistant) %></span>
|
||||||
<span class="streaming-indicator">●</span>
|
<span class="streaming-indicator">●</span>
|
||||||
@@ -147,7 +147,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%= unless @chat_editor.needs_api_key? do %>
|
<%= unless @chat_editor.needs_api_key? do %>
|
||||||
<div class="chat-input-container flex shrink-0 flex-col gap-3" data-testid="chat-input-container">
|
<div class="chat-input-container ui-field-stack flex shrink-0 flex-col gap-3" data-testid="chat-input-container">
|
||||||
<%= if @chat_editor.is_streaming do %>
|
<%= if @chat_editor.is_streaming do %>
|
||||||
<button class="chat-abort-button ui-button ui-button-secondary" data-testid="chat-abort-button" type="button" phx-click="abort_chat_editor_message" phx-target={@myself}>◼ <%= dgettext("ui", "Stop") %></button>
|
<button class="chat-abort-button ui-button ui-button-secondary" data-testid="chat-abort-button" type="button" phx-click="abort_chat_editor_message" phx-target={@myself}>◼ <%= dgettext("ui", "Stop") %></button>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<div class="media-editor editor flex h-full min-h-0 flex-col" data-testid="media-editor">
|
<div class="media-editor editor ui-editor-shell flex h-full min-h-0 flex-col" data-testid="media-editor">
|
||||||
<div class="editor-header flex shrink-0 items-start justify-between gap-3">
|
<div class="editor-header ui-editor-header flex shrink-0 items-start justify-between gap-3">
|
||||||
<div class="editor-tabs flex min-w-0 flex-1 overflow-hidden">
|
<div class="editor-tabs flex min-w-0 flex-1 overflow-hidden">
|
||||||
<div class={[
|
<div class={[
|
||||||
"editor-tab ui-tab ui-tab-active active inline-flex max-w-full items-center gap-2 overflow-hidden px-3 py-2",
|
"editor-tab ui-tab ui-tab-active ui-editor-tab-current active inline-flex max-w-full items-center gap-2 overflow-hidden px-3 py-2",
|
||||||
if(@media_editor.dirty?, do: "dirty")
|
if(@media_editor.dirty?, do: "dirty")
|
||||||
]}>
|
]}>
|
||||||
<span class="editor-tab-title truncate" data-testid="editor-title"><%= @media_editor.display_title %></span>
|
<span class="editor-tab-title truncate" data-testid="editor-title"><%= @media_editor.display_title %></span>
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="editor-actions flex flex-wrap items-center justify-end gap-2">
|
<div class="editor-actions ui-editor-actions flex flex-wrap items-center justify-end gap-2">
|
||||||
<%= if @media_editor.save_state in [:dirty, :saved] do %>
|
<%= if @media_editor.save_state in [:dirty, :saved] do %>
|
||||||
<span class="auto-save-indicator"><%= media_editor_save_state_label(@media_editor.save_state) %></span>
|
<span class="auto-save-indicator"><%= media_editor_save_state_label(@media_editor.save_state) %></span>
|
||||||
<% end %>
|
<% end %>
|
||||||
@@ -29,10 +29,10 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<%= if @media_editor.quick_actions_open? do %>
|
<%= if @media_editor.quick_actions_open? do %>
|
||||||
<div class="quick-actions-menu absolute right-0 top-full z-10 mt-2 flex min-w-72 flex-col">
|
<div class="quick-actions-menu ui-dropdown-menu absolute right-0 top-full z-10 mt-2 flex min-w-72 flex-col">
|
||||||
<%= if @media_editor.is_image do %>
|
<%= if @media_editor.is_image do %>
|
||||||
<button
|
<button
|
||||||
class="quick-action-item flex items-start gap-3 text-left"
|
class="quick-action-item ui-dropdown-item flex items-start gap-3 text-left"
|
||||||
data-testid="editor-toolbar-overlay-button"
|
data-testid="editor-toolbar-overlay-button"
|
||||||
type="button"
|
type="button"
|
||||||
phx-click="open_overlay"
|
phx-click="open_overlay"
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="quick-action-item flex items-start gap-3 text-left"
|
class="quick-action-item ui-dropdown-item flex items-start gap-3 text-left"
|
||||||
type="button"
|
type="button"
|
||||||
phx-click="detect_media_editor_language"
|
phx-click="detect_media_editor_language"
|
||||||
phx-target={@myself}
|
phx-target={@myself}
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
<div class="quick-actions-divider"></div>
|
<div class="quick-actions-divider"></div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="quick-action-item flex items-start gap-3 text-left"
|
class="quick-action-item ui-dropdown-item flex items-start gap-3 text-left"
|
||||||
data-testid="editor-toolbar-overlay-button"
|
data-testid="editor-toolbar-overlay-button"
|
||||||
type="button"
|
type="button"
|
||||||
phx-click="open_overlay"
|
phx-click="open_overlay"
|
||||||
@@ -100,7 +100,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="editor-content media-editor grid min-h-0 flex-1 gap-4 overflow-auto xl:grid-cols-[minmax(320px,1fr)_minmax(0,1.2fr)]">
|
<div class="editor-content media-editor grid min-h-0 flex-1 gap-4 overflow-auto p-4 xl:grid-cols-[minmax(320px,1fr)_minmax(0,1.2fr)]">
|
||||||
<div class="media-preview flex min-h-[16rem] items-center justify-center">
|
<div class="media-preview flex min-h-[16rem] items-center justify-center">
|
||||||
<%= if @media_editor.is_image and @media_editor.preview_url do %>
|
<%= if @media_editor.is_image and @media_editor.preview_url do %>
|
||||||
<div class="media-preview-image">
|
<div class="media-preview-image">
|
||||||
@@ -118,56 +118,56 @@
|
|||||||
|
|
||||||
<div class="media-details min-w-0">
|
<div class="media-details min-w-0">
|
||||||
<form class="media-editor-details-form flex flex-col gap-4" data-testid="media-editor-form" phx-change="change_media_editor" phx-target={@myself}>
|
<form class="media-editor-details-form flex flex-col gap-4" data-testid="media-editor-form" phx-change="change_media_editor" phx-target={@myself}>
|
||||||
<div class="editor-field flex flex-col gap-1.5">
|
<div class="editor-field ui-field-stack flex flex-col gap-1.5">
|
||||||
<label><%= dgettext("ui", "File Name") %></label>
|
<label><%= dgettext("ui", "File Name") %></label>
|
||||||
<input class="post-editor-input ui-input disabled ui-input-disabled" type="text" value={@media_editor.original_name} disabled />
|
<input class="post-editor-input ui-input disabled ui-input-disabled" type="text" value={@media_editor.original_name} disabled />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="editor-field flex flex-col gap-1.5">
|
<div class="editor-field ui-field-stack flex flex-col gap-1.5">
|
||||||
<label><%= dgettext("ui", "MIME Type") %></label>
|
<label><%= dgettext("ui", "MIME Type") %></label>
|
||||||
<input class="post-editor-input ui-input disabled ui-input-disabled" type="text" value={@media_editor.mime_type} disabled />
|
<input class="post-editor-input ui-input disabled ui-input-disabled" type="text" value={@media_editor.mime_type} disabled />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="editor-field-row grid gap-4 md:grid-cols-2">
|
<div class="editor-field-row ui-field-grid-2 grid gap-4 md:grid-cols-2">
|
||||||
<div class="editor-field flex flex-col gap-1.5">
|
<div class="editor-field ui-field-stack flex flex-col gap-1.5">
|
||||||
<label><%= dgettext("ui", "Size") %></label>
|
<label><%= dgettext("ui", "Size") %></label>
|
||||||
<input class="post-editor-input ui-input disabled ui-input-disabled" type="text" value={@media_editor.file_size} disabled />
|
<input class="post-editor-input ui-input disabled ui-input-disabled" type="text" value={@media_editor.file_size} disabled />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%= if @media_editor.dimensions do %>
|
<%= if @media_editor.dimensions do %>
|
||||||
<div class="editor-field flex flex-col gap-1.5">
|
<div class="editor-field ui-field-stack flex flex-col gap-1.5">
|
||||||
<label><%= dgettext("ui", "Dimensions") %></label>
|
<label><%= dgettext("ui", "Dimensions") %></label>
|
||||||
<input class="post-editor-input ui-input disabled ui-input-disabled" type="text" value={@media_editor.dimensions} disabled />
|
<input class="post-editor-input ui-input disabled ui-input-disabled" type="text" value={@media_editor.dimensions} disabled />
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="editor-field flex flex-col gap-1.5">
|
<div class="editor-field ui-field-stack flex flex-col gap-1.5">
|
||||||
<label><%= dgettext("ui", "Title") %></label>
|
<label><%= dgettext("ui", "Title") %></label>
|
||||||
<input class="post-editor-input ui-input" type="text" name="media_editor[title]" value={@media_editor.form["title"]} />
|
<input class="post-editor-input ui-input" type="text" name="media_editor[title]" value={@media_editor.form["title"]} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="editor-field flex flex-col gap-1.5">
|
<div class="editor-field ui-field-stack flex flex-col gap-1.5">
|
||||||
<label><%= dgettext("ui", "Alt Text") %></label>
|
<label><%= dgettext("ui", "Alt Text") %></label>
|
||||||
<input class="post-editor-input ui-input" type="text" name="media_editor[alt]" value={@media_editor.form["alt"]} />
|
<input class="post-editor-input ui-input" type="text" name="media_editor[alt]" value={@media_editor.form["alt"]} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="editor-field flex flex-col gap-1.5">
|
<div class="editor-field ui-field-stack flex flex-col gap-1.5">
|
||||||
<label><%= dgettext("ui", "Caption") %></label>
|
<label><%= dgettext("ui", "Caption") %></label>
|
||||||
<textarea class="post-editor-textarea ui-textarea" name="media_editor[caption]" rows="3"><%= @media_editor.form["caption"] %></textarea>
|
<textarea class="post-editor-textarea ui-textarea" name="media_editor[caption]" rows="3"><%= @media_editor.form["caption"] %></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="editor-field flex flex-col gap-1.5">
|
<div class="editor-field ui-field-stack flex flex-col gap-1.5">
|
||||||
<label><%= dgettext("ui", "Tags") %></label>
|
<label><%= dgettext("ui", "Tags") %></label>
|
||||||
<input class="post-editor-input ui-input" type="text" name="media_editor[tags]" value={@media_editor.form["tags"]} />
|
<input class="post-editor-input ui-input" type="text" name="media_editor[tags]" value={@media_editor.form["tags"]} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="editor-field flex flex-col gap-1.5">
|
<div class="editor-field ui-field-stack flex flex-col gap-1.5">
|
||||||
<label><%= dgettext("ui", "Author") %></label>
|
<label><%= dgettext("ui", "Author") %></label>
|
||||||
<input class="post-editor-input ui-input" type="text" name="media_editor[author]" value={@media_editor.form["author"]} />
|
<input class="post-editor-input ui-input" type="text" name="media_editor[author]" value={@media_editor.form["author"]} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="editor-field flex flex-col gap-1.5">
|
<div class="editor-field ui-field-stack flex flex-col gap-1.5">
|
||||||
<label><%= dgettext("ui", "Language") %></label>
|
<label><%= dgettext("ui", "Language") %></label>
|
||||||
<select class="post-editor-input ui-input" name="media_editor[language]">
|
<select class="post-editor-input ui-input" name="media_editor[language]">
|
||||||
<option value=""><%= dgettext("ui", "None") %></option>
|
<option value=""><%= dgettext("ui", "None") %></option>
|
||||||
@@ -281,15 +281,15 @@
|
|||||||
</div>
|
</div>
|
||||||
<form class="translation-modal-body flex flex-col gap-4 overflow-auto" phx-change="change_media_translation" phx-target={@myself}>
|
<form class="translation-modal-body flex flex-col gap-4 overflow-auto" phx-change="change_media_translation" phx-target={@myself}>
|
||||||
<input type="hidden" name="media_translation[language]" value={@media_editor.editing_translation["language"]} />
|
<input type="hidden" name="media_translation[language]" value={@media_editor.editing_translation["language"]} />
|
||||||
<div class="editor-field flex flex-col gap-1.5">
|
<div class="editor-field ui-field-stack flex flex-col gap-1.5">
|
||||||
<label><%= dgettext("ui", "Title") %></label>
|
<label><%= dgettext("ui", "Title") %></label>
|
||||||
<input class="post-editor-input ui-input" type="text" name="media_translation[title]" value={@media_editor.editing_translation["title"]} />
|
<input class="post-editor-input ui-input" type="text" name="media_translation[title]" value={@media_editor.editing_translation["title"]} />
|
||||||
</div>
|
</div>
|
||||||
<div class="editor-field flex flex-col gap-1.5">
|
<div class="editor-field ui-field-stack flex flex-col gap-1.5">
|
||||||
<label><%= dgettext("ui", "Alt Text") %></label>
|
<label><%= dgettext("ui", "Alt Text") %></label>
|
||||||
<input class="post-editor-input ui-input" type="text" name="media_translation[alt]" value={@media_editor.editing_translation["alt"]} />
|
<input class="post-editor-input ui-input" type="text" name="media_translation[alt]" value={@media_editor.editing_translation["alt"]} />
|
||||||
</div>
|
</div>
|
||||||
<div class="editor-field flex flex-col gap-1.5">
|
<div class="editor-field ui-field-stack flex flex-col gap-1.5">
|
||||||
<label><%= dgettext("ui", "Caption") %></label>
|
<label><%= dgettext("ui", "Caption") %></label>
|
||||||
<textarea class="post-editor-textarea ui-textarea" name="media_translation[caption]" rows="3"><%= @media_editor.editing_translation["caption"] %></textarea>
|
<textarea class="post-editor-textarea ui-textarea" name="media_translation[caption]" rows="3"><%= @media_editor.editing_translation["caption"] %></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
<div class="menu-editor-view flex h-full min-h-0 flex-col" data-testid="menu-editor" phx-window-keydown={if(@menu_editor.draft, do: "menu_editor_keydown")} phx-target={@myself}>
|
<div class="menu-editor-view ui-editor-shell flex h-full min-h-0 flex-col p-4" data-testid="menu-editor" phx-window-keydown={if(@menu_editor.draft, do: "menu_editor_keydown")} phx-target={@myself}>
|
||||||
<div class="menu-editor-header flex shrink-0 items-start justify-between gap-3">
|
<div class="menu-editor-header flex shrink-0 items-start justify-between gap-3">
|
||||||
<div>
|
<div class="ui-field-stack">
|
||||||
<h2><%= @menu_editor.title %></h2>
|
<h2><%= @menu_editor.title %></h2>
|
||||||
<p><%= @menu_editor.description %></p>
|
<p><%= @menu_editor.description %></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="menu-editor-main min-h-0 flex-1 overflow-hidden">
|
<div class="menu-editor-main min-h-0 flex-1 overflow-hidden">
|
||||||
<div class="menu-editor-tree-wrap flex h-full min-h-0 flex-col">
|
<div class="menu-editor-tree-wrap ui-section-card flex h-full min-h-0 flex-col">
|
||||||
<div class="menu-editor-toolbar flex flex-wrap items-center gap-2" data-testid="menu-editor-toolbar" role="toolbar" aria-label={@menu_editor.title}>
|
<div class="menu-editor-toolbar ui-toolbar flex flex-wrap items-center gap-2" data-testid="menu-editor-toolbar" role="toolbar" aria-label={@menu_editor.title}>
|
||||||
<button class="menu-editor-tool inline-flex h-9 min-w-9 items-center justify-center" data-testid="menu-editor-toolbar-button" data-action="add-entry" type="button" phx-click="menu_editor_toolbar_action" phx-value-action="add-entry" phx-target={@myself} title={dgettext("ui", "menuEditor.addEntry")}>
|
<button class="menu-editor-tool inline-flex h-9 min-w-9 items-center justify-center" data-testid="menu-editor-toolbar-button" data-action="add-entry" type="button" phx-click="menu_editor_toolbar_action" phx-value-action="add-entry" phx-target={@myself} title={dgettext("ui", "menuEditor.addEntry")}>
|
||||||
<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M7 2h2v5h5v2H9v5H7V9H2V7h5V2z" /></svg>
|
<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor"><path d="M7 2h2v5h5v2H9v5H7V9H2V7h5V2z" /></svg>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<div class="post-editor editor flex h-full min-h-0 flex-col" data-testid="post-editor">
|
<div class="post-editor editor ui-editor-shell flex h-full min-h-0 flex-col" data-testid="post-editor">
|
||||||
<div class="editor-header flex shrink-0 items-start justify-between gap-3">
|
<div class="editor-header ui-editor-header flex shrink-0 items-start justify-between gap-3">
|
||||||
<div class="editor-tabs flex min-w-0 flex-1 overflow-hidden">
|
<div class="editor-tabs flex min-w-0 flex-1 overflow-hidden">
|
||||||
<div class={["editor-tab ui-tab ui-tab-active active inline-flex max-w-full items-center gap-2 overflow-hidden px-3 py-2", if(@post_editor.dirty?, do: "dirty")]}>
|
<div class={["editor-tab ui-tab ui-tab-active ui-editor-tab-current active inline-flex max-w-full items-center gap-2 overflow-hidden px-3 py-2", if(@post_editor.dirty?, do: "dirty")]}>
|
||||||
<span class="editor-tab-title truncate" data-testid="editor-title"><%= @post_editor.display_title %></span>
|
<span class="editor-tab-title truncate" data-testid="editor-title"><%= @post_editor.display_title %></span>
|
||||||
<%= if @post_editor.dirty? do %>
|
<%= if @post_editor.dirty? do %>
|
||||||
<span class="editor-tab-dirty" title={dgettext("ui", "Unsaved")}>●</span>
|
<span class="editor-tab-dirty" title={dgettext("ui", "Unsaved")}>●</span>
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="editor-actions flex flex-wrap items-center justify-end gap-2">
|
<div class="editor-actions ui-editor-actions flex flex-wrap items-center justify-end gap-2">
|
||||||
<span class={["status-badge", "ui-badge", "status-#{@post_editor.status}"]} data-testid="post-status-badge">
|
<span class={["status-badge", "ui-badge", "status-#{@post_editor.status}"]} data-testid="post-status-badge">
|
||||||
<%= post_status_label(@post_editor.status) %>
|
<%= post_status_label(@post_editor.status) %>
|
||||||
</span>
|
</span>
|
||||||
@@ -29,9 +29,9 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<%= if @post_editor.quick_actions_open? do %>
|
<%= if @post_editor.quick_actions_open? do %>
|
||||||
<div class="quick-actions-menu absolute right-0 top-full z-10 mt-2 flex min-w-72 flex-col">
|
<div class="quick-actions-menu ui-dropdown-menu absolute right-0 top-full z-10 mt-2 flex min-w-72 flex-col">
|
||||||
<button
|
<button
|
||||||
class="quick-action-item flex items-start gap-3 text-left"
|
class="quick-action-item ui-dropdown-item flex items-start gap-3 text-left"
|
||||||
data-testid="editor-toolbar-overlay-button"
|
data-testid="editor-toolbar-overlay-button"
|
||||||
type="button"
|
type="button"
|
||||||
phx-click="open_overlay"
|
phx-click="open_overlay"
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
<div class="quick-actions-divider"></div>
|
<div class="quick-actions-divider"></div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="quick-action-item flex items-start gap-3 text-left"
|
class="quick-action-item ui-dropdown-item flex items-start gap-3 text-left"
|
||||||
data-testid="editor-toolbar-overlay-button"
|
data-testid="editor-toolbar-overlay-button"
|
||||||
type="button"
|
type="button"
|
||||||
phx-click="open_overlay"
|
phx-click="open_overlay"
|
||||||
@@ -83,7 +83,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form class="post-editor-form editor-content flex min-h-0 flex-1 flex-col gap-4 overflow-auto" data-testid="post-editor-form" phx-change="change_post_editor" phx-target={@myself}>
|
<form class="post-editor-form editor-content flex min-h-0 flex-1 flex-col gap-4 overflow-auto p-4" data-testid="post-editor-form" phx-change="change_post_editor" phx-target={@myself}>
|
||||||
<div class="metadata-toggle-header flex items-center justify-between gap-3">
|
<div class="metadata-toggle-header flex items-center justify-between gap-3">
|
||||||
<button class={["metadata-toggle", if(@post_editor.metadata_expanded, do: "expanded")]} type="button" phx-click="toggle_post_metadata" phx-target={@myself}>
|
<button class={["metadata-toggle", if(@post_editor.metadata_expanded, do: "expanded")]} type="button" phx-click="toggle_post_metadata" phx-target={@myself}>
|
||||||
<span class="metadata-toggle-chevron"><%= if @post_editor.metadata_expanded, do: "▼", else: "▶" %></span>
|
<span class="metadata-toggle-chevron"><%= if @post_editor.metadata_expanded, do: "▼", else: "▶" %></span>
|
||||||
@@ -113,12 +113,12 @@
|
|||||||
|
|
||||||
<div class={["editor-header-row grid gap-4 xl:grid-cols-[minmax(0,2fr)_minmax(280px,1fr)]", if(not @post_editor.metadata_expanded, do: "is-collapsed")]}>
|
<div class={["editor-header-row grid gap-4 xl:grid-cols-[minmax(0,2fr)_minmax(280px,1fr)]", if(not @post_editor.metadata_expanded, do: "is-collapsed")]}>
|
||||||
<div class="editor-meta flex min-w-0 flex-col gap-4">
|
<div class="editor-meta flex min-w-0 flex-col gap-4">
|
||||||
<div class="editor-field flex flex-col gap-1.5">
|
<div class="editor-field ui-field-stack flex flex-col gap-1.5">
|
||||||
<label><%= dgettext("ui", "Title") %></label>
|
<label><%= dgettext("ui", "Title") %></label>
|
||||||
<input class="post-editor-input ui-input" type="text" name="post_editor[title]" value={@post_editor.form["title"]} />
|
<input class="post-editor-input ui-input" type="text" name="post_editor[title]" value={@post_editor.form["title"]} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="editor-field flex flex-col gap-1.5">
|
<div class="editor-field ui-field-stack flex flex-col gap-1.5">
|
||||||
<label><%= dgettext("ui", "Tags") %></label>
|
<label><%= dgettext("ui", "Tags") %></label>
|
||||||
<div class="tag-input-container relative">
|
<div class="tag-input-container relative">
|
||||||
<input type="hidden" name="post_editor[tags]" value={@post_editor.form["tags"]} />
|
<input type="hidden" name="post_editor[tags]" value={@post_editor.form["tags"]} />
|
||||||
@@ -162,12 +162,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="editor-field flex flex-col gap-1.5">
|
<div class="editor-field ui-field-stack flex flex-col gap-1.5">
|
||||||
<label><%= dgettext("ui", "Author") %></label>
|
<label><%= dgettext("ui", "Author") %></label>
|
||||||
<input class="post-editor-input ui-input" type="text" name="post_editor[author]" value={@post_editor.form["author"]} />
|
<input class="post-editor-input ui-input" type="text" name="post_editor[author]" value={@post_editor.form["author"]} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="editor-field flex flex-col gap-1.5">
|
<div class="editor-field ui-field-stack flex flex-col gap-1.5">
|
||||||
<label><%= dgettext("ui", "Language") %></label>
|
<label><%= dgettext("ui", "Language") %></label>
|
||||||
<div class="editor-language-row flex items-center gap-2">
|
<div class="editor-language-row flex items-center gap-2">
|
||||||
<select class="post-editor-input ui-input" name="post_editor[language]">
|
<select class="post-editor-input ui-input" name="post_editor[language]">
|
||||||
@@ -189,7 +189,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="editor-field flex flex-col gap-1.5">
|
<div class="editor-field ui-field-stack flex flex-col gap-1.5">
|
||||||
<label class="editor-checkbox-label">
|
<label class="editor-checkbox-label">
|
||||||
<input type="hidden" name="post_editor[do_not_translate]" value="false" />
|
<input type="hidden" name="post_editor[do_not_translate]" value="false" />
|
||||||
<input type="checkbox" name="post_editor[do_not_translate]" value="true" checked={@post_editor.form["do_not_translate"]} />
|
<input type="checkbox" name="post_editor[do_not_translate]" value="true" checked={@post_editor.form["do_not_translate"]} />
|
||||||
@@ -197,13 +197,13 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="editor-field-row grid gap-4 md:grid-cols-2">
|
<div class="editor-field-row ui-field-grid-2 grid gap-4 md:grid-cols-2">
|
||||||
<div class="editor-field flex flex-col gap-1.5">
|
<div class="editor-field ui-field-stack flex flex-col gap-1.5">
|
||||||
<label><%= dgettext("ui", "Slug") %></label>
|
<label><%= dgettext("ui", "Slug") %></label>
|
||||||
<input class="post-editor-input ui-input is-readonly ui-input-readonly" type="text" readonly value={@post_editor.slug} />
|
<input class="post-editor-input ui-input is-readonly ui-input-readonly" type="text" readonly value={@post_editor.slug} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="editor-field flex flex-col gap-1.5">
|
<div class="editor-field ui-field-stack flex flex-col gap-1.5">
|
||||||
<label><%= dgettext("ui", "Categories") %></label>
|
<label><%= dgettext("ui", "Categories") %></label>
|
||||||
<div class="tag-input-container relative">
|
<div class="tag-input-container relative">
|
||||||
<input type="hidden" name="post_editor[categories]" value={@post_editor.form["categories"]} />
|
<input type="hidden" name="post_editor[categories]" value={@post_editor.form["categories"]} />
|
||||||
@@ -246,7 +246,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%= if @post_editor.show_template_selector? do %>
|
<%= if @post_editor.show_template_selector? do %>
|
||||||
<div class="editor-field flex flex-col gap-1.5">
|
<div class="editor-field ui-field-stack flex flex-col gap-1.5">
|
||||||
<label><%= dgettext("ui", "Template") %></label>
|
<label><%= dgettext("ui", "Template") %></label>
|
||||||
<select class="post-editor-input ui-input" name="post_editor[template_slug]">
|
<select class="post-editor-input ui-input" name="post_editor[template_slug]">
|
||||||
<option value=""><%= dgettext("ui", "Default") %></option>
|
<option value=""><%= dgettext("ui", "Default") %></option>
|
||||||
@@ -321,12 +321,12 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="editor-body flex min-h-0 flex-1 flex-col overflow-hidden">
|
<div class="editor-body flex min-h-0 flex-1 flex-col overflow-hidden">
|
||||||
<div class="editor-toolbar flex items-center gap-3">
|
<div class="editor-toolbar ui-toolbar flex items-center gap-3">
|
||||||
<div class="editor-toolbar-left flex items-center gap-2">
|
<div class="editor-toolbar-left ui-toolbar-group flex items-center gap-2">
|
||||||
<label><%= dgettext("ui", "Content") %></label>
|
<label><%= dgettext("ui", "Content") %></label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="editor-toolbar-center flex flex-1 justify-center">
|
<div class="editor-toolbar-center ui-toolbar-group flex flex-1 justify-center">
|
||||||
<div class="editor-mode-toggle">
|
<div class="editor-mode-toggle">
|
||||||
<%= for mode <- [:markdown, :preview] do %>
|
<%= for mode <- [:markdown, :preview] do %>
|
||||||
<button
|
<button
|
||||||
@@ -342,7 +342,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="editor-toolbar-right flex items-center gap-2">
|
<div class="editor-toolbar-right ui-toolbar-group flex items-center gap-2">
|
||||||
<%= if @post_editor.mode == :markdown do %>
|
<%= if @post_editor.mode == :markdown do %>
|
||||||
<button
|
<button
|
||||||
class="insert-post-link-button"
|
class="insert-post-link-button"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<div class="scripts-view-shell editor flex h-full min-h-0 flex-col" data-testid="script-editor">
|
<div class="scripts-view-shell editor ui-editor-shell flex h-full min-h-0 flex-col" data-testid="script-editor">
|
||||||
<div class="editor-header scripts-header flex shrink-0 items-start justify-between gap-3">
|
<div class="editor-header scripts-header ui-editor-header flex shrink-0 items-start justify-between gap-3">
|
||||||
<div class="editor-tabs flex min-w-0 flex-1 overflow-hidden"><div class="editor-tab ui-tab ui-tab-active active inline-flex max-w-full items-center overflow-hidden px-3 py-2"><span class="editor-tab-title truncate"><%= @script_editor.title %></span></div></div>
|
<div class="editor-tabs flex min-w-0 flex-1 overflow-hidden"><div class="editor-tab ui-tab ui-tab-active ui-editor-tab-current active inline-flex max-w-full items-center overflow-hidden px-3 py-2"><span class="editor-tab-title truncate"><%= @script_editor.title %></span></div></div>
|
||||||
<div class="editor-actions flex flex-wrap items-center justify-end gap-2">
|
<div class="editor-actions ui-editor-actions flex flex-wrap items-center justify-end gap-2">
|
||||||
<span class={[
|
<span class={[
|
||||||
"status-badge",
|
"status-badge",
|
||||||
"ui-badge",
|
"ui-badge",
|
||||||
@@ -16,22 +16,22 @@
|
|||||||
<button class="secondary danger ui-button ui-button-secondary ui-button-danger" type="button" phx-click="delete_script_editor" phx-target={@myself}><%= dgettext("ui", "Delete") %></button>
|
<button class="secondary danger ui-button ui-button-secondary ui-button-danger" type="button" phx-click="delete_script_editor" phx-target={@myself}><%= dgettext("ui", "Delete") %></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<form class="editor-content scripts-view flex min-h-0 flex-1 flex-col gap-4 overflow-hidden" phx-change="change_script_editor" phx-target={@myself}>
|
<form class="editor-content scripts-view flex min-h-0 flex-1 flex-col gap-4 overflow-hidden p-4" phx-change="change_script_editor" phx-target={@myself}>
|
||||||
<div class="editor-header-row scripts-meta-row grid gap-4">
|
<div class="editor-header-row scripts-meta-row grid gap-4">
|
||||||
<div class="editor-meta flex min-w-0 flex-col gap-4">
|
<div class="editor-meta flex min-w-0 flex-col gap-4">
|
||||||
<div class="editor-field-row grid gap-4 md:grid-cols-2">
|
<div class="editor-field-row ui-field-grid-2 grid gap-4 md:grid-cols-2">
|
||||||
<div class="editor-field flex flex-col gap-1.5"><label><%= dgettext("ui", "Title") %></label><input class="ui-input" type="text" name="script_editor[title]" value={@script_editor.title} /></div>
|
<div class="editor-field ui-field-stack flex flex-col gap-1.5"><label><%= dgettext("ui", "Title") %></label><input class="ui-input" type="text" name="script_editor[title]" value={@script_editor.title} /></div>
|
||||||
<div class="editor-field flex flex-col gap-1.5"><label><%= dgettext("ui", "Slug") %></label><input class="ui-input" type="text" name="script_editor[slug]" value={@script_editor.slug} /></div>
|
<div class="editor-field ui-field-stack flex flex-col gap-1.5"><label><%= dgettext("ui", "Slug") %></label><input class="ui-input" type="text" name="script_editor[slug]" value={@script_editor.slug} /></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="editor-field-row grid gap-4 md:grid-cols-[minmax(0,1fr)_minmax(0,1fr)_auto]">
|
<div class="editor-field-row ui-field-grid-3 grid gap-4 md:grid-cols-[minmax(0,1fr)_minmax(0,1fr)_auto]">
|
||||||
<div class="editor-field flex flex-col gap-1.5"><label><%= dgettext("ui", "Kind") %></label><select class="ui-input" name="script_editor[kind]"><option value="utility" selected={@script_editor.kind == "utility"}>utility</option><option value="macro" selected={@script_editor.kind == "macro"}>macro</option><option value="transform" selected={@script_editor.kind == "transform"}>transform</option></select></div>
|
<div class="editor-field ui-field-stack flex flex-col gap-1.5"><label><%= dgettext("ui", "Kind") %></label><select class="ui-input" name="script_editor[kind]"><option value="utility" selected={@script_editor.kind == "utility"}>utility</option><option value="macro" selected={@script_editor.kind == "macro"}>macro</option><option value="transform" selected={@script_editor.kind == "transform"}>transform</option></select></div>
|
||||||
<div class="editor-field flex flex-col gap-1.5"><label><%= dgettext("ui", "Entrypoint") %></label><select class="ui-input" name="script_editor[entrypoint]"><%= for entrypoint <- @script_editor.entrypoints do %><option value={entrypoint} selected={entrypoint == @script_editor.entrypoint}><%= entrypoint %></option><% end %></select></div>
|
<div class="editor-field ui-field-stack flex flex-col gap-1.5"><label><%= dgettext("ui", "Entrypoint") %></label><select class="ui-input" name="script_editor[entrypoint]"><%= for entrypoint <- @script_editor.entrypoints do %><option value={entrypoint} selected={entrypoint == @script_editor.entrypoint}><%= entrypoint %></option><% end %></select></div>
|
||||||
<div class="editor-field scripts-enabled-field flex flex-col justify-end gap-1.5"><label><input type="checkbox" name="script_editor[enabled]" checked={@script_editor.enabled} /> <%= dgettext("ui", "Enabled") %></label></div>
|
<div class="editor-field scripts-enabled-field flex flex-col justify-end gap-1.5"><label><input type="checkbox" name="script_editor[enabled]" checked={@script_editor.enabled} /> <%= dgettext("ui", "Enabled") %></label></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="editor-body scripts-editor flex min-h-0 flex-1 flex-col overflow-hidden">
|
<div class="editor-body scripts-editor flex min-h-0 flex-1 flex-col overflow-hidden">
|
||||||
<div class="editor-toolbar scripts-toolbar flex items-center gap-3"><div class="editor-toolbar-left flex items-center gap-2"><label><%= dgettext("ui", "Content") %></label></div></div>
|
<div class="editor-toolbar scripts-toolbar ui-toolbar flex items-center gap-3"><div class="editor-toolbar-left ui-toolbar-group flex items-center gap-2"><label><%= dgettext("ui", "Content") %></label></div></div>
|
||||||
<div
|
<div
|
||||||
id={"script-editor-monaco-shell-#{@script_editor.id}"}
|
id={"script-editor-monaco-shell-#{@script_editor.id}"}
|
||||||
class="scripts-monaco monaco-editor-shell min-h-0 flex-1 overflow-hidden"
|
class="scripts-monaco monaco-editor-shell min-h-0 flex-1 overflow-hidden"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<div
|
<div
|
||||||
id="settings-editor-shell"
|
id="settings-editor-shell"
|
||||||
class="settings-view-shell flex h-full min-h-0 flex-col overflow-hidden"
|
class="settings-view-shell ui-editor-shell flex h-full min-h-0 flex-col overflow-hidden"
|
||||||
data-testid="settings-editor"
|
data-testid="settings-editor"
|
||||||
phx-hook="SettingsSectionScroll"
|
phx-hook="SettingsSectionScroll"
|
||||||
data-selected-settings-section={@settings_editor.selected_section}
|
data-selected-settings-section={@settings_editor.selected_section}
|
||||||
@@ -22,8 +22,8 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= if @settings_editor.project_visible? do %>
|
<%= if @settings_editor.project_visible? do %>
|
||||||
<div class="setting-section" id="settings-section-project">
|
<div class="setting-section ui-section-card" id="settings-section-project">
|
||||||
<div class="setting-section-header">
|
<div class="setting-section-header ui-field-stack">
|
||||||
<h3><%= dgettext("ui", "Project") %></h3>
|
<h3><%= dgettext("ui", "Project") %></h3>
|
||||||
<p class="setting-section-description"><%= dgettext("ui", "Blog identity, URLs, authoring defaults, and bookmarklet setup") %></p>
|
<p class="setting-section-description"><%= dgettext("ui", "Blog identity, URLs, authoring defaults, and bookmarklet setup") %></p>
|
||||||
</div>
|
</div>
|
||||||
@@ -102,8 +102,8 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= if @settings_editor.editor_visible? do %>
|
<%= if @settings_editor.editor_visible? do %>
|
||||||
<div class="setting-section" id="settings-section-editor">
|
<div class="setting-section ui-section-card" id="settings-section-editor">
|
||||||
<div class="setting-section-header">
|
<div class="setting-section-header ui-field-stack">
|
||||||
<h3><%= dgettext("ui", "Editor") %></h3>
|
<h3><%= dgettext("ui", "Editor") %></h3>
|
||||||
<p class="setting-section-description"><%= dgettext("ui", "Default editing mode and diff presentation") %></p>
|
<p class="setting-section-description"><%= dgettext("ui", "Default editing mode and diff presentation") %></p>
|
||||||
</div>
|
</div>
|
||||||
@@ -141,8 +141,8 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= if @settings_editor.content_visible? do %>
|
<%= if @settings_editor.content_visible? do %>
|
||||||
<div class="setting-section" id="settings-section-content">
|
<div class="setting-section ui-section-card" id="settings-section-content">
|
||||||
<div class="setting-section-header"><h3><%= dgettext("ui", "Content Categories") %></h3><p class="setting-section-description"><%= dgettext("ui", "Category defaults, rendering flags, and template wiring") %></p></div>
|
<div class="setting-section-header ui-field-stack"><h3><%= dgettext("ui", "Content Categories") %></h3><p class="setting-section-description"><%= dgettext("ui", "Category defaults, rendering flags, and template wiring") %></p></div>
|
||||||
<div class="setting-section-content">
|
<div class="setting-section-content">
|
||||||
<table class="categories-table">
|
<table class="categories-table">
|
||||||
<thead>
|
<thead>
|
||||||
@@ -209,8 +209,8 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= if @settings_editor.ai_visible? do %>
|
<%= if @settings_editor.ai_visible? do %>
|
||||||
<div class="setting-section" id="settings-section-ai">
|
<div class="setting-section ui-section-card" id="settings-section-ai">
|
||||||
<div class="setting-section-header"><h3><%= dgettext("ui", "AI") %></h3><p class="setting-section-description"><%= dgettext("ui", "OpenAI-compatible endpoints, model routing, airplane mode, and system prompt") %></p></div>
|
<div class="setting-section-header ui-field-stack"><h3><%= dgettext("ui", "AI") %></h3><p class="setting-section-description"><%= dgettext("ui", "OpenAI-compatible endpoints, model routing, airplane mode, and system prompt") %></p></div>
|
||||||
<form class="setting-section-content" phx-change="change_settings_ai" phx-target={@myself}>
|
<form class="setting-section-content" phx-change="change_settings_ai" phx-target={@myself}>
|
||||||
<div class="setting-row">
|
<div class="setting-row">
|
||||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Online Endpoint URL") %></label></div>
|
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Online Endpoint URL") %></label></div>
|
||||||
@@ -310,8 +310,8 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= if @settings_editor.technology_visible? do %>
|
<%= if @settings_editor.technology_visible? do %>
|
||||||
<div class="setting-section" id="settings-section-technology">
|
<div class="setting-section ui-section-card" id="settings-section-technology">
|
||||||
<div class="setting-section-header"><h3><%= dgettext("ui", "Technology") %></h3><p class="setting-section-description"><%= dgettext("ui", "Application-level runtime behavior and semantic indexing") %></p></div>
|
<div class="setting-section-header ui-field-stack"><h3><%= dgettext("ui", "Technology") %></h3><p class="setting-section-description"><%= dgettext("ui", "Application-level runtime behavior and semantic indexing") %></p></div>
|
||||||
<form class="setting-section-content" phx-change="change_settings_project" phx-target={@myself}>
|
<form class="setting-section-content" phx-change="change_settings_project" phx-target={@myself}>
|
||||||
<div class="setting-row">
|
<div class="setting-row">
|
||||||
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Semantic Similarity") %></label></div>
|
<div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Semantic Similarity") %></label></div>
|
||||||
@@ -327,8 +327,8 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= if @settings_editor.publishing_visible? do %>
|
<%= if @settings_editor.publishing_visible? do %>
|
||||||
<div class="setting-section" id="settings-section-publishing">
|
<div class="setting-section ui-section-card" id="settings-section-publishing">
|
||||||
<div class="setting-section-header"><h3><%= dgettext("ui", "Publishing") %></h3><p class="setting-section-description"><%= dgettext("ui", "Deployment credentials for upload tasks") %></p></div>
|
<div class="setting-section-header ui-field-stack"><h3><%= dgettext("ui", "Publishing") %></h3><p class="setting-section-description"><%= dgettext("ui", "Deployment credentials for upload tasks") %></p></div>
|
||||||
<form class="setting-section-content" phx-change="change_settings_publishing" phx-target={@myself}>
|
<form class="setting-section-content" phx-change="change_settings_publishing" phx-target={@myself}>
|
||||||
<div class="setting-row"><div class="setting-info"><label class="setting-label"><%= dgettext("ui", "SSH Mode") %></label></div><div class="setting-control"><select class="ui-input" name="settings_publishing[ssh_mode]"><option value="scp" selected={@settings_editor.publishing["ssh_mode"] == "scp"}>scp</option><option value="rsync" selected={@settings_editor.publishing["ssh_mode"] == "rsync"}>rsync</option></select></div></div>
|
<div class="setting-row"><div class="setting-info"><label class="setting-label"><%= dgettext("ui", "SSH Mode") %></label></div><div class="setting-control"><select class="ui-input" name="settings_publishing[ssh_mode]"><option value="scp" selected={@settings_editor.publishing["ssh_mode"] == "scp"}>scp</option><option value="rsync" selected={@settings_editor.publishing["ssh_mode"] == "rsync"}>rsync</option></select></div></div>
|
||||||
<div class="setting-row"><div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Host") %></label></div><div class="setting-control"><input class="ui-input" type="text" name="settings_publishing[ssh_host]" value={@settings_editor.publishing["ssh_host"]} /></div></div>
|
<div class="setting-row"><div class="setting-info"><label class="setting-label"><%= dgettext("ui", "Host") %></label></div><div class="setting-control"><input class="ui-input" type="text" name="settings_publishing[ssh_host]" value={@settings_editor.publishing["ssh_host"]} /></div></div>
|
||||||
@@ -340,8 +340,8 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= if @settings_editor.mcp_visible? do %>
|
<%= if @settings_editor.mcp_visible? do %>
|
||||||
<div class="setting-section" id="settings-section-mcp">
|
<div class="setting-section ui-section-card" id="settings-section-mcp">
|
||||||
<div class="setting-section-header"><h3><%= dgettext("ui", "MCP") %></h3><p class="setting-section-description"><%= dgettext("ui", "Agent configuration files for the built-in bDS MCP server") %></p></div>
|
<div class="setting-section-header ui-field-stack"><h3><%= dgettext("ui", "MCP") %></h3><p class="setting-section-description"><%= dgettext("ui", "Agent configuration files for the built-in bDS MCP server") %></p></div>
|
||||||
<div class="setting-section-content">
|
<div class="setting-section-content">
|
||||||
<%= for agent <- @settings_editor.mcp do %>
|
<%= for agent <- @settings_editor.mcp do %>
|
||||||
<div class="setting-row">
|
<div class="setting-row">
|
||||||
@@ -361,8 +361,8 @@
|
|||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= if @settings_editor.data_visible? do %>
|
<%= if @settings_editor.data_visible? do %>
|
||||||
<div class="setting-section" id="settings-section-data">
|
<div class="setting-section ui-section-card" id="settings-section-data">
|
||||||
<div class="setting-section-header"><h3><%= dgettext("ui", "Data Maintenance") %></h3><p class="setting-section-description"><%= dgettext("ui", "Rebuild filesystem-backed records and thumbnails") %></p></div>
|
<div class="setting-section-header ui-field-stack"><h3><%= dgettext("ui", "Data Maintenance") %></h3><p class="setting-section-description"><%= dgettext("ui", "Rebuild filesystem-backed records and thumbnails") %></p></div>
|
||||||
<div class="setting-actions">
|
<div class="setting-actions">
|
||||||
<button class="secondary ui-button ui-button-secondary" type="button" phx-click="settings_shell_command" phx-value-action="rebuild_posts_from_files"><%= dgettext("ui", "Rebuild Posts From Files") %></button>
|
<button class="secondary ui-button ui-button-secondary" type="button" phx-click="settings_shell_command" phx-value-action="rebuild_posts_from_files"><%= dgettext("ui", "Rebuild Posts From Files") %></button>
|
||||||
<button class="secondary ui-button ui-button-secondary" type="button" phx-click="settings_shell_command" phx-value-action="rebuild_media_from_files"><%= dgettext("ui", "Rebuild Media From Files") %></button>
|
<button class="secondary ui-button ui-button-secondary" type="button" phx-click="settings_shell_command" phx-value-action="rebuild_media_from_files"><%= dgettext("ui", "Rebuild Media From Files") %></button>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<div class="templates-view-shell editor flex h-full min-h-0 flex-col" data-testid="template-editor">
|
<div class="templates-view-shell editor ui-editor-shell flex h-full min-h-0 flex-col" data-testid="template-editor">
|
||||||
<div class="editor-header templates-header flex shrink-0 items-start justify-between gap-3">
|
<div class="editor-header templates-header ui-editor-header flex shrink-0 items-start justify-between gap-3">
|
||||||
<div class="editor-tabs flex min-w-0 flex-1 overflow-hidden"><div class="editor-tab ui-tab ui-tab-active active inline-flex max-w-full items-center overflow-hidden px-3 py-2"><span class="editor-tab-title truncate"><%= @template_editor.title %></span></div></div>
|
<div class="editor-tabs flex min-w-0 flex-1 overflow-hidden"><div class="editor-tab ui-tab ui-tab-active ui-editor-tab-current active inline-flex max-w-full items-center overflow-hidden px-3 py-2"><span class="editor-tab-title truncate"><%= @template_editor.title %></span></div></div>
|
||||||
<div class="editor-actions flex flex-wrap items-center justify-end gap-2">
|
<div class="editor-actions ui-editor-actions flex flex-wrap items-center justify-end gap-2">
|
||||||
<span class={[
|
<span class={[
|
||||||
"status-badge",
|
"status-badge",
|
||||||
"ui-badge",
|
"ui-badge",
|
||||||
@@ -15,21 +15,21 @@
|
|||||||
<button class="secondary danger ui-button ui-button-secondary ui-button-danger" type="button" phx-click="delete_template_editor" phx-target={@myself}><%= dgettext("ui", "Delete") %></button>
|
<button class="secondary danger ui-button ui-button-secondary ui-button-danger" type="button" phx-click="delete_template_editor" phx-target={@myself}><%= dgettext("ui", "Delete") %></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<form class="editor-content templates-view flex min-h-0 flex-1 flex-col gap-4 overflow-hidden" phx-change="change_template_editor" phx-target={@myself}>
|
<form class="editor-content templates-view flex min-h-0 flex-1 flex-col gap-4 overflow-hidden p-4" phx-change="change_template_editor" phx-target={@myself}>
|
||||||
<div class="editor-header-row templates-meta-row grid gap-4">
|
<div class="editor-header-row templates-meta-row grid gap-4">
|
||||||
<div class="editor-meta flex min-w-0 flex-col gap-4">
|
<div class="editor-meta flex min-w-0 flex-col gap-4">
|
||||||
<div class="editor-field-row grid gap-4 md:grid-cols-2">
|
<div class="editor-field-row ui-field-grid-2 grid gap-4 md:grid-cols-2">
|
||||||
<div class="editor-field flex flex-col gap-1.5"><label><%= dgettext("ui", "Title") %></label><input class="ui-input" type="text" name="template_editor[title]" value={@template_editor.title} /></div>
|
<div class="editor-field ui-field-stack flex flex-col gap-1.5"><label><%= dgettext("ui", "Title") %></label><input class="ui-input" type="text" name="template_editor[title]" value={@template_editor.title} /></div>
|
||||||
<div class="editor-field flex flex-col gap-1.5"><label><%= dgettext("ui", "Slug") %></label><input class="ui-input" type="text" name="template_editor[slug]" value={@template_editor.slug} /></div>
|
<div class="editor-field ui-field-stack flex flex-col gap-1.5"><label><%= dgettext("ui", "Slug") %></label><input class="ui-input" type="text" name="template_editor[slug]" value={@template_editor.slug} /></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="editor-field-row grid gap-4 md:grid-cols-[minmax(0,1fr)_auto]">
|
<div class="editor-field-row ui-field-grid-3 grid gap-4 md:grid-cols-[minmax(0,1fr)_auto]">
|
||||||
<div class="editor-field flex flex-col gap-1.5"><label><%= dgettext("ui", "Kind") %></label><select class="ui-input" name="template_editor[kind]"><option value="post" selected={@template_editor.kind == :post or @template_editor.kind == "post"}>post</option><option value="list" selected={@template_editor.kind == :list or @template_editor.kind == "list"}>list</option><option value="not-found" selected={@template_editor.kind == :"not-found" or @template_editor.kind == "not-found"}>not-found</option><option value="partial" selected={@template_editor.kind == :partial or @template_editor.kind == "partial"}>partial</option></select></div>
|
<div class="editor-field ui-field-stack flex flex-col gap-1.5"><label><%= dgettext("ui", "Kind") %></label><select class="ui-input" name="template_editor[kind]"><option value="post" selected={@template_editor.kind == :post or @template_editor.kind == "post"}>post</option><option value="list" selected={@template_editor.kind == :list or @template_editor.kind == "list"}>list</option><option value="not-found" selected={@template_editor.kind == :"not-found" or @template_editor.kind == "not-found"}>not-found</option><option value="partial" selected={@template_editor.kind == :partial or @template_editor.kind == "partial"}>partial</option></select></div>
|
||||||
<div class="editor-field templates-enabled-field flex flex-col justify-end gap-1.5"><label><input type="checkbox" name="template_editor[enabled]" checked={@template_editor.enabled} /> <%= dgettext("ui", "Enabled") %></label></div>
|
<div class="editor-field templates-enabled-field flex flex-col justify-end gap-1.5"><label><input type="checkbox" name="template_editor[enabled]" checked={@template_editor.enabled} /> <%= dgettext("ui", "Enabled") %></label></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="editor-body templates-editor flex min-h-0 flex-1 flex-col overflow-hidden">
|
<div class="editor-body templates-editor flex min-h-0 flex-1 flex-col overflow-hidden">
|
||||||
<div class="editor-toolbar templates-toolbar flex items-center gap-3"><div class="editor-toolbar-left flex items-center gap-2"><label><%= dgettext("ui", "Content") %></label></div></div>
|
<div class="editor-toolbar templates-toolbar ui-toolbar flex items-center gap-3"><div class="editor-toolbar-left ui-toolbar-group flex items-center gap-2"><label><%= dgettext("ui", "Content") %></label></div></div>
|
||||||
<div
|
<div
|
||||||
id={"template-editor-monaco-shell-#{@template_editor.id}"}
|
id={"template-editor-monaco-shell-#{@template_editor.id}"}
|
||||||
class="templates-monaco monaco-editor-shell min-h-0 flex-1 overflow-hidden"
|
class="templates-monaco monaco-editor-shell min-h-0 flex-1 overflow-hidden"
|
||||||
|
|||||||
@@ -292,27 +292,27 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
settings_html = render_component(&BDS.Desktop.ShellLive.SettingsEditor.render/1, phase3_settings_editor_assigns())
|
settings_html = render_component(&BDS.Desktop.ShellLive.SettingsEditor.render/1, phase3_settings_editor_assigns())
|
||||||
tags_html = render_component(&BDS.Desktop.ShellLive.TagsEditor.render/1, phase3_tags_editor_assigns())
|
tags_html = render_component(&BDS.Desktop.ShellLive.TagsEditor.render/1, phase3_tags_editor_assigns())
|
||||||
|
|
||||||
assert post_html =~ "post-editor editor flex h-full min-h-0 flex-col"
|
assert post_html =~ "post-editor editor ui-editor-shell flex h-full min-h-0 flex-col"
|
||||||
assert post_html =~ "editor-header flex shrink-0 items-start justify-between gap-3"
|
assert post_html =~ "editor-header ui-editor-header flex shrink-0 items-start justify-between gap-3"
|
||||||
assert post_html =~ "editor-field flex flex-col gap-1.5"
|
assert post_html =~ "editor-field ui-field-stack flex flex-col gap-1.5"
|
||||||
assert post_html =~ "editor-toolbar flex items-center gap-3"
|
assert post_html =~ "editor-toolbar ui-toolbar flex items-center gap-3"
|
||||||
|
|
||||||
assert media_html =~ "media-editor editor flex h-full min-h-0 flex-col"
|
assert media_html =~ "media-editor editor ui-editor-shell flex h-full min-h-0 flex-col"
|
||||||
assert media_html =~ "editor-content media-editor grid min-h-0 flex-1 gap-4"
|
assert media_html =~ "editor-content media-editor grid min-h-0 flex-1 gap-4 overflow-auto p-4"
|
||||||
|
|
||||||
assert script_html =~ "scripts-view-shell editor flex h-full min-h-0 flex-col"
|
assert script_html =~ "scripts-view-shell editor ui-editor-shell flex h-full min-h-0 flex-col"
|
||||||
assert script_html =~ "editor-content scripts-view flex min-h-0 flex-1 flex-col gap-4 overflow-hidden"
|
assert script_html =~ "editor-content scripts-view flex min-h-0 flex-1 flex-col gap-4 overflow-hidden p-4"
|
||||||
|
|
||||||
assert template_html =~ "templates-view-shell editor flex h-full min-h-0 flex-col"
|
assert template_html =~ "templates-view-shell editor ui-editor-shell flex h-full min-h-0 flex-col"
|
||||||
assert template_html =~ "editor-content templates-view flex min-h-0 flex-1 flex-col gap-4 overflow-hidden"
|
assert template_html =~ "editor-content templates-view flex min-h-0 flex-1 flex-col gap-4 overflow-hidden p-4"
|
||||||
|
|
||||||
assert chat_html =~ "chat-panel flex h-full min-h-0 flex-col"
|
assert chat_html =~ "chat-panel ui-editor-shell flex h-full min-h-0 flex-col"
|
||||||
assert chat_html =~ "chat-panel-header flex shrink-0 items-center justify-between gap-3"
|
assert chat_html =~ "chat-panel-header flex shrink-0 items-center justify-between gap-3"
|
||||||
|
|
||||||
assert menu_html =~ "menu-editor-view flex h-full min-h-0 flex-col"
|
assert menu_html =~ "menu-editor-view ui-editor-shell flex h-full min-h-0 flex-col p-4"
|
||||||
assert menu_html =~ "menu-editor-toolbar flex flex-wrap items-center gap-2"
|
assert menu_html =~ "menu-editor-toolbar ui-toolbar flex flex-wrap items-center gap-2"
|
||||||
|
|
||||||
assert settings_html =~ "settings-view-shell flex h-full min-h-0 flex-col overflow-hidden"
|
assert settings_html =~ "settings-view-shell ui-editor-shell flex h-full min-h-0 flex-col overflow-hidden"
|
||||||
assert settings_html =~ "settings-header flex shrink-0 items-center justify-between gap-3"
|
assert settings_html =~ "settings-header flex shrink-0 items-center justify-between gap-3"
|
||||||
|
|
||||||
assert tags_html =~ "tags-view-shell flex h-full min-h-0 flex-col overflow-hidden"
|
assert tags_html =~ "tags-view-shell flex h-full min-h-0 flex-col overflow-hidden"
|
||||||
@@ -377,6 +377,34 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
assert panel_html =~ ~s(class="panel-entry ui-panel-entry panel-empty-state ui-empty-state)
|
assert panel_html =~ ~s(class="panel-entry ui-panel-entry panel-empty-state ui-empty-state)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@tag :phase5
|
||||||
|
test "phase 5 desktop-specific surfaces keep shell, media, menu, and chat contracts" do
|
||||||
|
conn = Plug.Conn.put_private(build_conn(), :phoenix_endpoint, BDS.Desktop.Endpoint)
|
||||||
|
{:ok, _view, shell_html} = live_isolated(conn, BDS.Desktop.ShellLive)
|
||||||
|
|
||||||
|
media_html = render_component(&BDS.Desktop.ShellLive.MediaEditor.render/1, phase3_media_editor_assigns())
|
||||||
|
chat_html = render_component(&BDS.Desktop.ShellLive.ChatEditor.render/1, phase3_chat_editor_assigns())
|
||||||
|
menu_html = render_component(&BDS.Desktop.ShellLive.MenuEditor.render/1, phase3_menu_editor_assigns())
|
||||||
|
|
||||||
|
assert shell_html =~ ~s(class="assistant-sidebar-context flex shrink-0 flex-col gap-2")
|
||||||
|
assert shell_html =~ ~s(class="assistant-sidebar-prompt min-h-[8rem] w-full resize-y")
|
||||||
|
assert shell_html =~ ~s(class="assistant-sidebar-start-button ui-button ui-button-primary")
|
||||||
|
assert shell_html =~ ~s(class="assistant-sidebar-welcome min-h-0 flex-1 overflow-auto")
|
||||||
|
|
||||||
|
assert media_html =~ "class=\"editor-content media-editor grid min-h-0 flex-1 gap-4 overflow-auto p-4 xl:grid-cols-[minmax(320px,1fr)_minmax(0,1.2fr)]\""
|
||||||
|
assert media_html =~ "class=\"media-preview flex min-h-[16rem] items-center justify-center\""
|
||||||
|
assert media_html =~ ~s(class="media-details min-w-0")
|
||||||
|
|
||||||
|
assert chat_html =~ ~s(class="chat-panel ui-editor-shell flex h-full min-h-0 flex-col")
|
||||||
|
assert chat_html =~ ~s(class="chat-model-selector-button chat-model-selector-inline ui-button ui-button-secondary inline-flex items-center gap-2")
|
||||||
|
assert chat_html =~ ~s(class="chat-input-container ui-field-stack flex shrink-0 flex-col gap-3")
|
||||||
|
assert chat_html =~ ~s(class="chat-input-wrapper flex items-end gap-2")
|
||||||
|
|
||||||
|
assert menu_html =~ ~s(class="menu-editor-view ui-editor-shell flex h-full min-h-0 flex-col p-4")
|
||||||
|
assert menu_html =~ ~s(class="menu-editor-toolbar ui-toolbar flex flex-wrap items-center gap-2")
|
||||||
|
assert menu_html =~ ~s(class="menu-editor-empty flex min-h-0 flex-1 items-center justify-center")
|
||||||
|
end
|
||||||
|
|
||||||
alias BDS.Persistence
|
alias BDS.Persistence
|
||||||
alias BDS.AI
|
alias BDS.AI
|
||||||
alias BDS.CliSync.Watcher
|
alias BDS.CliSync.Watcher
|
||||||
@@ -3195,7 +3223,7 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
"subtitle" => "published"
|
"subtitle" => "published"
|
||||||
})
|
})
|
||||||
|
|
||||||
assert published_script_html =~ ~s(class="scripts-view-shell editor flex h-full min-h-0 flex-col")
|
assert published_script_html =~ ~s(class="scripts-view-shell editor ui-editor-shell flex h-full min-h-0 flex-col")
|
||||||
assert published_script_html =~ ~s(data-testid="script-editor")
|
assert published_script_html =~ ~s(data-testid="script-editor")
|
||||||
assert published_script_html =~ ~s(data-testid="script-status-badge")
|
assert published_script_html =~ ~s(data-testid="script-status-badge")
|
||||||
assert published_script_html =~ ~s(class="status-badge ui-badge status-published")
|
assert published_script_html =~ ~s(class="status-badge ui-badge status-published")
|
||||||
@@ -3216,7 +3244,7 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
"subtitle" => "published"
|
"subtitle" => "published"
|
||||||
})
|
})
|
||||||
|
|
||||||
assert published_template_html =~ ~s(class="templates-view-shell editor flex h-full min-h-0 flex-col")
|
assert published_template_html =~ ~s(class="templates-view-shell editor ui-editor-shell flex h-full min-h-0 flex-col")
|
||||||
assert published_template_html =~ ~s(data-testid="template-editor")
|
assert published_template_html =~ ~s(data-testid="template-editor")
|
||||||
assert published_template_html =~ ~s(data-testid="template-status-badge")
|
assert published_template_html =~ ~s(data-testid="template-status-badge")
|
||||||
assert published_template_html =~ ~s(class="status-badge ui-badge status-published")
|
assert published_template_html =~ ~s(class="status-badge ui-badge status-published")
|
||||||
@@ -3410,7 +3438,7 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
})
|
})
|
||||||
|
|
||||||
assert html =~
|
assert html =~
|
||||||
"class=\"editor-content media-editor grid min-h-0 flex-1 gap-4 overflow-auto xl:grid-cols-[minmax(320px,1fr)_minmax(0,1.2fr)]\""
|
"class=\"editor-content media-editor grid min-h-0 flex-1 gap-4 overflow-auto p-4 xl:grid-cols-[minmax(320px,1fr)_minmax(0,1.2fr)]\""
|
||||||
assert html =~ ~s(class="quick-actions-wrapper relative")
|
assert html =~ ~s(class="quick-actions-wrapper relative")
|
||||||
refute html =~ ~s(class="media-editor-form")
|
refute html =~ ~s(class="media-editor-form")
|
||||||
|
|
||||||
@@ -3523,8 +3551,8 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
"subtitle" => "Project settings"
|
"subtitle" => "Project settings"
|
||||||
})
|
})
|
||||||
|
|
||||||
assert settings_html =~ ~s(class="settings-view-shell flex h-full min-h-0 flex-col overflow-hidden")
|
assert settings_html =~ ~s(class="settings-view-shell ui-editor-shell flex h-full min-h-0 flex-col overflow-hidden")
|
||||||
assert settings_html =~ ~s(class="setting-section")
|
assert settings_html =~ ~s(class="setting-section ui-section-card")
|
||||||
refute settings_html =~ "Desktop workbench content routed through the Elixir shell."
|
refute settings_html =~ "Desktop workbench content routed through the Elixir shell."
|
||||||
|
|
||||||
tags_html =
|
tags_html =
|
||||||
@@ -3559,7 +3587,7 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
"subtitle" => script.slug
|
"subtitle" => script.slug
|
||||||
})
|
})
|
||||||
|
|
||||||
assert script_html =~ ~s(class="scripts-view-shell editor flex h-full min-h-0 flex-col")
|
assert script_html =~ ~s(class="scripts-view-shell editor ui-editor-shell flex h-full min-h-0 flex-col")
|
||||||
assert script_html =~ "scripts-monaco"
|
assert script_html =~ "scripts-monaco"
|
||||||
assert script_html =~ ~s(data-monaco-language="lua")
|
assert script_html =~ ~s(data-monaco-language="lua")
|
||||||
assert script_html =~ ~s(data-monaco-word-wrap="on")
|
assert script_html =~ ~s(data-monaco-word-wrap="on")
|
||||||
@@ -3574,7 +3602,7 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
"subtitle" => template.slug
|
"subtitle" => template.slug
|
||||||
})
|
})
|
||||||
|
|
||||||
assert template_html =~ ~s(class="templates-view-shell editor flex h-full min-h-0 flex-col")
|
assert template_html =~ ~s(class="templates-view-shell editor ui-editor-shell flex h-full min-h-0 flex-col")
|
||||||
assert template_html =~ "templates-monaco"
|
assert template_html =~ "templates-monaco"
|
||||||
assert template_html =~ ~s(data-monaco-language="liquid")
|
assert template_html =~ ~s(data-monaco-language="liquid")
|
||||||
assert template_html =~ ~s(data-monaco-word-wrap="on")
|
assert template_html =~ ~s(data-monaco-word-wrap="on")
|
||||||
@@ -3589,8 +3617,8 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
"subtitle" => conversation.model || "chat"
|
"subtitle" => conversation.model || "chat"
|
||||||
})
|
})
|
||||||
|
|
||||||
assert chat_html =~ ~s(class="chat-panel flex h-full min-h-0 flex-col")
|
assert chat_html =~ ~s(class="chat-panel ui-editor-shell flex h-full min-h-0 flex-col")
|
||||||
assert chat_html =~ ~s(class="chat-input-container flex shrink-0 flex-col gap-3")
|
assert chat_html =~ ~s(class="chat-input-container ui-field-stack flex shrink-0 flex-col gap-3")
|
||||||
refute chat_html =~ "Desktop workbench content routed through the Elixir shell."
|
refute chat_html =~ "Desktop workbench content routed through the Elixir shell."
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -3658,7 +3686,7 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
|> element("[data-testid='chat-model-selector-button']")
|
|> element("[data-testid='chat-model-selector-button']")
|
||||||
|> render_click()
|
|> render_click()
|
||||||
|
|
||||||
assert selector_html =~ ~s(class="chat-model-selector-menu absolute right-0 top-full z-10 mt-2 flex min-w-56 flex-col")
|
assert selector_html =~ ~s(class="chat-model-selector-menu ui-dropdown-menu absolute right-0 top-full z-10 mt-2 flex min-w-56 flex-col")
|
||||||
assert selector_html =~ ~s(data-testid="chat-model-selector-option")
|
assert selector_html =~ ~s(data-testid="chat-model-selector-option")
|
||||||
assert selector_html =~ "llama-current"
|
assert selector_html =~ "llama-current"
|
||||||
|
|
||||||
|
|||||||
@@ -152,6 +152,138 @@ defmodule BDS.UI.ShellTest do
|
|||||||
assert css =~ ".ui-badge {"
|
assert css =~ ".ui-badge {"
|
||||||
assert css =~ ".ui-panel-entry {"
|
assert css =~ ".ui-panel-entry {"
|
||||||
assert css =~ ".ui-empty-state {"
|
assert css =~ ".ui-empty-state {"
|
||||||
|
assert css =~ ".ui-editor-shell {"
|
||||||
|
assert css =~ ".ui-editor-header {"
|
||||||
|
assert css =~ ".ui-editor-tab-current {"
|
||||||
|
assert css =~ ".ui-editor-actions {"
|
||||||
|
assert css =~ ".ui-toolbar {"
|
||||||
|
assert css =~ ".ui-toolbar-group {"
|
||||||
|
assert css =~ ".ui-field-stack {"
|
||||||
|
assert css =~ ".ui-field-grid-2 {"
|
||||||
|
assert css =~ ".ui-field-grid-3 {"
|
||||||
|
assert css =~ ".ui-dropdown-menu {"
|
||||||
|
assert css =~ ".ui-dropdown-item {"
|
||||||
|
assert css =~ ".ui-section-card {"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "phase 3 templates use shared shell and form primitives for common layout" do
|
||||||
|
post_template =
|
||||||
|
File.read!("/Users/gb/Projects/bDS2/lib/bds/desktop/shell_live/post_editor_html/post_editor.html.heex")
|
||||||
|
|
||||||
|
media_template =
|
||||||
|
File.read!("/Users/gb/Projects/bDS2/lib/bds/desktop/shell_live/media_editor_html/media_editor.html.heex")
|
||||||
|
|
||||||
|
script_template =
|
||||||
|
File.read!("/Users/gb/Projects/bDS2/lib/bds/desktop/shell_live/script_editor_html/script_editor.html.heex")
|
||||||
|
|
||||||
|
template_template =
|
||||||
|
File.read!("/Users/gb/Projects/bDS2/lib/bds/desktop/shell_live/template_editor_html/template_editor.html.heex")
|
||||||
|
|
||||||
|
chat_template =
|
||||||
|
File.read!("/Users/gb/Projects/bDS2/lib/bds/desktop/shell_live/chat_editor_html/chat_editor.html.heex")
|
||||||
|
|
||||||
|
menu_template =
|
||||||
|
File.read!("/Users/gb/Projects/bDS2/lib/bds/desktop/shell_live/menu_editor_html/menu_editor.html.heex")
|
||||||
|
|
||||||
|
settings_template =
|
||||||
|
File.read!("/Users/gb/Projects/bDS2/lib/bds/desktop/shell_live/settings_editor_html/settings_editor.html.heex")
|
||||||
|
|
||||||
|
assert post_template =~ "ui-editor-shell"
|
||||||
|
assert post_template =~ "ui-editor-header"
|
||||||
|
assert post_template =~ "ui-editor-tab-current"
|
||||||
|
assert post_template =~ "ui-editor-actions"
|
||||||
|
assert post_template =~ "ui-field-stack"
|
||||||
|
assert post_template =~ "ui-field-grid-2"
|
||||||
|
assert post_template =~ "ui-toolbar"
|
||||||
|
assert post_template =~ "ui-toolbar-group"
|
||||||
|
assert post_template =~ "ui-dropdown-menu"
|
||||||
|
assert post_template =~ "ui-dropdown-item"
|
||||||
|
|
||||||
|
assert media_template =~ "ui-editor-shell"
|
||||||
|
assert media_template =~ "ui-editor-header"
|
||||||
|
assert media_template =~ "ui-editor-tab-current"
|
||||||
|
assert media_template =~ "ui-editor-actions"
|
||||||
|
assert media_template =~ "ui-field-stack"
|
||||||
|
assert media_template =~ "ui-field-grid-2"
|
||||||
|
assert media_template =~ "ui-dropdown-menu"
|
||||||
|
assert media_template =~ "ui-dropdown-item"
|
||||||
|
|
||||||
|
assert script_template =~ "ui-editor-shell"
|
||||||
|
assert script_template =~ "ui-editor-header"
|
||||||
|
assert script_template =~ "ui-editor-tab-current"
|
||||||
|
assert script_template =~ "ui-editor-actions"
|
||||||
|
assert script_template =~ "ui-field-stack"
|
||||||
|
|
||||||
|
assert template_template =~ "ui-editor-shell"
|
||||||
|
assert template_template =~ "ui-editor-header"
|
||||||
|
assert template_template =~ "ui-editor-tab-current"
|
||||||
|
assert template_template =~ "ui-editor-actions"
|
||||||
|
assert template_template =~ "ui-field-stack"
|
||||||
|
|
||||||
|
assert chat_template =~ "ui-editor-shell"
|
||||||
|
assert chat_template =~ "ui-section-card"
|
||||||
|
assert chat_template =~ "ui-dropdown-menu"
|
||||||
|
assert chat_template =~ "ui-dropdown-item"
|
||||||
|
assert chat_template =~ "ui-field-stack"
|
||||||
|
|
||||||
|
assert menu_template =~ "ui-editor-shell"
|
||||||
|
assert menu_template =~ "ui-section-card"
|
||||||
|
assert menu_template =~ "ui-toolbar"
|
||||||
|
|
||||||
|
assert settings_template =~ "ui-editor-shell"
|
||||||
|
assert settings_template =~ "ui-field-stack"
|
||||||
|
assert settings_template =~ "ui-section-card"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "phase 3 trims redundant common-case layout rules from authored css slices" do
|
||||||
|
editor_css = File.read!("/Users/gb/Projects/bDS2/assets/css/editor.css")
|
||||||
|
media_css = File.read!("/Users/gb/Projects/bDS2/assets/css/media_editor.css")
|
||||||
|
assistant_css = File.read!("/Users/gb/Projects/bDS2/assets/css/assistant.css")
|
||||||
|
menu_css = File.read!("/Users/gb/Projects/bDS2/assets/css/menu_editor.css")
|
||||||
|
|
||||||
|
refute editor_css =~ ".post-editor .editor-header,\n.scripts-view-shell.editor .editor-header,\n.templates-view-shell.editor .editor-header {\n display: flex;"
|
||||||
|
refute editor_css =~ ".post-editor .editor-actions,\n.scripts-view-shell.editor .editor-actions,\n.templates-view-shell.editor .editor-actions {\n display: flex;"
|
||||||
|
refute editor_css =~ ".post-editor .quick-actions-menu {"
|
||||||
|
refute media_css =~ "[data-testid=\"media-editor\"] .editor-header {"
|
||||||
|
refute media_css =~ "[data-testid=\"media-editor\"] .editor-actions {"
|
||||||
|
refute media_css =~ "[data-testid=\"media-editor\"] .quick-actions-menu {"
|
||||||
|
refute assistant_css =~ ".chat-panel-header {\n display: flex;"
|
||||||
|
refute assistant_css =~ ".chat-panel .chat-input-wrapper {\n display: flex;"
|
||||||
|
refute menu_css =~ ".menu-editor-view {\n padding: 1rem;"
|
||||||
|
refute menu_css =~ ".menu-editor-toolbar {\n display: flex;"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "phase 5 desktop-specific surfaces stay in source modules with responsive behavior" do
|
||||||
|
css = css_source()
|
||||||
|
app_js = File.read!("/Users/gb/Projects/bDS2/assets/js/app.js")
|
||||||
|
|
||||||
|
assert css =~ ".ai-suggestions-modal-backdrop"
|
||||||
|
assert css =~ ".gallery-overlay"
|
||||||
|
assert css =~ ".lightbox-overlay"
|
||||||
|
|
||||||
|
assert css =~ ".menu-editor-row.is-dragging"
|
||||||
|
assert css =~ ".menu-editor-row.is-drop-before::before"
|
||||||
|
assert css =~ ".menu-editor-row.is-drop-after::after"
|
||||||
|
assert css =~ ".menu-editor-row.is-drop-inside"
|
||||||
|
assert app_js =~ "MenuEditorTree"
|
||||||
|
assert app_js =~ "classList.add(\"is-dragging\")"
|
||||||
|
assert app_js =~ "pushEvent(\"menu_editor_drop_item\""
|
||||||
|
|
||||||
|
assert css =~ ".media-preview {"
|
||||||
|
assert css =~ ".media-preview-image img {"
|
||||||
|
assert css =~ "object-fit: contain;"
|
||||||
|
assert css =~ ".media-details {"
|
||||||
|
assert css =~ "width: 320px;"
|
||||||
|
|
||||||
|
assert css =~ ".assistant-sidebar-context {"
|
||||||
|
assert css =~ ".assistant-sidebar-message {"
|
||||||
|
assert css =~ ".chat-panel .chat-input-container"
|
||||||
|
assert css =~ ".chat-model-selector-menu"
|
||||||
|
|
||||||
|
assert css =~ "@media (max-width: 720px) {\n .chat-panel-header {\n align-items: stretch;\n flex-direction: column;"
|
||||||
|
assert css =~ ".chat-model-selector-wrap {\n width: 100%;"
|
||||||
|
assert css =~ ".chat-panel .chat-model-selector-button.chat-model-selector-inline {\n justify-content: space-between;\n width: 100%;"
|
||||||
|
assert css =~ ".chat-panel .chat-input-container {\n padding: 8px 12px;"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "tailwind source keeps theme tokens and shared component primitives" do
|
test "tailwind source keeps theme tokens and shared component primitives" do
|
||||||
|
|||||||
Reference in New Issue
Block a user