Compare commits

..
2 Commits
Author SHA1 Message Date
kate522 1a0b023658 fix(submit): raise TinyMCE fullscreen above the fixed navbar
TinyMCE's skin injects .mce-fullscreen { z-index: 100 } at editor init,
after core editor.css, so the fullscreen editor (toolbar at the viewport
top) lost to the fixed navbar (.nav-fixed .header, z-index 999). Restore
core's intended 100010 with an element+class selector that outranks the
skin regardless of injection order; it stays below the submit media
dialog (100080). Also remove the dead .mce-top-part.affix rules (no
AKSRT script drives them) and pin both in a contract test.
2026-09-06 10:10:20 +08:00
kate522 daee4cd67f feat(submit): make video cover row icon-only with hover titles
Replace the video-pic/video/clear button labels with icon-only buttons
carrying title + aria-label (设置视频封面/视频/清除), center the glyphs
via flex to fix the upward icon shift, rename the media picker title to
为视频设置封面, and drop the unused setVideoCover i18n key. Contract
test updated to pin the new markup and string set.
2026-09-06 10:10:12 +08:00
7 changed files with 90 additions and 22 deletions

No files matched your search

+1
View File
@@ -56,6 +56,7 @@
!/tests/front-submit/
/tests/front-submit/*
!/tests/front-submit/video-cover-contract.test.cjs
!/tests/front-submit/editor-fullscreen-contract.test.cjs
!/tests/asset-ledger/
!/tests/asset-ledger/*.php
!/tests/visual/docker-compose.zibll.yml
+25 -8
View File
@@ -51,19 +51,18 @@
border: 0;
}
.newposts-editor .mce-top-part.affix {
position: fixed;
}
.newposts-editor .mce-top-part.affix-bottom {
position: absolute;
}
.newposts-editor .wp-editor-container {
overflow: hidden;
border-radius: 0;
}
/* TinyMCE 全屏容器:skin 样式在编辑器初始化时注入(晚于核心 editor.css),
其 .mce-fullscreen 的 z-index:100 压不过固定导航栏(.nav-fixed .header 为 999)。
用元素+类选择器提高优先级,恢复 WP 核心预期的 100010,仍低于投稿媒体弹层(100080)。 */
div.mce-fullscreen {
z-index: 100010;
}
.newposts-editor .mce-toolbar-grp,
.newposts-editor div.mce-toolbar-grp {
border-bottom: 0;
@@ -547,12 +546,30 @@ body.aksrt-editor-media-open {
font-size: 1em;
}
/* 纯图标按钮(设置视频封面/视频/清除):fa 字形行高与文字不同导致图标上偏,
用 flex 居中修正;并去掉 legacy .but 给图标加的文字间距。btn-delete 的
hidden 属性切换依赖默认 display,故用 :not([hidden]) 守卫。 */
.featured-edit .btns-full .btn-video,
.featured-edit .btns-full .btn-delete:not([hidden]) {
display: inline-flex;
align-items: center;
justify-content: center;
}
.featured-edit .btns-full .btn-video-pic {
display: none;
}
.featured-loaded.video .btns-full .btn-video-pic {
display: inline-flex;
align-items: center;
justify-content: center;
}
.featured-edit .btns-full .btn-video-pic > .fa,
.featured-edit .btns-full .btn-video > .fa,
.featured-edit .btns-full .btn-delete > .fa {
margin-right: 0;
}
.featured-loaded.image .btn-video,
+2 -2
View File
@@ -522,7 +522,7 @@
type: 'image',
multiple: false,
allowExternal: false,
title: i18n.setVideoCoverTitle || '为视频设置首图封面',
title: i18n.setVideoCoverTitle || '为视频设置封面',
onSelect: function (cover) {
self.setFeatured('video', {
url: current.data.url,
@@ -554,7 +554,7 @@
type: 'image',
multiple: false,
allowExternal: false,
title: i18n.setVideoCoverTitle || '为视频设置首图封面',
title: i18n.setVideoCoverTitle || '为视频设置封面',
onSelect: function (cover) {
self.setFeatured('video', {
url: video.url,
+1 -2
View File
@@ -147,8 +147,7 @@ if (file_exists($js)) {
'fileTooLarge' => __('文件大小超过限制', 'aksrt'),
'lastSaved' => __('最后保存:', 'aksrt'),
'preview' => __('预览文章', 'aksrt'),
'setVideoCover' => __('设置视频首图封面', 'aksrt'),
'setVideoCoverTitle' => __('为视频设置首图封面', 'aksrt'),
'setVideoCoverTitle' => __('为视频设置封面', 'aksrt'),
'close' => __('关闭', 'aksrt'),
'insertImage' => __('插入图片', 'aksrt'),
'uploadImage' => __('上传图片', 'aksrt'),
+12 -6
View File
@@ -47,8 +47,10 @@ $can_video = !empty($ctx['can_cover_video']);
</button>
<?php endif; ?>
<?php if ($can_video) : ?>
<button type="button" class="but btn-video-pic" data-featured-action="video_pic">
<i class="fa fas fa-image"></i> <?php esc_html_e('设置视频首图封面', 'aksrt'); ?>
<button type="button" class="but btn-video-pic" data-featured-action="video_pic"
title="<?php esc_attr_e('设置视频封面', 'aksrt'); ?>"
aria-label="<?php esc_attr_e('设置视频封面', 'aksrt'); ?>">
<i class="fa fas fa-image"></i>
</button>
<?php endif; ?>
<?php if ($can_slide) : ?>
@@ -57,12 +59,16 @@ $can_video = !empty($ctx['can_cover_video']);
</button>
<?php endif; ?>
<?php if ($can_video) : ?>
<button type="button" class="but btn-video" data-featured-action="video">
<i class="fa fas fa-video"></i> <?php esc_html_e('视频', 'aksrt'); ?>
<button type="button" class="but btn-video" data-featured-action="video"
title="<?php esc_attr_e('视频', 'aksrt'); ?>"
aria-label="<?php esc_attr_e('视频', 'aksrt'); ?>">
<i class="fa fas fa-video"></i>
</button>
<?php endif; ?>
<button type="button" class="but c-red btn-delete" data-featured-action="close" <?php echo $type === '' ? 'hidden' : ''; ?>>
<i class="fa fas fa-trash-alt"></i> <?php esc_html_e('清除', 'aksrt'); ?>
<button type="button" class="but c-red btn-delete" data-featured-action="close" <?php echo $type === '' ? 'hidden' : ''; ?>
title="<?php esc_attr_e('清除', 'aksrt'); ?>"
aria-label="<?php esc_attr_e('清除', 'aksrt'); ?>">
<i class="fa fas fa-trash-alt"></i>
</button>
</div>
<input type="hidden" name="featured_data" value="<?php echo esc_attr(wp_json_encode(['type' => $type, 'data' => $data])); ?>" data-featured-json>
@@ -0,0 +1,35 @@
'use strict';
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const test = require('node:test');
const root = path.resolve(__dirname, '..', '..');
const read = (file) => fs.readFileSync(path.join(root, file), 'utf8');
test('submit.css raises the TinyMCE fullscreen container above the fixed navbar', () => {
const css = read('assets/css/frontend/pages/submit.css');
// TinyMCE's skin injects .mce-fullscreen { z-index: 100 } at editor init,
// after core editor.css, so the fullscreen editor loses to the fixed navbar
// (.nav-fixed .header, z-index 999). The element+class selector outranks the
// skin regardless of injection order and restores core's intended 100010,
// which stays below the submit media dialog (100080).
assert.match(css, /div\.mce-fullscreen \{\s*\n z-index: 100010;\s*\n\}/);
});
test('the Zibll toolbar affix rules stay removed', () => {
const css = read('assets/css/frontend/pages/submit.css');
const jsRoot = path.join(root, 'assets/js');
const js = fs
.readdirSync(jsRoot, { withFileTypes: true })
.filter((entry) => entry.isFile())
.map((entry) => read(`assets/js/${entry.name}`))
.join('\n');
// No AKSRT script adds .affix to .mce-top-part (that was Zibll's
// editextend.js), so the affix positioning rules must not come back.
assert.doesNotMatch(css, /mce-top-part\.affix/);
assert.doesNotMatch(js, /mce-top-part/);
});
@@ -17,7 +17,12 @@ test('featured box renders a playable video preview and a video-cover button', (
assert.match(featured, /fa fas fa-play-circle/);
// The video-cover button is capability-gated like the video button.
assert.match(featured, /if \(\$can_video\) : \?>\s*\n\s*<button type="button" class="but btn-video-pic" data-featured-action="video_pic">/);
assert.match(featured, /if \(\$can_video\) : \?>\s*\n\s*<button type="button" class="but btn-video-pic" data-featured-action="video_pic"\s*\n\s*title="<\?php esc_attr_e\('设置视频封面', 'aksrt'\); \?>"/);
// The video row (video cover / video / clear) is icon-only with hover titles.
assert.match(featured, /btn-video" data-featured-action="video"\s*\n\s*title="<\?php esc_attr_e\('视频', 'aksrt'\); \?>"\s*\n\s*aria-label="<\?php esc_attr_e\('视频', 'aksrt'\); \?>">\s*\n\s*<i class="fa fas fa-video"><\/i>\s*\n\s*<\/button>/);
assert.match(featured, /btn-delete" data-featured-action="close"[\s\S]*?title="<\?php esc_attr_e\('清除', 'aksrt'\); \?>"/);
assert.doesNotMatch(featured, /首图|>视频<|>清除</);
// Dead capability attributes are gone.
assert.doesNotMatch(featured, /data-can-(image|slide|video)=/);
@@ -46,7 +51,9 @@ test('submit.css toggles the video-cover button like the reference theme', () =>
const css = read('assets/css/frontend/pages/submit.css');
assert.match(css, /\.featured-edit \.btns-full \.btn-video-pic \{\s*\n display: none;\s*\n\}/);
assert.match(css, /\.featured-loaded\.video \.btns-full \.btn-video-pic \{\s*\n display: inline-flex;\s*\n\}/);
assert.match(css, /\.featured-loaded\.video \.btns-full \.btn-video-pic \{\s*\n display: inline-flex;\s*\n align-items: center;\s*\n justify-content: center;\s*\n\}/);
assert.match(css, /\.featured-edit \.btns-full \.btn-video,\s*\n\.featured-edit \.btns-full \.btn-delete:not\(\[hidden\]\) \{\s*\n display: inline-flex;\s*\n align-items: center;\s*\n justify-content: center;\s*\n\}/);
assert.match(css, /\.featured-edit \.btns-full \.btn-video-pic > \.fa,[\s\S]*?\.featured-edit \.btns-full \.btn-delete > \.fa \{\s*\n margin-right: 0;\s*\n\}/);
assert.doesNotMatch(css, /featured-video-placeholder/);
});
@@ -71,8 +78,11 @@ test('i18n exposes the video-cover strings and dead keys are removed', () => {
const editorJs = read('assets/js/front-submit-editor.js');
const submitJs = read('assets/js/front-submit.js');
assert.match(page, /'setVideoCover' => __\('设置视频首图封面', 'aksrt'\)/);
assert.match(page, /'setVideoCoverTitle' => __\('为视频设置首图封面', 'aksrt'\)/);
assert.match(page, /'setVideoCoverTitle' => __\('为视频设置封面', 'aksrt'\)/);
// The unused setVideoCover key died with the visible button label it served.
assert.doesNotMatch(page, /'setVideoCover' =>/);
assert.doesNotMatch(page, /首图/);
assert.doesNotMatch(submitJs, /首图/);
assert.doesNotMatch(page, /'(pickImage|pickVideo|pickVideoCover|pickVideoResource|videoUrl)'/);
// data-video-name / mediaUrl stay: pinned by tinymce.css and the