@sequent-org/moodboard 1.4.45 → 1.4.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/core/events/Events.js +1 -0
- package/src/core/flows/ClipboardFlow.js +29 -1
- package/src/core/flows/Model3dFlow.js +15 -0
- package/src/core/index.js +4 -2
- package/src/moodboard/bootstrap/MoodBoardUiFactory.js +2 -0
- package/src/objects/Model3dScreenshotImageObject.js +8 -0
- package/src/objects/ObjectFactory.js +2 -0
- package/src/services/ai/AiClient.js +130 -2
- package/src/services/ai/Model3dSessionController.js +238 -0
- package/src/ui/ImagePropertiesPanel.js +8 -0
- package/src/ui/chat/ChatComposer.js +50 -3
- package/src/ui/chat/ChatWindow.js +452 -5
- package/src/ui/chat/Model3dBoardSkeleton.js +93 -0
- package/src/ui/chat/Model3dProgressOverlay.js +114 -0
- package/src/ui/chat/icons.js +1 -0
- package/src/ui/handles/HandlesDomRenderer.js +30 -0
- package/src/ui/styles/chat.css +173 -0
- package/src/ui/styles/panels.css +264 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOM-заглушка прогресса генерации 3D-модели.
|
|
3
|
+
*
|
|
4
|
+
* Одна ответственность: показывать стадии (геометрия / текстура) и прогресс-бар
|
|
5
|
+
* поверх composer-контейнера. Не знает ни про сессию, ни про доску.
|
|
6
|
+
*
|
|
7
|
+
* Lifecycle: attach(container) → update(state)* → detach() → destroy()
|
|
8
|
+
* attach/detach идемпотентны. Listeners не дублируются.
|
|
9
|
+
*/
|
|
10
|
+
export class Model3dProgressOverlay {
|
|
11
|
+
constructor() {
|
|
12
|
+
this._el = null;
|
|
13
|
+
this._barFill = null;
|
|
14
|
+
this._stageLabel = null;
|
|
15
|
+
this._percentLabel = null;
|
|
16
|
+
this._container = null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Добавляет оверлей в container.
|
|
21
|
+
* @param {HTMLElement} container
|
|
22
|
+
*/
|
|
23
|
+
attach(container) {
|
|
24
|
+
if (this._el) return;
|
|
25
|
+
this._container = container;
|
|
26
|
+
|
|
27
|
+
const el = document.createElement('div');
|
|
28
|
+
el.className = 'moodboard-chat__3d-overlay';
|
|
29
|
+
|
|
30
|
+
const inner = document.createElement('div');
|
|
31
|
+
inner.className = 'moodboard-chat__3d-overlay-inner';
|
|
32
|
+
|
|
33
|
+
const stageLabel = document.createElement('span');
|
|
34
|
+
stageLabel.className = 'moodboard-chat__3d-overlay-stage';
|
|
35
|
+
stageLabel.textContent = 'Генерация геометрии…';
|
|
36
|
+
|
|
37
|
+
const barWrap = document.createElement('div');
|
|
38
|
+
barWrap.className = 'moodboard-chat__3d-overlay-bar-wrap';
|
|
39
|
+
|
|
40
|
+
const barFill = document.createElement('div');
|
|
41
|
+
barFill.className = 'moodboard-chat__3d-overlay-bar-fill';
|
|
42
|
+
barFill.style.width = '0%';
|
|
43
|
+
|
|
44
|
+
const percentLabel = document.createElement('span');
|
|
45
|
+
percentLabel.className = 'moodboard-chat__3d-overlay-percent';
|
|
46
|
+
percentLabel.textContent = '0%';
|
|
47
|
+
|
|
48
|
+
barWrap.appendChild(barFill);
|
|
49
|
+
inner.appendChild(stageLabel);
|
|
50
|
+
inner.appendChild(barWrap);
|
|
51
|
+
inner.appendChild(percentLabel);
|
|
52
|
+
el.appendChild(inner);
|
|
53
|
+
|
|
54
|
+
this._el = el;
|
|
55
|
+
this._barFill = barFill;
|
|
56
|
+
this._stageLabel = stageLabel;
|
|
57
|
+
this._percentLabel = percentLabel;
|
|
58
|
+
|
|
59
|
+
container.appendChild(el);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Обновляет отображение по состоянию Model3dSessionController.
|
|
64
|
+
* @param {{ status: string, progress: number, stage: string|null, error: string|null }} state
|
|
65
|
+
*/
|
|
66
|
+
update(state) {
|
|
67
|
+
if (!this._el) return;
|
|
68
|
+
|
|
69
|
+
const { status, progress, stage, error } = state;
|
|
70
|
+
|
|
71
|
+
if (status === 'error') {
|
|
72
|
+
this._el.classList.add('is-error');
|
|
73
|
+
this._stageLabel.textContent = error ? `Ошибка: ${error}` : 'Ошибка генерации';
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
this._el.classList.remove('is-error');
|
|
78
|
+
|
|
79
|
+
const pct = Math.round(Math.min(100, Math.max(0, progress ?? 0)));
|
|
80
|
+
|
|
81
|
+
// Пока прогресс равен нулю и API ещё не вернул данные (submitting / начало polling),
|
|
82
|
+
// показываем indeterminate-полоску — пользователь видит активную работу, а не «0% = завис».
|
|
83
|
+
const isIndeterminate = pct === 0;
|
|
84
|
+
this._barFill.classList.toggle('is-indeterminate', isIndeterminate);
|
|
85
|
+
if (!isIndeterminate) {
|
|
86
|
+
this._barFill.style.width = `${pct}%`;
|
|
87
|
+
}
|
|
88
|
+
this._percentLabel.textContent = `${pct}%`;
|
|
89
|
+
|
|
90
|
+
if (stage === 'texture') {
|
|
91
|
+
this._stageLabel.textContent = 'Генерация текстуры…';
|
|
92
|
+
} else if (status === 'submitting') {
|
|
93
|
+
this._stageLabel.textContent = 'Отправка запроса…';
|
|
94
|
+
} else {
|
|
95
|
+
this._stageLabel.textContent = 'Генерация геометрии…';
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Убирает DOM-элемент из контейнера, не разрушая объект. */
|
|
100
|
+
detach() {
|
|
101
|
+
if (this._el && this._el.parentNode) {
|
|
102
|
+
this._el.parentNode.removeChild(this._el);
|
|
103
|
+
}
|
|
104
|
+
this._el = null;
|
|
105
|
+
this._barFill = null;
|
|
106
|
+
this._stageLabel = null;
|
|
107
|
+
this._percentLabel = null;
|
|
108
|
+
this._container = null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
destroy() {
|
|
112
|
+
this.detach();
|
|
113
|
+
}
|
|
114
|
+
}
|
package/src/ui/chat/icons.js
CHANGED
|
@@ -59,6 +59,7 @@ const MODEL_BOT_ICON = `<svg xmlns="http://www.w3.org/2000/svg" width="36" heigh
|
|
|
59
59
|
|
|
60
60
|
export const ICONS = {
|
|
61
61
|
bot: svg('<path d="M12 8V4H8"/><rect width="16" height="12" x="4" y="8" rx="2"/><path d="M2 14h2"/><path d="M20 14h2"/><path d="M15 13v2"/><path d="M9 13v2"/>'),
|
|
62
|
+
cube: svg('<path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/>'),
|
|
62
63
|
modelBot: MODEL_BOT_ICON,
|
|
63
64
|
image: IMAGE_ICON,
|
|
64
65
|
video: VIDEO_ICON,
|
|
@@ -10,6 +10,7 @@ import { MindmapStatePatchCommand } from '../../core/commands/MindmapStatePatchC
|
|
|
10
10
|
|
|
11
11
|
const HANDLES_ACCENT_COLOR = '#80D8FF';
|
|
12
12
|
const REVIT_SHOW_IN_MODEL_ICON_SVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640" aria-hidden="true" focusable="false"><path d="M384 64C366.3 64 352 78.3 352 96C352 113.7 366.3 128 384 128L466.7 128L265.3 329.4C252.8 341.9 252.8 362.2 265.3 374.7C277.8 387.2 298.1 387.2 310.6 374.7L512 173.3L512 256C512 273.7 526.3 288 544 288C561.7 288 576 273.7 576 256L576 96C576 78.3 561.7 64 544 64L384 64zM144 160C99.8 160 64 195.8 64 240L64 496C64 540.2 99.8 576 144 576L400 576C444.2 576 480 540.2 480 496L480 416C480 398.3 465.7 384 448 384C430.3 384 416 398.3 416 416L416 496C416 504.8 408.8 512 400 512L144 512C135.2 512 128 504.8 128 496L128 240C128 231.2 135.2 224 144 224L224 224C241.7 224 256 209.7 256 192C256 174.3 241.7 160 224 160L144 160z"/></svg>';
|
|
13
|
+
const MODEL3D_SHOW_IN_VIEWER_ICON_SVG = REVIT_SHOW_IN_MODEL_ICON_SVG;
|
|
13
14
|
const MINDMAP_CHILD_WIDTH_FACTOR = 0.9;
|
|
14
15
|
const MINDMAP_CHILD_HEIGHT_FACTOR = 0.8;
|
|
15
16
|
const MINDMAP_CHILD_PADDING_FACTOR = 0.5;
|
|
@@ -1158,6 +1159,9 @@ export class HandlesDomRenderer {
|
|
|
1158
1159
|
let isMindmapOnlyGroupTarget = false;
|
|
1159
1160
|
let isRevitScreenshotTarget = false;
|
|
1160
1161
|
let revitViewPayload = null;
|
|
1162
|
+
let isModel3dScreenshotTarget = false;
|
|
1163
|
+
let model3dModelUrl = null;
|
|
1164
|
+
let model3dFormat = null;
|
|
1161
1165
|
let sourceMindmapProperties = null;
|
|
1162
1166
|
const occupiedOutgoingSides = new Set();
|
|
1163
1167
|
const hiddenIncomingSide = { value: null };
|
|
@@ -1170,6 +1174,9 @@ export class HandlesDomRenderer {
|
|
|
1170
1174
|
isMindmapTarget = mbType === 'mindmap';
|
|
1171
1175
|
isRevitScreenshotTarget = mbType === 'revit-screenshot-img';
|
|
1172
1176
|
revitViewPayload = req.pixiObject?._mb?.properties?.view || null;
|
|
1177
|
+
isModel3dScreenshotTarget = mbType === 'model3d-screenshot-img';
|
|
1178
|
+
model3dModelUrl = req.pixiObject?._mb?.properties?.modelUrl || null;
|
|
1179
|
+
model3dFormat = req.pixiObject?._mb?.properties?.format || null;
|
|
1173
1180
|
if (isMindmapTarget) {
|
|
1174
1181
|
sourceMindmapProperties = req.pixiObject?._mb?.properties || null;
|
|
1175
1182
|
const allObjects = this.host.core?.state?.state?.objects || [];
|
|
@@ -1667,6 +1674,29 @@ export class HandlesDomRenderer {
|
|
|
1667
1674
|
}
|
|
1668
1675
|
}
|
|
1669
1676
|
|
|
1677
|
+
if (isModel3dScreenshotTarget && typeof model3dModelUrl === 'string' && model3dModelUrl.length > 0) {
|
|
1678
|
+
const showInViewerButton = document.createElement('button');
|
|
1679
|
+
showInViewerButton.type = 'button';
|
|
1680
|
+
showInViewerButton.className = 'mb-revit-show-in-model';
|
|
1681
|
+
showInViewerButton.innerHTML = `${MODEL3D_SHOW_IN_VIEWER_ICON_SVG}<span>Показать в модели</span>`;
|
|
1682
|
+
showInViewerButton.style.left = `${Math.round(left + width / 2)}px`;
|
|
1683
|
+
showInViewerButton.style.top = `${Math.round(top - 34)}px`;
|
|
1684
|
+
showInViewerButton.addEventListener('pointerdown', (evt) => {
|
|
1685
|
+
evt.preventDefault();
|
|
1686
|
+
evt.stopPropagation();
|
|
1687
|
+
});
|
|
1688
|
+
showInViewerButton.addEventListener('click', (evt) => {
|
|
1689
|
+
evt.preventDefault();
|
|
1690
|
+
evt.stopPropagation();
|
|
1691
|
+
this.host.eventBus.emit(Events.UI.Model3dShowInViewer, {
|
|
1692
|
+
objectId: id,
|
|
1693
|
+
modelUrl: model3dModelUrl,
|
|
1694
|
+
format: model3dFormat,
|
|
1695
|
+
});
|
|
1696
|
+
});
|
|
1697
|
+
this.host.layer.appendChild(showInViewerButton);
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1670
1700
|
if (isRevitScreenshotTarget && typeof revitViewPayload === 'string' && revitViewPayload.length > 0) {
|
|
1671
1701
|
const showInModelButton = document.createElement('button');
|
|
1672
1702
|
showInModelButton.type = 'button';
|
package/src/ui/styles/chat.css
CHANGED
|
@@ -745,6 +745,179 @@
|
|
|
745
745
|
text-align: left;
|
|
746
746
|
}
|
|
747
747
|
|
|
748
|
+
/* ── Прогресс генерации 3D-модели в композере ───────────────────────── */
|
|
749
|
+
.moodboard-chat__3d-overlay {
|
|
750
|
+
box-sizing: border-box;
|
|
751
|
+
width: 100%;
|
|
752
|
+
padding: 2px 6px 4px 10px;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
.moodboard-chat__3d-overlay-inner {
|
|
756
|
+
display: flex;
|
|
757
|
+
align-items: center;
|
|
758
|
+
gap: 10px;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
.moodboard-chat__3d-overlay-stage {
|
|
762
|
+
flex: 0 0 auto;
|
|
763
|
+
font-family: 'GeistSans', 'GeistSans Fallback', 'Roboto', Arial, sans-serif;
|
|
764
|
+
font-size: 12px;
|
|
765
|
+
line-height: 1;
|
|
766
|
+
white-space: nowrap;
|
|
767
|
+
/* «Живой» переливающийся текст, как у статус-бара генерации изображений */
|
|
768
|
+
background: linear-gradient(
|
|
769
|
+
90deg,
|
|
770
|
+
#9CA3AF 0%,
|
|
771
|
+
#9CA3AF 20%,
|
|
772
|
+
#4B5563 40%,
|
|
773
|
+
#4B5563 60%,
|
|
774
|
+
#9CA3AF 80%,
|
|
775
|
+
#9CA3AF 100%
|
|
776
|
+
);
|
|
777
|
+
background-size: 200% 100%;
|
|
778
|
+
background-clip: text;
|
|
779
|
+
-webkit-background-clip: text;
|
|
780
|
+
color: transparent;
|
|
781
|
+
-webkit-text-fill-color: transparent;
|
|
782
|
+
animation: moodboard-chat-shimmer 1.8s linear infinite;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
.moodboard-chat__3d-overlay-bar-wrap {
|
|
786
|
+
position: relative;
|
|
787
|
+
flex: 1 1 auto;
|
|
788
|
+
min-width: 0;
|
|
789
|
+
height: 6px;
|
|
790
|
+
border-radius: 999px;
|
|
791
|
+
background: rgba(17, 24, 39, 0.08);
|
|
792
|
+
overflow: hidden;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
.moodboard-chat__3d-overlay-bar-fill {
|
|
796
|
+
height: 100%;
|
|
797
|
+
width: 0;
|
|
798
|
+
border-radius: inherit;
|
|
799
|
+
/* Двухцветная гамма мудборда: тёмный → светло-серый → тёмный, мягкое переливание */
|
|
800
|
+
background-image: linear-gradient(90deg, #111827 0%, #9CA3AF 50%, #111827 100%);
|
|
801
|
+
background-size: 300% 100%;
|
|
802
|
+
transition: width 0.45s cubic-bezier(0.22, 1, 0.36, 1);
|
|
803
|
+
animation: moodboard-chat-bar-shimmer 2.5s linear infinite;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
/* Indeterminate: прогресс ещё не пришёл — скользящая полоса вместо застывшего 0% */
|
|
807
|
+
.moodboard-chat__3d-overlay-bar-fill.is-indeterminate {
|
|
808
|
+
width: 35% !important;
|
|
809
|
+
transition: none;
|
|
810
|
+
animation: moodboard-chat-bar-indeterminate 1.5s ease-in-out infinite;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
@keyframes moodboard-chat-bar-shimmer {
|
|
814
|
+
from { background-position: 100% 0; }
|
|
815
|
+
to { background-position: 0% 0; }
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
@keyframes moodboard-chat-bar-indeterminate {
|
|
819
|
+
0% { transform: translateX(-160%); }
|
|
820
|
+
100% { transform: translateX(450%); }
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
.moodboard-chat__3d-overlay-percent {
|
|
824
|
+
flex: 0 0 auto;
|
|
825
|
+
min-width: 30px;
|
|
826
|
+
text-align: right;
|
|
827
|
+
font-family: 'GeistSans', 'GeistSans Fallback', 'Roboto', Arial, sans-serif;
|
|
828
|
+
font-size: 12px;
|
|
829
|
+
font-weight: 500;
|
|
830
|
+
line-height: 1;
|
|
831
|
+
color: #111827;
|
|
832
|
+
font-variant-numeric: tabular-nums;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/* Состояние ошибки: красный текст, полоса и проценты скрыты */
|
|
836
|
+
.moodboard-chat__3d-overlay.is-error .moodboard-chat__3d-overlay-stage {
|
|
837
|
+
background: none;
|
|
838
|
+
-webkit-text-fill-color: #B91C1C;
|
|
839
|
+
color: #B91C1C;
|
|
840
|
+
animation: none;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
.moodboard-chat__3d-overlay.is-error .moodboard-chat__3d-overlay-bar-wrap,
|
|
844
|
+
.moodboard-chat__3d-overlay.is-error .moodboard-chat__3d-overlay-percent {
|
|
845
|
+
display: none;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
/* ── Скелетон 3D-модели на доске ─────────────────────────────────────── */
|
|
849
|
+
.moodboard-chat__3d-skeleton {
|
|
850
|
+
position: fixed;
|
|
851
|
+
box-sizing: border-box;
|
|
852
|
+
background: #ffffff;
|
|
853
|
+
border-radius: 12px;
|
|
854
|
+
box-shadow:
|
|
855
|
+
0 0 0 1px rgba(17, 24, 39, 0.08),
|
|
856
|
+
0 8px 24px rgba(15, 23, 42, 0.10);
|
|
857
|
+
overflow: hidden;
|
|
858
|
+
pointer-events: none;
|
|
859
|
+
z-index: 10;
|
|
860
|
+
display: flex;
|
|
861
|
+
align-items: center;
|
|
862
|
+
justify-content: center;
|
|
863
|
+
transition:
|
|
864
|
+
left var(--moodboard-chat-board-animation-ms, 520ms) cubic-bezier(0.22, 1, 0.36, 1),
|
|
865
|
+
top var(--moodboard-chat-board-animation-ms, 520ms) cubic-bezier(0.22, 1, 0.36, 1),
|
|
866
|
+
opacity 360ms ease,
|
|
867
|
+
transform 360ms cubic-bezier(0.22, 1, 0.36, 1);
|
|
868
|
+
will-change: transform, opacity;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
/* Бегущий блик по поверхности скелетона */
|
|
872
|
+
.moodboard-chat__3d-skeleton::after {
|
|
873
|
+
content: '';
|
|
874
|
+
position: absolute;
|
|
875
|
+
inset: 0;
|
|
876
|
+
background: linear-gradient(
|
|
877
|
+
100deg,
|
|
878
|
+
transparent 30%,
|
|
879
|
+
rgba(17, 24, 39, 0.07) 50%,
|
|
880
|
+
transparent 70%
|
|
881
|
+
);
|
|
882
|
+
background-size: 220% 100%;
|
|
883
|
+
animation: moodboard-chat-skeleton-sweep 1.6s linear infinite;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
.moodboard-chat__3d-skeleton-icon {
|
|
887
|
+
position: relative;
|
|
888
|
+
z-index: 1;
|
|
889
|
+
width: 34%;
|
|
890
|
+
max-width: 72px;
|
|
891
|
+
color: #9CA3AF;
|
|
892
|
+
animation: moodboard-chat-skeleton-pulse 1.8s ease-in-out infinite;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
.moodboard-chat__3d-skeleton-icon svg {
|
|
896
|
+
width: 100%;
|
|
897
|
+
height: 100%;
|
|
898
|
+
display: block;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
.moodboard-chat__3d-skeleton--enter {
|
|
902
|
+
opacity: 0;
|
|
903
|
+
transform: scale(0.92);
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
.moodboard-chat__3d-skeleton--entered {
|
|
907
|
+
opacity: 1;
|
|
908
|
+
transform: scale(1);
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
@keyframes moodboard-chat-skeleton-sweep {
|
|
912
|
+
from { background-position: 160% 0; }
|
|
913
|
+
to { background-position: -60% 0; }
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
@keyframes moodboard-chat-skeleton-pulse {
|
|
917
|
+
0%, 100% { opacity: 0.4; transform: translateY(0) scale(1); }
|
|
918
|
+
50% { opacity: 0.85; transform: translateY(-2px) scale(1.05); }
|
|
919
|
+
}
|
|
920
|
+
|
|
748
921
|
/* Блок ошибки сервера */
|
|
749
922
|
.moodboard-chat__error-block {
|
|
750
923
|
display: none;
|
package/src/ui/styles/panels.css
CHANGED
|
@@ -340,3 +340,267 @@
|
|
|
340
340
|
flex-shrink: 0;
|
|
341
341
|
}
|
|
342
342
|
|
|
343
|
+
/* Image properties toolbar */
|
|
344
|
+
.image-properties-panel {
|
|
345
|
+
position: absolute;
|
|
346
|
+
pointer-events: auto;
|
|
347
|
+
display: flex;
|
|
348
|
+
flex-direction: row;
|
|
349
|
+
align-items: center;
|
|
350
|
+
gap: 2px;
|
|
351
|
+
padding: 4px 8px;
|
|
352
|
+
background: #ffffff;
|
|
353
|
+
border: 1px solid #E5E7EB;
|
|
354
|
+
border-radius: 8px;
|
|
355
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.06);
|
|
356
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
357
|
+
font-size: 13px;
|
|
358
|
+
z-index: 1000;
|
|
359
|
+
user-select: none;
|
|
360
|
+
white-space: nowrap;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
.ipp-filename {
|
|
364
|
+
padding: 0 6px;
|
|
365
|
+
font-size: 13px;
|
|
366
|
+
color: #374151;
|
|
367
|
+
font-weight: 400;
|
|
368
|
+
max-width: 140px;
|
|
369
|
+
overflow: hidden;
|
|
370
|
+
text-overflow: ellipsis;
|
|
371
|
+
white-space: nowrap;
|
|
372
|
+
cursor: text;
|
|
373
|
+
border-radius: 4px;
|
|
374
|
+
outline: none;
|
|
375
|
+
transition: background 0.15s;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.ipp-filename:hover,
|
|
379
|
+
.ipp-filename:focus {
|
|
380
|
+
background: #F3F4F6;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.ipp-filename:focus {
|
|
384
|
+
text-overflow: clip;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
.ipp-divider {
|
|
388
|
+
width: 1px;
|
|
389
|
+
height: 18px;
|
|
390
|
+
background: #E5E7EB;
|
|
391
|
+
margin: 0 4px;
|
|
392
|
+
flex-shrink: 0;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.ipp-btn {
|
|
396
|
+
display: inline-flex;
|
|
397
|
+
align-items: center;
|
|
398
|
+
justify-content: center;
|
|
399
|
+
width: 28px;
|
|
400
|
+
height: 28px;
|
|
401
|
+
padding: 0;
|
|
402
|
+
border: none;
|
|
403
|
+
border-radius: 6px;
|
|
404
|
+
background: none;
|
|
405
|
+
color: #6B7280;
|
|
406
|
+
cursor: pointer;
|
|
407
|
+
flex-shrink: 0;
|
|
408
|
+
transition: background 0.15s, color 0.15s;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
.ipp-btn:hover {
|
|
412
|
+
background: #F3F4F6;
|
|
413
|
+
color: #111827;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
.ipp-btn:active,
|
|
417
|
+
.ipp-btn.is-active {
|
|
418
|
+
background: #E5E7EB;
|
|
419
|
+
color: #111827;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.ipp-icon-frame-wrap {
|
|
423
|
+
position: relative;
|
|
424
|
+
display: inline-block;
|
|
425
|
+
width: 20px;
|
|
426
|
+
height: 20px;
|
|
427
|
+
border-radius: 50%;
|
|
428
|
+
overflow: hidden;
|
|
429
|
+
transition: transform 100ms ease-in-out;
|
|
430
|
+
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAySURBVHgB3Y2xDQAwDMJIr+D/5+CLtAewVMoUjyDLJakRIJlmHHyyQah+pMM2RgobhAuV0grHtNJqlwAAAABJRU5ErkJggg==);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.ipp-icon-frame {
|
|
434
|
+
position: relative;
|
|
435
|
+
display: inline-block;
|
|
436
|
+
width: inherit;
|
|
437
|
+
height: inherit;
|
|
438
|
+
border-radius: 50%;
|
|
439
|
+
box-shadow: rgba(0, 0, 0, 0.25) 0 0 0 1px inset;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
.ipp-icon-frame::after {
|
|
443
|
+
content: "";
|
|
444
|
+
position: absolute;
|
|
445
|
+
left: 50%;
|
|
446
|
+
top: 50%;
|
|
447
|
+
transform: translate(-50%, -50%);
|
|
448
|
+
width: 50%;
|
|
449
|
+
height: 50%;
|
|
450
|
+
background-color: rgb(250, 250, 250);
|
|
451
|
+
box-shadow: rgba(0, 0, 0, 0.25) 0 0 0 1px;
|
|
452
|
+
border-radius: 50%;
|
|
453
|
+
pointer-events: none;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/* Split Button (Crop) */
|
|
457
|
+
.ipp-btn-split-wrapper {
|
|
458
|
+
position: relative;
|
|
459
|
+
display: inline-flex;
|
|
460
|
+
align-items: center;
|
|
461
|
+
border-radius: 6px;
|
|
462
|
+
background: transparent;
|
|
463
|
+
transition: background 0.15s;
|
|
464
|
+
flex-shrink: 0;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
.ipp-btn-split-wrapper:hover {
|
|
468
|
+
background: #F3F4F6;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
.ipp-btn-split-main {
|
|
472
|
+
display: inline-flex;
|
|
473
|
+
align-items: center;
|
|
474
|
+
justify-content: center;
|
|
475
|
+
width: 28px;
|
|
476
|
+
height: 28px;
|
|
477
|
+
padding: 0;
|
|
478
|
+
border: none;
|
|
479
|
+
border-radius: 6px 0 0 6px;
|
|
480
|
+
background: transparent;
|
|
481
|
+
color: #6B7280;
|
|
482
|
+
cursor: pointer;
|
|
483
|
+
transition: color 0.15s;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
.ipp-btn-split-expand {
|
|
487
|
+
display: inline-flex;
|
|
488
|
+
align-items: center;
|
|
489
|
+
justify-content: center;
|
|
490
|
+
width: 18px;
|
|
491
|
+
height: 28px;
|
|
492
|
+
padding: 0;
|
|
493
|
+
border: none;
|
|
494
|
+
border-radius: 0 6px 6px 0;
|
|
495
|
+
background: transparent;
|
|
496
|
+
color: #6B7280;
|
|
497
|
+
cursor: pointer;
|
|
498
|
+
transition: color 0.15s;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.ipp-btn-split-main:hover,
|
|
502
|
+
.ipp-btn-split-expand:hover {
|
|
503
|
+
color: #111827;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
.ipp-btn-split-wrapper:active {
|
|
507
|
+
background: #E5E7EB;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
.ipp-btn-split-expand svg {
|
|
511
|
+
transition: transform 0.2s ease;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
.ipp-btn-split-expand.is-expanded svg {
|
|
515
|
+
transform: rotate(180deg);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/* Crop Dropdown */
|
|
519
|
+
.ipp-crop-dropdown {
|
|
520
|
+
position: absolute;
|
|
521
|
+
top: calc(100% + 6px);
|
|
522
|
+
left: 50%;
|
|
523
|
+
transform: translateX(-50%);
|
|
524
|
+
background: #ffffff;
|
|
525
|
+
border: 1px solid #E5E7EB;
|
|
526
|
+
border-radius: 8px;
|
|
527
|
+
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1), 0 4px 10px rgba(0, 0, 0, 0.05);
|
|
528
|
+
padding: 6px;
|
|
529
|
+
display: none;
|
|
530
|
+
flex-direction: column;
|
|
531
|
+
min-width: 170px;
|
|
532
|
+
z-index: 1001;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
.ipp-crop-dropdown.is-open {
|
|
536
|
+
display: flex;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/* More Dropdown */
|
|
540
|
+
.ipp-more-dropdown {
|
|
541
|
+
position: absolute;
|
|
542
|
+
top: calc(100% + 6px);
|
|
543
|
+
right: 0;
|
|
544
|
+
background: #ffffff;
|
|
545
|
+
border: 1px solid #E5E7EB;
|
|
546
|
+
border-radius: 8px;
|
|
547
|
+
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1), 0 4px 10px rgba(0, 0, 0, 0.05);
|
|
548
|
+
padding: 6px;
|
|
549
|
+
display: none;
|
|
550
|
+
flex-direction: column;
|
|
551
|
+
min-width: 220px;
|
|
552
|
+
z-index: 1001;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
.ipp-more-dropdown.is-open {
|
|
556
|
+
display: flex;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
.ipp-dropdown-item {
|
|
560
|
+
display: flex;
|
|
561
|
+
align-items: center;
|
|
562
|
+
padding: 6px 8px;
|
|
563
|
+
border: none;
|
|
564
|
+
background: transparent;
|
|
565
|
+
border-radius: 6px;
|
|
566
|
+
cursor: pointer;
|
|
567
|
+
color: #374151;
|
|
568
|
+
gap: 10px;
|
|
569
|
+
font-size: 13px;
|
|
570
|
+
font-family: inherit;
|
|
571
|
+
text-align: left;
|
|
572
|
+
transition: background 0.15s;
|
|
573
|
+
width: 100%;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
.ipp-dropdown-item:hover {
|
|
577
|
+
background: #F3F4F6;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
.ipp-dropdown-item-ratio {
|
|
581
|
+
margin-left: auto;
|
|
582
|
+
color: #9CA3AF;
|
|
583
|
+
font-size: 12px;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
.ipp-dropdown-item-shortcut {
|
|
587
|
+
margin-left: auto;
|
|
588
|
+
color: #9CA3AF;
|
|
589
|
+
font-size: 12px;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
.ipp-dropdown-divider {
|
|
593
|
+
height: 1px;
|
|
594
|
+
background: #E5E7EB;
|
|
595
|
+
margin: 4px 2px;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
.ipp-dropdown-icon {
|
|
599
|
+
width: 16px;
|
|
600
|
+
height: 16px;
|
|
601
|
+
display: inline-flex;
|
|
602
|
+
align-items: center;
|
|
603
|
+
justify-content: center;
|
|
604
|
+
color: #6B7280;
|
|
605
|
+
}
|
|
606
|
+
|