+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -605,6 +1580,211 @@ function handleWindowPointerUp(): void {
line-height: 1.4;
}
+.article-editor-canvas__table-context-menu {
+ position: fixed;
+ z-index: 1200;
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ min-width: 620px;
+ max-width: 720px;
+ padding: 16px;
+ border: 1px solid #edf2fa;
+ border-radius: 16px;
+ background: #ffffff;
+ box-shadow:
+ 0 20px 48px rgba(15, 23, 42, 0.08),
+ 0 6px 16px rgba(15, 23, 42, 0.04);
+}
+
+.article-editor-canvas__table-context-row--top {
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+ gap: 12px;
+}
+
+.article-editor-canvas__table-context-row--bottom {
+ display: grid;
+ grid-template-columns: repeat(5, 1fr);
+ gap: 12px;
+}
+
+.article-editor-canvas__table-context-divider {
+ height: 1px;
+ background: #f0f3f8;
+}
+
+.article-editor-canvas__table-context-action {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 10px;
+ min-height: 86px;
+ padding: 12px 10px;
+ border: 1px solid transparent;
+ border-radius: 12px;
+ background: #f7f9fa;
+ color: #3f4a5c;
+ cursor: pointer;
+ transition: all 0.2s ease;
+}
+
+.article-editor-canvas__table-context-action span {
+ font-size: 13px;
+ font-weight: 600;
+ line-height: 1.4;
+ text-align: center;
+}
+
+.article-editor-canvas__table-context-action :deep(svg) {
+ font-size: 22px;
+ color: #5c6b81;
+ transition: color 0.2s ease;
+}
+
+.article-editor-canvas__table-context-action:hover:not(:disabled) {
+ background: #eff4ff;
+ border-color: #c2d6ff;
+ color: #245bdb;
+}
+
+.article-editor-canvas__table-context-action:hover:not(:disabled) :deep(svg) {
+ color: #245bdb;
+}
+
+.article-editor-canvas__table-context-action--danger:hover:not(:disabled) {
+ background: #fff1f1;
+ border-color: #ffccc7;
+ color: #cf1322;
+}
+
+.article-editor-canvas__table-context-action--danger:hover:not(:disabled) :deep(svg) {
+ color: #cf1322;
+}
+
+.article-editor-canvas__table-context-action:disabled {
+ opacity: 0.46;
+ cursor: not-allowed;
+}
+
+.article-editor-canvas__image-selection {
+ position: fixed;
+ z-index: 1100;
+ box-sizing: border-box;
+ border: 2px solid #2f6bff;
+ border-radius: 18px;
+ box-shadow:
+ 0 0 0 1px rgba(255, 255, 255, 0.9),
+ 0 14px 28px rgba(47, 107, 255, 0.12);
+ pointer-events: none;
+}
+
+.article-editor-canvas__image-handle {
+ position: absolute;
+ width: 14px;
+ height: 14px;
+ border: 2px solid #ffffff;
+ border-radius: 999px;
+ background: #2f6bff;
+ box-shadow: 0 4px 10px rgba(47, 107, 255, 0.28);
+ pointer-events: auto;
+}
+
+.article-editor-canvas__image-handle--nw {
+ top: 0;
+ left: 0;
+ cursor: nwse-resize;
+ transform: translate(-50%, -50%);
+}
+
+.article-editor-canvas__image-handle--ne {
+ top: 0;
+ right: 0;
+ cursor: nesw-resize;
+ transform: translate(50%, -50%);
+}
+
+.article-editor-canvas__image-handle--sw {
+ bottom: 0;
+ left: 0;
+ cursor: nesw-resize;
+ transform: translate(-50%, 50%);
+}
+
+.article-editor-canvas__image-handle--se {
+ right: 0;
+ bottom: 0;
+ cursor: nwse-resize;
+ transform: translate(50%, 50%);
+}
+
+.article-editor-canvas__image-context-menu {
+ position: fixed;
+ z-index: 1200;
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 10px;
+ min-width: 300px;
+ max-width: min(360px, calc(100vw - 32px));
+ padding: 12px;
+ border: 1px solid #e8eef8;
+ border-radius: 16px;
+ background:
+ linear-gradient(180deg, rgba(255, 255, 255, 0.98) 0%, rgba(248, 251, 255, 0.98) 100%);
+ box-shadow:
+ 0 20px 48px rgba(15, 23, 42, 0.1),
+ 0 6px 18px rgba(15, 23, 42, 0.06);
+ backdrop-filter: blur(14px);
+}
+
+.article-editor-canvas__image-context-action {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ min-height: 46px;
+ padding: 0 14px;
+ border: 1px solid transparent;
+ border-radius: 12px;
+ background: #f7f9fc;
+ color: #344054;
+ font-size: 13px;
+ font-weight: 600;
+ cursor: pointer;
+ transition:
+ border-color 0.18s ease,
+ background-color 0.18s ease,
+ color 0.18s ease,
+ transform 0.18s ease;
+}
+
+.article-editor-canvas__image-context-action :deep(svg) {
+ font-size: 18px;
+ color: #52607a;
+ transition: color 0.18s ease;
+}
+
+.article-editor-canvas__image-context-action:hover {
+ border-color: #c8d8ff;
+ background: #edf4ff;
+ color: #245bdb;
+ transform: translateY(-1px);
+}
+
+.article-editor-canvas__image-context-action:hover :deep(svg) {
+ color: #245bdb;
+}
+
+.article-editor-canvas__image-context-action--danger:hover {
+ border-color: #ffd2cf;
+ background: #fff1f0;
+ color: #cf1322;
+}
+
+.article-editor-canvas__image-context-action--danger:hover :deep(svg) {
+ color: #cf1322;
+}
+
.article-editor-canvas__surface {
position: relative;
z-index: 1;
@@ -655,6 +1835,30 @@ function handleWindowPointerUp(): void {
outline: none;
}
+.article-editor-canvas__surface :deep(.milkdown-image-block > .image-wrapper) {
+ margin: 0 auto;
+}
+
+.article-editor-canvas__surface :deep(.milkdown-image-block.selected > .image-wrapper::before) {
+ background: transparent;
+ border: 0;
+}
+
+.article-editor-canvas__surface :deep(.milkdown-image-block img) {
+ max-width: min(100%, 960px);
+ margin: 0 auto;
+}
+
+.article-editor-canvas__surface :deep(.milkdown-image-block > .image-wrapper .operation) {
+ opacity: 0 !important;
+ pointer-events: none;
+}
+
+.article-editor-canvas__surface :deep(.milkdown-image-block > .image-wrapper .image-resize-handle) {
+ opacity: 0 !important;
+ pointer-events: none;
+}
+
.article-editor-canvas__mask {
position: absolute;
inset: 0;
diff --git a/apps/admin-web/src/i18n/messages/en-US.ts b/apps/admin-web/src/i18n/messages/en-US.ts
index 01e2119..53bdd6f 100644
--- a/apps/admin-web/src/i18n/messages/en-US.ts
+++ b/apps/admin-web/src/i18n/messages/en-US.ts
@@ -410,6 +410,24 @@ const enUS = {
empty: "Drag to choose a table size",
hint: "Keep dragging near the edge to expand the grid",
},
+ tableMenu: {
+ addRowBefore: "Insert row above",
+ addRowAfter: "Insert row below",
+ deleteRow: "Delete row",
+ addColBefore: "Insert left column",
+ addColAfter: "Insert right column",
+ deleteCol: "Delete column",
+ alignLeft: "Align left",
+ alignCenter: "Align center",
+ alignRight: "Align right",
+ deleteTable: "Delete table",
+ },
+ imageMenu: {
+ replace: "Replace image",
+ caption: "Toggle caption",
+ resetSize: "Reset size",
+ delete: "Delete image",
+ },
messages: {
saved: "Article saved.",
generating: "This article is still generating. Try editing it later.",
diff --git a/apps/admin-web/src/i18n/messages/zh-CN.ts b/apps/admin-web/src/i18n/messages/zh-CN.ts
index 183b314..0acda0a 100644
--- a/apps/admin-web/src/i18n/messages/zh-CN.ts
+++ b/apps/admin-web/src/i18n/messages/zh-CN.ts
@@ -417,6 +417,24 @@ const zhCN = {
empty: "拖拽选择表格尺寸",
hint: "按住拖动到边缘可继续扩展",
},
+ tableMenu: {
+ addRowBefore: "上方插行",
+ addRowAfter: "下方插行",
+ deleteRow: "删除行",
+ addColBefore: "左侧插列",
+ addColAfter: "右侧插列",
+ deleteCol: "删除列",
+ alignLeft: "左对齐",
+ alignCenter: "居中",
+ alignRight: "右对齐",
+ deleteTable: "删除表格",
+ },
+ imageMenu: {
+ replace: "替换图片",
+ caption: "切换图注",
+ resetSize: "重置大小",
+ delete: "删除图片",
+ },
messages: {
saved: "文章已保存",
generating: "文章还在生成中,请稍后再编辑。",