fix(creator-studio): 固定公众号式创作工具栏
- 顶部改为 fixed 创作工具栏:左侧返回/品牌/模式,中间文章标题与保存状态, 右侧撤销/重做、保存草稿、预览、发布 - 新增工作台级撤销/重做快照栈,连续编辑合并记录;支持 Ctrl/Cmd+Z / Shift+Z, 不覆盖 RichText 输入框自身的编辑历史 - 移动端收敛工具栏布局,保留撤销/重做、预览、发布 - 先保存草稿再请求审核/直接发布;请求使用服务端更新后的文章与版本 ID - 固定工具栏以独立 chrome 层布局,正文避开顶部工具栏高度 - 补充固定工具栏、历史操作与键盘快捷键契约 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
f5e9420266
commit
9e8913ea0a
9 files changed
+518
-50
No files matched your search
@@ -1 +1 @@
|
||||
<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-block-editor', 'wp-block-library', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-wordcount'), 'version' => '71ff9994e44c86df3517');
|
||||
<?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-block-editor', 'wp-block-library', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-wordcount'), 'version' => '635f07e202f61f4e377f');
|
||||
@@ -1 +1 @@
|
||||
.jio-page-submit{background:#fafbfa}.jio-page-submit .float-btn,.jio-page-submit .footer,.jio-page-submit .footer-tabbar,.jio-page-submit .header,.jio-page-submit .jio-float-buttons{display:none!important}.jio-page-submit #main-content.container{max-width:none;padding:0}#jio-creator-studio.is-ready{display:block}.jio-creator{--jc-ink:#243330;--jc-ink-soft:#5c6b66;--jc-paper:#fafbfa;--jc-line:#e2e9e5;--jc-accent:#267665;--jc-accent-strong:#1d5f52;--jc-warn:#b4573f;--jc-bar:60px;-webkit-font-smoothing:antialiased;background:var(--jc-paper);color:var(--jc-ink);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Noto Sans SC,PingFang SC,Microsoft YaHei,sans-serif;min-height:100vh;min-height:100dvh}.jio-creator *,.jio-creator :after,.jio-creator :before{box-sizing:border-box}.jio-creator button{color:inherit;font:inherit}.jio-creator__bar{align-items:center;backdrop-filter:blur(10px);background:rgba(250,251,250,.92);border-bottom:1px solid var(--jc-line);display:flex;gap:16px;height:var(--jc-bar);padding:0 clamp(16px,4vw,40px);position:sticky;top:0;z-index:30}.jio-creator__back{align-items:center;color:var(--jc-ink-soft);display:inline-flex;gap:6px;text-decoration:none}.jio-creator__back:focus-visible,.jio-creator__back:hover{color:var(--jc-accent)}.jio-creator__brand{color:var(--jc-ink-soft);font-size:13px;letter-spacing:.06em;margin-right:auto}.jio-creator__actions{align-items:center;display:flex;gap:10px}.jio-creator__save{align-items:center;color:var(--jc-ink-soft);display:inline-flex;font-size:13px;gap:6px}.jio-creator__save>i{color:var(--jc-line);font-size:7px}.jio-creator__save time{font-variant-numeric:tabular-nums;opacity:.7}.jio-creator__save.is-saved>i{color:var(--jc-accent)}.jio-creator__save.is-conflict,.jio-creator__save.is-conflict>i,.jio-creator__save.is-error,.jio-creator__save.is-error>i{color:var(--jc-warn)}.jio-creator__btn,.jio-creator__ghost,.jio-creator__primary{align-items:center;border:1px solid transparent;border-radius:8px;cursor:pointer;display:inline-flex;font-size:14px;justify-content:center;min-height:36px;padding:0 16px;transition:background .18s ease,border-color .18s ease,color .18s ease}.jio-creator__ghost{background:transparent;border-color:var(--jc-line)}.jio-creator__ghost:focus-visible,.jio-creator__ghost:hover{border-color:var(--jc-accent);color:var(--jc-accent)}.jio-creator__primary{background:var(--jc-accent);color:#fff}.jio-creator__primary:focus-visible,.jio-creator__primary:hover{background:var(--jc-accent-strong)}.jio-creator__primary:disabled{cursor:not-allowed;opacity:.5}.jio-creator__notice{align-items:center;background:#f1f5f2;border-bottom:1px solid var(--jc-line);color:var(--jc-ink-soft);display:flex;font-size:13px;gap:12px;margin:0 auto;max-width:820px;padding:10px 16px}.jio-creator__notice>span{flex:1}.jio-creator__notice button{background:transparent;border:0;color:var(--jc-accent);cursor:pointer}.jio-creator__notice.is-error{background:#fdf2ee;color:var(--jc-warn)}.jio-creator__hint{color:var(--jc-ink-soft);font-size:13px;margin:18px auto 0;max-width:820px;padding:0 clamp(16px,4vw,40px)}.jio-creator__canvas{margin:0 auto;padding:clamp(20px,4vw,44px) clamp(20px,5vw,52px) 140px;width:min(720px,100%)}.jio-creator__title{display:block;margin-bottom:6px}.jio-creator__title textarea{background:transparent;border:0;color:var(--jc-ink);font-family:Songti SC,"Noto Serif SC","Source Han Serif SC",serif;font-size:clamp(23px,2.6vw,27px);font-weight:500;letter-spacing:.01em;line-height:1.4;min-height:50px;padding:0;resize:none;width:100%}.jio-creator__title textarea:focus{outline:0}.jio-creator__title textarea::-moz-placeholder{color:#b7c2bd}.jio-creator__title textarea::placeholder{color:#b7c2bd}.jio-creator__excerpt{display:block;margin-bottom:22px}.jio-creator__excerpt>span{color:#9aa8a2;display:block;font-size:12px;letter-spacing:.02em;margin-bottom:4px}.jio-creator__excerpt textarea{background:transparent;border:0;color:var(--jc-ink-soft);font-family:inherit;font-size:14px;line-height:1.7;padding:6px 0;resize:none;width:100%}.jio-creator__excerpt textarea:focus{outline:0}.jio-creator__editor{min-height:40vh;position:relative}.jio-creator__editor .block-editor-writing-flow{font-size:16px;letter-spacing:.01em;line-height:1.85}.jio-creator__editor .block-editor-default-block-appender textarea.block-editor-default-block-appender__content{color:#b7c2bd}.jio-creator__editor .block-editor-block-list__layout{padding-bottom:40px}.jio-creator__editor .wp-block{max-width:none}.jio-creator__editor .block-editor-block-list__block{margin-bottom:1.1em;margin-top:0}.jio-creator__editor .block-editor-block-list__block[data-type="core/heading"]{margin-top:1.6em}.jio-creator__editor .block-editor-block-list__block.is-hovered:after,.jio-creator__editor .block-editor-block-list__block.is-selected:after{border-radius:3px;box-shadow:0 0 0 1.5px rgba(38,118,101,.35)}.jio-creator__editor .block-editor-block-contextual-toolbar{margin-left:0}.jio-creator__editor .block-editor-block-contexLine truncated
|
||||
.jio-page-submit{background:#fafbfa}body.jio-page-submit.has-static-nav,body.jio-page-submit.nav-fixed{padding-top:0!important}.jio-page-submit .float-btn,.jio-page-submit .footer,.jio-page-submit .footer-tabbar,.jio-page-submit .header,.jio-page-submit .jio-float-buttons{display:none!important}.jio-page-submit #main-content.container{max-width:none;padding:0}#jio-creator-studio.is-ready{display:block}.jio-creator{--jc-ink:#243330;--jc-ink-soft:#5c6b66;--jc-paper:#fafbfa;--jc-line:#e2e9e5;--jc-accent:#267665;--jc-accent-strong:#1d5f52;--jc-warn:#b4573f;--jc-bar:60px;-webkit-font-smoothing:antialiased;background:var(--jc-paper);color:var(--jc-ink);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Noto Sans SC,PingFang SC,Microsoft YaHei,sans-serif;min-height:100vh;min-height:100dvh;padding-top:var(--jc-bar)}.jio-creator *,.jio-creator :after,.jio-creator :before{box-sizing:border-box}.jio-creator button{color:inherit;font:inherit}.jio-creator__bar{align-items:center;backdrop-filter:blur(14px);background:rgba(250,251,250,.97);border-bottom:1px solid var(--jc-line);box-shadow:0 2px 8px rgba(27,46,39,.035);display:flex;gap:20px;height:var(--jc-bar);justify-content:space-between;left:0;padding:0 clamp(16px,3vw,34px);position:fixed;right:0;top:0;z-index:300}.jio-creator__bar-actions,.jio-creator__bar-center,.jio-creator__bar-left{align-items:center;display:flex;gap:12px;min-width:0}.jio-creator__bar-left{flex:0 0 auto}.jio-creator__bar-center{flex:1 1 auto;gap:14px;justify-content:center}.jio-creator__bar-actions{flex:0 0 auto;gap:8px}.jio-creator__back{align-items:center;border-radius:7px;color:var(--jc-ink-soft);display:inline-flex;height:30px;justify-content:center;text-decoration:none;width:30px}.jio-creator__back:focus-visible,.jio-creator__back:hover{background:#edf3f0;color:var(--jc-accent);outline:0}.jio-creator__bar-brand{color:var(--jc-accent-strong);font-family:Songti SC,"Noto Serif SC","Source Han Serif SC",serif;font-size:17px;font-weight:700;letter-spacing:-.04em}.jio-creator__bar-divider{background:var(--jc-line);height:18px;width:1px}.jio-creator__bar-mode{color:var(--jc-ink-soft);font-size:13px}.jio-creator__doc-title{color:#65736d;font-size:13px;max-width:min(32vw,360px);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.jio-creator__icon-btn{align-items:center;background:transparent;border:0;border-radius:7px;color:var(--jc-ink-soft);cursor:pointer;display:inline-flex;height:32px;justify-content:center;width:32px}.jio-creator__icon-btn:focus-visible:not(:disabled),.jio-creator__icon-btn:hover:not(:disabled){background:#edf3f0;color:var(--jc-accent-strong);outline:0}.jio-creator__icon-btn:disabled{color:#c7d0cc;cursor:default}.jio-creator__history{align-items:center;border-right:1px solid var(--jc-line);display:inline-flex;gap:2px;padding-right:8px}.jio-creator__save-draft{background:#fff;border-color:var(--jc-line);color:var(--jc-ink-soft)}.jio-creator__save-draft:focus-visible,.jio-creator__save-draft:hover{background:#f7faf8;border-color:#b7cdc3;color:var(--jc-accent-strong)}.jio-creator__save{align-items:center;color:var(--jc-ink-soft);display:inline-flex;font-size:13px;gap:6px}.jio-creator__save>i{color:var(--jc-line);font-size:7px}.jio-creator__save time{font-variant-numeric:tabular-nums;opacity:.7}.jio-creator__save.is-saved>i{color:var(--jc-accent)}.jio-creator__save.is-conflict,.jio-creator__save.is-conflict>i,.jio-creator__save.is-error,.jio-creator__save.is-error>i{color:var(--jc-warn)}.jio-creator__btn,.jio-creator__ghost,.jio-creator__primary{align-items:center;border:1px solid transparent;border-radius:8px;cursor:pointer;display:inline-flex;font-size:14px;justify-content:center;min-height:36px;padding:0 16px;transition:background .18s ease,border-color .18s ease,color .18s ease}.jio-creator__ghost{background:transparent;border-color:var(--jc-line)}.jio-creator__ghost:focus-visible,.jio-creator__ghost:hover{border-color:var(--jc-accent);color:var(--jc-accent)}.jio-creator__primary{background:var(--jc-accent);color:#fff}.jio-creator__primary:focus-visible,.jio-creator__primary:hover{background:var(--jc-accent-strong)}.jio-creator__primary:disabled{cursor:not-allowed;opacity:.5}.jio-creator__notice{align-items:center;background:#f1f5f2;border-bottom:1px solid var(--jc-line);color:var(--jc-ink-soft);display:flex;font-size:13px;gap:12px;margin:0 auto;max-width:820px;padding:10px 16px}.jio-creator__notice>span{flex:1}.jio-creator__notice button{background:transparent;border:0;color:var(--jc-accent);cursor:pointer}.jio-creator__notice.is-error{background:#fdf2ee;color:var(--jc-warn)}.jio-creator__hint{color:var(--jc-ink-soft);font-size:13px;margin:18px auto 0;max-width:820px;padding:0 clamp(16px,4vw,40px)}.jio-creator__canvas{margin:0 auto;padding:clamp(20px,4vw,44px) clamp(20px,5vw,52px) 140px;width:min(720px,100%)}.jio-creator__title{display:block;margin-bottom:6px}.jio-creator__title textarea{background:transparent;border:0;color:var(--jc-ink);font-family:Songti SC,"Noto SLine truncated
|
||||
@@ -1 +1 @@
|
||||
(()=>{"use strict";const e={n:t=>{const a=t&&t.__esModule?()=>t.default:()=>t;return e.d(a,{a}),a},d:(t,a)=>{for(var r in a)e.o(a,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:a[r]})},o:(e,t)=>Object.hasOwn(e,t)},t=window.React,a=window.wp.element,r=window.wp.blocks,n=window.wp.blockLibrary,o=window.wp.data,i=window.wp.blockEditor,l=window.wp.wordcount,c=window.wp.i18n;function s(e,t){const a=e&&(e.message||e.error)||(401===t?"登录状态已失效,请刷新页面重新登录。":"请求失败,请稍后重试。"),r=new Error(String(a));return r.status=t,r.code=e&&e.code?String(e.code):"",r.data=e,r}async function m(e){try{return await e.json()}catch(e){return null}}async function u(e,t,a){const r=await fetch(e,{method:"POST",credentials:"same-origin",headers:{"Content-Type":"application/json","X-WP-Nonce":a},body:JSON.stringify(t)}),n=await m(r);if(!r.ok)throw s(n,r.status);return n}async function d(e,t,a,r={}){const n=new FormData;n.append("file",t),Object.entries(r).forEach(([e,t])=>n.append(e,t));const o=await fetch(e,{method:"POST",credentials:"same-origin",headers:{"X-WP-Nonce":a},body:n}),i=await m(o);if(!o.ok)throw s(i,o.status);return i}const p=window.wp.apiFetch;var _=e.n(p);const f=window.wp.hooks,b=window.wp.compose,y=window.wp.components,E=[{key:"half",label:(0,c.__)("半屏","jio"),className:"jio-img-w--half"},{key:"medium",label:(0,c.__)("中等","jio"),className:"jio-img-w--medium"},{key:"wide",label:(0,c.__)("通栏","jio"),className:"jio-img-w--wide"},{key:"full",label:(0,c.__)("全宽","jio"),className:"jio-img-w--full"}];function j(e){return a=>{if("core/image"!==a.name)return(0,t.createElement)(e,{...a});const r=String(a.attributes?.className||""),n=function(e){const t=E.find(t=>(e||"").split(/\s+/).includes(t.className));return t?t.key:"wide"}(r);return(0,t.createElement)(t.Fragment,null,(0,t.createElement)(e,{...a}),(0,t.createElement)(i.BlockControls,{group:"inline"},(0,t.createElement)(y.ToolbarGroup,{controls:E.map(e=>({title:e.label,children:e.label,isActive:n===e.key,onClick:()=>(e=>{const t=r.split(/\s+/).filter(e=>e&&!E.some(t=>t.className===e));a.setAttributes({className:[...t,e.className].join(" ")})})(e)}))})))}}const g=window.jioCreatorStudio||{},h=g.nonce||"";function w(e){const{draft:r,setDraft:n,capabilities:o,limits:i,taxonomy:l,isPublishedEdit:s,onClose:m,onSaveDraft:u,onSubmit:p,privateStorage:_,privateUploads:f,saving:b,captcha:y}=e,[E,j]=(0,a.useState)(""),[w,v]=(0,a.useState)(""),[N,k]=(0,a.useState)(!1),S=(0,a.useRef)(null),C=(0,a.useRef)(null),I=(0,a.useRef)(null);(0,a.useEffect)(()=>{const e=I.current,t=document.getElementById("jio-creator-captcha");if(!e||!t)return;const a=t.parentElement;return e.appendChild(t),t.removeAttribute("hidden"),()=>{a&&(a.appendChild(t),t.setAttribute("hidden",""))}},[]);const x=(0,a.useMemo)(()=>(r.title||"").trim().length,[r.title])>=Number(i.titleMin||1),L=()=>{const e=E.trim();if(!e)return;const t=e.split(/[,,\n]+/u).map(e=>e.trim()).filter(Boolean);n({tags:Array.from(new Set([...r.tags,...t]))}),j("")};return(0,t.createElement)("div",{className:"jio-creator-drawer",role:"dialog","aria-modal":"true","aria-labelledby":"jio-creator-publish-title"},(0,t.createElement)("button",{type:"button",className:"jio-creator-drawer__backdrop","aria-label":(0,c.__)("关闭","jio"),onClick:m}),(0,t.createElement)("div",{className:"jio-creator-drawer__panel"},(0,t.createElement)("header",{className:"jio-creator-drawer__head"},(0,t.createElement)("h2",{id:"jio-creator-publish-title"},s?(0,c.__)("提交改稿","jio"):(0,c.__)("发布文章","jio")),(0,t.createElement)("button",{type:"button",className:"jio-creator-drawer__close",onClick:m,"aria-label":(0,c.__)("关闭","jio")},(0,t.createElement)("i",{className:"fa fas fa-times","aria-hidden":"true"}))),(0,t.createElement)("div",{className:"jio-creator-drawer__body"},s&&(0,t.createElement)("p",{className:"jio-creator-drawer__note"},(0,c.__)("审核通过前,读者看到的仍然是当前线上版本。","jio")),(0,t.createElement)("section",{className:"jio-creator-field"},(0,t.createElement)("h3",null,(0,c.__)("封面","jio")),r.featured?.data?.url?(0,t.createElement)("div",{className:"jio-creator-cover"},(0,t.createElement)("img",{src:String(r.featured.data.url),alt:""}),(0,t.createElement)("button",{type:"button",onClick:()=>n({featured:{type:"",data:{}}}),"aria-label":(0,c.__)("移除封面","jio")},(0,t.createElement)("i",{className:"fa fas fa-times","aria-hidden":"true"}))):o.canUploadImage?(0,t.createElement)("label",{className:"jio-creator-cover-empty"},(0,t.createElement)("input",{ref:S,type:"file",accept:"image/*",onChange:()=>{(async()=>{const e=S.current?.files?.[0];if(e&&g.media?.uploadUrl?.image){v(""),k(!0);try{const t=await d(g.media.uploadUrl.image,e,h),a=Number(t.attachment_id||t.id||0),r=String(t.url||"");if(!a||!r)throw new Error((0,c.__)("上传后无法读取图片。","jio"));n({featured:{type:"image",mediaId:a,data:{id:a,url:r}}})}catch(e){v(String(e?.Line truncated
|
||||
(()=>{"use strict";const e={n:t=>{const a=t&&t.__esModule?()=>t.default:()=>t;return e.d(a,{a}),a},d:(t,a)=>{for(var r in a)e.o(a,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:a[r]})},o:(e,t)=>Object.hasOwn(e,t)},t=window.React,a=window.wp.element,r=window.wp.blocks,n=window.wp.blockLibrary,o=window.wp.data,i=window.wp.blockEditor,c=window.wp.wordcount,l=window.wp.i18n;function s(e,t){const a=e&&(e.message||e.error)||(401===t?"登录状态已失效,请刷新页面重新登录。":"请求失败,请稍后重试。"),r=new Error(String(a));return r.status=t,r.code=e&&e.code?String(e.code):"",r.data=e,r}async function u(e){try{return await e.json()}catch(e){return null}}async function m(e,t,a){const r=await fetch(e,{method:"POST",credentials:"same-origin",headers:{"Content-Type":"application/json","X-WP-Nonce":a},body:JSON.stringify(t)}),n=await u(r);if(!r.ok)throw s(n,r.status);return n}async function d(e,t,a,r={}){const n=new FormData;n.append("file",t),Object.entries(r).forEach(([e,t])=>n.append(e,t));const o=await fetch(e,{method:"POST",credentials:"same-origin",headers:{"X-WP-Nonce":a},body:n}),i=await u(o);if(!o.ok)throw s(i,o.status);return i}const p=window.wp.apiFetch;var _=e.n(p);const f=window.wp.hooks,b=window.wp.compose,j=window.wp.components,y=[{key:"half",label:(0,l.__)("半屏","jio"),className:"jio-img-w--half"},{key:"medium",label:(0,l.__)("中等","jio"),className:"jio-img-w--medium"},{key:"wide",label:(0,l.__)("通栏","jio"),className:"jio-img-w--wide"},{key:"full",label:(0,l.__)("全宽","jio"),className:"jio-img-w--full"}];function E(e){return a=>{if("core/image"!==a.name)return(0,t.createElement)(e,{...a});const r=String(a.attributes?.className||""),n=function(e){const t=y.find(t=>(e||"").split(/\s+/).includes(t.className));return t?t.key:"wide"}(r);return(0,t.createElement)(t.Fragment,null,(0,t.createElement)(e,{...a}),(0,t.createElement)(i.BlockControls,{group:"inline"},(0,t.createElement)(j.ToolbarGroup,{controls:y.map(e=>({title:e.label,children:e.label,isActive:n===e.key,onClick:()=>(e=>{const t=r.split(/\s+/).filter(e=>e&&!y.some(t=>t.className===e));a.setAttributes({className:[...t,e.className].join(" ")})})(e)}))})))}}const g=window.jioCreatorStudio||{},h=g.nonce||"";function w(e){const{draft:r,setDraft:n,capabilities:o,limits:i,taxonomy:c,isPublishedEdit:s,onClose:u,onSaveDraft:m,onSubmit:p,privateStorage:_,privateUploads:f,saving:b,captcha:j}=e,[y,E]=(0,a.useState)(""),[w,v]=(0,a.useState)(""),[N,k]=(0,a.useState)(!1),C=(0,a.useRef)(null),S=(0,a.useRef)(null),x=(0,a.useRef)(null);(0,a.useEffect)(()=>{const e=x.current,t=document.getElementById("jio-creator-captcha");if(!e||!t)return;const a=t.parentElement;return e.appendChild(t),t.removeAttribute("hidden"),()=>{a&&(a.appendChild(t),t.setAttribute("hidden",""))}},[]);const I=(0,a.useMemo)(()=>(r.title||"").trim().length,[r.title])>=Number(i.titleMin||1),L=()=>{const e=y.trim();if(!e)return;const t=e.split(/[,,\n]+/u).map(e=>e.trim()).filter(Boolean);n({tags:Array.from(new Set([...r.tags,...t]))}),E("")};return(0,t.createElement)("div",{className:"jio-creator-drawer",role:"dialog","aria-modal":"true","aria-labelledby":"jio-creator-publish-title"},(0,t.createElement)("button",{type:"button",className:"jio-creator-drawer__backdrop","aria-label":(0,l.__)("关闭","jio"),onClick:u}),(0,t.createElement)("div",{className:"jio-creator-drawer__panel"},(0,t.createElement)("header",{className:"jio-creator-drawer__head"},(0,t.createElement)("h2",{id:"jio-creator-publish-title"},s?(0,l.__)("提交改稿","jio"):(0,l.__)("发布文章","jio")),(0,t.createElement)("button",{type:"button",className:"jio-creator-drawer__close",onClick:u,"aria-label":(0,l.__)("关闭","jio")},(0,t.createElement)("i",{className:"fa fas fa-times","aria-hidden":"true"}))),(0,t.createElement)("div",{className:"jio-creator-drawer__body"},s&&(0,t.createElement)("p",{className:"jio-creator-drawer__note"},(0,l.__)("审核通过前,读者看到的仍然是当前线上版本。","jio")),(0,t.createElement)("section",{className:"jio-creator-field"},(0,t.createElement)("h3",null,(0,l.__)("封面","jio")),r.featured?.data?.url?(0,t.createElement)("div",{className:"jio-creator-cover"},(0,t.createElement)("img",{src:String(r.featured.data.url),alt:""}),(0,t.createElement)("button",{type:"button",onClick:()=>n({featured:{type:"",data:{}}}),"aria-label":(0,l.__)("移除封面","jio")},(0,t.createElement)("i",{className:"fa fas fa-times","aria-hidden":"true"}))):o.canUploadImage?(0,t.createElement)("label",{className:"jio-creator-cover-empty"},(0,t.createElement)("input",{ref:C,type:"file",accept:"image/*",onChange:()=>{(async()=>{const e=C.current?.files?.[0];if(e&&g.media?.uploadUrl?.image){v(""),k(!0);try{const t=await d(g.media.uploadUrl.image,e,h),a=Number(t.attachment_id||t.id||0),r=String(t.url||"");if(!a||!r)throw new Error((0,l.__)("上传后无法读取图片。","jio"));n({featured:{type:"image",mediaId:a,data:{id:a,url:r}}})}catch(e){v(String(e?.Line truncated
|
||||
@@ -3,6 +3,7 @@
|
||||
* 并记录无法捕获的 promise 拒绝。
|
||||
*/
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const puppeteer = require('puppeteer-core');
|
||||
|
||||
const CANDIDATES = [
|
||||
@@ -21,7 +22,31 @@ const URL = process.argv[2] || 'https://localhost/jio-preview.html';
|
||||
args: ['--disable-gpu', '--no-sandbox'],
|
||||
});
|
||||
const page = await browser.newPage();
|
||||
await page.setViewport({ width: 1440, height: 1000 });
|
||||
await page.setViewport({ width: 1440, height: 560 });
|
||||
|
||||
if (process.env.JIO_USE_WORKTREE_BUILD === '1') {
|
||||
await page.setRequestInterception(true);
|
||||
page.on('request', (request) => {
|
||||
const pathname = new URL(request.url()).pathname;
|
||||
const jsSuffix = '/assets/creator-studio/build/index.js';
|
||||
const cssSuffix = '/assets/creator-studio/build/index.css';
|
||||
if (pathname.endsWith(jsSuffix)) {
|
||||
request.respond({
|
||||
status: 200,
|
||||
contentType: 'application/javascript',
|
||||
body: fs.readFileSync(path.join(__dirname, '../build/index.js')),
|
||||
});
|
||||
} else if (pathname.endsWith(cssSuffix)) {
|
||||
request.respond({
|
||||
status: 200,
|
||||
contentType: 'text/css',
|
||||
body: fs.readFileSync(path.join(__dirname, '../build/index.css')),
|
||||
});
|
||||
} else {
|
||||
request.continue();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const errors = [];
|
||||
page.on('pageerror', (e) => errors.push('pageerror: ' + e.message));
|
||||
@@ -33,8 +58,12 @@ const URL = process.argv[2] || 'https://localhost/jio-preview.html';
|
||||
blocks: document.querySelectorAll('.block-editor-block-list__block').length,
|
||||
hasInsertBtn: !!document.querySelector('.jio-creator__insert'),
|
||||
hasCover: !!document.querySelector('.jio-creator__cover'),
|
||||
toolbarTop: document.querySelector('.jio-creator__bar')?.getBoundingClientRect().top,
|
||||
}));
|
||||
console.log('BEFORE ' + JSON.stringify(before));
|
||||
await page.evaluate(() => window.scrollTo(0, 700));
|
||||
await new Promise((r) => setTimeout(r, 300));
|
||||
const toolbarAfterScroll = await page.$eval('.jio-creator__bar', (el) => el.getBoundingClientRect().top);
|
||||
console.log('BEFORE ' + JSON.stringify({ ...before, toolbarAfterScroll }));
|
||||
|
||||
// 打开插入面板
|
||||
await page.click('.jio-creator__insert');
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
/**
|
||||
* 工作台级撤销/重做历史。
|
||||
*
|
||||
* BlockEditorProvider 独立运行时不带 core/editor 的 post history store,
|
||||
* 因此为标题、摘要、区块树保存快照。输入以 450ms 合并成一条历史记录,
|
||||
* 避免每个字符都占用一步。
|
||||
*
|
||||
* @package Jio
|
||||
*/
|
||||
|
||||
import { useCallback, useEffect, useRef, useState } from '@wordpress/element';
|
||||
|
||||
type Snapshot<T> = { value: T; signature: string };
|
||||
type HistoryState<T> = { past: Snapshot<T>[]; future: Snapshot<T>[]; present: Snapshot<T>; pending: T | null };
|
||||
|
||||
type Options<T> = {
|
||||
initial: T;
|
||||
onRestore: (value: T) => void;
|
||||
limit?: number;
|
||||
mergeDelay?: number;
|
||||
};
|
||||
|
||||
function copy<T>(value: T): T {
|
||||
return JSON.parse(JSON.stringify(value)) as T;
|
||||
}
|
||||
|
||||
function snapshot<T>(value: T): Snapshot<T> {
|
||||
const cloned = copy(value);
|
||||
return { value: cloned, signature: JSON.stringify(cloned) };
|
||||
}
|
||||
|
||||
export function useEditorHistory<T>({ initial, onRestore, limit = 80, mergeDelay = 450 }: Options<T>) {
|
||||
const state = useRef<HistoryState<T>>({
|
||||
past: [],
|
||||
future: [],
|
||||
present: snapshot(initial),
|
||||
pending: null,
|
||||
});
|
||||
const timer = useRef<number | null>(null);
|
||||
const restoreRef = useRef(onRestore);
|
||||
restoreRef.current = onRestore;
|
||||
const [availability, setAvailability] = useState({ canUndo: false, canRedo: false });
|
||||
|
||||
const updateAvailability = useCallback(() => {
|
||||
setAvailability({
|
||||
canUndo: state.current.past.length > 0 || state.current.pending !== null,
|
||||
canRedo: state.current.future.length > 0,
|
||||
});
|
||||
}, []);
|
||||
|
||||
const flush = useCallback(() => {
|
||||
if (timer.current !== null) {
|
||||
window.clearTimeout(timer.current);
|
||||
timer.current = null;
|
||||
}
|
||||
const current = state.current;
|
||||
if (current.pending === null) {
|
||||
return;
|
||||
}
|
||||
const next = snapshot(current.pending);
|
||||
current.pending = null;
|
||||
if (next.signature === current.present.signature) {
|
||||
updateAvailability();
|
||||
return;
|
||||
}
|
||||
current.past = [...current.past, current.present].slice(-limit);
|
||||
current.present = next;
|
||||
current.future = [];
|
||||
updateAvailability();
|
||||
}, [limit, updateAvailability]);
|
||||
|
||||
const record = useCallback(
|
||||
(value: T) => {
|
||||
state.current.pending = copy(value);
|
||||
if (timer.current !== null) {
|
||||
window.clearTimeout(timer.current);
|
||||
}
|
||||
timer.current = window.setTimeout(flush, mergeDelay);
|
||||
updateAvailability();
|
||||
},
|
||||
[flush, mergeDelay, updateAvailability]
|
||||
);
|
||||
|
||||
const undo = useCallback(() => {
|
||||
flush();
|
||||
const current = state.current;
|
||||
if (!current.past.length) {
|
||||
return false;
|
||||
}
|
||||
const previous = current.past[current.past.length - 1];
|
||||
current.past = current.past.slice(0, -1);
|
||||
current.future = [current.present, ...current.future].slice(0, limit);
|
||||
current.present = previous;
|
||||
restoreRef.current(copy(previous.value));
|
||||
updateAvailability();
|
||||
return true;
|
||||
}, [flush, limit, updateAvailability]);
|
||||
|
||||
const redo = useCallback(() => {
|
||||
flush();
|
||||
const current = state.current;
|
||||
if (!current.future.length) {
|
||||
return false;
|
||||
}
|
||||
const next = current.future[0];
|
||||
current.future = current.future.slice(1);
|
||||
current.past = [...current.past, current.present].slice(-limit);
|
||||
current.present = next;
|
||||
restoreRef.current(copy(next.value));
|
||||
updateAvailability();
|
||||
return true;
|
||||
}, [flush, limit, updateAvailability]);
|
||||
|
||||
useEffect(() => () => {
|
||||
if (timer.current !== null) {
|
||||
window.clearTimeout(timer.current);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return { ...availability, record, undo, redo, flush };
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import { InsertPanel, type InsertItem } from './panels/insert-panel';
|
||||
import { buildInsertGroups } from './utils/insert-items';
|
||||
import { SaveState, type SaveStateValue } from './components/save-state';
|
||||
import { useAutosave } from './hooks/use-autosave';
|
||||
import { useEditorHistory } from './hooks/use-editor-history';
|
||||
import { useLocalRecovery } from './hooks/use-local-recovery';
|
||||
import { normalizePayForRequest } from './utils/pay';
|
||||
import './styles/studio.scss';
|
||||
@@ -109,12 +110,55 @@ function initialDraft(): Draft {
|
||||
function Studio(): JSX.Element {
|
||||
const [draft, setDraft] = useState<Draft>(initialDraft);
|
||||
const [blocks, setBlocks] = useState(() => parse(draft.content || ''));
|
||||
const [saveState, setSaveState] = useState<SaveStateValue>({ state: 'ready', at: '' });
|
||||
const blocksRef = useRef(blocks);
|
||||
blocksRef.current = blocks;
|
||||
const draftRef = useRef(draft);
|
||||
draftRef.current = draft;
|
||||
const [saveState, setSaveState] = useState<SaveStateValue>(() => ({
|
||||
state: bootstrap.sourceId || bootstrap.versionId || bootstrap.content ? 'saved' : 'ready',
|
||||
at: '',
|
||||
}));
|
||||
const [drawerOpen, setDrawerOpen] = useState(false);
|
||||
const [insertOpen, setInsertOpen] = useState(false);
|
||||
const [notice, setNotice] = useState<{ tone: 'ok' | 'error'; text: string } | null>(null);
|
||||
const [recovery, setRecovery] = useState<null | { savedAt: string }>(null);
|
||||
const dirtyRef = useRef(false);
|
||||
const restoreBlockSignatureRef = useRef('');
|
||||
|
||||
const restoreHistory = useCallback((value: { blocks: any[]; title: string; excerpt: string }) => {
|
||||
dirtyRef.current = true;
|
||||
blocksRef.current = value.blocks;
|
||||
restoreBlockSignatureRef.current = serialize(value.blocks);
|
||||
setBlocks(value.blocks);
|
||||
const next = { ...draftRef.current, title: value.title, excerpt: value.excerpt };
|
||||
draftRef.current = next;
|
||||
setDraft(next);
|
||||
}, []);
|
||||
const history = useEditorHistory({
|
||||
initial: { blocks, title: draft.title, excerpt: draft.excerpt },
|
||||
onRestore: restoreHistory,
|
||||
});
|
||||
const historyRef = useRef(history);
|
||||
historyRef.current = history;
|
||||
|
||||
const undo = useCallback(() => historyRef.current.undo(), []);
|
||||
const redo = useCallback(() => historyRef.current.redo(), []);
|
||||
|
||||
useEffect(() => {
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
if (!(event.metaKey || event.ctrlKey) || event.key.toLowerCase() !== 'z') {
|
||||
return;
|
||||
}
|
||||
const target = event.target as HTMLElement | null;
|
||||
if (target?.closest('input, textarea, [contenteditable="true"]')) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
event.shiftKey ? redo() : undo();
|
||||
};
|
||||
window.addEventListener('keydown', onKeyDown);
|
||||
return () => window.removeEventListener('keydown', onKeyDown);
|
||||
}, [redo, undo]);
|
||||
|
||||
const capabilities = bootstrap.capabilities || {};
|
||||
const limits: Limits = {
|
||||
@@ -130,11 +174,32 @@ function Studio(): JSX.Element {
|
||||
|
||||
const patchDraft = useCallback((patch: Partial<Draft>) => {
|
||||
dirtyRef.current = true;
|
||||
setDraft((current) => ({ ...current, ...patch }));
|
||||
const next = { ...draftRef.current, ...patch };
|
||||
draftRef.current = next;
|
||||
if ('title' in patch || 'excerpt' in patch) {
|
||||
historyRef.current.record({
|
||||
blocks: blocksRef.current,
|
||||
title: next.title,
|
||||
excerpt: next.excerpt,
|
||||
});
|
||||
}
|
||||
setDraft(next);
|
||||
}, []);
|
||||
|
||||
const onChangeBlocks = useCallback((next: any[]) => {
|
||||
dirtyRef.current = true;
|
||||
blocksRef.current = next;
|
||||
const signature = serialize(next);
|
||||
if (restoreBlockSignatureRef.current !== '' && signature === restoreBlockSignatureRef.current) {
|
||||
restoreBlockSignatureRef.current = '';
|
||||
setBlocks(next);
|
||||
return;
|
||||
}
|
||||
historyRef.current.record({
|
||||
blocks: next,
|
||||
title: draftRef.current.title,
|
||||
excerpt: draftRef.current.excerpt,
|
||||
});
|
||||
setBlocks(next);
|
||||
}, []);
|
||||
|
||||
@@ -183,7 +248,9 @@ function Studio(): JSX.Element {
|
||||
const versionId = Number(response.version_id || draft.versionId || 0);
|
||||
const rev = Number(response.rev || response.versionRev || draft.rev || 0);
|
||||
dirtyRef.current = false;
|
||||
setDraft((current) => ({ ...current, postId: savedId, versionId, rev }));
|
||||
const updated = { ...draftRef.current, postId: savedId, versionId, rev };
|
||||
draftRef.current = updated;
|
||||
setDraft(updated);
|
||||
setSaveState({ state: 'saved', at: new Date().toLocaleTimeString() });
|
||||
return true;
|
||||
} catch (error: any) {
|
||||
@@ -220,7 +287,13 @@ function Studio(): JSX.Element {
|
||||
}
|
||||
const response = await apiPost(
|
||||
bootstrap.creator.submitUrl,
|
||||
buildPayload({ publish_now: publishNow, captcha_proof: captchaProof }),
|
||||
buildPayload({
|
||||
post_id: draftRef.current.postId,
|
||||
version_id: draftRef.current.versionId,
|
||||
rev: draftRef.current.rev,
|
||||
publish_now: publishNow,
|
||||
captcha_proof: captchaProof,
|
||||
}),
|
||||
nonce
|
||||
);
|
||||
dirtyRef.current = false;
|
||||
@@ -249,7 +322,7 @@ function Studio(): JSX.Element {
|
||||
try {
|
||||
const response = await apiPost(
|
||||
bootstrap.creator.previewUrl,
|
||||
{ post_id: draft.postId, version_id: draft.versionId },
|
||||
{ post_id: draftRef.current.postId, version_id: draftRef.current.versionId },
|
||||
nonce
|
||||
);
|
||||
if (response.url) {
|
||||
@@ -315,12 +388,10 @@ function Studio(): JSX.Element {
|
||||
if (raw) {
|
||||
const parsed = JSON.parse(raw);
|
||||
if (typeof parsed.title === 'string') {
|
||||
dirtyRef.current = true;
|
||||
setDraft((current) => ({ ...current, title: parsed.title }));
|
||||
patchDraft({ title: parsed.title });
|
||||
}
|
||||
if (typeof parsed.content === 'string') {
|
||||
dirtyRef.current = true;
|
||||
setBlocks(parse(parsed.content));
|
||||
onChangeBlocks(parse(parsed.content));
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -340,28 +411,59 @@ function Studio(): JSX.Element {
|
||||
(item: InsertItem) => {
|
||||
setInsertOpen(false);
|
||||
dirtyRef.current = true;
|
||||
setBlocks((current: any[]) => {
|
||||
// 空文档时直接替换初始的空白段落,避免留下一个空行。
|
||||
const next = [...current];
|
||||
if (next.length === 1 && next[0].name === 'core/paragraph' && !next[0].attributes?.content) {
|
||||
return [createBlock(item.name, item.attributes || {})];
|
||||
}
|
||||
return [...next, createBlock(item.name, item.attributes || {})];
|
||||
const current = [...blocksRef.current];
|
||||
let next: any[];
|
||||
// 空文档时直接替换初始的空白段落,避免留下一个空行。
|
||||
if (current.length === 1 && current[0].name === 'core/paragraph' && !current[0].attributes?.content) {
|
||||
next = [createBlock(item.name, item.attributes || {})];
|
||||
} else {
|
||||
next = [...current, createBlock(item.name, item.attributes || {})];
|
||||
}
|
||||
blocksRef.current = next;
|
||||
historyRef.current.record({
|
||||
blocks: next,
|
||||
title: draftRef.current.title,
|
||||
excerpt: draftRef.current.excerpt,
|
||||
});
|
||||
setBlocks(next);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="jio-creator" data-status={bootstrap.status || ''} data-blocks={selectedBlockCount}> <header className="jio-creator__bar">
|
||||
<a className="jio-creator__back" href={bootstrap.urls?.center || '/'}>
|
||||
<i className="fa fas fa-angle-left" aria-hidden="true" />
|
||||
<span>{__('返回', 'jio')}</span>
|
||||
</a>
|
||||
<div className="jio-creator__brand">Jio Creator Studio</div>
|
||||
<div className="jio-creator__actions">
|
||||
<div className="jio-creator" data-status={bootstrap.status || ''} data-blocks={selectedBlockCount}>
|
||||
<header className="jio-creator__bar">
|
||||
<div className="jio-creator__bar-left">
|
||||
<a className="jio-creator__back" href={bootstrap.urls?.center || '/'} aria-label={__('返回作者中心', 'jio')}>
|
||||
<i className="fa fas fa-angle-left" aria-hidden="true" />
|
||||
</a>
|
||||
<span className="jio-creator__bar-brand">Jio</span>
|
||||
<span className="jio-creator__bar-divider" aria-hidden="true" />
|
||||
<span className="jio-creator__bar-mode">
|
||||
{isPublishedEdit ? __('修改文章', 'jio') : __('写文章', 'jio')}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="jio-creator__bar-center">
|
||||
<span className="jio-creator__doc-title">
|
||||
{draft.title.trim() || __('无标题文章', 'jio')}
|
||||
</span>
|
||||
<SaveState value={saveState} />
|
||||
<button type="button" className="jio-creator__ghost" onClick={() => void preview()}>
|
||||
</div>
|
||||
|
||||
<div className="jio-creator__bar-actions">
|
||||
<div className="jio-creator__history" role="group" aria-label={__('编辑历史', 'jio')}>
|
||||
<button type="button" className="jio-creator__icon-btn" onClick={() => history.undo()} disabled={!history.canUndo} aria-label={__('撤销', 'jio')} title={__('撤销', 'jio')}>
|
||||
<i className="fa fas fa-undo" aria-hidden="true" />
|
||||
</button>
|
||||
<button type="button" className="jio-creator__icon-btn" onClick={() => history.redo()} disabled={!history.canRedo} aria-label={__('重做', 'jio')} title={__('重做', 'jio')}>
|
||||
<i className="fa fas fa-redo" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
<button type="button" className="jio-creator__ghost jio-creator__save-draft" onClick={() => void save(false)}>
|
||||
{__('保存草稿', 'jio')}
|
||||
</button>
|
||||
<button type="button" className="jio-creator__ghost jio-creator__preview" onClick={() => void preview()}>
|
||||
{__('预览', 'jio')}
|
||||
</button>
|
||||
<button type="button" className="jio-creator__primary" onClick={() => setDrawerOpen(true)}>
|
||||
|
||||
@@ -21,6 +21,12 @@ $warn: #b4573f;
|
||||
background: $paper;
|
||||
}
|
||||
|
||||
/* 主题固定导航会给 body.nav-fixed 预留 88px;工作台隐藏了导航,不应保留空位。 */
|
||||
body.jio-page-submit.nav-fixed,
|
||||
body.jio-page-submit.has-static-nav {
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
|
||||
.jio-page-submit .header,
|
||||
.jio-page-submit .footer,
|
||||
.jio-page-submit .float-btn,
|
||||
@@ -50,6 +56,8 @@ $warn: #b4573f;
|
||||
|
||||
min-height: 100vh;
|
||||
min-height: 100dvh;
|
||||
/* 固定顶栏属于工作台 chrome,不随正文一起滚动。 */
|
||||
padding-top: var(--jc-bar);
|
||||
background: var(--jc-paper);
|
||||
color: var(--jc-ink);
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;
|
||||
@@ -68,43 +76,192 @@ $warn: #b4573f;
|
||||
}
|
||||
|
||||
.jio-creator__bar {
|
||||
position: sticky;
|
||||
z-index: 30;
|
||||
position: fixed;
|
||||
z-index: 300;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
height: var(--jc-bar);
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding: 0 clamp(16px, 4vw, 40px);
|
||||
justify-content: space-between;
|
||||
gap: 20px;
|
||||
padding: 0 clamp(16px, 3vw, 34px);
|
||||
border-bottom: 1px solid var(--jc-line);
|
||||
background: rgba(250, 251, 250, 0.92);
|
||||
backdrop-filter: blur(10px);
|
||||
background: rgba(250, 251, 250, 0.97);
|
||||
backdrop-filter: blur(14px);
|
||||
box-shadow: 0 2px 8px rgba(27, 46, 39, 0.035);
|
||||
}
|
||||
|
||||
.jio-creator__bar-left,
|
||||
.jio-creator__bar-center,
|
||||
.jio-creator__bar-actions {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.jio-creator__bar-left {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.jio-creator__bar-center {
|
||||
flex: 1 1 auto;
|
||||
justify-content: center;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.jio-creator__doc-title {
|
||||
max-width: min(32vw, 360px);
|
||||
overflow: hidden;
|
||||
color: #65736d;
|
||||
font-size: 13px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.jio-creator__bar-actions {
|
||||
flex: 0 0 auto;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.jio-creator__back {
|
||||
display: inline-flex;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
justify-content: center;
|
||||
border-radius: 7px;
|
||||
color: var(--jc-ink-soft);
|
||||
text-decoration: none;
|
||||
|
||||
&:hover,
|
||||
&:focus-visible {
|
||||
background: #edf3f0;
|
||||
color: var(--jc-accent);
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.jio-creator__brand {
|
||||
margin-right: auto;
|
||||
color: var(--jc-ink-soft);
|
||||
font-size: 13px;
|
||||
letter-spacing: 0.06em;
|
||||
.jio-creator__bar-brand {
|
||||
color: var(--jc-accent-strong);
|
||||
font-family: "Songti SC", "Noto Serif SC", "Source Han Serif SC", serif;
|
||||
font-size: 17px;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.04em;
|
||||
}
|
||||
|
||||
.jio-creator__actions {
|
||||
display: flex;
|
||||
.jio-creator__bar-divider {
|
||||
width: 1px;
|
||||
height: 18px;
|
||||
background: var(--jc-line);
|
||||
}
|
||||
|
||||
.jio-creator__bar-mode {
|
||||
color: var(--jc-ink-soft);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.jio-creator__doc-title {
|
||||
max-width: min(32vw, 360px);
|
||||
overflow: hidden;
|
||||
color: #65736d;
|
||||
font-size: 13px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.jio-creator__history {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
gap: 2px;
|
||||
padding-right: 8px;
|
||||
border-right: 1px solid var(--jc-line);
|
||||
}
|
||||
|
||||
.jio-creator__icon-btn {
|
||||
display: inline-flex;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 0;
|
||||
border-radius: 7px;
|
||||
background: transparent;
|
||||
color: var(--jc-ink-soft);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover:not(:disabled),
|
||||
&:focus-visible:not(:disabled) {
|
||||
background: #edf3f0;
|
||||
color: var(--jc-accent-strong);
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
color: #c7d0cc;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
.jio-creator__save-draft {
|
||||
border-color: var(--jc-line);
|
||||
background: #fff;
|
||||
color: var(--jc-ink-soft);
|
||||
}
|
||||
|
||||
.jio-creator__save-draft:hover,
|
||||
.jio-creator__save-draft:focus-visible {
|
||||
border-color: #b7cdc3;
|
||||
background: #f7faf8;
|
||||
color: var(--jc-accent-strong);
|
||||
}
|
||||
|
||||
.jio-creator__icon-btn {
|
||||
display: inline-flex;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 0;
|
||||
border-radius: 7px;
|
||||
background: transparent;
|
||||
color: var(--jc-ink-soft);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover:not(:disabled),
|
||||
&:focus-visible:not(:disabled) {
|
||||
background: #edf3f0;
|
||||
color: var(--jc-accent-strong);
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
color: #c7d0cc;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
.jio-creator__history {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
padding-right: 8px;
|
||||
border-right: 1px solid var(--jc-line);
|
||||
}
|
||||
|
||||
.jio-creator__save-draft {
|
||||
border-color: var(--jc-line);
|
||||
background: #fff;
|
||||
color: var(--jc-ink-soft);
|
||||
}
|
||||
|
||||
.jio-creator__save-draft:hover,
|
||||
.jio-creator__save-draft:focus-visible {
|
||||
border-color: #b7cdc3;
|
||||
background: #f7faf8;
|
||||
color: var(--jc-accent-strong);
|
||||
}
|
||||
|
||||
.jio-creator__save {
|
||||
@@ -1136,16 +1293,61 @@ $warn: #b4573f;
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.jio-creator {
|
||||
--jc-bar: 54px;
|
||||
}
|
||||
|
||||
.jio-creator__brand {
|
||||
display: none;
|
||||
--jc-bar: 56px;
|
||||
}
|
||||
|
||||
.jio-creator__bar {
|
||||
gap: 10px;
|
||||
gap: 8px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.jio-creator__bar-left,
|
||||
.jio-creator__bar-center,
|
||||
.jio-creator__bar-actions {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.jio-creator__bar-brand {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.jio-creator__bar-mode,
|
||||
.jio-creator__bar-divider,
|
||||
.jio-creator__doc-title,
|
||||
.jio-creator__save-draft {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.jio-creator__bar-center {
|
||||
flex: 0 0 auto;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.jio-creator__bar-actions {
|
||||
flex: 1 1 auto;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.jio-creator__icon-btn {
|
||||
width: 28px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.jio-creator__history {
|
||||
gap: 0;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.jio-creator__preview {
|
||||
min-height: 32px;
|
||||
padding: 0 9px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.jio-creator__primary {
|
||||
min-height: 32px;
|
||||
padding: 0 12px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.jio-creator__save time {
|
||||
|
||||
Reference in new issue
Block a user