diff --git a/README.md b/README.md
index 7314df9..0b29cca 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
###### ~DIV&搬运工:只是站在巨人的肩膀上~MINI-VLOOK: Vlook的简化版本^2022.08.27^`#对外公开|1.10.0#(green)` **路人二***COPYRIGHT © 2022 路人二. All Right Reserved*
-< `#作者|路人二#(theme1)` | `#对外公开|1.6.0#(green!)` | `#日期|2022-07-03#(blue)` ]
+< `#作者|路人二#(theme1)` | `#对外公开|2.0.0#(green!)` | `#日期|2022-09-22#(blue)` ]
---
@@ -10,7 +10,7 @@
---
-* V1.0.0
+* V2.0.0
* 新的配色方案
* 文字竖排块属性**f=sp**
@@ -45,10 +45,21 @@
* 块属性**f=chk**可关闭
* 在行内代码块中输入(可以把x换成一个空格)
- * +[x]+
+ * +[ ]+
* +[x]|标签+
+ * +[ ]|标签+(red)
* +[x]|标签+(red)
* +[x]|标签+(red!)
+ * 微章复选框
+
+ * 块属性**f=chk-wz**可关闭
+ * 在行内代码块中输入(可以把x换成一个空格)
+
+ * #[ ]#+
+ * #[x]|标签#
+ * #[ ]|标签#(red)
+ * #[x]|标签#(red)
+ * #[x]|标签#(red!)
* 命令
* `@@kanban` 和 `@@map` 快速生成`#看板#`和`#导图#`
@@ -124,7 +135,7 @@
`代码块解析功能` 开关方式: Ctrl + Alt + 0 或者思源笔记左上角的按钮![image.png](assets/image-20220429170848-hmkfeh0.png)关闭或开启渲染。
-如果是通过左上角按钮开启渲染,在文档内按下任意键,立即渲染整篇文档。
+在文档内按下 Alt + 0 ,立即渲染整篇文档。(改变!!2.0.0)
已渲染的需要在对应行输入字符,才会取消渲染效果。
@@ -405,9 +416,9 @@
你好世界
-#### 批注
+#### 评论
-Dark+ 主题的批注功能。感谢大佬!
+Dark+ 主题的评论功能。感谢大佬!
## 参考/感谢
diff --git a/app/comment/comment.css b/app/comment/comment.css
index 4bca6a0..40ef06d 100644
--- a/app/comment/comment.css
+++ b/app/comment/comment.css
@@ -1,5 +1,5 @@
/* Common */
-strong[style="blank"]{
+strong[style="blank"] {
background-color: var(--b3-theme-on-background);
color: var(--b3-theme-on-background);
font-weight: normal;
@@ -8,12 +8,15 @@ strong[style="blank"]{
transition: all 300ms ease-in-out;
/* cursor: pointer; */
}
-strong[style="blank"]:hover{
+
+strong[style="blank"]:hover {
color: var(--b3-theme-on-background);
background-color: var(--b3-theme-background);
transition: all 300ms ease-in-out;
}
-.lz-overlay, .lz-overlay-black{
+
+.lz-overlay,
+.lz-overlay-black {
z-index: 999;
position: fixed;
left: 0;
@@ -25,17 +28,19 @@ strong[style="blank"]:hover{
display: none;
opacity: 1;
}
-.lz-overlay-black{
+
+.lz-overlay-black {
background-color: rgba(0, 0, 0, 0.2);
}
/* flex card layout */
-.protyle-wysiwyg.card{
+.protyle-wysiwyg.card {
display: flex;
flex-wrap: wrap;
/* background-color: var(--b3-theme-surface); */
}
-.protyle-wysiwyg.card div[data-node-id]{
+
+.protyle-wysiwyg.card div[data-node-id] {
/* box-shadow: 0 0 2px rgba(0, 0, 0, 0.3); */
margin: 5px;
padding: 10px;
@@ -46,18 +51,21 @@ strong[style="blank"]:hover{
/* Inline comment */
-strong[style*="quote"]{
+strong[style*="quote"],
+span[data-type~=strong][style*="quote"] {
border-bottom: 2px solid rgb(255, 212, 0);
- background-color: rgba(255,212,0,0.16);
+ background-color: rgba(255, 212, 0, 0.16);
padding-bottom: 1px;
font-weight: normal;
}
-strong[style*="quote"]:hover{
+
+strong[style*="quote"]:hover,
+span[data-type~=strong][style*="quote"]:hover {
cursor: pointer;
/* background-color: rgb(255, 212, 0); */
}
-#lz-comment-box{
+#lz-comment-box {
font-size: 15px;
display: none;
position: fixed;
@@ -71,82 +79,98 @@ strong[style*="quote"]:hover{
border-radius: 3px;
padding: 10px 0;
}
-#lz-comment-box a{
+
+#lz-comment-box a {
border-bottom: 1px solid var(--b3-theme-primary);
}
-#lz-comment-box a:hover{
+
+#lz-comment-box a:hover {
text-decoration: none;
}
-#lz-comment-box .list{
+
+#lz-comment-box .list {
width: 100%;
max-height: 370px;
overflow-y: scroll;
}
-#lz-comment-box .list::-webkit-scrollbar{
+
+#lz-comment-box .list::-webkit-scrollbar {
width: 3px;
border-radius: 5px;
}
-#lz-comment-box .list .quote{
+
+#lz-comment-box .list .quote {
border-left: 3px solid #F9DE6D;
padding: 2px 0 2px 8px;
margin: 10px 15px;
color: var(--b3-theme-on-surface);
}
-#lz-comment-box .quote .delete-quote{
+
+#lz-comment-box .quote .delete-quote {
color: #f56c6c;
font-size: 0.8em;
margin-left: 5px;
opacity: 0;
}
-#lz-comment-box .quote:hover .delete-quote{
+
+#lz-comment-box .quote:hover .delete-quote {
opacity: 1;
cursor: pointer;
}
-#lz-comment-box .list-item{
+
+#lz-comment-box .list-item {
/* width: calc(100% - 30px); */
padding: 0px 15px 5px 15px;
border-bottom: 1px solid var(--b3-border-color);
}
-#lz-comment-box .list-item:last-child{
+
+#lz-comment-box .list-item:last-child {
border-bottom: none;
}
-#lz-comment-box .list-item .header{
+
+#lz-comment-box .list-item .header {
margin-top: 12px;
display: flex;
justify-content: space-between;
align-items: center;
}
-#lz-comment-box .list-item .date{
+
+#lz-comment-box .list-item .date {
color: #999;
font-size: 0.9em;
}
-#lz-comment-box .list-item .actions{
+#lz-comment-box .list-item .actions {
display: flex;
}
-#lz-comment-box .list-item .delete-comment{
+
+#lz-comment-box .list-item .delete-comment {
color: #f56c6c;
font-size: 0.8em;
opacity: 0;
margin-left: 8px;
}
-#lz-comment-box .list-item .actions a{
+
+#lz-comment-box .list-item .actions a {
border: none;
}
-#lz-comment-box .list-item:hover .delete-comment{
+
+#lz-comment-box .list-item:hover .delete-comment {
opacity: 1;
cursor: pointer;
}
-#lz-comment-box .list-item .comment{
+#lz-comment-box .list-item .comment {
margin: 5px 0;
color: var(--b3-theme-on-background);
}
-#lz-comment-box .add{
+
+#lz-comment-box .add {
display: flex;
- padding: 0 15px ;
+ padding: 0 15px;
}
-#lz-comment-box .add .input{
+
+#lz-comment-box .add .input {
min-height: 30px;
background-color: var(--b3-theme-surface);
padding: 4px 5px;
@@ -156,13 +180,15 @@ strong[style*="quote"]:hover{
outline: none;
flex-grow: 1;
}
-#lz-comment-box .input:empty:before{
+
+#lz-comment-box .input:empty:before {
content: attr(placeholder);
/* content: 'hello'; */
- color:var(--b3-theme-on-surface);
+ color: var(--b3-theme-on-surface);
opacity: 0.4;
}
-#lz-comment-box .add .btn{
+
+#lz-comment-box .add .btn {
flex-shrink: 0;
height: 30px;
line-height: 30px;
@@ -177,7 +203,7 @@ strong[style*="quote"]:hover{
/* Toolbar */
-#lz-toolbar{
+#lz-toolbar {
font-size: 15px;
position: fixed;
right: 60px;
@@ -191,22 +217,27 @@ strong[style*="quote"]:hover{
padding: 5px 10px;
z-index: 99;
}
-#lz-toolbar.close i:not(.closeBtn){
+
+#lz-toolbar.close i:not(.closeBtn) {
display: none;
}
-#lz-toolbar .toolbar-btn{
- display:inline-block;
- font-size:20px;
- padding:5px;
+
+#lz-toolbar .toolbar-btn {
+ display: inline-block;
+ font-size: 20px;
+ padding: 5px;
margin: 0 5px;
- cursor:pointer;
+ cursor: pointer;
}
-#lz-toolbar .toolbar-btn:hover,#lz-toolbar .toolbar-btn.show{
+
+#lz-toolbar .toolbar-btn:hover,
+#lz-toolbar .toolbar-btn.show {
color: var(--b3-theme-primary);
/* background-color: #efefef; */
}
-#lz-toolbar .menu, #lz-toolbar .submenu{
+#lz-toolbar .menu,
+#lz-toolbar .submenu {
position: fixed;
padding: 10px 0px;
border-radius: 4px;
@@ -219,19 +250,23 @@ strong[style*="quote"]:hover{
max-width: 150px;
display: none;
}
-#lz-toolbar .menu{
+
+#lz-toolbar .menu {
bottom: 70px;
}
-#lz-toolbar .submenu{
- right:-999px;
+
+#lz-toolbar .submenu {
+ right: -999px;
/* position: absolute; */
}
-#lz-toolbar .menu.show,#lz-toolbar .submenu.show{
+
+#lz-toolbar .menu.show,
+#lz-toolbar .submenu.show {
display: block;
}
-.menu-mask{
+.menu-mask {
z-index: 98;
position: fixed;
left: 0;
@@ -241,14 +276,16 @@ strong[style*="quote"]:hover{
background-color: transparent;
display: none;
}
-.menu-item{
+
+.menu-item {
cursor: pointer;
padding: 5px 20px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
-.menu-item:hover{
+
+.menu-item:hover {
background-color: var(--b3-list-hover);
color: var(--b3-theme-primary);
}
@@ -268,16 +305,20 @@ strong[style*="quote"]:hover{
top: 30px;
font-size: 14px;
}
-#snackbar.info{
+
+#snackbar.info {
background-color: #444;
}
-#snackbar.success{
+
+#snackbar.success {
background-color: #67c23a;
}
-#snackbar.danger{
+
+#snackbar.danger {
background-color: #f56c6c;
}
-#snackbar.warning{
+
+#snackbar.warning {
background-color: #e6a23c;
}
@@ -288,27 +329,55 @@ strong[style*="quote"]:hover{
}
@-webkit-keyframes fadein {
- from {top: 0; opacity: 0;}
- to {top: 30px; opacity: 1;}
+ from {
+ top: 0;
+ opacity: 0;
+ }
+
+ to {
+ top: 30px;
+ opacity: 1;
+ }
}
@keyframes fadein {
- from {top: 0; opacity: 0;}
- to {top: 30px; opacity: 1;}
+ from {
+ top: 0;
+ opacity: 0;
+ }
+
+ to {
+ top: 30px;
+ opacity: 1;
+ }
}
@-webkit-keyframes fadeout {
- from {top: 30px; opacity: 1;}
- to {top: 0; opacity: 0;}
+ from {
+ top: 30px;
+ opacity: 1;
+ }
+
+ to {
+ top: 0;
+ opacity: 0;
+ }
}
@keyframes fadeout {
- from {top: 30px; opacity: 1;}
- to {top: 0; opacity: 0;}
+ from {
+ top: 30px;
+ opacity: 1;
+ }
+
+ to {
+ top: 0;
+ opacity: 0;
+ }
}
-#popup{
+#popup {
min-width: 120px;
max-height: 150px;
overflow-y: scroll;
@@ -316,27 +385,30 @@ strong[style*="quote"]:hover{
padding: 5px 0;
box-shadow: var(--b3-dialog-shadow);
background-color: var(--b3-menu-background);
- color:var(--b3-theme-on-surface);
+ color: var(--b3-theme-on-surface);
border-radius: 3px;
display: none;
z-index: 99;
}
-#popup::-webkit-scrollbar{
+#popup::-webkit-scrollbar {
width: 0px;
border-radius: 5px;
}
-.popup-item{
+
+.popup-item {
font-size: 15px;
padding: 5px 10px;
}
-.popup-item:hover, .popup-item.on{
- background-color: var(--b3-border-color) ;
+
+.popup-item:hover,
+.popup-item.on {
+ background-color: var(--b3-border-color);
cursor: pointer;
}
-.popup-mask{
+.popup-mask {
position: fixed;
left: 0;
right: 0;
@@ -346,17 +418,17 @@ strong[style*="quote"]:hover{
display: none;
}
-.popup-success{
+.popup-success {
background-color: #67c23a23;
transition: all ease-in-out 500ms;
}
-.popup-error{
+.popup-error {
background-color: #f56c6c23;
transition: all ease-in-out 500ms;
}
-/* 批注显示批注时间 */
+/* 评论显示评论时间 */
.protyle-wysiwyg div[data-node-id][custom-quote-time]::before {
content: attr(custom-quote-time);
font-size: 12px;
diff --git a/app/comment/comment.js b/app/comment/comment.js
index 27d0adc..df1d09d 100644
--- a/app/comment/comment.js
+++ b/app/comment/comment.js
@@ -6,6 +6,7 @@ import {
saveViaTransaction,
formatSYDate,
dateFormat,
+ compareVersion,
} from './utils.js'
import {
querySQL,
@@ -15,14 +16,18 @@ import {
setBlockAttrs,
} from './network.js'
+const VERSION_LE_2_1_14 = compareVersion(
+ window.siyuan.config.system.kernelVersion,
+ '2.1.14',
+) <= 0; // 当前版本号 <= v2.1.14
class Comment {
constructor() {
this.icons = config.icons
- this.isShow = false //是否弹出批注框
- setTimeout(() => this.appendToolbarBtn(), 1000) //添加 toolbar 批注按钮
- // setTimeout(()=>this.resolveCommentNodes(),1000) //等待文章内容加载完整后解析批注span todo
+ this.isShow = false //是否弹出评论框
+ setTimeout(() => this.appendToolbarBtn(), 1000) //添加 toolbar 评论按钮
+ // setTimeout(()=>this.resolveCommentNodes(),1000) //等待文章内容加载完整后解析评论span todo
}
async handleKeyDown(e) {
@@ -33,7 +38,7 @@ class Comment {
// this.showBox(e)
// }
- // 回车键提交批注
+ // 回车键提交评论
if (this.isShow && e.key == 'Enter') {
e.preventDefault()
e.stopPropagation()
@@ -47,7 +52,7 @@ class Comment {
}
/**
- * 渲染弹出框内的批注列表
+ * 渲染弹出框内的评论列表
* @param {*} node
* @param {*} from 点击来源位置
*/
@@ -81,8 +86,8 @@ class Comment {
@@ -90,7 +95,7 @@ class Comment {
`
}
} else {
- html += `
`
+ html += `
`
}
this.input.setAttribute('data-quote-id', quoteId)
@@ -105,22 +110,23 @@ class Comment {
}
/**
- * 提交批注
+ * 提交评论
* @returns
*/
async submitComment() {
// 输入框内容为空
if (!this.input.innerText) {
this.hiddenBox()
- console.log('未填写批注内容');
+ console.log('未填写评论内容');
return
}
// 如果已有 quoteid,则是追加,否则是新增
let quoteId = this.input.dataset.quoteId
if (quoteId) {
- //追加批注
+ //追加评论
let blockId = document.querySelector(`.protyle-wysiwyg [custom-${quoteId}]`).dataset.nodeId //comment 所在 block
- let quoteText = document.querySelector(`strong[style*="quote-${quoteId}"]`).innerText
+ let quoteText = document.querySelector(`strong[style*="quote-${quoteId}"]`)?.innerText
+ ?? document.querySelector(`span[data-type~=strong][style*="quote-${quoteId}"]`)?.innerText
this.appendBlocks(quoteText, blockId, quoteId)
let selection = getSelection()
selection.removeAllRanges()
@@ -128,7 +134,7 @@ class Comment {
this.hiddenBox()
} else {
- //全新批注
+ //全新评论
let selection = getSelection()
let range = this.range
let start = range.startContainer
@@ -138,10 +144,16 @@ class Comment {
if (start == null) {
return
}
- let block = start //由于没有一炮三响了,所以列表项上无法在属性弹框中看到存储的批注内容
+ let block = start //由于没有一炮三响了,所以列表项上无法在属性弹框中看到存储的评论内容
let txt = range.toString() //引用的内容
range.deleteContents()
- let strongNode = document.createElement('strong')
+
+ /* 兼容 <= 2.1.14 版本 */
+ let strongNode = VERSION_LE_2_1_14
+ ? document.createElement('strong')
+ : document.createElement('span')
+ if (!VERSION_LE_2_1_14) strongNode.setAttribute('data-type', 'strong')
+
strongNode.innerText = txt
quoteId = createBlockId()
this.appendBlocks(txt, block.dataset.nodeId, quoteId)
@@ -169,7 +181,7 @@ class Comment {
}
/**
- * 将批注内容以内容块的形式插入到文章尾部
+ * 将评论内容以内容块的形式插入到文章尾部
* @param {*} quoteText 引文内容
* @param {*} blockId 引文所在 blockid
* @param {*} quoteId 引文 id
@@ -179,14 +191,14 @@ class Comment {
let background = activeEditor.querySelector('div.protyle:not(.fn__none) .protyle-background') || activeEditor.querySelector('.protyle-background') // 获得桌面端当前编辑的文章
let docId = background.dataset.nodeId //获得当前编辑的文章 id
- // 批注 h1 标题
- // let headerHtml = ``
+ // 评论 h1 标题
+ // let headerHtml = ``
let headerMd = `
-# 批注
+# 评论
{: custom-quote-type="${config.attrs.type.heading}"}
`
- // 批注内容块
+ // 评论内容块
// let commentHtml = ``
let commentMd = `
${this.input.innerHTML}
@@ -202,7 +214,7 @@ ${this.input.innerHTML}
// 分割线
// let hrHtml = `
`
- // 先判断是否存在「批注」header,没有则添加,然后依次插入 block(虽然可以一次性批量添加,但不建议,因为可能导致不会及时更新到页面)
+ // 先判断是否存在「评论」header,没有则添加,然后依次插入 block(虽然可以一次性批量添加,但不建议,因为可能导致不会及时更新到页面)
// let header = activeEditor.querySelector('.fn__flex-1.protyle:not(.fn__none) div[style*="comment-header"]')
let res = await querySQL(`
select
@@ -216,7 +228,7 @@ ${this.input.innerHTML}
`)
// console.log(res)
if (res && res.code == 0 && res.data.length == 0) {
- // 没有批注标题块,则添加
+ // 没有评论标题块,则添加
await this.appendBlockMd(headerMd, docId)
}
@@ -238,7 +250,7 @@ ${this.input.innerHTML}
// console.log(res)
if (res && res.code == 0) {
if (res.data.length == 0) {
- // 没有关联当前批注的超级块(容器块),则添加
+ // 没有关联当前评论的超级块(容器块),则添加
let containerMd = `
{{{row
${quoteMd}
@@ -255,7 +267,7 @@ ${commentMd}
}
}
- // 如果已经存在之前的引文批注,则直接在其下方插入新批注
+ // 如果已经存在之前的引文评论,则直接在其下方插入新评论
// let existQuote = activeEditor.querySelector(`.fn__flex-1.protyle:not(.fn__none) .bq[custom-quote-id*="${quoteId}"]`)
// if(existQuote){
// await this.insertBlockDom(commentHtml, existQuote.dataset.nodeId)
@@ -266,13 +278,13 @@ ${commentMd}
}
- /* 批注列表事件,主要是移除批注和引文 */
+ /* 评论列表事件,主要是移除评论和引文 */
async handleListEvents(e) {
e.stopPropagation()
let target = e.target
- // 删除批注
+ // 删除评论
if (target.className == 'delete-comment') {
- // 移除批注按钮
+ // 移除评论按钮
let quoteId = target.dataset.quoteId
let commentId = target.dataset.commentId
let block = document.querySelector(`.protyle-wysiwyg [custom-${quoteId}]`)
@@ -282,11 +294,13 @@ ${commentMd}
}
if (target.className == 'delete-quote') {
- // 移除引文按钮, 移除批注块与原文块中的批注 ID 属性
+ // 移除引文按钮, 移除评论块与原文块中的评论 ID 属性
let quoteId = target.dataset.quoteId,
- quoteNode = document.querySelector(`strong[style*="quote-${quoteId}"]`),
+ quoteNode = VERSION_LE_2_1_14
+ ? document.querySelector(`strong[style*="quote-${quoteId}"]`)
+ : document.querySelector(`span[data-type~=strong][style*="quote-${quoteId}"]`),
block = document.querySelector(`.protyle-wysiwyg [data-node-id][custom-${quoteId}]`)
- let blockId = block.dataset.nodeId
+
if (block) {
// 移除 block 中的属性
let attr_key = `custom-${quoteId}`
@@ -315,7 +329,7 @@ ${commentMd}
saveViaTransaction()
}
- // 移除文章末尾批注内容
+ // 移除文章末尾评论内容
// let nodes = document.querySelectorAll(`div[custom-quote-id="${quoteId}"]`)
// if(nodes){
// for(var node of nodes) {
@@ -406,7 +420,7 @@ ${commentMd}
}
/**
- * TODO: 批注输入框支持粘贴内容块链接
+ * TODO: 评论输入框支持粘贴内容块链接
* @param {*} e
*/
handlePaste(e) {
@@ -463,7 +477,9 @@ ${commentMd}
* 解析文章中的 comment 元素
*/
resolveCommentNodes() {
- let elements = document.querySelectorAll('strong[style*="quote"]')
+ let elements = VERSION_LE_2_1_14
+ ? document.querySelectorAll('strong[style*="quote"]')
+ : document.querySelector('span[data-type~=strong][style*="quote"]')
if (elements) {
elements.forEach((item, index, node) => {
// 在内容块右侧添加图标
@@ -515,8 +531,10 @@ ${commentMd}
if (range) {
// 需要进一步判断选取是否是在 strong 标签里面
let start = range.startContainer, end = range.endContainer
- if (start.parentElement.tagName == 'STRONG' || end.parentElement.tagName == 'STRONG') {
- snackbar('请在非批注区操作', 'warning')
+ if ((start.parentElement.localName == 'strong' || end.parentElement.localName == 'strong')
+ || (start.parentElement.localName == 'span' || end.parentElement.localName == 'span')
+ ) {
+ snackbar('请不要在行内元素中评论', 'warning')
} else if (!range.toString()) {
snackbar('没有选中内容', 'danger')
} else {
@@ -553,7 +571,7 @@ ${commentMd}
this.input.focus()
}
- this.renderCommentsHtml(target, from) //获取批注列表
+ this.renderCommentsHtml(target, from) //获取评论列表
// 如果是从 toolbar 触发,box 的坐标不参照事件坐标,而是参照文本选区坐标
if (from == 'toolbar') {
@@ -567,7 +585,7 @@ ${commentMd}
}
/**
- * 创建批注框
+ * 创建评论框
*/
createBox() {
let fragment = document.createDocumentFragment()
@@ -589,12 +607,12 @@ ${commentMd}
this.btn = document.createElement('div')
this.btn.className = 'btn'
- this.btn.innerText = '批注'
+ this.btn.innerText = '评论'
this.btn.addEventListener('click', async () => this.submitComment())
this.add.appendChild(this.input)
this.add.appendChild(this.btn)
- //遮罩层,用于实现点击空白处关闭批注框
+ //遮罩层,用于实现点击空白处关闭评论框
this.overlay = document.createElement('div')
this.overlay.className = 'lz-overlay'
this.overlay.addEventListener('click', () => this.hiddenBox())
@@ -608,7 +626,7 @@ ${commentMd}
}
/**
- * 关闭批注框
+ * 关闭评论框
*/
hiddenBox() {
if (this.box) {
@@ -658,7 +676,7 @@ ${commentMd}
let btn = document.createElement('button')
btn.className = 'protyle-toolbar__item b3-tooltips b3-tooltips__n'
btn.setAttribute('data-type', 'comment')
- btn.setAttribute('aria-label', '批注')
+ btn.setAttribute('aria-label', '评论')
btn.innerHTML = this.icons.comment
btn.addEventListener('click', (e) => {
btn.parentElement.classList.add('fn__none') //关闭 toolbar
diff --git a/app/comment/config.js b/app/comment/config.js
index 0b65903..65dd8bf 100644
--- a/app/comment/config.js
+++ b/app/comment/config.js
@@ -10,7 +10,7 @@ const config = {
// 块属性类型值
heading: "heading", // 标题
quote: "quote", // 原文引用
- comment: "comment", // 批注
+ comment: "comment", // 评论
container: "container", // 容器
},
},
diff --git a/app/comment/index.js b/app/comment/index.js
index 98b5c2c..109f632 100644
--- a/app/comment/index.js
+++ b/app/comment/index.js
@@ -1,10 +1,10 @@
/**
- * 行内批注功能
+ * 行内评论功能
* REF: [siyuan-note/siyuan-comment at main · langzhou/siyuan-note · GitHub](https://github.com/langzhou/siyuan-note/tree/main/siyuan-comment)
*/
import Comment from './comment.js'
-import { config } from '../../script/module/b320config.js';
+import { config } from './../../script/module/b320config.js';
class SiyuanUtil {
constructor() {
@@ -18,6 +18,7 @@ class SiyuanUtil {
this.appendStyleSheet()
this.comment = new Comment()
this.domWatcher()
+ this.popoverDomWatcher()
this.handleEvents()
}
@@ -65,7 +66,7 @@ class SiyuanUtil {
domWatcher() {
var targetNode = document.querySelector('.layout__center.fn__flex.fn__flex-1');
if (!targetNode) {
- setTimeout(() => { this.domWatcher() }, 300);
+ setTimeout(() => { this.domWatcher() }, 500);
} else {
const config = { attributes: false, childList: true, subtree: true };
const callback = (mutationsList, observer) => {
@@ -87,7 +88,7 @@ class SiyuanUtil {
/* 处理观察对象节点变动事件 */
childListChangedHook(mutation) {
// 监听 node added 事件
- if (mutation.addedNodes) {
+ if (mutation.addedNodes.length > 0) {
let node = mutation.addedNodes.item(0)
// 新增 protyle 节点,即判断为打开了新文档
if (node && node.className == 'fn__flex-1 protyle') {
@@ -102,6 +103,35 @@ class SiyuanUtil {
}
}
+
+ /* 检测子窗口 dom 变动,用于动态插入元素 */
+ popoverDomWatcher() {
+ const config = { childList: true };
+ const callback = (mutationsList, observer) => {
+ for (let mutation of mutationsList) {
+ // console.log(mutation)
+ this.popoverChildListChangedHook(mutation)
+ }
+ };
+ const observer = new MutationObserver(callback);
+ observer.observe(document.body, config);
+ // observer.disconnect();
+ }
+
+
+ /* 处理观察对象节点变动事件 */
+ popoverChildListChangedHook(mutation) {
+ // 监听 node added 事件
+ if (mutation.addedNodes.length > 0) {
+ let node = mutation.addedNodes.item(0)
+ // 新增 protyle 节点,即判断为打开了新文档
+ if (node && node.classList.contains('block__popover')) {
+ // 因为 dom 树可能没有完全加载,需要延迟处理
+ this.comment.appendToolbarBtn(node.querySelector('.protyle'))
+ }
+ }
+ }
+
/* 插入样式表 */
appendStyleSheet() {
let node = document.querySelector('#protyleHljsStyle')
diff --git a/app/comment/utils.js b/app/comment/utils.js
index bd77416..9423619 100644
--- a/app/comment/utils.js
+++ b/app/comment/utils.js
@@ -170,3 +170,47 @@ export function formatSYDate(value, from = "date") {
return year + '-' + month + '-' + day + ' ' + hour + ':' + min + ':' + second
}
+
+/**
+ * 比较版本号
+ * @params {string} version1: 版本号1
+ * @params {string} version2: 版本号2
+ * @return {number}: 1: v1 > v2; -1: v1 < v2; 0: v1 = v2
+ */
+export function compareVersion(version1, version2) {
+ let v1_arr = version1.split('.');
+ let v2_arr = version2.split('.');
+ for (let i = 0; i < v1_arr.length; i++) {
+ let v1, v2;
+ if (!isNaN(v1_arr[i]) && !isNaN(v2_arr[i])) { // 两者都为数字
+ v1 = parseInt(v1_arr[i]);
+ v2 = parseInt(v2_arr[i]);
+ }
+ else if (!isNaN(v1_arr[i]) || !isNaN(v2_arr[i])) { // 其中一者为数字
+ v1 = v1_arr[i];
+ v2 = v2_arr[i];
+ if (v1 == undefined || v2 == undefined) // 其中一者没有更细分的版本号
+ return 0;
+ else if (!isNaN(v1) && isNaN(v2)) // v1 是发行版 | v2 是内测(x-alphaX)/公测版(x-devX)
+ return 1;
+ else if (isNaN(v1) && !isNaN(v2)) // v2 是发行版 | v1 是内测(x-alphaX)/公测版(x-devX)
+ return -1;
+ else // 意外的情况
+ return 0;
+ }
+ else { // 都不为数字, 比较字符串, 内测版(alpha)比公测版(dev)版本号小
+ v1 = v1_arr[i];
+ v2 = v2_arr[i];
+ }
+ switch (true) {
+ case v1 > v2:
+ return 1;
+ case v1 < v2:
+ return -1;
+ case v1 === v2:
+ default:
+ break;
+ }
+ }
+ return 0;
+}
diff --git a/app/comment2/comment.css b/app/comment2/comment.css
new file mode 100644
index 0000000..4bca6a0
--- /dev/null
+++ b/app/comment2/comment.css
@@ -0,0 +1,363 @@
+/* Common */
+strong[style="blank"]{
+ background-color: var(--b3-theme-on-background);
+ color: var(--b3-theme-on-background);
+ font-weight: normal;
+ /* border: 1px solid var(--b3-theme-on-background); */
+ border-radius: 1px;
+ transition: all 300ms ease-in-out;
+ /* cursor: pointer; */
+}
+strong[style="blank"]:hover{
+ color: var(--b3-theme-on-background);
+ background-color: var(--b3-theme-background);
+ transition: all 300ms ease-in-out;
+}
+.lz-overlay, .lz-overlay-black{
+ z-index: 999;
+ position: fixed;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ background-color: transparent;
+ transition: opacity 150ms linear;
+ display: none;
+ opacity: 1;
+}
+.lz-overlay-black{
+ background-color: rgba(0, 0, 0, 0.2);
+}
+
+/* flex card layout */
+.protyle-wysiwyg.card{
+ display: flex;
+ flex-wrap: wrap;
+ /* background-color: var(--b3-theme-surface); */
+}
+.protyle-wysiwyg.card div[data-node-id]{
+ /* box-shadow: 0 0 2px rgba(0, 0, 0, 0.3); */
+ margin: 5px;
+ padding: 10px;
+ border-radius: 3px;
+ flex-basis: 200px;
+ background-color: var(--b3-theme-surface);
+}
+
+
+/* Inline comment */
+strong[style*="quote"]{
+ border-bottom: 2px solid rgb(255, 212, 0);
+ background-color: rgba(255,212,0,0.16);
+ padding-bottom: 1px;
+ font-weight: normal;
+}
+strong[style*="quote"]:hover{
+ cursor: pointer;
+ /* background-color: rgb(255, 212, 0); */
+}
+
+#lz-comment-box{
+ font-size: 15px;
+ display: none;
+ position: fixed;
+ z-index: 1000;
+ left: 800px;
+ top: 200px;
+ width: 480px;
+ border: 1px solid var(--b3-border-color);
+ box-shadow: var(--b3-dialog-shadow);
+ background-color: var(--b3-theme-background);
+ border-radius: 3px;
+ padding: 10px 0;
+}
+#lz-comment-box a{
+ border-bottom: 1px solid var(--b3-theme-primary);
+}
+#lz-comment-box a:hover{
+ text-decoration: none;
+}
+#lz-comment-box .list{
+ width: 100%;
+ max-height: 370px;
+ overflow-y: scroll;
+}
+#lz-comment-box .list::-webkit-scrollbar{
+ width: 3px;
+ border-radius: 5px;
+}
+#lz-comment-box .list .quote{
+ border-left: 3px solid #F9DE6D;
+ padding: 2px 0 2px 8px;
+ margin: 10px 15px;
+ color: var(--b3-theme-on-surface);
+}
+#lz-comment-box .quote .delete-quote{
+ color: #f56c6c;
+ font-size: 0.8em;
+ margin-left: 5px;
+ opacity: 0;
+}
+#lz-comment-box .quote:hover .delete-quote{
+ opacity: 1;
+ cursor: pointer;
+}
+#lz-comment-box .list-item{
+ /* width: calc(100% - 30px); */
+ padding: 0px 15px 5px 15px;
+ border-bottom: 1px solid var(--b3-border-color);
+}
+#lz-comment-box .list-item:last-child{
+ border-bottom: none;
+}
+#lz-comment-box .list-item .header{
+ margin-top: 12px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+#lz-comment-box .list-item .date{
+ color: #999;
+ font-size: 0.9em;
+}
+
+#lz-comment-box .list-item .actions{
+ display: flex;
+}
+#lz-comment-box .list-item .delete-comment{
+ color: #f56c6c;
+ font-size: 0.8em;
+ opacity: 0;
+ margin-left: 8px;
+}
+#lz-comment-box .list-item .actions a{
+ border: none;
+}
+#lz-comment-box .list-item:hover .delete-comment{
+ opacity: 1;
+ cursor: pointer;
+}
+
+#lz-comment-box .list-item .comment{
+ margin: 5px 0;
+ color: var(--b3-theme-on-background);
+}
+#lz-comment-box .add{
+ display: flex;
+ padding: 0 15px ;
+}
+#lz-comment-box .add .input{
+ min-height: 30px;
+ background-color: var(--b3-theme-surface);
+ padding: 4px 5px;
+ line-height: 30px;
+ color: var(--b3-theme-on-background);
+ border-radius: 3px;
+ outline: none;
+ flex-grow: 1;
+}
+#lz-comment-box .input:empty:before{
+ content: attr(placeholder);
+ /* content: 'hello'; */
+ color:var(--b3-theme-on-surface);
+ opacity: 0.4;
+}
+#lz-comment-box .add .btn{
+ flex-shrink: 0;
+ height: 30px;
+ line-height: 30px;
+ text-align: center;
+ padding: 4px 10px;
+ background-color: var(--b3-theme-primary);
+ color: var(--b3-theme-on-primary);
+ border-radius: 3px;
+ margin-left: 8px;
+ cursor: pointer;
+}
+
+
+/* Toolbar */
+#lz-toolbar{
+ font-size: 15px;
+ position: fixed;
+ right: 60px;
+ bottom: 25px;
+ background-color: #fefefe;
+ background-color: var(--b3-theme-background);
+ box-shadow: var(--b3-point-shadow);
+ /* box-shadow: 0 2px 10px rgba(0, 0, 0, 0.14); */
+ /* border: 1px solid #efefef; */
+ border-radius: 20px;
+ padding: 5px 10px;
+ z-index: 99;
+}
+#lz-toolbar.close i:not(.closeBtn){
+ display: none;
+}
+#lz-toolbar .toolbar-btn{
+ display:inline-block;
+ font-size:20px;
+ padding:5px;
+ margin: 0 5px;
+ cursor:pointer;
+}
+#lz-toolbar .toolbar-btn:hover,#lz-toolbar .toolbar-btn.show{
+ color: var(--b3-theme-primary);
+ /* background-color: #efefef; */
+}
+
+#lz-toolbar .menu, #lz-toolbar .submenu{
+ position: fixed;
+ padding: 10px 0px;
+ border-radius: 4px;
+ background-color: white;
+ background-color: var(--b3-theme-background);
+ /* box-shadow: 0 0px 2px rgba(0, 0, 0, 0.14); */
+ box-shadow: var(--b3-point-shadow);
+ z-index: 99;
+ min-width: 100px;
+ max-width: 150px;
+ display: none;
+}
+#lz-toolbar .menu{
+ bottom: 70px;
+}
+#lz-toolbar .submenu{
+ right:-999px;
+ /* position: absolute; */
+
+}
+#lz-toolbar .menu.show,#lz-toolbar .submenu.show{
+ display: block;
+}
+
+.menu-mask{
+ z-index: 98;
+ position: fixed;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ background-color: transparent;
+ display: none;
+}
+.menu-item{
+ cursor: pointer;
+ padding: 5px 20px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.menu-item:hover{
+ background-color: var(--b3-list-hover);
+ color: var(--b3-theme-primary);
+}
+
+#snackbar {
+ visibility: hidden;
+ min-width: 120px;
+ margin-left: -125px;
+ background-color: #666;
+ color: #fff;
+ text-align: center;
+ border-radius: 6px;
+ padding: 10px;
+ position: fixed;
+ z-index: 10000;
+ left: 50%;
+ top: 30px;
+ font-size: 14px;
+}
+#snackbar.info{
+ background-color: #444;
+}
+#snackbar.success{
+ background-color: #67c23a;
+}
+#snackbar.danger{
+ background-color: #f56c6c;
+}
+#snackbar.warning{
+ background-color: #e6a23c;
+}
+
+#snackbar.show {
+ visibility: visible;
+ -webkit-animation: fadein 0.5s, fadeout 0.5s 2.5s;
+ animation: fadein 0.5s, fadeout 0.5s 2.5s;
+}
+
+@-webkit-keyframes fadein {
+ from {top: 0; opacity: 0;}
+ to {top: 30px; opacity: 1;}
+}
+
+@keyframes fadein {
+ from {top: 0; opacity: 0;}
+ to {top: 30px; opacity: 1;}
+}
+
+@-webkit-keyframes fadeout {
+ from {top: 30px; opacity: 1;}
+ to {top: 0; opacity: 0;}
+}
+
+@keyframes fadeout {
+ from {top: 30px; opacity: 1;}
+ to {top: 0; opacity: 0;}
+}
+
+
+#popup{
+ min-width: 120px;
+ max-height: 150px;
+ overflow-y: scroll;
+ position: absolute;
+ padding: 5px 0;
+ box-shadow: var(--b3-dialog-shadow);
+ background-color: var(--b3-menu-background);
+ color:var(--b3-theme-on-surface);
+ border-radius: 3px;
+ display: none;
+ z-index: 99;
+}
+
+#popup::-webkit-scrollbar{
+ width: 0px;
+ border-radius: 5px;
+}
+.popup-item{
+ font-size: 15px;
+ padding: 5px 10px;
+}
+.popup-item:hover, .popup-item.on{
+ background-color: var(--b3-border-color) ;
+ cursor: pointer;
+}
+
+
+.popup-mask{
+ position: fixed;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ z-index: 98;
+ display: none;
+}
+
+.popup-success{
+ background-color: #67c23a23;
+ transition: all ease-in-out 500ms;
+}
+
+.popup-error{
+ background-color: #f56c6c23;
+ transition: all ease-in-out 500ms;
+}
+
+/* 批注显示批注时间 */
+.protyle-wysiwyg div[data-node-id][custom-quote-time]::before {
+ content: attr(custom-quote-time);
+ font-size: 12px;
+}
diff --git a/app/comment2/comment.js b/app/comment2/comment.js
new file mode 100644
index 0000000..27d0adc
--- /dev/null
+++ b/app/comment2/comment.js
@@ -0,0 +1,674 @@
+import config from './config.js'
+import {
+ snackbar,
+ computeBoxPosition,
+ createBlockId,
+ saveViaTransaction,
+ formatSYDate,
+ dateFormat,
+} from './utils.js'
+import {
+ querySQL,
+ insertBlock,
+ appendBlock,
+ deleteBlock,
+ setBlockAttrs,
+} from './network.js'
+
+
+class Comment {
+
+ constructor() {
+ this.icons = config.icons
+ this.isShow = false //是否弹出批注框
+ setTimeout(() => this.appendToolbarBtn(), 1000) //添加 toolbar 批注按钮
+ // setTimeout(()=>this.resolveCommentNodes(),1000) //等待文章内容加载完整后解析批注span todo
+ }
+
+ async handleKeyDown(e) {
+ // 监听组合快捷键(暂时没用)
+ // if(e.shiftKey && e.altKey && e.code =='KeyC'){
+ // e.preventDefault()
+ // e.stopPropagation()
+ // this.showBox(e)
+ // }
+
+ // 回车键提交批注
+ if (this.isShow && e.key == 'Enter') {
+ e.preventDefault()
+ e.stopPropagation()
+ await this.submitComment()
+ }
+
+ // esc 关闭 box
+ if (this.isShow && e.key == 'Escape') {
+ this.hiddenBox()
+ }
+ }
+
+ /**
+ * 渲染弹出框内的批注列表
+ * @param {*} node
+ * @param {*} from 点击来源位置
+ */
+ async renderCommentsHtml(node, from) {
+
+ let html = ''
+
+ switch (from) {
+ case 'toolbar':
+ html = `${this.range.toString()}
`
+ this.list.innerHTML = html
+ break
+ case 'attr':
+ let item = node
+ this.list.innerHTML = '//Todo: fetch all comments in the block'
+ break
+ case 'block':
+ let quoteId = node.getAttribute('style');
+ if (quoteId && quoteId.indexOf('quote') > -1) {
+ quoteId = quoteId.replace("quote-", "") //移除 style 属性中用于表示的“quote”,获得原始 id
+ let sql = `select * from blocks as b left join attributes as a on b.id = a.block_id where a.name = 'custom-quote-id' and a.value = '${quoteId}' and b.type = 'p' order by b.created`,
+ res = await querySQL(sql),
+ quote = node.innerText,
+ comments = res.data
+ html += `${quote}移除引文
`
+
+ if (res.data.length > 0) {
+ for (let key in comments) {
+ html += `
+
+
+
+
+ `
+ }
+ } else {
+ html += `
`
+ }
+
+ this.input.setAttribute('data-quote-id', quoteId)
+ this.list.innerHTML = html
+ }
+
+ break
+
+ default:
+ break
+ }
+ }
+
+ /**
+ * 提交批注
+ * @returns
+ */
+ async submitComment() {
+ // 输入框内容为空
+ if (!this.input.innerText) {
+ this.hiddenBox()
+ console.log('未填写批注内容');
+ return
+ }
+ // 如果已有 quoteid,则是追加,否则是新增
+ let quoteId = this.input.dataset.quoteId
+ if (quoteId) {
+ //追加批注
+ let blockId = document.querySelector(`.protyle-wysiwyg [custom-${quoteId}]`).dataset.nodeId //comment 所在 block
+ let quoteText = document.querySelector(`strong[style*="quote-${quoteId}"]`).innerText
+ this.appendBlocks(quoteText, blockId, quoteId)
+ let selection = getSelection()
+ selection.removeAllRanges()
+ selection.addRange(this.range) // 使得 protyle 获得光标
+ this.hiddenBox()
+
+ } else {
+ //全新批注
+ let selection = getSelection()
+ let range = this.range
+ let start = range.startContainer
+ while (start != null && (start.dataset == null || start.dataset.nodeId == null)) {
+ start = start.parentElement
+ }
+ if (start == null) {
+ return
+ }
+ let block = start //由于没有一炮三响了,所以列表项上无法在属性弹框中看到存储的批注内容
+ let txt = range.toString() //引用的内容
+ range.deleteContents()
+ let strongNode = document.createElement('strong')
+ strongNode.innerText = txt
+ quoteId = createBlockId()
+ this.appendBlocks(txt, block.dataset.nodeId, quoteId)
+ strongNode.setAttribute('style', 'quote-' + quoteId)
+
+ let attr = { key: `custom-${quoteId}`, value: "true" }
+ block.setAttribute(attr.key, attr.value)
+ // 使用 API 设置块属性
+ await setBlockAttrs({
+ id: block.dataset.nodeId,
+ attrs: {
+ [attr.key]: attr.value,
+ },
+ })
+ range.insertNode(strongNode)
+ range.setStartAfter(strongNode)
+ range.collapse(true) //取消文本选择状态
+ selection.removeAllRanges()
+ selection.addRange(range)
+ this.hiddenBox()
+ }
+
+ saveViaTransaction()
+
+ }
+
+ /**
+ * 将批注内容以内容块的形式插入到文章尾部
+ * @param {*} quoteText 引文内容
+ * @param {*} blockId 引文所在 blockid
+ * @param {*} quoteId 引文 id
+ */
+ async appendBlocks(quoteText, blockId, quoteId) {
+ let activeEditor = document.querySelector('.layout__center [data-type="wnd"].layout__wnd--active') || document.querySelector('.layout__center [data-type="wnd"]') || document.getElementById('editor') //获得当前光标所在页面
+ let background = activeEditor.querySelector('div.protyle:not(.fn__none) .protyle-background') || activeEditor.querySelector('.protyle-background') // 获得桌面端当前编辑的文章
+ let docId = background.dataset.nodeId //获得当前编辑的文章 id
+
+ // 批注 h1 标题
+ // let headerHtml = ``
+ let headerMd = `
+# 批注
+{: custom-quote-type="${config.attrs.type.heading}"}
+`
+
+ // 批注内容块
+ // let commentHtml = ``
+ let commentMd = `
+${this.input.innerHTML}
+{: custom-quote-id="${quoteId}" custom-quote-type="${config.attrs.type.comment}" custom-quote-time="${dateFormat("YYYY-mm-dd HH:MM:SS", new Date())}"}
+`
+
+ // 引文内容块
+ // let quoteHtml = ``
+ let quoteMd = `
+> [${quoteText}](siyuan://blocks/${blockId})
+{: custom-quote-id="${quoteId}" custom-quote-type="${config.attrs.type.quote}"}
+`
+
+ // 分割线
+ // let hrHtml = `
`
+ // 先判断是否存在「批注」header,没有则添加,然后依次插入 block(虽然可以一次性批量添加,但不建议,因为可能导致不会及时更新到页面)
+ // let header = activeEditor.querySelector('.fn__flex-1.protyle:not(.fn__none) div[style*="comment-header"]')
+ let res = await querySQL(`
+ select
+ *
+ from
+ attributes as a
+ where
+ a.root_id = '${docId}'
+ and a.name = 'custom-quote-type'
+ and a.value = '${config.attrs.type.heading}'
+ `)
+ // console.log(res)
+ if (res && res.code == 0 && res.data.length == 0) {
+ // 没有批注标题块,则添加
+ await this.appendBlockMd(headerMd, docId)
+ }
+
+ res = await querySQL(`
+ select
+ b.id
+ from
+ blocks as b
+ inner join
+ attributes as a
+ on
+ a.block_id = b.id
+ where
+ a.root_id = '${docId}'
+ and a.name = 'custom-quote-id'
+ and a.value = '${quoteId}'
+ and b.type = 's'
+ `)
+ // console.log(res)
+ if (res && res.code == 0) {
+ if (res.data.length == 0) {
+ // 没有关联当前批注的超级块(容器块),则添加
+ let containerMd = `
+{{{row
+${quoteMd}
+
+${commentMd}
+}}}
+{: custom-quote-id="${quoteId}" custom-quote-type="${config.attrs.type.container}"}
+`
+ await this.appendBlockMd(containerMd, docId)
+ }
+ else if (res.data.length == 1) {
+ let containerId = res.data[0].id
+ await this.appendBlockMd(commentMd, containerId)
+ }
+ }
+
+ // 如果已经存在之前的引文批注,则直接在其下方插入新批注
+ // let existQuote = activeEditor.querySelector(`.fn__flex-1.protyle:not(.fn__none) .bq[custom-quote-id*="${quoteId}"]`)
+ // if(existQuote){
+ // await this.insertBlockDom(commentHtml, existQuote.dataset.nodeId)
+ // }else{
+ // await this.appendBlockDom(quoteHtml, docId)
+ // await this.appendBlockDom(commentHtml, docId)
+ // }
+
+ }
+
+ /* 批注列表事件,主要是移除批注和引文 */
+ async handleListEvents(e) {
+ e.stopPropagation()
+ let target = e.target
+ // 删除批注
+ if (target.className == 'delete-comment') {
+ // 移除批注按钮
+ let quoteId = target.dataset.quoteId
+ let commentId = target.dataset.commentId
+ let block = document.querySelector(`.protyle-wysiwyg [custom-${quoteId}]`)
+ deleteBlock(commentId)
+ target.parentNode.parentNode.parentNode.remove()
+ return
+ }
+
+ if (target.className == 'delete-quote') {
+ // 移除引文按钮, 移除批注块与原文块中的批注 ID 属性
+ let quoteId = target.dataset.quoteId,
+ quoteNode = document.querySelector(`strong[style*="quote-${quoteId}"]`),
+ block = document.querySelector(`.protyle-wysiwyg [data-node-id][custom-${quoteId}]`)
+ let blockId = block.dataset.nodeId
+ if (block) {
+ // 移除 block 中的属性
+ let attr_key = `custom-${quoteId}`
+ block.removeAttribute(attr_key)
+ // 使用 API 移除块属性
+ await setBlockAttrs({
+ id: block.dataset.nodeId,
+ attrs: {
+ [attr_key]: '',
+ },
+ })
+ }
+ if (quoteNode) {
+ // 移除 strong 标签
+ let selection = getSelection(),
+ range = document.createRange(),
+ text = document.createTextNode(quoteNode.innerText)
+ range.setStart(quoteNode.firstChild, 0)
+ range.setEnd(quoteNode.firstChild, quoteNode.firstChild.length)
+ selection.removeAllRanges()
+ selection.addRange(range)
+ quoteNode.remove()
+ range.deleteContents()
+ range.insertNode(text)
+ range.setStartAfter(text)
+ saveViaTransaction()
+ }
+
+ // 移除文章末尾批注内容
+ // let nodes = document.querySelectorAll(`div[custom-quote-id="${quoteId}"]`)
+ // if(nodes){
+ // for(var node of nodes) {
+ // let blockId = node.dataset.nodeId
+ // if(blockId){
+ // deleteBlock(blockId)
+ // }
+ // }
+ // }
+ let res = await querySQL(`
+ select
+ b.id
+ from
+ blocks as b
+ inner join
+ attributes as a
+ on
+ a.block_id = b.id
+ where
+ a.name = 'custom-quote-id'
+ and a.value = '${quoteId}'
+ and b.type = 's'
+ `)
+ // console.log(res)
+ if (res && res.code == 0) {
+ if (res.data.length == 1) {
+ await deleteBlock(res.data[0].id)
+ }
+ }
+ }
+ this.hiddenBox()
+ }
+
+ /**
+ * 插入新块
+ * @param {*} html
+ * @param {*} previousId 前一个块的位置
+ * @returns
+ */
+ insertBlockDom(html, previousId) {
+ return insertBlock({
+ "data": html,
+ "dataType": "dom",
+ "previousID": previousId
+ })
+ }
+
+ /**
+ * 以 markdown 的形式插入新块
+ * @param {*} html
+ * @param {*} previousId 前一个块的位置
+ * @returns
+ */
+ insertBlockMd(md, previousId) {
+ return insertBlock({
+ "data": md,
+ "dataType": "markdown",
+ "previousID": previousId
+ })
+ }
+
+ /**
+ * 以 dom 的形式插入后置子块
+ * @param {string} html
+ * @param {string} parentId
+ * @returns
+ */
+ appendBlockDom(html, parentId) {
+ return appendBlock({
+ "data": html,
+ "dataType": "dom",
+ "parentID": parentId
+ })
+ }
+
+ /**
+ * 以 markdown 的形式插入后置子块
+ * @param {string} md
+ * @param {string} parentId
+ * @returns
+ */
+ appendBlockMd(md, parentId) {
+ return appendBlock({
+ "data": md,
+ "dataType": "markdown",
+ "parentID": parentId
+ })
+ }
+
+ /**
+ * TODO: 批注输入框支持粘贴内容块链接
+ * @param {*} e
+ */
+ handlePaste(e) {
+ e.stopPropagation()
+ const clipdata = e.clipboardData || window.clipboardData;
+ const data = clipdata.getData("text/plain")
+ let selection = getSelection()
+ if (data && selection.toString()) {
+ let reg1 = /.*\(\((\d{14}-.*)\)\).*/ //匹配格式:((20210815214330-btqo1b2))
+ let reg2 = /.*siyuan:\/\/blocks\/(\d{14}-\S{7})/ //匹配格式:siyuan://blocks/20210815214330-btqo1b2
+ let result = data.match(reg1) || data.match(reg2)
+ if (result) {
+ e.preventDefault()
+ let link = document.createElement('a')
+ link.setAttribute('href', 'siyuan://blocks/' + result[1])
+ link.innerText = selection.toString()
+ let range = selection.getRangeAt(0)
+ range.deleteContents()
+ range.insertNode(link)
+ range.setStartAfter(link)
+ }
+ }
+ }
+
+ /**
+ * 响应文本选择事件
+ * @param {event} e
+ */
+ handleSelectionEvent(e) {
+ let node = e.target, inProtyle = false
+ // 判断事件是否发生在 protyle 中
+ while (node != document) {
+ if (node.classList.contains('protyle-wysiwyg')) {
+ inProtyle = true
+ break
+ }
+ node = node.parentNode
+ }
+
+ if (inProtyle) {
+ let selection = getSelection()
+ // 获得文本选择事件的坐标,用于确定弹出 comment box 的位置
+ if (selection.rangeCount > 0 && selection.getRangeAt(0).toString()) {
+ this.selectionX = e.clientX
+ this.selectionY = e.clientY
+ } else {
+ this.selectionX = null
+ this.selectionY = null
+ }
+ }
+ }
+
+ /**
+ * 解析文章中的 comment 元素
+ */
+ resolveCommentNodes() {
+ let elements = document.querySelectorAll('strong[style*="quote"]')
+ if (elements) {
+ elements.forEach((item, index, node) => {
+ // 在内容块右侧添加图标
+ this.createBlockIcon(item.parentElement)
+ })
+ }
+ }
+
+ /**
+ * 在内容块右侧添加图标
+ * @param {*} contentBlock
+ */
+ createBlockIcon(contentBlock) {
+ let sibling = contentBlock.nextSibling
+ if (sibling && !sibling.querySelector('.protyle-attr--comment')) {
+ let div = document.createElement('div')
+ div.className = 'protyle-attr--comment'
+ div.innerHTML = this.icons.comment
+ div.addEventListener('click', (e) => this.showBox(e))
+ contentBlock.nextSibling.appendChild(div)
+ }
+ }
+
+ /**
+ * 弹出 box
+ * @param {*} e
+ */
+ showBox(e) {
+ let show = false, //用来决定是否弹出 box
+ from = '', //判断弹出 box 点击来源
+ x = e.clientX, //事件坐标,用于计算弹框位置
+ y = e.clientY,
+ target = e.target,
+ parent = target.parentNode || target,
+ grandParent = parent.parentNode || target, //可能会点击到按钮中的svg、path 元素,所以需要获取父级元素
+ style = target.getAttribute('style') //获取 strong 的 style 属性
+
+ // 如果之前不存在box,则创建
+ if (!this.box) { this.createBox() }
+
+ // 首先根据点击事件来源决定哪些情况下要弹出 box
+ if (target != null && target.dataset != null && target.dataset.type == 'comment'
+ || parent != null && parent.dataset != null && parent.dataset.type == 'comment'
+ || grandParent != null && grandParent.dataset != null && grandParent.dataset.type == 'comment') {
+ // 1)点击 toolbar 图标触发
+ e.stopPropagation()
+ let selection = getSelection(),
+ range = selection.getRangeAt(0)
+ if (range) {
+ // 需要进一步判断选取是否是在 strong 标签里面
+ let start = range.startContainer, end = range.endContainer
+ if (start.parentElement.tagName == 'STRONG' || end.parentElement.tagName == 'STRONG') {
+ snackbar('请在非批注区操作', 'warning')
+ } else if (!range.toString()) {
+ snackbar('没有选中内容', 'danger')
+ } else {
+ this.range = range // 因为弹出 box 后,选区会消失,所以提前存储 range
+ show = true
+ from = 'toolbar'
+ }
+ }
+
+ } else
+ if (style && style.indexOf('quote') > -1 && getSelection().toString() == '') {
+ // 2)点击 block 引文触发
+ e.stopPropagation()
+ show = true
+ from = 'block'
+ this.range = getSelection().getRangeAt(0)
+ } else if (target.classList && target.classList.contains('protyle-attr--comment')
+ || parent.classList && parent.classList.contains('protyle-attr--comment')
+ || grandParent.classList && grandParent.classList.contains('protyle-attr--comment')) {
+ // 3)点击内容块右侧图标触发
+ e.stopPropagation()
+ show = true
+ from = 'attr'
+ }
+
+ if (show) {
+ this.isShow = true
+ this.box.style.display = 'block'
+ this.overlay.style.display = 'block'
+ if (from == 'attr') {
+ this.add.style.display = 'none' //点击attr区图标时不展示输入框
+ } else {
+ this.add.style.display = 'flex'
+ this.input.focus()
+ }
+
+ this.renderCommentsHtml(target, from) //获取批注列表
+
+ // 如果是从 toolbar 触发,box 的坐标不参照事件坐标,而是参照文本选区坐标
+ if (from == 'toolbar') {
+ x = this.selectionX || x
+ y = this.selectionY || y
+ }
+ let p = computeBoxPosition(this.box, x, y)
+ this.box.style.left = p.x + 'px'
+ this.box.style.top = p.y + 'px'
+ }
+ }
+
+ /**
+ * 创建批注框
+ */
+ createBox() {
+ let fragment = document.createDocumentFragment()
+ this.box = document.createElement('div')
+ this.box.id = 'lz-comment-box'
+ this.list = document.createElement('div')
+ this.list.className = 'list'
+ this.list.addEventListener('click', async e => this.handleListEvents(e))
+
+ this.add = document.createElement('div')
+ this.add.className = 'add'
+ this.input = document.createElement('div')
+ this.input.setAttribute('contenteditable', true)
+ this.input.className = 'input'
+ this.input.setAttribute('placeholder', '说点啥呗 ..')
+ this.input.setAttribute('spellcheck', false)
+ this.input.setAttribute('data-quote-id', '')
+ this.input.addEventListener('paste', e => this.handlePaste(e))
+
+ this.btn = document.createElement('div')
+ this.btn.className = 'btn'
+ this.btn.innerText = '批注'
+ this.btn.addEventListener('click', async () => this.submitComment())
+ this.add.appendChild(this.input)
+ this.add.appendChild(this.btn)
+
+ //遮罩层,用于实现点击空白处关闭批注框
+ this.overlay = document.createElement('div')
+ this.overlay.className = 'lz-overlay'
+ this.overlay.addEventListener('click', () => this.hiddenBox())
+
+ this.box.appendChild(this.list)
+ this.box.appendChild(this.add)
+
+ fragment.appendChild(this.box)
+ fragment.appendChild(this.overlay)
+ document.body.appendChild(fragment)
+ }
+
+ /**
+ * 关闭批注框
+ */
+ hiddenBox() {
+ if (this.box) {
+ this.box.style.display = 'none'
+ this.overlay.style.display = 'none'
+ this.input.innerText = ''
+ this.input.setAttribute('data-quote-id', '')
+ this.isShow = false
+ }
+ }
+
+ /**
+ * 往 toolbar 中添加按钮
+ * @param {node} protyle 需要添加功能按钮的 protyle editor
+ */
+ appendToolbarBtn(protyle) {
+ if (protyle) {
+ // 处理新增的 protyle
+ let icon = protyle.querySelector('[data-type="comment"]')
+ if (!icon) {
+ let toolbar = protyle.querySelector('.protyle-toolbar')
+ let fragment = this.createToolbarBtn()
+ toolbar.appendChild(fragment)
+ }
+ } else {
+ // 初始化时找到所有 protyle-toolbar
+ let toolbars = document.querySelectorAll('.protyle-toolbar')
+ if (toolbars) {
+ toolbars.forEach((item, index, node) => {
+ if (!item.querySelector('[data-type="comment"]')) {
+ let fragment = this.createToolbarBtn()
+ item.appendChild(fragment)
+ }
+ })
+ }
+ }
+ }
+
+ /**
+ * 创建 toolbar 功能按钮
+ * @returns
+ */
+ createToolbarBtn() {
+ let fragment = document.createDocumentFragment()
+ let divider = document.createElement('div')
+ divider.className = 'protyle-toolbar__divider'
+ let btn = document.createElement('button')
+ btn.className = 'protyle-toolbar__item b3-tooltips b3-tooltips__n'
+ btn.setAttribute('data-type', 'comment')
+ btn.setAttribute('aria-label', '批注')
+ btn.innerHTML = this.icons.comment
+ btn.addEventListener('click', (e) => {
+ btn.parentElement.classList.add('fn__none') //关闭 toolbar
+ this.showBox(e)
+ })
+ fragment.appendChild(divider)
+ fragment.appendChild(btn)
+ return fragment
+ }
+
+}
+
+export default Comment
diff --git a/app/comment2/config.js b/app/comment2/config.js
new file mode 100644
index 0000000..0b65903
--- /dev/null
+++ b/app/comment2/config.js
@@ -0,0 +1,19 @@
+const config = {
+ api_token: "", //在 设置 - 关于 里查看 API token
+ icons: {
+ comment: ' ',
+ brush: ' ',
+ },
+ attrs: {
+ // 块属性
+ type: {
+ // 块属性类型值
+ heading: "heading", // 标题
+ quote: "quote", // 原文引用
+ comment: "comment", // 批注
+ container: "container", // 容器
+ },
+ },
+}
+
+export default config
diff --git a/app/comment2/index.js b/app/comment2/index.js
new file mode 100644
index 0000000..98b5c2c
--- /dev/null
+++ b/app/comment2/index.js
@@ -0,0 +1,132 @@
+/**
+ * 行内批注功能
+ * REF: [siyuan-note/siyuan-comment at main · langzhou/siyuan-note · GitHub](https://github.com/langzhou/siyuan-note/tree/main/siyuan-comment)
+ */
+
+import Comment from './comment.js'
+import { config } from '../../script/module/b320config.js';
+
+class SiyuanUtil {
+ constructor() {
+ // this.themeName = this.getThemeName()
+ // if(this.themeName){
+ // this.appendStyleSheet()
+ // this.comment = new Comment()
+ // this.domWatcher()
+ // this.handleEvents()
+ // }
+ this.appendStyleSheet()
+ this.comment = new Comment()
+ this.domWatcher()
+ this.handleEvents()
+ }
+
+ /* 事件委托 */
+ handleEvents() {
+ // 按键按下事件
+ window.addEventListener('keydown', e => {
+ // this.shortcutKey(e)
+ if (this.comment) this.comment.handleKeyDown(e)
+ })
+
+ // 输入防抖
+ // window.addEventListener('keyup',lodash.debounce(e =>{
+ // if(this.searchBox) this.searchBox.handleInput(e)
+ // },800))
+
+ // 按键弹起事件
+ // window.addEventListener('keyup',e =>{
+ // if(this.searchBox) this.searchBox.actionTrigger(e)
+ // })
+
+ // 鼠标单击事件
+ window.addEventListener('click', e => {
+ if (this.comment) this.comment.showBox(e)
+ })
+
+ // 鼠标松开事件
+ window.addEventListener('mouseup', e => {
+ if (this.comment) this.comment.handleSelectionEvent(e)
+ })
+ }
+
+ // 获取当前主题名称
+ // getThemeName(){
+ // let themeStyle = document.querySelector('#themeStyle')
+ // if(themeStyle){
+ // let url = themeStyle.getAttribute('href').split('/')
+ // return url[url.length - 2]
+ // }else{
+ // setTimeout(()=>this.getThemeName(),500)
+ // }
+ // }
+
+ /* 检测 dom 变动,用于动态插入元素 */
+ domWatcher() {
+ var targetNode = document.querySelector('.layout__center.fn__flex.fn__flex-1');
+ if (!targetNode) {
+ setTimeout(() => { this.domWatcher() }, 300);
+ } else {
+ const config = { attributes: false, childList: true, subtree: true };
+ const callback = (mutationsList, observer) => {
+ for (let mutation of mutationsList) {
+ if (mutation.type === 'childList') {
+ this.childListChangedHook(mutation)
+ }
+ else if (mutation.type === 'attributes') {
+ console.log('The ' + mutation.attributeName + ' attribute was modified.');
+ }
+ }
+ };
+ const observer = new MutationObserver(callback);
+ observer.observe(targetNode, config);
+ // observer.disconnect();
+ }
+ }
+
+ /* 处理观察对象节点变动事件 */
+ childListChangedHook(mutation) {
+ // 监听 node added 事件
+ if (mutation.addedNodes) {
+ let node = mutation.addedNodes.item(0)
+ // 新增 protyle 节点,即判断为打开了新文档
+ if (node && node.className == 'fn__flex-1 protyle') {
+ // 因为 dom 树可能没有完全加载,需要延迟处理
+ setTimeout(() => {
+ if (this.comment) {
+ this.comment.appendToolbarBtn()
+ // this.comment.resolveCommentNodes() //todo
+ }
+ }, 1000)
+ }
+ }
+ }
+
+ /* 插入样式表 */
+ appendStyleSheet() {
+ let node = document.querySelector('#protyleHljsStyle')
+ if (!node) {
+ setTimeout(() => {
+ this.appendStyleSheet()
+ }, 500);
+ } else {
+ let fragment = document.createDocumentFragment()
+ let css = document.createElement('link')
+ css.setAttribute('type', 'text/css')
+ css.setAttribute('rel', 'stylesheet')
+ css.setAttribute('href', '/appearance/themes/Dark+/app/comment/comment.css')
+ fragment.appendChild(css)
+ document.head.insertBefore(fragment, node)
+ }
+ }
+}
+
+(() => {
+ try {
+ if (config.theme.comment.enable) {
+ new SiyuanUtil()
+ }
+ } catch (err) {
+ console.error(err);
+ }
+})();
diff --git a/app/comment2/network.js b/app/comment2/network.js
new file mode 100644
index 0000000..11956b1
--- /dev/null
+++ b/app/comment2/network.js
@@ -0,0 +1,101 @@
+/**
+ * 设置属性
+ * @param {object} data
+ * @returns
+ */
+export function setBlockAttrs(data) {
+ return request("/api/attr/setBlockAttrs", data)
+}
+
+export function insertBlock(data) {
+ return request("/api/block/insertBlock", data)
+}
+
+export function prependBlock(data) {
+ return request("/api/block/prependBlock", data)
+}
+
+export function appendBlock(data) {
+ return request("/api/block/appendBlock", data)
+}
+
+export function updateBlock(data) {
+ return request("/api/block/updateBlock", data)
+}
+
+export function deleteBlock(id) {
+ return request("/api/block/deleteBlock", { "id": id })
+}
+
+export function querySQL(sql) {
+ return request("/api/query/sql", { "stmt": sql })
+}
+
+/**
+ * 网络请求
+ * @param {*} url 请求地址
+ * @param {object} data
+ * @param {*} method 请求方法 get post
+ * @returns
+ */
+export function request(url, data, method = 'POST') {
+ return new Promise((resolve, reject) => {
+ if (method.toUpperCase() == 'POST') {
+ fetch(url, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(data)
+ // body:data
+ }).then(handleResponse)
+ .then(data => resolve(data))
+ .then(error => reject(error))
+
+ } else {
+ fetch(url)
+ .then(handleResponse)
+ .then(data => resolve(data))
+ .then(error => reject(error))
+ }
+ })
+
+ function handleResponse(response) {
+ let contentType = response.headers.get('content-type')
+ if (contentType.includes('application/json')) {
+ return handleJSONResponse(response)
+ } else if (contentType.includes('text/html')) {
+ return handleTextResponse(response)
+ } else {
+ throw new Error(`Sorry, content-type ${contentType} not supported`)
+ }
+ }
+
+ function handleJSONResponse(response) {
+ return response.json()
+ .then(json => {
+ if (response.ok) {
+ return json
+ } else {
+ return Promise.reject(Object.assign({}, json, {
+ status: response.status,
+ statusText: response.statusText
+ }))
+ }
+ })
+ }
+ function handleTextResponse(response) {
+ return response.text()
+ .then(text => {
+ if (response.ok) {
+ return text
+ } else {
+ return Promise.reject({
+ status: response.status,
+ statusText: response.statusText,
+ err: text
+ })
+ }
+ })
+ }
+}
diff --git a/app/comment2/utils.js b/app/comment2/utils.js
new file mode 100644
index 0000000..bd77416
--- /dev/null
+++ b/app/comment2/utils.js
@@ -0,0 +1,172 @@
+/*
+** randomWord 产生任意长度随机字母数字组合
+** randomFlag-是否任意长度 min-任意长度最小位[固定位数] max-任意长度最大位
+** xuanfeng 2014-08-28
+*/
+
+function randomWord(randomFlag, min, max) {
+ var str = "",
+ range = min,
+ arr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];
+
+ // 随机产生
+ if (randomFlag) {
+ range = Math.round(Math.random() * (max - min)) + min;
+ }
+ for (var i = 0; i < range; i++) {
+ let pos = Math.round(Math.random() * (arr.length - 1));
+ str += arr[pos];
+ }
+ return str;
+}
+
+export function dateFormat(fmt, date) {
+ let ret;
+ const opt = {
+ "Y+": date.getFullYear().toString(), // 年
+ "m+": (date.getMonth() + 1).toString(), // 月
+ "d+": date.getDate().toString(), // 日
+ "H+": date.getHours().toString(), // 时
+ "M+": date.getMinutes().toString(), // 分
+ "S+": date.getSeconds().toString(),
+ "s+": date.getMilliseconds().toString() // 毫秒
+ // 有其他格式化字符需求可以继续添加,必须转化成字符串
+ };
+ for (let k in opt) {
+ ret = new RegExp("(" + k + ")").exec(fmt);
+ if (ret) {
+ fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0")))
+ };
+ };
+ return fmt;
+}
+
+/**
+ * 构建符合规范的 blockid
+ * @param {bool} suffix 是否添加 7 为字母数字组合的后缀,默认为不添加,仅通过日期构建 id
+ * @returns blockid
+ */
+export function createBlockId(suffix = true) {
+ let id = dateFormat("YYYYmmddHHMMSS", new Date())
+ if (suffix) {
+ id += "-" + randomWord(true, 7, 7)
+ }
+ return id
+}
+
+/**
+ * 创建弹框遮罩
+ * @param {*} obj 对象句柄
+ * @param {*} type 弹框样式: black 半透明遮罩;default 透明遮罩
+ * @returns
+ */
+export function createOverlay(obj = this, type) {
+ let className = ''
+ if (type == 'black') {
+ className = 'lz-overlay-black'
+ } else {
+ className = 'lz-overlay'
+ }
+ let overlay = document.querySelector(`.${className}`)
+ if (overlay) {
+ obj.overlay = overlay
+ } else {
+ obj.overlay = document.createElement('div')
+ obj.overlay.className = className
+ }
+ return obj.overlay
+}
+
+
+/**
+ * 通过触发 protyle input 事件来保存 block 内容,需要确保 protyle 获得焦点
+ */
+export function saveViaTransaction() {
+ let protyle = document.querySelector('.fn__flex-1.protyle:not(.fn__none) .protyle-wysiwyg.protyle-wysiwyg--attr') //需要获取到当前正在编辑的 protyle
+ let e = document.createEvent('HTMLEvents')
+ e.initEvent('input', true, false)
+ protyle.dispatchEvent(e)
+}
+
+/**
+ * 消息提示 toast
+ * @param text 提示文案
+ * @param type 样式,取值:info / success / danger / warning
+ **/
+export function snackbar(text, type = 'info') {
+ let snackbar = document.querySelector('#snackbar')
+ if (!snackbar) {
+ snackbar = document.createElement('div')
+ snackbar.id = 'snackbar'
+ document.body.appendChild(snackbar)
+ }
+ snackbar.classList.add('show', type)
+ snackbar.innerText = text
+ setTimeout(function () { snackbar.classList.remove("show", type); }, 3000);
+}
+
+/**
+ * 计算弹出框的坐标位置,使得 box 不会超出页面范围
+ * @param box 元素 node
+ * @param x 事件 Event x 坐标
+ * @param y 事件 Event y 坐标
+ * @param offsetX x 偏移量
+ * @param offsetY y 偏移量
+ * @param offsetPostion 设置 box 相对于点击坐标的位置
+ */
+export function computeBoxPosition(box, x, y, offsetX = 10, offsetY = 20, offsetPostion = 'center') {
+ let boxWidth = box.clientWidth,
+ boxHeight = box.clientHeight,
+ docWidth = document.body.clientWidth,
+ docHeight = document.body.clientHeight,
+ top = y + offsetY,
+ left = 0
+
+ switch (offsetPostion) {
+ case 'left':
+ left = x - boxWidth - offsetX
+ break
+ case 'right':
+ left = x + offsetX
+ break
+ default:
+ left = x - boxWidth / 2 - offsetX
+ break
+ }
+
+ // box右侧超出页面
+ if (left + boxWidth > docWidth) left = docWidth - boxWidth - offsetX
+ // box下侧抽出页面
+ if (top + boxHeight > docHeight) top = docHeight - boxHeight - offsetY
+ // box遮挡了点击位置
+ if (y > top && y < top + boxHeight) top = y - boxHeight - offsetY
+ top = top < 0 ? offsetY : top
+ left = left < 0 ? offsetX : left
+ return { x: left, y: top }
+}
+
+/**
+ * 格式化思源笔记字符串日期
+ * @param {*} value
+ * @param {*} from 可选值:date | blockid
+ * @returns
+ */
+export function formatSYDate(value, from = "date") {
+
+ let str = ''
+ if (from == "blockid") {
+ let arr = value.split('-')
+ str = arr[0]
+ } else {
+ str = value
+ }
+
+ let year = str.substring(0, 4),
+ month = str.substring(4, 6),
+ day = str.substring(6, 8),
+ hour = str.substring(8, 10),
+ min = str.substring(10, 12),
+ second = str.substring(12, 14)
+
+ return year + '-' + month + '-' + day + ' ' + hour + ':' + min + ':' + second
+}
diff --git a/script/commons/domex.js b/script/commons/domex.js
index b2e6c7d..252bbe0 100644
--- a/script/commons/domex.js
+++ b/script/commons/domex.js
@@ -276,6 +276,9 @@ mv.GetDomBySelectors = (selector1,selector2,dom)=>{
console.warn(" function mv.GetDomBySelectors(): param selector1 cannot be empty." )
return null;
}
+
+ if (dom === null|| dom === undefined) return null;
+
let elementAll=dom.querySelectorAll(selector1)
if (mv.Empty(selector2)) return elementAll;
diff --git a/script/module/b320config copy.js b/script/module/b320config copy.js
deleted file mode 100644
index 191315d..0000000
--- a/script/module/b320config copy.js
+++ /dev/null
@@ -1,1465 +0,0 @@
-import { CodeLabelParse } from "../utils/codelabel-parse.js"
-import { TASK_HANDLER } from "../utils/ui.js";
-import { mv } from "../commons/domex.js";
-import { InputData, Messagebox, MessageboxInputs, MessageboxYesNo } from "../commons/widget.js";
-
-
-export function render(nodoDom) {
- for (let value of config.theme.codelabel.ptype) {
- new CodeLabelParse(value, nodoDom).render();
- }
-}
-
-export const createUL = (e) => {
- let ul = document.createElement('ul');
- e.parentNode.insertBefore(ul, e.nextElementSibling);
- e.appendChild(ul);
-
- // 创建
- ul.createli = (text, dataValue, indexValue) => {
- let li = document.createElement('li');
- li.setAttribute('custom-li-data', dataValue);
- li.setAttribute('custom-li-index', indexValue);
- li.innerHTML = text;
- ul.appendChild(li);
-
- return li;
- };
-
- return ul;
-};
-
-export var config = {
- token: '', // API token, 无需填写
- theme: {
- regs: {
-
- // 正则表达式
- url: /^siyuan:\/\/blocks\/(\d{14}\-[0-9a-z]{7})\/*(?:(?:\?)(\w+=\w+)(?:(?:\&)(\w+=\w+))+)?$/, // 思源 URL Scheme 正则表达式
- time: /^(\d+)(:[0-5]?[0-9]){0,2}(\.\d*)?$/, // 时间戳正则表达式
- id: /^\d{14}\-[0-9a-z]{7}$/, // 块 ID 正则表达式
-
- colorvalue: '^#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$', // 匹配#开头的颜色
- },
-
- common: {
- // 通用的配置
- colors: {
- names: [
- // 支持的颜色名称
- 'red', 'orange', 'yellow', 'lime', 'green',
- 'aqua', 'cyan', 'blue', 'sea', 'steel', 'purple',
- 'magenta', 'pink', 'gold', 'brown', 'gray', 'black',
- 'theme1', 'theme2'
- ],
- values: {
- 'red': {
- 'value': '#CC3140',
- 'titlecolor': '#f6eef3',
- 'msgbgcolor': 'rgba(255,255,255,0.6)',
- 'msgcolor': '2b1c29',
- },
-
- 'orange': {
- 'value': '#F87000',
- 'titlecolor': '#f6eef3',
- 'msgbgcolor': 'rgba(255,255,255,0.6)',
- 'msgcolor': '2b1c29',
- },
-
- 'yellow': {
- 'value': '#FDC000',
- 'titlecolor': '#2b1c29',
- 'msgbgcolor': 'rgba(255,255,255,0.6)',
- 'msgcolor': '2b1c29',
- },
-
- 'lime': {
- 'value': '#B2D115',
- 'titlecolor': '#2b1c29',
- 'msgbgcolor': 'rgba(255,255,255,0.6)',
- 'msgcolor': '2b1c29',
- },
-
- 'green': {
- 'value': '#30A830',
- 'titlecolor': '#f6eef3',
- 'msgbgcolor': 'rgba(255,255,255,0.6)',
- 'msgcolor': '2b1c29',
- },
-
- 'aqua': {
- 'value': '#2DE0C8',
- 'titlecolor': '#2b1c29',
- 'msgbgcolor': 'rgba(255,255,255,0.6)',
- 'msgcolor': '2b1c29',
- },
-
- 'cyan': {
- 'value': '#17B1C2',
- 'titlecolor': '#f6eef3',
- 'msgbgcolor': 'rgba(255,255,255,0.6)',
- 'msgcolor': '2b1c29',
- },
-
- 'blue': {
- 'value': '#2290F0',
- 'titlecolor': '#f6eef3',
- 'msgbgcolor': 'rgba(255,255,255,0.6)',
- 'msgcolor': '2b1c29',
- },
-
- 'sea': {
- 'value': '#2D51E0',
- 'titlecolor': '#f6eef3',
- 'msgbgcolor': 'rgba(255,255,255,0.6)',
- 'msgcolor': '2b1c29',
- },
- 'steel': {
- 'value': '#7073D6',
- 'titlecolor': '#f6eef3',
- 'msgbgcolor': 'rgba(255,255,255,0.6)',
- 'msgcolor': '2b1c29',
- },
- 'purple': {
- 'value': '#954ECC',
- 'titlecolor': '#f6eef3',
- 'msgbgcolor': 'rgba(255,255,255,0.6)',
- 'msgcolor': '2b1c29',
- },
- 'magenta': {
- 'value': '#E64ED6',
- 'titlecolor': '#f6eef3',
- 'msgbgcolor': 'rgba(255,255,255,0.6)',
- 'msgcolor': '2b1c29',
- },
- 'pink': {
- 'value': '#FAB9D1',
- 'titlecolor': '#2b1c29',
- 'msgbgcolor': 'rgba(255,255,255,0.6)',
- 'msgcolor': '2b1c29',
- },
- 'gold': {
- 'value': '#E0BF9D',
- 'titlecolor': '#2b1c29',
- 'msgbgcolor': 'rgba(255,255,255,0.6)',
- 'msgcolor': '2b1c29',
- },
- 'brown': {
- 'value': '#855F3A',
- 'titlecolor': '#2b1c29',
- 'msgbgcolor': 'rgba(255,255,255,0.6)',
- 'msgcolor': '2b1c29',
- },
- 'gray': {
- 'value': '#9498A0',
- 'titlecolor': '#f6eef3',
- 'msgbgcolor': 'rgba(255,255,255,0.6)',
- 'msgcolor': '2b1c29',
- },
- 'black': {
- 'value': '#16192a',
- 'titlecolor': '#f6eef3',
- 'msgbgcolor': 'rgba(255,255,255,0.9)',
- 'msgcolor': '2b1c29',
- },
- 'theme1': {
- 'value': '#8064A9',
- 'titlecolor': '#f6eef3',
- 'msgbgcolor': 'rgba(255,255,255,0.6)',
- 'msgcolor': '2b1c29',
- },
- 'theme2': {
- 'value': '#2AA899',
- 'titlecolor': '#f6eef3',
- 'msgbgcolor': 'rgba(255,255,255,0.6)',
- 'msgcolor': '2b1c29',
- }
- },
- },
- },
-
- codelabel: { // 标签增强解析
- enable: true, // 是否启用自定义样式渲染
- render: { // 渲染信息
- enable: true, // 是否启用自定义样式渲染
- toolbar: { // 菜单栏
- enable: true,
- id: 'toolbar-theme-style-codelabel',
- hotkey: () => config.theme.hotkeys.codelabel.render,
- label: {
- zh_CN: '标签解析增强',
- zh_CNT: null,
- fr_FR: null,
- en_US: null,
- other: 'Inline Code Parse',
- },
- icon: '#iconSuper',
- index: -3,
- },
- },
- ptype: [ // 解析类型
-
- // ptypeItem 对应的值
- // { // 解析配置项
- // typeid: "唯一ID",
- // reg: '正则表达式', // 针对 innerHTML
- // tagName: "标签名称,如 code、strong",
- // customf: '禁止渲染的块属性值,如 wz', // 自定义属性 f=wz 即可。
- // className: 'css类属性名称',
- // // select1: `.protyle-wysiwyg *[data-node-id] ${this.tagName}` // 要选择的,默认不用设置
- // // select2: `.protyle-wysiwyg *[data-node-id][custom-f~=${this.customf}] ${this.tagName}` // 要选择的,默认不用设置
- // maps: { // 解析后-分组的别名,也是 parseInfo 中的字段
- // /**
- // * 以下字段名称被占用,不要用于下面列表的值中.
- // * value, // code 标签的 InnerHTML
- // * color1,bgcolor1, // 主颜色计算结果和适配背景色
- // * color2,bgcolor2, // 次颜色计算结果和适配背景色
- // * $0~$9 也不要用.
- // */
- // '$0': 'value', // 占用,code 原始的 innerHTML 内容
- // '$1': '',
- // '$2': 'title',
- // '$3': 'msg',
- // '$4': '',
- // '$5': 'color',
- // '$6': 'endsuffix',
- // '$7': '',
- // '$8': '',
- // '$9': '',
- // },
- // emptys: ['title','msg'], // 不能为null,undefined或者空值的字段,用 '$0'-'$9' 对应的别名
- // emptysValues:{ // 当值为null,undefined或者空值时,要设置的值,用 'key 用:$0'-'$9' 对应的别名,value 是对应的值。
- // 'title':'ke',
- // },
- // onlyValue:{ // 不为null时,必须在这个范围内取值,可以不设置,
- // 'title':['1','2'],
- // },
- // style:{ // 样式映射信息
- // rerender:true, // 是否计算颜色
- // color: {
- // value:'color', // 主颜色对应的字段,用 $0'-'$9' 对应的别名
- // suffix:'endsuffix', // 颜色后缀对应的字段,用 $0'-'$9' 对应的别名
- // },
- // default:'theme2', // 主颜色缺省时,默认的颜色值
- // defaultSuffix:false, // 颜色后缀缺省时,默认的后缀内容,表示的值(suffixs中value)
- // colors:{
- // suffixs:{ // 颜色后缀内容,表示的值
- // '!':true,
- // },
- // names: ()=>config.theme.common.colors.names, // 主颜色,支持的颜色名称-列表,
- // values: ()=>config.theme.common.colors.values, // 主颜色,对应的适配配色-列表
- // }
- // },
- // customAttr: { // 自定义属性,key表示属性名,value是属性值,支持类似js的模板语法,${别名}, 会被实际的值替换
- // 'custom-codelabel-wz-title': "${title}",
- // 'custom-codelabel-wz-msg': "${msg}",
- // },
- // inlineStyle: { // 自定义内联样式,key表示属性名,value是属性值,支持类似js的模板语法,${别名}, 会被实际的值替换
- // "--theme-wz-bgcolor":"${bgcolor1}",
- // "--theme-wz-title-color":"${color1}",
- // "--theme-wz-msg-color":"${color2}",
- // "--theme-wz-msg-bgcolor":"${bgcolor2}",
- // },
- // innerHTML: '${value} ', // 解析后 code 标签的 innerHTML 内容,支持类似js的模板语法,${别名}, 会被实际的值替换
- // renderEnd: (parse, element,oldHTML) => { //在每个元素渲染解析完成后的回调函数
- // // parse是解析信息,$0~$9 的别名,如果开启 style.rerender为true,还有 color1,bgcolor1,color2,bgcolor2 (主颜色和适配颜色)
- // // element 当前元素(解析后的)
- // // oldHTML (解析前的 innerHTML 内容)
- // },
- // },
-
-
- { // done微章
- typeid: "wz",
- // reg: '(#(.*?)[|](.*?)#){1,1}?([\(](#?[\\d\\w]+)(!)?[\)])?', // 正则表达式
- reg: '(#(.*?)([|](.*?))?#){1,1}?([\(](#?[\\d\\w]+)(!)?[\)])?', // 正则表达式
- tagName: "code",
- customf: 'wz', // 忽略解析的属性值
- className: 'custom-codelabel-wz', // 自定义的属性名称
- maps: { // 解析后-分组的别名,也是 parseInfo 中的字段
- /**
- * 以下字段名称被占用,不要用于下面列表的值中.
- * value, // code 标签的 InnerHTML
- * color1,bgcolor1, // 主颜色计算结果和适配背景色
- * color2,bgcolor2, // 次颜色计算结果和适配背景色
- * $0~$9 也不要用.
- */
- '$0': 'value', // 占用,code 原始的 innerHTML 内容
- '$1': '',
- '$2': 'title',
- '$3': 'msgG', // 是否有消息
- '$4': 'msg',
- '$5': '',
- '$6': 'color',
- '$7': 'endsuffix',
- '$8': '',
- '$9': '',
- },
-
- emptys: ['title'], // 不能为空的字段
- emptysValues: { // 当值为空值的值
- 'msg': ''
- },
- style: { // 样式映射信息
- rerender: true, // 是否计算配色
- color: {
- value: 'color', // 主颜色字段
- suffix: 'endsuffix', // 颜色后缀对应的字段
- },
- default: 'theme2', // 缺省颜色值
- defaultSuffix: false, // 缺省时,颜色后缀,对应的值.
- colors: {
- suffixs: {
- '!': true,
- },
- names: () => config.theme.common.colors.names, // 颜色名称-列表
- values: () => config.theme.common.colors.values, // 适配配色-列表
- }
- },
- customAttr: { // 自定义属性
- // 'custom-codelabel-value':'${value}',
- 'custom-codelabel-wz-title': "${title}",
- 'custom-codelabel-wz-msg': "${msg}",
- },
- inlineStyle: {
- "--theme-wz-bgcolor": "${bgcolor1}",
- "--theme-wz-title-color": "${color1}",
- "--theme-wz-msg-color": "${color2}",
- "--theme-wz-msg-bgcolor": "${bgcolor2}",
- },
- innerHTML: '${value} ',
- renderEnd: (parse, element, oldHTML) => { // 渲染完单个元素的回调.
- },
- },
- { // done复选框
- typeid: "chk",
- // reg: '(#(.*?)[|](.*?)#){1,1}?([\(](#?[\\d\\w]+)(!)?[\)])?', // 正则表达式
- // reg: '(\\\+(\\\[()\\\])([|](.*?))?){1,1}?([\(](#?[\\d\\w]+)(!)?[\)])?', // 正则表达式
- reg: '(\\\+(\\\[(\\s|x)\\\])([|](.*?))?\\\+){1,1}?([\(](#?[\\d\\w]+)(!)?[\)])?', // 正则表达式
-
- tagName: "code",
- customf: 'chk', // 忽略解析的属性值
- className: 'custom-codelabel-chk', // 自定义的属性名称
- maps: { // 解析后-分组的别名,也是 parseInfo 中的字段
- /**
- * 以下字段名称被占用,不要用于下面列表的值中.
- * value, // code 标签的 InnerHTML
- * color1,bgcolor1, // 主颜色计算结果和适配背景色
- * color2,bgcolor2, // 次颜色计算结果和适配背景色
- * $0~$9 也不要用.
- */
- '$0': 'value', // 占用,code 原始的 innerHTML 内容
- '$1': '',
- '$2': 'chkG',
- '$3': 'chk', // 是否有消息
- '$4': 'msgG',
- '$5': 'msg',
- '$6': 'colorG',
- '$7': 'color',
- '$8': 'endsuffix',
- '$9': '',
- },
- emptys: ['chkG'], // 不能为空的字段
- emptysValues: { // 当值为空值的值
- 'chk': ' ',
- 'msg': '',
- },
- style: { // 样式映射信息
- rerender: true, // 是否计算配色
- color: {
- value: 'color', // 主颜色字段
- suffix: 'endsuffix', // 颜色后缀对应的字段
- },
- default: 'theme2', // 缺省颜色值
- defaultSuffix: false, // 缺省时,颜色后缀,对应的值.
- colors: {
- suffixs: {
- '!': true,
- },
- names: () => config.theme.common.colors.names, // 颜色名称-列表
- values: () => config.theme.common.colors.values, // 适配配色-列表
- }
- },
- customAttr: { // 自定义属性
- // 'custom-codelabel-value':'${value}',
- 'custom-codelabel-chk-chk': "${chk}",
- 'custom-codelabel-chk-msg': "${msg}",
- 'custom-codelabel-chk-colorG': "${colorG}",
- 'custom-codelabel-chk-endsuffix': "${endsuffix}",
- },
- inlineStyle: {
- "--theme-wz-bgcolor": "${bgcolor1}",
- "--theme-wz-title-color": "${color1}",
- "--theme-wz-msg-color": "${color2}",
- "--theme-wz-msg-bgcolor": "${bgcolor2}",
- },
- innerHTML: '${value}',
- renderEnd: (parse, element, oldHTML) => { // 渲染完单个元素的回调.
-
- // let parentNode = mv.GetSiyuanBlock(element);
- let id = mv.GetSiyuanBlockId(element)
-
- let div= mv.GetDomByAtrrs(element,'class','cw-chk-wrap','span')[0];
- if (div===null || div===undefined){
- div = mv.CreateSpan(null,'cw-chk-wrap',null);
- let span1 = mv.CreateSpan(null,'cw-chk-gaph-one',null)
- let span2 = mv.CreateSpan(null,'cw-chk-gaph-two',null)
- div.appendChild(span1);
- div.appendChild(span2);
- element.appendChild(div);
- }
-
- div.classList.remove('cw-chk-tick')
- if (parse.parseInfo.chk === 'x'){
- div.classList.add('cw-chk-tick')
- }
-
- div['onclick'] = async function(){
- let msg = mv.GetAttrs(element,"custom-codelabel-chk-msg")
- if (msg === undefined) msg=''
-
- msg = mv.Empty(msg)?"":`|${msg}`
- let colorG = mv.GetAttrs(element,"custom-codelabel-chk-colorG")
- if (colorG === "undefined" || colorG===undefined){
- colorG=""
- }
-
- if (div.classList.contains('cw-chk-tick')){
- div.classList.remove('cw-chk-tick')
- element.innerHTML = `+[ ]${msg}+${colorG} `;
- mv.SetAttrs(element,'custom-codelabel-chk-chk',' ')
- mv.SetAttrs(element,'custom-codelabel-value',`+[ ]${msg}+${colorG}`)
- }
- else{
- div.classList.add('cw-chk-tick')
- element.innerHTML =`+[x]${msg}+${colorG} `;
- mv.SetAttrs(element,'custom-codelabel-chk-chk','x')
- mv.SetAttrs(element,'custom-codelabel-value',`+[ ]${msg}+${colorG}`)
- }
-
- let md = mv.GetLute().BlockDOM2Md(element.parentNode.parentNode.innerHTML)
- console.log(md)
- let kid = await mv.UpdateBlockByMd_API(id,md)
- let dom = document.querySelectorAll(`div[data-node-id="${kid}"]`)[0];
- render(dom)
- }
-
- },
- },
- { // done刮刮乐
- typeid: "rb",
- reg: '^\\\*\\\{(.*)\\\}\\\((.*?)(\\s*\\\"(#?[\\d\\w]+)\\\")?\\\)$', // 正则表达式
- tagName: "code",
- customf: 'rb', // 忽略解析的属性值
- className: 'v-rb-coat', // 自定义的属性名称
- maps: { // 解析后-分组的别名,也是 parseInfo 中的字段
- /**
- * 以下字段名称被占用,不要用于下面列表的值中.
- * value, // code 标签的 InnerHTML
- * color1,bgcolor1, // 主颜色计算结果和适配背景色
- * color2,bgcolor2, // 次颜色计算结果和适配背景色
- * $0~$9 也不要用.
- */
- '$0': 'value', // 占用,code 原始的 innerHTML 内容
- '$1': 'coat_text',
- '$2': 'coat_data',
- '$3': '',
- '$4': 'color',
- '$5': '',
- '$6': '',
- '$7': '',
- '$8': '',
- '$9': '',
- },
-
- emptys: ['coat_data'], // 不能为空的字段
- emptysValues: { // 当值为空值的值
- 'coat_text': '****'
- },
- style: { // 样式映射信息
- rerender: true, // 是否计算配色
- color: {
- value: 'color', // 主颜色字段
- suffix: '', // 颜色后缀对应的字段
- },
- default: 'gray', // 缺省颜色值
- defaultSuffix: false, // 缺省时,颜色后缀,对应的值.
- colors: {
- suffixs: {
- '!': true,
- },
- names: () => config.theme.common.colors.names, // 颜色名称-列表
- values: () => config.theme.common.colors.values, // 适配配色-列表
- }
- },
- customAttr: { // 自定义属性
- // 'custom-codelabel-value':'${value}',
- 'custom-codelabel-rb-coat-text': "${coat_text}",
- 'custom-codelabel-rb-coat-data': "${coat_data}",
- 'custom-codelabel-rb-coat-showe': 'false',
- },
- inlineStyle: {
- '--theme-rb-bgcolor': "${bgcolor1}",
- '--theme-rb-title-color': "${color1}",
- '--theme-rb-msg-color': "${color2}",
- '--theme-rb-msg-bgcolor': "${bgcolor2}",
- },
- innerHTML: '${value} ',
- renderEnd: (parse, element, oldHTML) => { // 渲染完单个元素的回调.
- function bingOnClick(button) {
- let value = button.getAttribute('custom-codelabel-rb-coat-showe') === false ||
- button.getAttribute('custom-codelabel-rb-coat-showe') === 'false' ?
- 'true' : 'false';
- button.setAttribute('custom-codelabel-rb-coat-showe', value)
- }
- element.onclick = bingOnClick.bind(element, element);
- },
- },
- { // done注音
- typeid: "pg",
- reg: '^\\\{(.*)\\\}\\s*\\\((.*)\\\)$', // 正则表达式
- tagName: "code",
- customf: 'pg', // 忽略解析的属性值
- className: 'vk-pg', // 自定义的属性名称
- maps: { // 解析后-分组的别名,也是 parseInfo 中的字段
- /**
- * 以下字段名称被占用,不要用于下面列表的值中.
- * value, // code 标签的 InnerHTML
- * color1,bgcolor1, // 主颜色计算结果和适配背景色
- * color2,bgcolor2, // 次颜色计算结果和适配背景色
- * $0~$9 也不要用.
- */
- '$0': 'value', // 占用,code 原始的 innerHTML 内容
- '$1': 'text',
- '$2': 'pgdata',
- '$3': '',
- '$4': '',
- '$5': '',
- '$6': '',
- '$7': '',
- '$8': '',
- '$9': '',
- },
-
- emptys: ['text', 'pgdata'], // 不能为空的字段
- emptysValues: { // 当值为空值的值
- },
- style: { // 样式映射信息
- rerender: false, // 是否计算配色
- // color: {
- // value:'color', // 主颜色字段
- // suffix:'', // 颜色后缀对应的字段
- // },
- // default:'gray', // 缺省颜色值
- // defaultSuffix:false, // 缺省时,颜色后缀,对应的值.
- // colors:{
- // suffixs:{
- // '!':true,
- // },
- // names: ()=>config.theme.common.colors.names, // 颜色名称-列表
- // values: ()=>config.theme.common.colors.values, // 适配配色-列表
- // }
- },
- customAttr: { // 自定义属性
- // 'custom-codelabel-value':'${value}',
- 'custom-codelabel-pg-text': "${text}",
- 'custom-codelabel-pg-data': "${pgdata}",
- },
- inlineStyle: {
- },
- innerHTML: '{ ${text}} ( ${pgdata} ) ',
- renderEnd: (parse, element, oldHTML) => { // 渲染完单个元素的回调.
- },
- },
- { // done计数任务
- typeid: "todo",
- reg: '^\\\+\\\[(\\d+)\\\]\\s*\\\((.*?)(\\s*\\\"([\\d\\w]+)\\\")?\\\)$', // 正则表达式
- tagName: "code",
- customf: 'todo', // 忽略解析的属性值
- className: 'vk-todo', // 自定义的属性名称
- maps: { // 解析后-分组的别名,也是 parseInfo 中的字段
- '$0': 'value', // 占用,code 原始的 innerHTML 内容
- '$1': 'count',
- '$2': 'data',
- '$3': 'colorTag',
- '$4': 'color',
- '$5': '',
- '$6': '',
- '$7': '',
- '$8': '',
- '$9': '',
- },
-
- emptys: ['count', 'data'], // 不能为空的字段
- emptysValues: { // 当值为空值的值
- 'colorTag': ''
- },
- style: { // 样式映射信息
- rerender: true, // 是否计算配色
- color: {
- value: 'color', // 主颜色字段
- suffix: '', // 颜色后缀对应的字段
- },
- default: 'black', // 缺省颜色值
- defaultSuffix: false, // 缺省时,颜色后缀,对应的值.
- colors: {
- suffixs: {
- '!': true,
- },
- names: () => config.theme.common.colors.names, // 颜色名称-列表
- values: () => config.theme.common.colors.values, // 适配配色-列表
- }
- },
- customAttr: { // 自定义属性
- 'custom-codelabel-value': '${value}',
- 'custom-codelabel-todo-count': "${count}",
- 'custom-codelabel-todo-data': "${data}",
- 'custom-codelabel-todo-colorTag': "${colorTag}",
- },
- inlineStyle: {
- 'color': "${color}",
- },
- innerHTML: '+ [${count}] ( ${data}${colorTag}) ',
- renderEnd: async (parse, element, oldHTML) => {
-
- async function bingOnClick(parse, e, oldHTML) {
-
- // 获取父节点
- let parentNode = mv.GetSiyuanBlock(e);
- let id = mv.GetSiyuanBlockId(e);
-
- parse.reinitFormat(parse.ptypeItem)
-
- let parseInfo = parse.clacParseInfo(oldHTML);
- let c = 1 + +parseInfo.count;
- parseInfo.count = "" + c;
- parse.parseInfo = parseInfo;
-
- // 渲染
- parse.renderSingle(e, parseInfo);
- // 这里已经更新了,所有旧的 oldHTML 和 parse.Value 就没用了。重新组合
- let newInnerHtml = `+[${parse.parseInfo.count}](${parse.parseInfo.data}${parse.parseInfo.colorTag})`
-
- e.firstChild.onclick = bingOnClick.bind(e.firstChild, parse, e, newInnerHtml)
-
- // 设置新的 自定义的value
- e.setAttribute("custom-codelabel-value", newInnerHtml);
-
- e.firstChild.setAttribute(
- "custom-codelabel-todo-count",
- parse.parseInfo.count
- );
-
- var tmd = mv.GetLute().BlockDOM2Md(parentNode.innerHTML);
- let did = await mv.UpdateBlockByMd_API(id, tmd);
- let dom = document.querySelectorAll(`div[data-node-id="${did}"]`)[0];
- render(dom);
-
- }
-
- element.firstChild.onclick = bingOnClick.bind(element.firstChild, parse, element, oldHTML)
- element.firstChild.setAttribute(
- "custom-codelabel-todo-count",
- element.getAttribute("custom-codelabel-todo-count"),
- );
-
- },
- },
- { // done多级标签
- typeid: "mtag",
- reg: '^(.*)/(.*)$', // 针对 innerHTML
- tagName: 'span[data-type="tag"]',
- customf: 'mtag', // 自定义属性 f=mtag 即可。
- className: 'mult-tag',
- maps: { // 解析后-分组的别名,也是 parseInfo 中的字段
- /**
- * 以下字段名称被占用,不要用于下面列表的值中.
- * value, // code 标签的 InnerHTML
- * color1,bgcolor1, // 主颜色计算结果和适配背景色
- * color2,bgcolor2, // 次颜色计算结果和适配背景色
- * $0~$9 也不要用.
- */
- '$0': 'value', // 占用,code 原始的 innerHTML 内容
- '$1': 'lv1',
- '$2': 'lv2',
- '$3': '',
- '$4': '',
- '$5': '',
- '$6': '',
- '$7': '',
- '$8': '',
- '$9': '',
- },
- emptys: ['lv1', 'lv2'], // 不能为null,undefined或者空值的字段,用 '$0'-'$9' 对应的别名
- emptysValues: { // 当值为null,undefined或者空值时,要设置的值,用 'key 用:$0'-'$9' 对应的别名,value 是对应的值。
- // 'title':'ke',
- },
- style: { // 样式映射信息
- rerender: false, // 是否计算颜色
- // color: {
- // value:'color', // 主颜色对应的字段,用 $0'-'$9' 对应的别名
- // suffix:'endsuffix', // 颜色后缀对应的字段,用 $0'-'$9' 对应的别名
- // },
- // default:'theme2', // 主颜色缺省时,默认的颜色值
- // defaultSuffix:false, // 颜色后缀缺省时,默认的后缀内容,表示的值(suffixs中value)
- // colors:{
- // suffixs:{ // 颜色后缀内容,表示的值
- // '!':true,
- // },
- // names: ()=>config.theme.common.colors.names, // 主颜色,支持的颜色名称-列表,
- // values: ()=>config.theme.common.colors.values, // 主颜色,对应的适配配色-列表
- // }
- },
- customAttr: { // 自定义属性,key表示属性名,value是属性值,支持类似js的模板语法,${别名}, 会被实际的值替换
- // 'mult-tag-lv1': "${lv1}",
- // 'mult-tag-lv2': "${lv2}",
- },
- inlineStyle: { // 自定义内联样式,key表示属性名,value是属性值,支持类似js的模板语法,${别名}, 会被实际的值替换
- // "--theme-wz-bgcolor":"${bgcolor1}",
- // "--theme-wz-title-color":"${color1}",
- // "--theme-wz-msg-color":"${color2}",
- // "--theme-wz-msg-bgcolor":"${bgcolor2}",
- },
- // 解析后 code 标签的 innerHTML 内容,支持类似js的模板语法,${别名}, 会被实际的值替换
- innerHTML: "${lv1} "
- + "/ "
- + "${lv2} ",
- renderEnd: (parse, element, oldHTML) => { //在每个元素渲染解析完成后的回调函数
- // parse是解析信息,$0~$9 的别名,如果开启 style.rerender为true,还有 color1,bgcolor1,color2,bgcolor2 (主颜色和适配颜色)
- // element 当前元素(解析后的)
- // oldHTML (解析前的 innerHTML 内容)
- // let innerHTML = '';
- // if (parse['lv1'] !==undefined && parse['lv1'] !==null && parse['lv1'] !==''){
- // innerHTML += `${parse['lv1']}`
- // }
- // if (parse['lv2'] !==undefined && parse['lv2'] !==null && parse['lv2'] !==''){
- // innerHTML += `${parse['lv2']}`
- // }
-
- // element.innerHTML = innerHTML
- },
- },
- { // done下拉框
- typeid: "cx",
- reg: '\\\^\\\[(\\d+)\\\](>\\\(.+\\\))', // 针对 innerHTML
- tagName: "code",
- customf: 'cx', // 自定义属性 f=wz 即可。
- className: 'cw-cbox',
- maps: { // 解析后-分组的别名,也是 parseInfo 中的字段
- /**
- * 以下字段名称被占用,不要用于下面列表的值中.
- * value, // code 标签的 InnerHTML
- * color1,bgcolor1, // 主颜色计算结果和适配背景色
- * color2,bgcolor2, // 次颜色计算结果和适配背景色
- * $0~$9 也不要用.
- */
- '$0': 'value', // 占用,code 原始的 innerHTML 内容
- '$1': 'index',
- '$2': 'itms',
- '$3': '',
- '$4': '',
- '$5': '',
- '$6': '',
- '$7': '',
- '$8': '',
- '$9': '',
- },
- // reg2mapts:{
- // // key: 上一轮匹配的地方 ; value: 对应的解析规则
- // '$0':{
- // reg:'\\\$\\\{(.*?)\\\}',
- // isMatchAll:true, // 是否是匹配所有
- // type:'array', // 类型
- // },
- // },
- emptys: ['index', 'itms'], // 不能为null,undefined或者空值的字段,用 '$0'-'$9' 对应的别名
- emptysValues: { // 当值为null,undefined或者空值时,要设置的值,用 'key 用:$0'-'$9' 对应的别名,value 是对应的值。
- },
- style: { // 样式映射信息
- rerender: false, // 是否计算颜色
- // color: {
- // value:'color', // 主颜色对应的字段,用 $0'-'$9' 对应的别名
- // suffix:'endsuffix', // 颜色后缀对应的字段,用 $0'-'$9' 对应的别名
- // },
- // default:'theme2', // 主颜色缺省时,默认的颜色值
- // defaultSuffix:false, // 颜色后缀缺省时,默认的后缀内容,表示的值(suffixs中value)
- // colors:{
- // suffixs:{ // 颜色后缀内容,表示的值
- // '!':true,
- // },
- // names: ()=>config.theme.common.colors.names, // 主颜色,支持的颜色名称-列表,
- // values: ()=>config.theme.common.colors.values, // 主颜色,对应的适配配色-列表
- // }
- },
- customAttr: { // 自定义属性,key表示属性名,value是属性值,支持类似js的模板语法,${别名}, 会被实际的值替换
- 'custom-codelabel-cx-index': "${index}",
- 'custom-codelabel-cx-itmes': "${itms}",
- },
- inlineStyle: { // 自定义内联样式,key表示属性名,value是属性值,支持类似js的模板语法,${别名}, 会被实际的值替换
- // "--theme-wz-bgcolor":"${bgcolor1}",
- // "--theme-wz-title-color":"${color1}",
- // "--theme-wz-msg-color":"${color2}",
- // "--theme-wz-msg-bgcolor":"${bgcolor2}",
- },
- innerHTML: '${value} ', // 解析后 code 标签的 innerHTML 内容,支持类似js的模板语法,${别名}, 会被实际的值替换
- renderEnd: async (parse, element, oldHTML) => { //在每个元素渲染解析完成后的回调函数
- // parse是解析信息,$0~$9 的别名,如果开启 style.rerender为true,还有 color1,bgcolor1,color2,bgcolor2 (主颜色和适配颜色)
- // element 当前元素(解析后的)
- // oldHTML (解析前的 innerHTML 内容)
-
- let tIndex = parse.parseInfo['index'];
- let tItms = parse.parseInfo['itms'];
-
- // 获取父节点
- let parentNode = mv.GetSiyuanBlock(element);
- let id = mv.GetSiyuanBlockId(element);
-
- let setHtml = async (index, tItms) => {
-
- let itmes = [...tItms.matchAll('\\\((.*?)\\\)')]
-
- element.innerHTML = `^[${index}]${tItms} `
-
- let slt = itmes[index][1];
- element.setAttribute('custom-select-data', slt)
-
- element.setAttribute('custom-codelabel-cx-index', index)
- let pstionX = element.getBoundingClientRect().left - element.parentNode.getBoundingClientRect().left + 20;
- let pstionY = element.getBoundingClientRect().bottom - element.parentNode.getBoundingClientRect().bottom;
- let mUl = createUL(element);
- mUl.setAttribute('style', "margin-left:" + pstionX + "px;margin-top:" + pstionY + "px");
-
- let i = 0;
- for (let item of itmes) {
- let itext = item[1];
- let eli = mUl.createli("", itext, i++);
- eli.onclick = async (e) => {
- // let idx=e.target.getAttribute('custom-li-data');
- let idx = e.target.getAttribute('custom-li-index');
- let tms = element.getAttribute('custom-codelabel-cx-itmes')
- setHtml(idx, tms);
-
- var tmd = mv.GetLute().BlockDOM2Md(parentNode.innerHTML);
- let did = await mv.UpdateBlockByMd_API(id, tmd);
- let dom = document.querySelectorAll(`div[data-node-id="${did}"]`)[0];
- render(dom);
-
- };
- }
-
- };
-
- setHtml(tIndex, tItms);
- },
- },
- { // done@@命令
- typeid: "cmd",
- reg: '^@@((kanban)|(map)|(bqcolor)|(bqtab))(\\\((.*)\\\))?$', // 针对 innerHTML
- tagName: "code",
- customf: 'cmd', // 自定义属性 f=wz 即可。
- className: 'custom-codelabel-cmd',
- maps: { // 解析后-分组的别名,也是 parseInfo 中的字段
- /**
- * 以下字段名称被占用,不要用于下面列表的值中.
- * value, // code 标签的 InnerHTML
- * color1,bgcolor1, // 主颜色计算结果和适配背景色
- * color2,bgcolor2, // 次颜色计算结果和适配背景色
- * $0~$9 也不要用.
- */
- '$0': 'value', // 占用,code 原始的 innerHTML 内容
- '$1': 'func',
- '$2': '',
- '$3': '',
- '$4': '',
- '$5': '',
- '$6': 'args',
- '$7': '',
- '$8': '',
- '$9': '',
- },
- emptys: ['func'], // 不能为null,undefined或者空值的字段,用 '$0'-'$9' 对应的别名
- onlyValue: { // 不为null时,必须在这个范围内取值,可以不设置,
- 'func': ['kanban', 'map', 'bqcolor','bqtab'],
- },
- emptysValues: { // 当值为null,undefined或者空值时,要设置的值,用 'key 用:$0'-'$9' 对应的别名,value 是对应的值。
- // 'func':'',
- },
- style: { // 样式映射信息
- rerender: false, // 是否计算颜色
- // color: {
- // value:'color', // 主颜色对应的字段,用 $0'-'$9' 对应的别名
- // suffix:'endsuffix', // 颜色后缀对应的字段,用 $0'-'$9' 对应的别名
- // },
- // default:'theme2', // 主颜色缺省时,默认的颜色值
- // defaultSuffix:false, // 颜色后缀缺省时,默认的后缀内容,表示的值(suffixs中value)
- // colors:{
- // suffixs:{ // 颜色后缀内容,表示的值
- // '!':true,
- // },
- // names: ()=>config.theme.common.colors.names, // 主颜色,支持的颜色名称-列表,
- // values: ()=>config.theme.common.colors.values, // 主颜色,对应的适配配色-列表
- // }
- },
- customAttr: { // 自定义属性,key表示属性名,value是属性值,支持类似js的模板语法,${别名}, 会被实际的值替换
- // 'custom-codelabel-cmd-func': "${func}",
- // 'custom-codelabel-cmd-args': "${args}",
- },
- inlineStyle: { // 自定义内联样式,key表示属性名,value是属性值,支持类似js的模板语法,${别名}, 会被实际的值替换
- // "--theme-wz-bgcolor":"${bgcolor1}",
- // "--theme-wz-title-color":"${color1}",
- // "--theme-wz-msg-color":"${color2}",
- // "--theme-wz-msg-bgcolor":"${bgcolor2}",
- },
- innerHTML: '${value} ', // 解析后 code 标签的 innerHTML 内容,支持类似js的模板语法,${别名}, 会被实际的值替换
- renderEnd: async (parse, element, oldHTML) => { //在每个元素渲染解析完成后的回调函数
- // parse是解析信息,$0~$9 的别名,如果开启 style.rerender为true,还有 color1,bgcolor1,color2,bgcolor2 (主颜色和适配颜色)
- // element 当前元素(解析后的)
- // oldHTML (解析前的 innerHTML 内容)
-
- var id = mv.GetSiyuanBlockId(element);
-
- if (parse.parseInfo['func'] === 'kanban') {
-
- let aid = await mv.InsertBlockByMd_API(id,'---');
- let bid = await mv.InsertBlockByMd_API(aid,'---');
- let cid = await mv.InsertBlockByMd_API(bid,'* 未开始 \n * 任务1 \n* 进行中 \n * 任务2 \n* 已完成 \n * 任务3');
- await mv.DeleteBlockById_API(id);
- return;
- }
-
- if (parse.parseInfo['func'] === 'map') {
-
- let ida = await mv.InsertBlockByMd_API(id,'---');
- let idb = await mv.InsertBlockByMd_API(ida,'---');
- let idc = await mv.InsertBlockByMd_API(idb,'---');
- let idd = await mv.InsertBlockByMd_API(idc,'* 中心主题 \n * 分支1 \n * 分支2 \n * 分支3 ');
- let ide = await mv.DeleteBlockById_API(id);
- return;
- }
-
- if (parse.parseInfo['func'] === 'bqcolor') {
- let szcolor = 'red'
- if (mv.Empty(parse.parseInfo['args']) === false) {
- szcolor = parse.parseInfo['args']
- }
- let aid = await mv.InsertBlockByMd_API(id,'> ');
- let bid = await mv.AppendBlockByMd_API(aid,'`>(' + szcolor + ')` .');
- let cid = await mv.UpdateBlockByMd_API(id, ' ')
- return;
- }
-
- if (parse.parseInfo['func'] === 'bqtab'){
-
- // 插入父容器
- let wid = await mv.InsertBlockByMd_API(id, '> `::tab`');
- await mv.SetAttrs_API(wid,'custom-type','bq-wrap');
-
- // 插入选项卡
- let tabtid = await mv.AppendBlockByMd_API(wid,'* 选项1 \n * 选项2 \n * 选项3 ');
- await mv.SetAttrs_API(tabtid,'custom-type','bq-tab_t');
-
- // 插入选项卡容器
- let tabcid = await mv.AppendBlockByMd_API(wid,'> > 内容1');
- await mv.SetAttrs_API(tabcid,'custom-type','bq-tab_c');
- let bid = await mv.AppendBlockByMd_API(tabcid,'> 内容2');
- let cid = await mv.AppendBlockByMd_API(tabcid,'> 内容3');
- let did = await mv.UpdateBlockByMd_API(id,' ');
-
- window.location.reload();
- }
- },
- },
- { // done彩虹引用
- typeid: "bq",
- reg: '>[\(]((#?[\\d\\w]+)(!)?)[\)]', // 针对 innerHTML
- tagName: "code",
- customf: 'bqcolor', // 自定义属性 f=wz 即可。
- className: 'bqcolor',
- //// select1: `.protyle-wysiwyg *[data-node-id] ${this.tagName}` // 要选择的,默认不用设置
- //// select2: `.protyle-wysiwyg *[data-node-id][custom-f~=${this.customf}] ${this.tagName}` // 要选择的,默认不用设置
- select1: '.protyle-wysiwyg .bq[data-node-id] .p code:first-of-type',
- select2: '.protyle-wysiwyg .bq[data-node-id][custom-f~=bqcolor] .p code:first-of-type',
- maps: { // 解析后-分组的别名,也是 parseInfo 中的字段
- /**
- * 以下字段名称被占用,不要用于下面列表的值中.
- * value, // code 标签的 InnerHTML
- * color1,bgcolor1, // 主颜色计算结果和适配背景色
- * color2,bgcolor2, // 次颜色计算结果和适配背景色
- * $0~$9 也不要用.
- */
- '$0': 'value', // 占用,code 原始的 innerHTML 内容
- '$1': 'colorTag',
- '$2': 'color',
- '$3': 'endsuffix',
- '$4': '',
- '$5': '',
- '$6': '',
- '$7': '',
- '$8': '',
- '$9': '',
- },
- emptys: [
- 'color'
- // 'title','msg'
- ], // 不能为null,undefined或者空值的字段,用 '$0'-'$9' 对应的别名
- emptysValues: { // 当值为null,undefined或者空值时,要设置的值,用 'key 用:$0'-'$9' 对应的别名,value 是对应的值。
- // 'title':'ke',
- },
- onlyValue: {
- 'color': () => config.theme.common.colors.names,
- },
- style: { // 样式映射信息
- rerender: true, // 是否计算颜色
- color: {
- value: 'color', // 主颜色对应的字段,用 $0'-'$9' 对应的别名
- suffix: 'endsuffix', // 颜色后缀对应的字段,用 $0'-'$9' 对应的别名
- },
- default: 'theme2', // 主颜色缺省时,默认的颜色值
- defaultSuffix: false, // 颜色后缀缺省时,默认的后缀内容,表示的值(suffixs中value)
- colors: {
- suffixs: { // 颜色后缀内容,表示的值
- '!': true,
- },
- names: () => config.theme.common.colors.names, // 主颜色,支持的颜色名称-列表,
- values: () => config.theme.common.colors.values, // 主颜色,对应的适配配色-列表
- }
- },
- customAttr: { // 自定义属性,key表示属性名,value是属性值,支持类似js的模板语法,${别名}, 会被实际的值替换
- // 'custom-codelabel-bq-title': "${title}",
- // 'custom-codelabel-bq-msg': "${msg}",
- },
- inlineStyle: { // 自定义内联样式,key表示属性名,value是属性值,支持类似js的模板语法,${别名}, 会被实际的值替换
- "--theme-bq-bgcolor": "${bgcolor1}",
- "--theme-bq-title-color": "${color1}",
- "--theme-bq-msg-color": "${color2}",
- "--theme-bq-msg-bgcolor": "${bgcolor2}",
- },
- innerHTML: '${value} ', // 解析后 code 标签的 innerHTML 内容,支持类似js的模板语法,${别名}, 会被实际的值替换
- renderEnd: (parse, element, oldHTML) => { //在每个元素渲染解析完成后的回调函数
- // parse是解析信息,$0~$9 的别名,如果开启 style.rerender为true,还有 color1,bgcolor1,color2,bgcolor2 (主颜色和适配颜色)
- // element 当前元素(解析后的)
- // oldHTML (解析前的 innerHTML 内容)
-
- // 获取父节点
- let parentNode = mv.GetSiyuanBlock(element);
- let id = mv.GetSiyuanBlockId(element);
-
- var itms = []
- // var items = config.theme.common.colors.names;
- for (let ims of config.theme.common.colors.names) {
- itms.push(ims)
- itms.push(`${ims}!`)
- }
-
- let setHtml = async (slt, slt2) => {
-
- let endsuffix = "";
- if (slt2 === '2') {
- endsuffix = "!"
- }
-
- element.innerHTML = `>(${slt}) `
-
- var bqNode = element.parentNode.parentNode.parentNode;
- mv.SetStyleValue(bqNode.style, '--theme-bq-bgcolor', parse.parseInfo.bgcolor1)
- mv.SetStyleValue(bqNode.style, '--theme-bq-color1', parse.parseInfo.color1)
- mv.SetStyleValue(bqNode.style, '--theme-bq-bgcolor2', parse.parseInfo.bgcolor2)
- mv.SetStyleValue(bqNode.style, '--theme-bq-color2', parse.parseInfo.color2)
-
- if (mv.Empty(endsuffix) === true) {
- bqNode.setAttribute("bqtype", "color1")
- } else {
- bqNode.setAttribute("bqtype", "color")
- }
-
- element.setAttribute('custom-select-data', slt)
- let pstionX = element.getBoundingClientRect().left - element.parentNode.getBoundingClientRect().left + 20;
- let pstionY = element.getBoundingClientRect().bottom - element.parentNode.getBoundingClientRect().bottom;
- let mUl = createUL(element);
- mUl.setAttribute('style', "margin-left:" + pstionX + "px;margin-top:" + pstionY + "px");
-
- let i = 0;
- for (let item of itms) {
- let itext = item;
- let eli = mUl.createli("", itext, i++);
- eli.onclick = async (e) => {
-
- let idx = e.target.getAttribute('custom-li-index');
- let slt1 = itms[idx];
- if (slt1.endsWith('!')) {
- slt2 = "2"
- } else {
- slt2 = "1"
- }
- setHtml(slt1, slt2);
-
- var tmd = mv.GetLute().BlockDOM2Md(parentNode.innerHTML);
- let did = await mv.UpdateBlockByMd_API(id, tmd);
- let dom = document.querySelectorAll(`div[data-node-id="${did}"]`)[0];
- render(dom)
-
- };
- }
-
- };
-
- setHtml(`${parse.parseInfo.color}${parse.parseInfo.endsuffix}`, mv.Empty(parse.parseInfo.endsuffix) ? "1" : "2");
-
- },
- },
- { // doneTab引用
- typeid: "bqtab",
- reg: '::(tab)(\\d*)', // 针对 innerHTML
- tagName: "code",
- customf: 'bqtab', // 自定义属性 f=wz 即可。
- className: 'bqtab',
- //// select1: `.protyle-wysiwyg *[data-node-id] ${this.tagName}` // 要选择的,默认不用设置
- //// select2: `.protyle-wysiwyg *[data-node-id][custom-f~=${this.customf}] ${this.tagName}` // 要选择的,默认不用设置
- select1: '.protyle-wysiwyg .bq[data-node-id] .p:first-of-type code:first-of-type', // 要选择的,默认不用设置
- select2: '.protyle-wysiwyg .bq[data-node-id][custom-f~=bqtab] .p:first-of-type code:first-of-type', // 要选择的,默认不用设置
-
- // select1: `.protyle-wysiwyg *[data-node-id] ${this.tagName}` // 要选择的,默认不用设置
- // select2: `.protyle-wysiwyg *[data-node-id][custom-f~=${this.customf}] ${this.tagName}` // 要选择的,默认不用设置
- maps: { // 解析后-分组的别名,也是 parseInfo 中的字段
- /**
- * 以下字段名称被占用,不要用于下面列表的值中.
- * value, // code 标签的 InnerHTML
- * color1,bgcolor1, // 主颜色计算结果和适配背景色
- * color2,bgcolor2, // 次颜色计算结果和适配背景色
- * $0~$9 也不要用.
- */
- '$0': 'value', // 占用,code 原始的 innerHTML 内容
- '$1': 'mode',
- '$2': 'index',
- '$3': '',
- '$4': '',
- '$5': '',
- '$6': '',
- '$7': '',
- '$8': '',
- '$9': '',
- },
- emptys: ['mode'], // 不能为null,undefined或者空值的字段,用 '$0'-'$9' 对应的别名
- emptysValues: { // 当值为null,undefined或者空值时,要设置的值,用 'key 用:$0'-'$9' 对应的别名,value 是对应的值。
- 'index': '0',
- },
- onlyValue: { // 不为null时,必须在这个范围内取值,可以不设置,
- 'mode': ['tab'],
- },
- style: { // 样式映射信息
- rerender: false, // 是否计算颜色
- // color: {
- // value:'color', // 主颜色对应的字段,用 $0'-'$9' 对应的别名
- // suffix:'endsuffix', // 颜色后缀对应的字段,用 $0'-'$9' 对应的别名
- // },
- // default:'theme2', // 主颜色缺省时,默认的颜色值
- // defaultSuffix:false, // 颜色后缀缺省时,默认的后缀内容,表示的值(suffixs中value)
- // colors:{
- // suffixs:{ // 颜色后缀内容,表示的值
- // '!':true,
- // },
- // names: ()=>config.theme.common.colors.names, // 主颜色,支持的颜色名称-列表,
- // values: ()=>config.theme.common.colors.values, // 主颜色,对应的适配配色-列表
- // }
- },
- customAttr: { // 自定义属性,key表示属性名,value是属性值,支持类似js的模板语法,${别名}, 会被实际的值替换
- // 'custom-codelabel-wz-title': "${title}",
- // 'custom-codelabel-wz-msg': "${msg}",
- },
- inlineStyle: { // 自定义内联样式,key表示属性名,value是属性值,支持类似js的模板语法,${别名}, 会被实际的值替换
- // "--theme-wz-bgcolor":"${bgcolor1}",
- // "--theme-wz-title-color":"${color1}",
- // "--theme-wz-msg-color":"${color2}",
- // "--theme-wz-msg-bgcolor":"${bgcolor2}",
- },
- innerHTML: '${value} ', // 解析后 code 标签的 innerHTML 内容,支持类似js的模板语法,${别名}, 会被实际的值替换
- renderEnd: async (parse, element, oldHTML) => { //在每个元素渲染解析完成后的回调函数
-
- // parse是解析信息,$0~$9 的别名,如果开启 style.rerender为true,还有 color1,bgcolor1,color2,bgcolor2 (主颜色和适配颜色)
- // element 当前元素(解析后的)
- // oldHTML (解析前的 innerHTML 内容)
- // 获取父节点
-
- let crtLine = mv.GetSiyuanBlock(element);
- let parentNode = crtLine.parentNode;
-
- let tab = async (tab_t, tab_t_tag, tab_c, tag_c_tag, evt,rix) => {
-
- // 设置 button
- element.classList.remove('bq-tab-button-mode')
- element.classList.add('bq-tab-button-mode')
-
- // 设置插入
- let button = mv.GetDomByAtrrs(parentNode,"bq-button-value","新建","button")[0]
- if (button === null || button === undefined){
- button = document.createElement('button')
- button.setAttribute('bq-button-value',"新建")
- element.parentNode.insertBefore(button,element.nextSibling);
- }
-
- button[evt] = async function () {
-
- let inner = async ()=>{
-
- var tab_t = mv.GetDomByAtrrs(parentNode, "custom-type", "bq-tab_t", "div")[0];
- var tab_t_li = mv.GetDomByAtrrs(tab_t, "class", "li", "div");
-
- var tab_c = mv.GetDomByAtrrs(parentNode, "custom-type", "bq-tab_c", "div")[0];
- var tab_c_li = mv.GetDomByAtrrs(tab_c, "class", "bq","div" );
-
- let t_ix=mv.GetSiyuanBlockId(tab_t_li[tab_t_li.length-1])
- let c_ix=mv.GetSiyuanBlockId(tab_c_li[tab_c_li.length-1])
-
- let aid = await mv.InsertBlockByMd_API(c_ix,"> 内容new");
- let bid = await mv.InsertBlockByMd_API(t_ix, '* 选项卡new');
- let c_li=mv.GetDomByAtrrs(tab_c,"data-node-id",bid,'div')[0];
- tab("bq-tab_t", "li", "bq-tab_c", "bq", "onclick",-1)
-
- }
-
- let msgbox=new MessageboxYesNo("确定新建一个标签页吗?",5);
- let mgdom = msgbox.Create("bqtmsgbox","bqtmsgbox","width: 480px;top: 45%; left: 45%;");
- document.body.appendChild(mgdom);
- msgbox.Start(inner,()=>{});
-
- }
-
- // 设置 bq-none
- let button2 = mv.GetDomByAtrrs(parentNode,"bq-button-value","展开/收缩","button")[0]
- if (button2 === null || button2 === undefined){
- button2 = document.createElement('button')
- button2.setAttribute('bq-button-value',"展开/收缩")
- element.parentNode.insertBefore(button2,element.nextSibling);
- }
-
- button2[evt] = async function(){
-
- let str2=parentNode.getAttribute('custom-type');
- if (str2.indexOf("bq-none")>=0){
- str2=str2.replace('bq-none','')
-
- if (str2.indexOf("bq-wrap")<0){
- str2 = str2 + " bq-wrap "
- }
-
- console.log(str2)
- parentNode.setAttribute("custom-type",str2)
- let kid = await mv.SetAttrs_API(mv.GetSiyuanBlockId(parentNode),"custom-type",str2)
- }
- else
- {
- if (str2.indexOf("bq-wrap") <0){
- str2 = str2 + " bq-wrap "
- }
-
- str2 = str2 + " bq-none "
- console.log(str2)
- parentNode.setAttribute("custom-type",str2)
- let kid = await mv.SetAttrs_API(mv.GetSiyuanBlockId(parentNode),"custom-type",str2)
- }
- }
-
- var tab_t = mv.GetDomByAtrrs(parentNode, "custom-type", tab_t, "div")[0];
- var tab_t_li = mv.GetDomByAtrrs(tab_t, "class", tab_t_tag, "div");
-
- var tab_c = mv.GetDomByAtrrs(parentNode, "custom-type", tab_c, "div")[0];
- var tab_c_li = mv.GetDomByAtrrs(tab_c, "class", tag_c_tag,"div" );
-
- var len = tab_t_li.length;
- var i = 0;
- for (i = 0; i < len; i++) {
- tab_t_li[i].index = i;
- tab_t_li[i][evt] = async function () {
-
- for (i = 0; i < len; i++) {
- tab_t_li[i].setAttribute('custom-type', 'null');
- tab_c_li[i].setAttribute('custom-type', 'bq-hide');
-
- let kid1 = await mv.SetAttrs_API(mv.GetSiyuanBlockId(tab_t_li[i]),"custom-type",'null')
- let kid2 = await mv.SetAttrs_API(mv.GetSiyuanBlockId(tab_c_li[i]),"custom-type",'bq-hide')
- }
-
- tab_t_li[this.index].setAttribute('custom-type', 'bq-act');
- tab_c_li[this.index].setAttribute('custom-type', 'null');
-
- let id = mv.GetSiyuanBlockId(tab_t_li[this.index]);
- let kid1 = await mv.SetAttrs_API(id,"custom-type",'bq-act');
- let kid2 = await mv.SetAttrs_API(mv.GetSiyuanBlockId(tab_c_li[this.index]),"custom-type",'null');
- }
- }
-
- console.log("1")
- if (rix!==null && rix!==undefined){
- if (rix>=0){
- tab_t_li[0][evt]();
- }else{
- tab_t_li[tab_t_li.length-1][evt]();
- }
- }
- }
-
- tab("bq-tab_t", "li", "bq-tab_c", "bq", "onclick")
- crtLine.onclick = async (e)=>{
- tab("bq-tab_t", "li", "bq-tab_c", "bq", "onclick");
- }
- },
- },
- { // 整行样式
- typeid: "wzline",
- // reg: '(#(.*?)[|](.*?)#){1,1}?([\(](#?[\\d\\w]+)(!)?[\)])?', // 正则表达式
- reg: '(\\:\\:(.*?)([|](.*?))?\\:\\:){1,1}?([\(](#?[\\d\\w]+)(!)?[\)])?', // 正则表达式
- tagName: "code",
- customf: 'wzline', // 忽略解析的属性值
- className: 'custom-codelabel-wzline', // 自定义的属性名称
- maps: { // 解析后-分组的别名,也是 parseInfo 中的字段
- /**
- * 以下字段名称被占用,不要用于下面列表的值中.
- * value, // code 标签的 InnerHTML
- * color1,bgcolor1, // 主颜色计算结果和适配背景色
- * color2,bgcolor2, // 次颜色计算结果和适配背景色
- * $0~$9 也不要用.
- */
- '$0': 'value', // 占用,code 原始的 innerHTML 内容
- '$1': '',
- '$2': 'title',
- '$3': 'msgG', // 是否有消息
- '$4': 'msg',
- '$5': '',
- '$6': 'color',
- '$7': 'endsuffix',
- '$8': '',
- '$9': '',
- },
-
- emptys: ['title'], // 不能为空的字段
- emptysValues: { // 当值为空值的值
- 'msg': ''
- },
- style: { // 样式映射信息
- rerender: true, // 是否计算配色
- color: {
- value: 'color', // 主颜色字段
- suffix: 'endsuffix', // 颜色后缀对应的字段
- },
- default: 'theme2', // 缺省颜色值
- defaultSuffix: false, // 缺省时,颜色后缀,对应的值.
- colors: {
- suffixs: {
- '!': true,
- },
- names: () => config.theme.common.colors.names, // 颜色名称-列表
- values: () => config.theme.common.colors.values, // 适配配色-列表
- }
- },
- customAttr: { // 自定义属性
- // 'custom-codelabel-value':'${value}',
- 'custom-codelabel-wzline-title': "${title}",
- 'custom-codelabel-wzline-msg': "${msg}",
- },
- inlineStyle: {
- "--theme-wzline-bgcolor": "${bgcolor1}",
- "--theme-wzline-title-color": "${color1}",
- "--theme-wzline-msg-color": "${color2}",
- "--theme-wzline-msg-bgcolor": "${bgcolor2}",
- },
- innerHTML: '${value}',
- renderEnd: (parse, element, oldHTML) => { // 渲染完单个元素的回调.
- },
- },
- ],
- },
-
- menu: {
- enable: true, // 行内代码编辑增强
- codelabel: {
- enable: true, // 行内代码编辑增强
- toolbar: { // 菜单栏
- enable: true, //
- id: 'toolbar-theme-menu-codelabel',
- hotkey: () => config.theme.hotkeys.menu.codelabel,
- label: {
- zh_CN: '行内代码编辑增强',
- zh_CNT: null,
- fr_FR: null,
- en_US: null,
- other: 'codelabel Menu Enhancements',
- },
- icon: '#iconMenu',
- index: -4,
- },
- items: [
- {
- enable: true, // 是否启用菜单项
- prefixSeparator: true, // 是否添加前缀分隔线
- suffixSeparator: false, // 是否添加后缀分隔线
- type: null, // 哪些类型的块启用, 值 null 则全部启用
- id: 'theme-menu-codelabel-common-editor', // 菜单项 ID
- mode: "button", // 菜单项类型
- icon: "#iconEdit", // 菜单项图标
- label: { // 菜单项标签
- zh_CN: "编辑行内代码块",
- other: "Edit Inline Code",
- },
- accelerator: "", // 菜单项快捷键
- click: { // 菜单项点击事件
- enable: true,
- callback: null,
- tasks: [
- {
- type: 'codelabel-editor',
- params: {
- 'style': null,
- 'custom-font-family': null,
- },
- },
- ],
- },
- itemsLoad: false, // 是否加载子菜单
- // itemsIcon: "#iconRight",
- items: null,
- },
- ],
- },
- },
-
- comment: {
- // 批注功能开关
- enable: true,
- },
-
-
- wordcount: {
- // 字数统计
- enable: true,
- },
-
- hotkeys: {
- // 快捷键
- codelabel: {
- render: {
- // 渲染(Ctrl + Alt + 0)
- ctrlKey: true,
- metaKey: true,
- shiftKey: false,
- altKey: true,
- key: '0',
- },
- },
-
- menu: {
- codelabel: {
- // 块菜单开关(Shift + Alt + M)
- ctrlKey: false,
- metaKey: false,
- shiftKey: true,
- altKey: true,
- key: 'M',
- },
- },
-
- },
-
- },
-};
-
diff --git a/script/module/b320config.js b/script/module/b320config.js
index 9f27fee..0edb1db 100644
--- a/script/module/b320config.js
+++ b/script/module/b320config.js
@@ -235,6 +235,9 @@ export var config = {
// onlyValue:{ // 不为null时,必须在这个范围内取值,可以不设置,
// 'title':['1','2'],
// },
+ // ignoreValue:{ // 不为null时,必须不在这个范围内的取,可以不设置,
+ // 'title':['1','2'],
+ // },
// style:{ // 样式映射信息
// rerender:true, // 是否计算颜色
// color: {
@@ -269,6 +272,7 @@ export var config = {
// },
// },
+
{ // 微章
typeid: "wz",
// reg: '(#(.*?)[|](.*?)#){1,1}?([\(](#?[\\d\\w]+)(!)?[\)])?', // 正则表达式
@@ -298,8 +302,11 @@ export var config = {
},
emptys: ['title'], // 不能为空的字段
- emptysValues: { // 当值为空值的值
+ emptysValues: { // 当值为空值的值
'msg': ''
+ },
+ ignoreValue:{ // 当取值是对应的值时,不处理
+ 'title':['[ ]','[x]'],
},
style: { // 样式映射信息
rerender: true, // 是否计算配色
@@ -585,7 +592,7 @@ export var config = {
}
setHtml(slt1, slt2);
- var tmd = mv.GetLute().BlockDOM2Md(parentNode.innerHTML);
+ var tmd = mv.GetLute().BlockDOM2StdMd(parentNode.innerHTML);
let did = await mv.UpdateBlockByMd_API(id, tmd);
let dom = document.querySelectorAll(`div[data-node-id="${did}"]`)[0];
render(dom)
@@ -775,7 +782,7 @@ export var config = {
let tms = element.getAttribute('custom-codelabel-cx-itmes')
setHtml(idx, tms);
- var tmd = mv.GetLute().BlockDOM2Md(parentNode.innerHTML);
+ var tmd = mv.GetLute().BlockDOM2StdMd(parentNode.innerHTML);
let did = await mv.UpdateBlockByMd_API(id, tmd);
let dom = document.querySelectorAll(`div[data-node-id="${did}"]`)[0];
render(dom);
@@ -894,7 +901,134 @@ export var config = {
mv.SetAttrs(element,'custom-codelabel-value',`+[ ]${msg}+${colorG}`)
}
- let md = mv.GetLute().BlockDOM2Md(element.parentNode.parentNode.innerHTML)
+ if (colorG!==null &&colorG!==undefined && colorG.includes("!")){
+ div.classList.add('cw-chk-endsuffix')
+ }else{
+ div.classList.remove('cw-chk-endsuffix')
+ }
+
+ let md = mv.GetLute().BlockDOM2StdMd(element.parentNode.parentNode.innerHTML)
+ console.log(md)
+ let kid = await mv.UpdateBlockByMd_API(id,md)
+ let dom = document.querySelectorAll(`div[data-node-id="${kid}"]`)[0];
+ render(dom)
+ }
+
+ },
+ },
+ { // 微章+复选框
+ typeid: "chk-wz",
+ // reg: '(#(.*?)[|](.*?)#){1,1}?([\(](#?[\\d\\w]+)(!)?[\)])?', // 正则表达式
+ // reg: '(\\\+(\\\[()\\\])([|](.*?))?){1,1}?([\(](#?[\\d\\w]+)(!)?[\)])?', // 正则表达式
+ reg: '(\\\#(\\\[(\\s|x)\\\])([|](.*?))?\\\#){1,1}?([\(](#?[\\d\\w]+)(!)?[\)])?', // 正则表达式
+ tagName: "span",
+ tagDataType:"code",
+ customf: 'chk-wz', // 忽略解析的属性值
+ className: 'custom-codelabel-chk', // 自定义的属性名称
+ maps: { // 解析后-分组的别名,也是 parseInfo 中的字段
+ /**
+ * 以下字段名称被占用,不要用于下面列表的值中.
+ * value, // code 标签的 InnerHTML
+ * color1,bgcolor1, // 主颜色计算结果和适配背景色
+ * color2,bgcolor2, // 次颜色计算结果和适配背景色
+ * $0~$9 也不要用.
+ */
+ '$0': 'value', // 占用,code 原始的 innerHTML 内容
+ '$1': '',
+ '$2': 'chkG',
+ '$3': 'chk', // 是否有消息
+ '$4': 'msgG',
+ '$5': 'msg',
+ '$6': 'colorG',
+ '$7': 'color',
+ '$8': 'endsuffix',
+ '$9': '',
+ },
+ emptys: ['chkG'], // 不能为空的字段
+ emptysValues: { // 当值为空值的值
+ 'chk': ' ',
+ 'msg': '',
+ },
+ style: { // 样式映射信息
+ rerender: true, // 是否计算配色
+ color: {
+ value: 'color', // 主颜色字段
+ suffix: 'endsuffix', // 颜色后缀对应的字段
+ },
+ default: 'theme2', // 缺省颜色值
+ defaultSuffix: false, // 缺省时,颜色后缀,对应的值.
+ colors: {
+ suffixs: {
+ '!': true,
+ },
+ names: () => config.theme.common.colors.names, // 颜色名称-列表
+ values: () => config.theme.common.colors.values, // 适配配色-列表
+ }
+ },
+ customAttr: { // 自定义属性
+ // 'custom-codelabel-value':'${value}',
+ 'custom-codelabel-chk-chk': "${chk}",
+ 'custom-codelabel-chk-msg': "${msg}",
+ 'custom-codelabel-chk-colorG': "${colorG}",
+ 'custom-codelabel-chk-endsuffix': "${endsuffix}",
+ },
+ inlineStyle: {
+ "--theme-wz-bgcolor": "${bgcolor1}",
+ "--theme-wz-title-color": "${color1}",
+ "--theme-wz-msg-color": "${color2}",
+ "--theme-wz-msg-bgcolor": "${bgcolor2}",
+ },
+ innerHTML: '${value}',
+ renderEnd: (parse, element, oldHTML) => { // 渲染完单个元素的回调.
+
+ // let parentNode = mv.GetSiyuanBlock(element);
+ let id = mv.GetSiyuanBlockId(element)
+
+ let div= mv.GetDomByAtrrs(element,'class','cw-chk-wrap','span')[0];
+ if (div===null || div===undefined){
+ div = mv.CreateSpan(null,'cw-chk-wrap',null);
+ let span1 = mv.CreateSpan(null,'cw-chk-gaph-one',null)
+ let span2 = mv.CreateSpan(null,'cw-chk-gaph-two',null)
+ div.appendChild(span1);
+ div.appendChild(span2);
+ element.appendChild(div);
+ }
+
+ div.classList.remove('cw-chk-tick')
+ if (parse.parseInfo.chk === 'x'){
+ div.classList.add('cw-chk-tick')
+ }
+
+ div['onclick'] = async function(){
+ let msg = mv.GetAttrs(element,"custom-codelabel-chk-msg")
+ if (msg === undefined) msg=''
+
+ msg = mv.Empty(msg)?"":`|${msg}`
+ let colorG = mv.GetAttrs(element,"custom-codelabel-chk-colorG")
+ if (colorG === "undefined" || colorG===undefined){
+ colorG=""
+ }
+
+ if (div.classList.contains('cw-chk-tick')){
+ div.classList.remove('cw-chk-tick')
+ element.innerHTML = `#[ ]${msg}#${colorG} `;
+ mv.SetAttrs(element,'custom-codelabel-chk-chk',' ')
+ mv.SetAttrs(element,'custom-codelabel-value',`#[ ]${msg}#${colorG}`)
+ }
+ else{
+ div.classList.add('cw-chk-tick')
+ element.innerHTML =`#[x]${msg}#${colorG} `;
+ mv.SetAttrs(element,'custom-codelabel-chk-chk','x')
+ mv.SetAttrs(element,'custom-codelabel-value',`#[ ]${msg}#${colorG}`)
+ }
+
+ if (colorG!==null &&colorG!==undefined && colorG.includes("!")){
+ div.classList.add('cw-chk-endsuffix')
+ }else{
+ div.classList.remove('cw-chk-endsuffix')
+ }
+
+ let md = mv.GetLute().BlockDOM2StdMd(element.parentNode.parentNode.innerHTML)
console.log(md)
let kid = await mv.UpdateBlockByMd_API(id,md)
let dom = document.querySelectorAll(`div[data-node-id="${kid}"]`)[0];
@@ -984,7 +1118,7 @@ export var config = {
parse.parseInfo.count
);
- var tmd = mv.GetLute().BlockDOM2Md(parentNode.innerHTML);
+ var tmd = mv.GetLute().BlockDOM2StdMd(parentNode.innerHTML);
let did = await mv.UpdateBlockByMd_API(id, tmd);
let dom = document.querySelectorAll(`div[data-node-id="${did}"]`)[0];
render(dom);
@@ -1321,6 +1455,27 @@ export var config = {
],
},
+ codelabelrender:{ // 渲染自定义样式
+ enable: true, // 是否启用自定义样式渲染
+ render:{
+ enable: true, // 是否启用自定义样式渲染
+ toolbar:{ // 菜单栏
+ enable: true, // 是否启用自定义样式渲染
+ id: 'toolbar-theme-style-codelabelrender',
+ hotkey: () => config.theme.hotkeys.codelabel.render2,
+ label: {
+ zh_CN: '渲染整个页面',
+ zh_CNT: null,
+ fr_FR: null,
+ en_US: null,
+ other: 'Render Inline Code Parse',
+ },
+ icon: '#iconPreview',
+ index: -5,
+ },
+ },
+ },
+
menu: {
enable: true, // 行内代码编辑增强
codelabel: {
@@ -1396,6 +1551,15 @@ export var config = {
altKey: true,
key: '0',
},
+
+ render2: {
+ // 渲染( Alt + 0)
+ ctrlKey: false,
+ metaKey: true,
+ shiftKey: false,
+ altKey: true,
+ key: '0',
+ },
},
menu: {
diff --git a/script/module/codelabel-custom copy.js b/script/module/codelabel-custom copy.js
deleted file mode 100644
index ac7dfb3..0000000
--- a/script/module/codelabel-custom copy.js
+++ /dev/null
@@ -1,96 +0,0 @@
-
-import { config } from './b320config.js';
-import { isKey } from '../utils/hotkey.js';
-import {CodeLabelParse} from '../utils/codelabel-parse.js'
-import {
- toolbarItemInit,
- toolbarItemChangeStatu,
-} from './../utils/ui.js';
-
-function render(){
- for(let value of config.theme.codelabel.ptype){
- new CodeLabelParse(value,document).render();
- }
-}
-
-function CodelabelEnable(){
-
- config.theme.codelabel.render.enable=!config.theme.codelabel.render.enable
-
- // console.log("enable:"+config.theme.codelabel.render.enable);
-
- if (config.theme.codelabel.render.toolbar){
- // 更改菜单栏按钮状态
- toolbarItemChangeStatu(
- config.theme.codelabel.render.toolbar.id,
- config.theme.codelabel.render.enable,
- 'SVG',
- undefined,
- 1,
- );
- }
- }
-
-// fn__flex layout-tab-bar
-
-// 观察器的配置(需要观察什么变动)
-const tab_config = { attributes: true, childList: true, subtree: true };
-// 当观察到变动时执行的回调函数
-const tab_callback = function(mutationsList, observer) {
- // Use traditional 'for loops' for IE 11
- for(let mutation of mutationsList) {
- if (mutation.type === 'childList') {
- // console.log('A child node has been added or removed.');
- setTimeout(render, 500);
- }
- else if (mutation.type === 'attributes') {
- // console.log('The ' + mutation.attributeName + ' attribute was modified.');
- setTimeout(render, 500);
-
- }
- }
-};
-
-(() => {
- try {
-
- let body = document.body;
-
- window.onload = setTimeout(render, 0);
-
- if (config.theme.codelabel.render.toolbar.enable) {
- let Fn_guidesEnable = toolbarItemInit(
- config.theme.codelabel.render.toolbar,
- CodelabelEnable,
- );
- }
-
- body.addEventListener('keyup', (e) => {
-
- // 通过 Ctrl+Alt+0切换开关
- if (isKey(e, config.theme.hotkeys.codelabel.render)) {
- config.theme.codelabel.render.enable = !config.theme.codelabel.render.enable;
- // console.warn("bug320_3:", config.theme.codelabel.render.enable)
- }
-
- // console.log("ll:"+config.theme.codelabel.enable);
- if (config.theme.codelabel.render.enable !== false) {
- setTimeout(render, 0);
- }
-
- })
-
- // 选择需要观察变动的节点
- // const targetNode = document.getElementById('some-id');
- const targetNode = document.getElementsByClassName('layout-tab-bar')[4];
- // 创建一个观察器实例并传入回调函数
- const observer = new MutationObserver(tab_callback);
- // 以上述配置开始观察目标节点
- observer.observe(targetNode, tab_config);
-
-
- } catch (err) {
- console.error(err);
- }
-})();
-
diff --git a/script/module/codelabel-custom.js b/script/module/codelabel-custom.js
index ac7dfb3..d1c3810 100644
--- a/script/module/codelabel-custom.js
+++ b/script/module/codelabel-custom.js
@@ -6,8 +6,15 @@ import {
toolbarItemInit,
toolbarItemChangeStatu,
} from './../utils/ui.js';
+import { mv } from '../commons/domex.js';
-function render(){
+function render(el){
+ for(let value of config.theme.codelabel.ptype){
+ new CodeLabelParse(value,el).render();
+ }
+}
+
+function renderAll(){
for(let value of config.theme.codelabel.ptype){
new CodeLabelParse(value,document).render();
}
@@ -19,6 +26,7 @@ function CodelabelEnable(){
// console.log("enable:"+config.theme.codelabel.render.enable);
+
if (config.theme.codelabel.render.toolbar){
// 更改菜单栏按钮状态
toolbarItemChangeStatu(
@@ -29,6 +37,25 @@ function CodelabelEnable(){
1,
);
}
+
+
+ }
+
+ function CodelabelEnable2(){
+
+ renderAll();
+
+ if (config.theme.codelabelrender.render.toolbar){
+ // 更改菜单栏按钮状态
+ toolbarItemChangeStatu(
+ config.theme.codelabelrender.render.toolbar.id,
+ config.theme.codelabelrender.render.enable,
+ 'SVG',
+ undefined,
+ 1,
+ );
+ }
+
}
// fn__flex layout-tab-bar
@@ -41,11 +68,11 @@ const tab_callback = function(mutationsList, observer) {
for(let mutation of mutationsList) {
if (mutation.type === 'childList') {
// console.log('A child node has been added or removed.');
- setTimeout(render, 500);
+ setTimeout(renderAll, 500);
}
else if (mutation.type === 'attributes') {
// console.log('The ' + mutation.attributeName + ' attribute was modified.');
- setTimeout(render, 500);
+ setTimeout(renderAll, 500);
}
}
@@ -56,7 +83,7 @@ const tab_callback = function(mutationsList, observer) {
let body = document.body;
- window.onload = setTimeout(render, 0);
+ window.onload = setTimeout(renderAll, 0);
if (config.theme.codelabel.render.toolbar.enable) {
let Fn_guidesEnable = toolbarItemInit(
@@ -65,8 +92,17 @@ const tab_callback = function(mutationsList, observer) {
);
}
+ if (config.theme.codelabelrender.render.toolbar.enable) {
+ let Fn_guidesEnable2 = toolbarItemInit(
+ config.theme.codelabelrender.render.toolbar,
+ CodelabelEnable2,
+ );
+ }
+
body.addEventListener('keyup', (e) => {
+ var el=mv.GetSiyuanBlock(document.getSelection().focusNode.parentElement);
+
// 通过 Ctrl+Alt+0切换开关
if (isKey(e, config.theme.hotkeys.codelabel.render)) {
config.theme.codelabel.render.enable = !config.theme.codelabel.render.enable;
@@ -75,7 +111,12 @@ const tab_callback = function(mutationsList, observer) {
// console.log("ll:"+config.theme.codelabel.enable);
if (config.theme.codelabel.render.enable !== false) {
- setTimeout(render, 0);
+ // setTimeout(render, 0);
+ render(el)
+ }
+
+ if (isKey(e, config.theme.hotkeys.codelabel.render2)){
+ setTimeout(renderAll, 0);
}
})
diff --git a/script/module/rightmenu.js b/script/module/rightmenu.js
index 11c6438..1028f93 100644
--- a/script/module/rightmenu.js
+++ b/script/module/rightmenu.js
@@ -138,7 +138,7 @@ setTimeout(() => {
function getCodeLabelMark(target){
let node = target;
- if (node.localName === 'code') {
+ if (node.localName === 'span' && node.getAttribute('data-type') === 'code') {
return {
element: target,
type:'code',
diff --git a/script/static/test7.html b/script/static/test7.html
deleted file mode 100644
index 00c14a9..0000000
--- a/script/static/test7.html
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
- Document
-
-
-
-
- 手机/邮箱
-
-
-
- 密码
-
-
-
-
-
验证码
-
-
换一张
-
-
-
- 注册
-
-
-
-
-
\ No newline at end of file
diff --git a/script/static/test8.html b/script/static/test8.html
deleted file mode 100644
index 817d566..0000000
--- a/script/static/test8.html
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
-
-
-
-
- Document
-
-
-
-
-
\ No newline at end of file
diff --git a/script/utils/codelabel-parse copy.js b/script/utils/codelabel-parse copy.js
deleted file mode 100644
index 226d860..0000000
--- a/script/utils/codelabel-parse copy.js
+++ /dev/null
@@ -1,553 +0,0 @@
-import { mv } from "../commons/domex.js";
-import { config } from "../module/b320config.js";
-import { rerenderColor } from "./codetag.js";
-
-export {
- CodeLabelParse,
-}
-
-// ptypeItem 对应的值
-// { // 解析配置项
-// typeid: "唯一ID",
-// reg: '正则表达式', // 针对 innerHTML
-// tagName: "标签名称,如 code、strong",
-// customf: '禁止渲染的块属性值,如 wz', // 自定义属性 f=wz 即可。
-// className: 'css类属性名称',
-
-// // select1: `.protyle-wysiwyg *[data-node-id] ${this.tagName}` // 要选择的,默认不用设置
-// // select2: `.protyle-wysiwyg *[data-node-id][custom-f~=${this.customf}] ${this.tagName}` // 要选择的,默认不用设置
-
-// maps: { // 解析后-分组的别名,也是 parseInfo 中的字段
-// /**
-// * 以下字段名称被占用,不要用于下面列表的值中.
-// * value, // code 标签的 InnerHTML
-// * color1,bgcolor1, // 主颜色计算结果和适配背景色
-// * color2,bgcolor2, // 次颜色计算结果和适配背景色
-// * $0~$9 也不要用.
-// */
-// '$0': 'value', // 占用,code 原始的 innerHTML 内容
-// '$1': '',
-// '$2': 'title',
-// '$3': 'msg',
-// '$4': '',
-// '$5': 'color',
-// '$6': 'endsuffix',
-// '$7': '',
-// '$8': '',
-// '$9': '',
-// },
-// emptys: ['title','msg'], // 不能为null,undefined或者空值的字段,用 '$0'-'$9' 对应的别名
-// onlyValue:{
-// 'title':['1','2'],
-// },
-// emptysValues:{ // 当值为null,undefined或者空值时,要设置的值,用 'key 用:$0'-'$9' 对应的别名,value 是对应的值。
-// 'title':'ke',
-// },
-// style:{ // 样式映射信息
-// rerender:true, // 是否计算颜色
-// color: {
-// value:'color', // 主颜色对应的字段,用 $0'-'$9' 对应的别名
-// suffix:'endsuffix', // 颜色后缀对应的字段,用 $0'-'$9' 对应的别名
-// },
-// default:'theme2', // 主颜色缺省时,默认的颜色值
-// defaultSuffix:false, // 颜色后缀缺省时,默认的后缀内容,表示的值(suffixs中value)
-// colors:{
-// suffixs:{ // 颜色后缀内容,表示的值
-// '!':true,
-// },
-// names: ()=>config.theme.common.colors.names, // 主颜色,支持的颜色名称-列表,
-// values: ()=>config.theme.common.colors.values, // 主颜色,对应的适配配色-列表
-// }
-// },
-// customAttr: { // 自定义属性,key表示属性名,value是属性值,支持类似js的模板语法,${别名}, 会被实际的值替换
-// 'custom-codelabel-wz-title': "${title}",
-// 'custom-codelabel-wz-msg': "${msg}",
-// },
-// inlineStyle: { // 自定义内联样式,key表示属性名,value是属性值,支持类似js的模板语法,${别名}, 会被实际的值替换
-// "--theme-wz-bgcolor":"${bgcolor1}",
-// "--theme-wz-title-color":"${color1}",
-// "--theme-wz-msg-color":"${color2}",
-// "--theme-wz-msg-bgcolor":"${bgcolor2}",
-// },
-// innerHTML: '${value} ', // 解析后 code 标签的 innerHTML 内容,支持类似js的模板语法,${别名}, 会被实际的值替换
-// renderEnd: (parse, element,oldHTML) => { //在每个元素渲染解析完成后的回调函数
-// // parse是解析信息,$0~$9 的别名,如果开启 style.rerender为true,还有 color1,bgcolor1,color2,bgcolor2 (主颜色和适配颜色)
-// // element 当前元素(解析后的)
-// // oldHTML (解析前的 innerHTML 内容)
-// },
-// },
-
-
-/**
- * Tag解析帮助类
- */
-class CodeLabelParse {
-
- constructor(ptypeItem,domNode) {
-
- this.ptypeItem = mv.deepCopy(ptypeItem);
- this.domNode = domNode;
-
- this.tagName = ptypeItem.tagName;
- this.styleConfig = mv.deepCopy(ptypeItem.style);
-
- this.className = ptypeItem.className; // 属性名称
- this.customf = ptypeItem.customf; // 忽略解析的属性值
- this.reg = ptypeItem.reg; // 正则表达式
- this.parseInfo = {}; // 解析后内容
- this.maps = ptypeItem.maps; // 映射关系
- this.emptys = ptypeItem.emptys; // 不能为空的字段
- this.onlyValue = ptypeItem.onlyValue;
- this.emptysValues = ptypeItem.emptysValues; // 为空字段的默认值
-
-
- this.select1 = `.protyle-wysiwyg *[data-node-id] ${this.tagName}` // 要选择的
- this.select2 = `.protyle-wysiwyg *[data-node-id][custom-f~=${this.customf}] ${this.tagName}` // 排除的
-
- if (mv.Empty(this.ptypeItem.select1) === false){
- this.select1 = this.ptypeItem.select1
- }
- if (mv.Empty(this.ptypeItem.select2) === false){
- this.select2 = this.ptypeItem.select2
- }
-
- this.innerHTML = ptypeItem.innerHTML; // 内置html
- this.customAttr = ptypeItem.customAttr; // 自定义属性
- this.inlineStyle = ptypeItem.inlineStyle; // 内敛样式
-
- this.renderEnd = ptypeItem.renderEnd;
-
- /**
- * 解决无法在 class 内调用的问题
- */
-
- this.renderEnd = this.renderEnd.bind(this);
- this.getElementWithoutCustomf = this.getElementWithoutCustomf.bind(this)
- this.isEmptyParse = this.isEmptyParse.bind(this)
- this.formatInlineHtml = this.formatInlineHtml.bind(this)
- this.formatInlineHtml = this.formatInlineHtml.bind(this)
- this.formatCustomAttr = this.formatCustomAttr.bind(this)
- this.formatInlineStyle = this.formatInlineStyle.bind(this)
- this.render = this.render.bind(this)
- this.renderSingle = this.renderSingle.bind(this)
- this.clacParseInfo = this.clacParseInfo.bind(this)
- this.reinitFormat = this.reinitFormat.bind(this)
- }
-
- /**
- * 重新初始化变量值
- * @param {object} ptypeItem
- */
- reinitFormat(ptypeItem) {
-
- this.ptypeItem = mv.deepCopy(ptypeItem);
-
- this.tagName=ptypeItem.tagName;
-
- this.styleConfig = mv.deepCopy(ptypeItem.style);
-
- this.className = ptypeItem.className; // 属性名称
- this.customf = ptypeItem.customf; // 忽略解析的属性值
- this.reg = ptypeItem.reg; // 正则表达式
- this.parseInfo = {}; // 解析后内容
- this.maps = ptypeItem.maps; // 映射关系
- this.emptys = ptypeItem.emptys; // 不能为空的字段
- this.onlyValue = ptypeItem.onlyValue;
- this.emptysValues = ptypeItem.emptysValues; // 为空字段的默认值
-
- this.innerHTML = ptypeItem.innerHTML; // 内置html
- this.customAttr = ptypeItem.customAttr; // 自定义属性
- this.inlineStyle = ptypeItem.inlineStyle; // 内敛样式
-
- this.select1 = `.protyle-wysiwyg *[data-node-id] ${this.tagName}` // 要选择的
- this.select2 = `.protyle-wysiwyg *[data-node-id][custom-f~=${this.customf}] ${this.tagName}` // 排除的
-
- if (mv.Empty(this.ptypeItem.select1) === false){
- this.select1 = this.ptypeItem.select1
- }
- if (mv.Empty(this.ptypeItem.select2) === false){
- this.select2 = this.ptypeItem.select2
- }
-
- this.innerHTML = mv.deepCopy(ptypeItem.innerHTML); // 内置html
- this.customAttr = mv.deepCopy(ptypeItem.customAttr); // 自定义属性
- this.inlineStyle = mv.deepCopy(ptypeItem.inlineStyle); // 内敛样式
- }
-
-
- /**
- * 获取不在 customf 属性影响范围内的所有 code 标签
- * @param {*} select1
- * @param {*} select2
- * @param {*} domNode
- * @returns
- */
- getElementWithoutCustomf(select1,select2,domNode) {
-
- let rst = mv.GetDomBySelectors(
- select1,select2,
- domNode
- );
- return rst;
- }
-
- /**
- * 判断是否符合条件
- * @param {Object} parseInfo 解析后的值
- * @param {Array} emptys 不能为空的字段
- * @param {Object} onlyValue 不为空时,必须包含在内的值
- * @returns
- */
- isEmptyParse(parseInfo,emptys,onlyValue) {
-
- let isEmpty = false;
- let count = emptys.length;
- for (let i = 0; i < count; i++) {
- let key = emptys[i];
- let value = parseInfo[key];
- if (mv.Empty(value)) {
- isEmpty = true;
- break;
- }
-
- if (onlyValue!==null&&onlyValue!==undefined){
- let values = []
- if (typeof onlyValue[key] === "function"){
- values = onlyValue[key]();
- }else{
- values = onlyValue[key];
- }
-
- if (values.length > 0 && values.includes(value) === false){
- isEmpty = true;
- break;
- }
- }
-
- }
-
-
- return isEmpty;
- }
-
- /**
- * 格式化 inlinehtml
- * @param {string} innerHTML 自定义书属性信息
- * @param {object} parseInfo 解析信息
- * @param {Array} maps 映射信息
- * @returns 返回格式化的信息
- */
- formatInlineHtml(innerHTML, parseInfo, maps) {
-
- // 替换 $1~$9 的别名
- for (let k in parseInfo) {
- let tv = parseInfo[k];
- if (tv === undefined || tv === null || mv.Empty(tv)) {
- tv = this.emptysValues[k];
- }
- innerHTML = innerHTML.replace(`\$\{${k}\}`, tv);
- }
-
- // 替换 $1~$9
- for (let k in maps) {
- let tv = parseInfo[maps[k]];
- if (tv === undefined || tv === null || mv.Empty(tv)) {
- tv = this.maps[k];
- }
- innerHTML = innerHTML.replace(`\$\{${k}\}`, tv);
- }
-
- return innerHTML;
- }
-
- /**
- * 格式化 自定义属性
- * @param {object} customAttr 自定义属性信息
- * @param {object} parseInfo 解析信息
- * @param {Array} maps 映射信息
- * @returns 返回格式化的信息
- */
- formatCustomAttr(customAttr, parseInfo, maps) {
-
- for (let ca in customAttr) {
-
- // 替换 $1~$9 的别名
- for (let k in parseInfo) {
-
- let tv = parseInfo[k];
- if (tv === undefined || tv === null || mv.Empty(tv)) {
- tv = this.emptysValues[k];
- }
-
- customAttr[ca] = customAttr[ca].replace(`\$\{${k}\}`, tv);
- }
-
- // 替换 $1~$9
- for (let k in maps) {
-
- let tv = parseInfo[maps[k]];
- if (tv === undefined || tv === null || mv.Empty(tv)) {
- tv = this.maps[k];
- }
-
- customAttr[ca] = customAttr[ca].replace(`\$\{${k}\}`, tv);
- }
-
- customAttr["custom-codelabel-value"] = parseInfo.value;
- }
-
-
- return mv.deepCopy(customAttr);
- }
-
-
-
- /**
- * 格式化 内敛样式
- * @param {object} inlineStyle 自定义样式信息
- * @param {object} parseInfo 解析信息
- * @param {object} maps 映射信息
- * @returns 返回格式化的信息
- */
- formatInlineStyle(inlineStyle, parseInfo, maps) {
-
- for (let ca in this.inlineStyle) {
-
- // 替换 $1~$9 的别名
- for (let k in parseInfo) {
-
- let tv = parseInfo[k];
- if (tv === undefined || tv === null || mv.Empty(tv)) {
- tv = this.emptysValues[k];
- }
-
- inlineStyle[ca] = inlineStyle[ca].replace(`\$\{${k}\}`, tv);
- }
-
- // 替换 $1~$9
- for (let k in maps) {
-
- let tv = parseInfo[maps[k]];
- if (tv === undefined || tv === null || mv.Empty(tv)) {
- tv = this.maps[k];
- }
-
- inlineStyle[ca] = inlineStyle[ca].replace(`\$\{${k}\}`, tv);
- }
-
- }
-
- return mv.deepCopy(inlineStyle);
-
- }
-
-
- /**
- * 渲染单个
- * @param {元素} e
- * @param {*} oldHTML
- * @param {*} isReParseInfo
- */
- renderSingle(e, parseInfo) {
-
- // 添加lclassName
- e.classList.add(this.className)
-
- // 重新计算 html
- this.innerHTML = this.formatInlineHtml(this.innerHTML, parseInfo, this.maps);
- e.innerHTML = this.innerHTML;
-
-
- // 设置属性
- this.customAttr = this.formatCustomAttr(this.customAttr, parseInfo, this.maps);
- for (let k in this.customAttr) {
- e.setAttribute(k, this.customAttr[k]);
- }
-
-
- // 设置 inline style
- this.inlineStyle = this.formatInlineStyle(this.inlineStyle, parseInfo, this.maps);
- for (let k in this.inlineStyle) {
- mv.SetStyleValue(e.style, k, this.inlineStyle[k]);
- }
-
- // 添加事件
- if (e.tagName.toLowerCase() === "code"){
-
- e.addEventListener('dblclick',(event)=>{
-
- config.theme.codelabel.render.enable = false;
-
- e.innerHTML = parseInfo["value"];
-
- while(e.attributes.length > 0)
- e.removeAttribute(e.attributes[0].name);
-
- e.focus();
-
- });
-
- }
- }
-
- /**
- * 计算 ParseInfo 的字段和对应值
- * @param {string} oldHTML
- * @returns
- */
- clacParseInfo(oldHTML) {
-
- let parseInfo = {};
- let simplePatt = new RegExp(this.reg);
-
- if (simplePatt.test(oldHTML)) {
- // 缓存 Regex.$1~Regex.$9 的值
- let vmap = {
- '$1': RegExp.$1,
- '$2': RegExp.$2,
- '$3': RegExp.$3,
- '$4': RegExp.$4,
- '$5': RegExp.$5,
- '$6': RegExp.$6,
- '$7': RegExp.$7,
- '$8': RegExp.$8,
- '$9': RegExp.$9,
- }
-
- // 设置 parseInfo 对象
- for (let k in this.maps) {
- let v = this.maps[k];
- if (mv.Empty(v) === false) {
- parseInfo[v] = vmap[k];
- }
- }
-
- parseInfo['value'] = oldHTML;
-
- // 如果需要计算配色
- if (this.styleConfig.rerender === true) {
- let styleinfo = new StyleColorInfo(this.styleConfig);
- styleinfo.rerender(
- parseInfo[this.styleConfig.color.value], // 颜色值
- parseInfo[this.styleConfig.color.suffix] // 颜色后缀
- );
-
- // 添加计算结果到 parseInfo
- for (let k in styleinfo) {
- parseInfo[k] = styleinfo[k];
- }
- }
- }
-
- return mv.deepCopy(parseInfo);
-
- }
-
- /**
- * 渲染
- * @param {html} params
- */
- render() {
- // 获取符合条件的 code 标签
- let elements = this.getElementWithoutCustomf(this.select1,this.select2,this.domNode);
-
- if (elements == null ||elements == undefined ) return;
-
- // 正则表达式
- let simplePatt = new RegExp(this.reg);
-
- for (let e of elements) {
-
- // 重新获取模板
- this.reinitFormat(this.ptypeItem);
-
- // 获取 code 标签 中的原始内容。
- let oldHTML = e.innerHTML;
-
- if (simplePatt.test(oldHTML)) {
-
- // 计算 ParseInfo
- this.parseInfo = this.clacParseInfo(oldHTML)
-
- // 判断关键解析结构是否为空,不为空的时候,设置
- let isEmpty = this.isEmptyParse(this.parseInfo,this.emptys,this.onlyValue);
- if (isEmpty === false && e.className !== this.className) {
-
- // 渲染当个节点
- this.renderSingle(e, this.parseInfo);
-
- // 渲染结束事件
- this.renderEnd(this, e, oldHTML);
-
- // e.addEventListener('focus',(event)=>{
- // event.target.style.background = 'pink';
- // console.warn("1");
- // });
-
- // e.addEventListener('blur', (event) => {
- // event.target.style.background = '';
- // console.warn("2");
- // });
-
- }
-
-
- }
-
- }
-
- }
-
-
-}
-
-
-/**
- * 样式-颜色信息转换类
- */
-class StyleColorInfo {
- constructor(styleConfig, colorEndsuffix) {
-
- this.styleConfig = mv.deepCopy(styleConfig)
- this.colorEndsuffix = colorEndsuffix
- let gcolor = mv.deepCopy(this.styleConfig.colors.values()[this.styleConfig.default]);
- this.bgcolor1 = gcolor.value
- this.color1 = gcolor.titlecolor
- this.bgcolor2 = gcolor.msgbgcolor
- this.color2 = gcolor.msgcolor
- }
-
-
- /**
- *
- * @param {string} color 主颜色
- * @param {string} suffix 主颜色后缀
- */
- async rerender(color, suffix) {
-
- let vsuffix = false;
-
- if (suffix !== undefined && suffix !== null && this.styleConfig.colors.suffixs.hasOwnProperty(suffix)) {
- vsuffix = this.styleConfig.colors.suffixs[suffix];
- }
- else {
- vsuffix = this.styleConfig.defaultSuffix
- }
-
- let vlook = rerenderColor(color, vsuffix,
- this.styleConfig.colors.values(),
- this.styleConfig.colors.names(),
- this.styleConfig.default)
-
- this.bgcolor1 = vlook.value
- this.color1 = vlook.titlecolor
- this.bgcolor2 = vlook.msgbgcolor
- this.color2 = vlook.msgcolor
-
- // this.bgcolor1 = vlook.value
- // this.color1 = vlook.titlecolor
- // this.bgcolor2 = vlook.msgcolor
- // this.color2 = vlook.msgbgcolor
- }
-
-}
\ No newline at end of file
diff --git a/script/utils/codelabel-parse.js b/script/utils/codelabel-parse.js
index c63c6c7..a0ad5a5 100644
--- a/script/utils/codelabel-parse.js
+++ b/script/utils/codelabel-parse.js
@@ -99,6 +99,7 @@ class CodeLabelParse {
this.maps = ptypeItem.maps; // 映射关系
this.emptys = ptypeItem.emptys; // 不能为空的字段
this.onlyValue = ptypeItem.onlyValue;
+ this.ignoreValue = ptypeItem.ignoreValue;
this.emptysValues = ptypeItem.emptysValues; // 为空字段的默认值
@@ -125,6 +126,7 @@ class CodeLabelParse {
this.renderEnd = this.renderEnd.bind(this);
this.getElementWithoutCustomf = this.getElementWithoutCustomf.bind(this)
this.isEmptyParse = this.isEmptyParse.bind(this)
+ this.isIgnoreParse = this.isIgnoreParse.bind(this)
this.formatInlineHtml = this.formatInlineHtml.bind(this)
this.formatInlineHtml = this.formatInlineHtml.bind(this)
this.formatCustomAttr = this.formatCustomAttr.bind(this)
@@ -155,6 +157,8 @@ class CodeLabelParse {
this.maps = ptypeItem.maps; // 映射关系
this.emptys = ptypeItem.emptys; // 不能为空的字段
this.onlyValue = ptypeItem.onlyValue;
+ this.ignoreValue = ptypeItem.ignoreValue;
+
this.emptysValues = ptypeItem.emptysValues; // 为空字段的默认值
this.innerHTML = ptypeItem.innerHTML; // 内置html
@@ -185,7 +189,7 @@ class CodeLabelParse {
* @returns
*/
getElementWithoutCustomf(select1,select2,domNode) {
-
+
let rst = mv.GetDomBySelectors(
select1,select2,
domNode
@@ -232,6 +236,47 @@ class CodeLabelParse {
return isEmpty;
}
+ /**
+ * 判断是否是忽略处理的值
+ * @param {Object} parseInfo 解析后的值
+ * @param {Array} emptys 不能为空的字段
+ * @param {Object} onlyValue 不为空时,必须包含在内的值
+ * @returns
+ */
+ isIgnoreParse(parseInfo,emptys,ignoreValue){
+
+ let isEmpty = false;
+ let count = emptys.length;
+
+ for (let i = 0; i < count; i++) {
+
+ let key = emptys[i];
+ let value = parseInfo[key];
+
+ if (mv.Empty(value)) {
+ isEmpty = true;
+ break;
+ }
+
+ if (ignoreValue!==null&&ignoreValue!==undefined){
+ let values = []
+ if (typeof ignoreValue[key] === "function"){
+ values = ignoreValue[key]();
+ }else{
+ values = ignoreValue[key];
+ }
+
+ if (values.length > 0 && values.includes(value) === true){
+ isEmpty = true;
+ break;
+ }
+ }
+
+ }
+
+ return isEmpty;
+ }
+
/**
* 格式化 inlinehtml
* @param {string} innerHTML 自定义书属性信息
@@ -476,7 +521,8 @@ class CodeLabelParse {
// 判断关键解析结构是否为空,不为空的时候,设置
let isEmpty = this.isEmptyParse(this.parseInfo,this.emptys,this.onlyValue);
- if (isEmpty === false && e.className !== this.className) {
+ let isIngore = this.isIgnoreParse(this.parseInfo,this.emptys,this.ignoreValue);
+ if (isEmpty === false && isIngore==false && e.className !== this.className) {
// 渲染当个节点
this.renderSingle(e, this.parseInfo);
diff --git a/script/utils/ui-ex.js b/script/utils/ui-ex.js
index 3d34ace..1da99f3 100644
--- a/script/utils/ui-ex.js
+++ b/script/utils/ui-ex.js
@@ -47,22 +47,26 @@ async function showUtil(element,callback){
let cts = new MessageboxInputs(its);
let idom=cts.Create(
async ()=>{
- console.log("Ok")
- console.log(cts.Doms.msg.value)
+ // console.log("Ok")
+ // console.log(cts.Doms.msg.value)
let value =cts.Doms.msg.value;
-
// 清空所有属性
- element.className = "";
+ // element.className = "";
+ console.log("showUtil")
+ console.log(element)
+
while (element.attributes.length > 0) {
element.removeAttributeNode(element.attributes[0]);
}
+ mv.SetAttrs(element,'data-type','code');
+
// 获取父节点
let parentNode=mv.GetSiyuanBlock(element);
let id = mv.GetSiyuanBlockId(element);
element.innerHTML = value;
- var tmd = mv.GetLute().BlockDOM2Md(parentNode.innerHTML);
+ var tmd = mv.GetLute().BlockDOM2StdMd(parentNode.innerHTML);
let did = await mv.UpdateBlockByMd_API(id, tmd);
let dom = document.querySelectorAll(`div[data-node-id="${did}"]`)[0];
render(dom);
@@ -70,7 +74,7 @@ async function showUtil(element,callback){
},
async ()=>{
console.log("NO")
- console.log(cts.Doms.msg.value)
+ // console.log(cts.Doms.msg.value)
},"cbeditor","width: 480px;top: 45%; left: 45%;",""
);
diff --git a/style/blocks/v-block-inline-style.css b/style/blocks/v-block-inline-style.css
index e36472c..1a87b42 100644
--- a/style/blocks/v-block-inline-style.css
+++ b/style/blocks/v-block-inline-style.css
@@ -1,13 +1,13 @@
/* --------------- 删除线 --------------- */
-.protyle-wysiwyg s{
+.protyle-wysiwyg span[data-type='s']{
color: var(--v-block-del-color) !important;
text-decoration: line-through solid !important;
}
/* --------------- 删除线 end --------------- */
/* --------------- 键盘按键块 --------------- */
-.protyle-wysiwyg kbd {
+.protyle-wysiwyg span[data-type='kbd'] {
font: var(--v-block-kbd-font);
border-radius: var(--v-block-kbd-border-radius);
margin: 0;
@@ -76,8 +76,8 @@
* 悬浮提示
*/
-kbd:hover,
-code:hover
+span[data-type='kbd']:hover,
+span[data-type='code']:hover
{
filter: brightness(1.1)
}
diff --git a/style/customs/code-rb-coad copy.css b/style/customs/code-rb-coad copy.css
deleted file mode 100644
index 8e8a451..0000000
--- a/style/customs/code-rb-coad copy.css
+++ /dev/null
@@ -1,42 +0,0 @@
-.protyle-wysiwyg *[data-node-id] :not([custom-f~=rb]) .v-rb-coat[custom-codelabel-rb-coat-showe="false"] {
- background: linear-gradient(45deg, var(--theme-rb-bgcolor) 0%, var(--theme-rb-bgcolor) 25%,var(--d-f-c) 25%, var(--d-f-c) 50%,var(--theme-rb-bgcolor) 50%, var(--theme-rb-bgcolor) 75%,var(--d-f-c) 75%, var(--d-f-c) 100%) ;
- border-color: var(--theme-rb-bgcolor) ;
- /* color: var(--d-bc); */
- color: var(--theme-rb-title-color);
- padding: 5px;
- border-radius: 3px;
-}
-
-.protyle-wysiwyg *[data-node-id] :not([custom-f~=rb]) .v-rb-coat[custom-codelabel-rb-coat-showe="true"] {
- background: var(--theme-rb-msg-color);
- color: var(--theme-rb-bgcolor);
- box-shadow: 0 0 0 1px var(--d-f-c), 0 2px 0 0 var(--d-f-c) inset;
- text-shadow: none;
- padding: 5px;
- border-radius: 3px;
-}
-
-.protyle-wysiwyg *[data-node-id] .v-rb-coat span{
- display: none;
-}
-
-.protyle-wysiwyg *[data-node-id] .v-rb-coat[custom-codelabel-rb-coat-showe="false"]::before{
- /* color: var(--theme-wz-title-color); */
- content: attr(custom-codelabel-rb-coat-text);
- padding-right: 3px;
-}
-
-.protyle-wysiwyg *[data-node-id] .v-rb-coat[custom-codelabel-rb-coat-showe="true"]::after{
- /* color: #1f2e3b; */
- /* color: var(--theme-wz-msg-color); */
- /* background-color: var(--theme-wz-msg-bgcolor); */
- content: attr(custom-codelabel-rb-coat-data);
- padding: 3px;
- border-radius: 3px;
-}
-
-.protyle-wysiwyg .v-rb-coat:hover {
- box-shadow: 0 0 0 1px #2aa899, 0 2px 0 0 #2aa899 inset !important;
- display: inline-block;
- transform: scale(1.2)
-}
\ No newline at end of file
diff --git a/style/customs/codelabel-block-bq copy.css b/style/customs/codelabel-block-bq copy.css
deleted file mode 100644
index c72860a..0000000
--- a/style/customs/codelabel-block-bq copy.css
+++ /dev/null
@@ -1,122 +0,0 @@
-/* --------------- 引用块 --------------- */
-
-/*在列表中*/
-/* .protyle-wysiwyg .li > .bq[bqtype^="color"]{
- border-left: 5px solid rgba(148, 152, 160, .2);
- background: 0 0;
- border-radius: 0;
- padding: 0 0.5em;
-} */
-
-/*独立的*/
-.protyle-wysiwyg .bq[bqtype="color"] {
-
- /* color: var(--v-block-bq-color);
- background: var(--v-block-bq-bg-color);
- */
- color: var(--theme-bq-color1);
- background: var(--theme-bq-bgcolor);
- border-radius: var(--v-block-bq-border-radius);
- padding: 0.75em 1em;
- margin-top: 0;
- margin-bottom: 20px;
-}
-
-.protyle-wysiwyg .bq[bqtype="color"] code.bqcolor{
- background: var(--theme-bq-bgcolor);
-}
-
-
-.protyle-wysiwyg .bq[bqtype="color1"] {
-
- /* color: var(--v-block-bq-color);
- background: var(--v-block-bq-bg-color);
- */
- color: var(--b3-theme-on-background);
- background: var(--b3-theme-background);
- border-width: 5px ;
- border-style: solid ;
- border-color: var(--theme-bq-bgcolor);
- border-radius: var(--v-block-bq-border-radius);
- padding: 0.75em 1em;
- margin-top: 0;
- margin-bottom: 20px;
-}
-
-
-.protyle-wysiwyg .bq[bqtype="color1"] code.bqcolor{
- background: var(--b3-theme-background);
-}
-
-/* --------------- 引用块 end --------------- */
-
-
-.protyle-wysiwyg .bq[bqtype^="color"] code.bqcolor span{
- display: none;
-}
-
-/* custom-select-endsuffix */
-
-.protyle-wysiwyg .bq[bqtype^="color"] code.bqcolor::before{
-
- content: attr(custom-select-data);
-
- font: var(--v-block-kbd-font);
- border-radius: var(--v-block-kbd-border-radius);
- margin: 1px;
- padding: 0 4px;
- color: var(--v-block-kbd-color);
- border: 2px solid var(--v-block-kbd-border-shadow);
- border-left-color: var(--v-block-kbd-border-color);
- border-top-color: var(--v-block-kbd-border-color);
- background: var(--v-theme2);
- box-shadow: 0 0 0 1px var(--v-block-kbd-border-shadow);
- transform: translate(-3px,-2px)
-}
-
-
-/* .protyle-wysiwyg .bq[bqtype^="color"] code::after{
-
- content: attr(custom-select-endsuffix);
-
- font: var(--v-block-kbd-font);
- border-radius: var(--v-block-kbd-border-radius);
- margin: 1px;
- padding: 0 4px;
- color: var(--v-block-kbd-color);
- border: 2px solid var(--v-block-kbd-border-shadow);
- border-left-color: var(--v-block-kbd-border-color);
- border-top-color: var(--v-block-kbd-border-color);
- background: var(--v-theme1);
- box-shadow: 0 0 0 1px var(--v-block-kbd-border-shadow);
- transform: translate(-3px,-2px)
-} */
-
-.protyle-wysiwyg .bq[bqtype^="color"] code.bqcolor li::after{
-
- content: attr(custom-li-data);
-
- font: var(--v-block-kbd-font);
- border-radius: var(--v-block-kbd-border-radius);
- margin: 1px;
- padding: 0 4px;
- color: var(--v-block-kbd-color);
- border: 2px solid var(--v-block-kbd-border-shadow);
- border-left-color: var(--v-block-kbd-border-color);
- border-top-color: var(--v-block-kbd-border-color);
- background: var(--key-bg);
- box-shadow: 0 0 0 1px var(--v-block-kbd-border-shadow);
- transform: translate(-3px,-2px)
-}
-
-.protyle-wysiwyg .bq[bqtype^="color"] code.bqcolor ul{
- display: none;
- list-style: none;
-}
-
-.protyle-wysiwyg .bq[bqtype^="color"] code.bqcolor:hover ul{
- display: block;
- list-style: none;
- /* margin-left: attr(custom-left); */
-}
-
diff --git a/style/customs/codelabel-block-bq.css b/style/customs/codelabel-block-bq.css
index d04fa95..58e562b 100644
--- a/style/customs/codelabel-block-bq.css
+++ b/style/customs/codelabel-block-bq.css
@@ -22,7 +22,7 @@
margin-bottom: 20px;
}
-.protyle-wysiwyg .bq[bqtype="color"] code.bqcolor{
+.protyle-wysiwyg .bq[bqtype="color"] span[data-type='code'].bqcolor{
background: var(--theme-bq-bgcolor);
}
diff --git a/style/customs/codelabel-tick copy.css b/style/customs/codelabel-tick copy.css
deleted file mode 100644
index 666886c..0000000
--- a/style/customs/codelabel-tick copy.css
+++ /dev/null
@@ -1,85 +0,0 @@
-span.hide{
- display: none;
-}
-.cw-chk-wrap {
- display: inline-block;
- width: 22px;
- height: 22px;
- -ms-transform: rotate(45deg);
- /* IE 9 */
- -webkit-transform: rotate(45deg);
- /* Chrome, Safari, Opera */
- transform: rotate(45deg) translate(4px,3px);
- /* background-color: var(--theme-wz-title-color) !important; */
-}
-
-code.custom-codelabel-chk[custom-codelabel-chk-msg]:not(:empty)::after{
- content: attr(custom-codelabel-chk-msg);
- height: 22px;
- margin: 0px !important;
- padding: 3px 4px !important;
- background-color: var(--theme-wz-bgcolor) !important;
- color: var(--theme-wz-title-color) !important;
- /* transform: translate(-10px,0px) !important; */
-}
-
-.cw-chk-gaph-one {
- position: absolute;
- width: 3px;
- height: 9px;
- background-color: #ccc !important;
- left: 11px;
- top: 6px;
-}
-
-.cw-chk-gaph-two {
- position: absolute;
- width: 3px;
- height: 3px;
- background-color: #ccc !important;
- left: 8px;
- top: 12px;
-}
-
-.cw-chk-tick .cw-chk-gaph-one,
-.cw-chk-tick .cw-chk-gaph-two {
- background-color: #4caf50 !important;
-}
-
-code.custom-codelabel-chk{
- padding-left: 0px !important;
- padding-right: 0px !important;
- background-color: var(--theme-wz-title-bgcolor) !important;
- /* border: 1px solid var(--theme-wz-bgcolor) !important; */
-}
-
-code.custom-codelabel-chk[custom-codelabel-chk-chk=" "]{
- border: 1px solid #ccc !important;
-}
-
-code.custom-codelabel-chk[custom-codelabel-chk-chk="x"]{
- border: 1px solid var(--theme-wz-bgcolor) !important;
-}
-
-
-code.custom-codelabel-chk[custom-codelabel-chk-msg]:not(:empty)::after{
- content: attr(custom-codelabel-chk-msg);
- height: 22px;
- margin: 0px !important;
- padding: 3px 4px !important;
- color: var(--theme-wz-title-color) !important;
-}
-
-code.custom-codelabel-chk[custom-codelabel-chk-chk="x"][custom-codelabel-chk-msg]:not(:empty)::after{
- background-color: var(--theme-wz-bgcolor) !important;
- /* color: var(--theme-wz-title-color) !important; */
-}
-
-code.custom-codelabel-chk[custom-codelabel-chk-chk=" "][custom-codelabel-chk-msg]:not(:empty)::after{
- background-color: #ccc !important;
-}
-
-[custom-codelabel-chk-endsuffix='!'] .cw-chk-tick .cw-chk-gaph-one,
-[custom-codelabel-chk-endsuffix='!'] .cw-chk-tick .cw-chk-gaph-two {
- background-color: var(--theme-wz-bgcolor) !important;
-}
\ No newline at end of file
diff --git a/style/customs/codelabel-tick.css b/style/customs/codelabel-tick.css
index ad8b012..f4ccb9f 100644
--- a/style/customs/codelabel-tick.css
+++ b/style/customs/codelabel-tick.css
@@ -3,21 +3,24 @@ span.hide{
}
.cw-chk-wrap {
display: inline-block;
- width: 22px;
- height: 22px;
+ width: 21px;
+ /* width: 22px; */
+ height: 15px;
+ /* height: 22px; */
-ms-transform: rotate(45deg);
/* IE 9 */
-webkit-transform: rotate(45deg);
/* Chrome, Safari, Opera */
- transform: rotate(45deg) translate(4px,3px);
+ transform: rotate(45deg) translate(2px,-2px);
/* background-color: var(--theme-wz-title-color) !important; */
}
span.custom-codelabel-chk[custom-codelabel-chk-msg]:not(:empty)::after{
content: attr(custom-codelabel-chk-msg);
- height: 22px;
+ height: 15px;
+ /* height: 22px; */
margin: 0px !important;
- padding: 3px 4px !important;
+ /* padding: 3px 4px !important; */
background-color: var(--theme-wz-bgcolor) !important;
color: var(--theme-wz-title-color) !important;
/* transform: translate(-10px,0px) !important; */
@@ -44,6 +47,13 @@ span.custom-codelabel-chk[custom-codelabel-chk-msg]:not(:empty)::after{
.cw-chk-tick .cw-chk-gaph-one,
.cw-chk-tick .cw-chk-gaph-two {
background-color: #4caf50 !important;
+ /* background-color: var(--theme-wz-bgcolor) !important; */
+}
+
+span.cw-chk-endsuffix .cw-chk-tick .cw-chk-gaph-one,
+span.cw-chk-endsuffix .cw-chk-tick .cw-chk-gaph-two {
+ /* background-color: #4caf50 !important; */
+ background-color: var(--theme-wz-bgcolor) !important;
}
span.custom-codelabel-chk{
@@ -66,7 +76,7 @@ span.custom-codelabel-chk[custom-codelabel-chk-msg]:not(:empty)::after{
content: attr(custom-codelabel-chk-msg);
height: 22px;
margin: 0px !important;
- padding: 3px 4px !important;
+ padding: 3px 5px !important;
color: var(--theme-wz-title-color) !important;
}
diff --git a/style/customs/codelabel-todo copy.css b/style/customs/codelabel-todo copy.css
deleted file mode 100644
index 4997565..0000000
--- a/style/customs/codelabel-todo copy.css
+++ /dev/null
@@ -1,45 +0,0 @@
-
-.protyle-wysiwyg *[data-node-id] .vk-todo span{
- display: none;
-}
-
-.protyle-wysiwyg *[data-node-id] .vk-todo{
-
- font: var(--v-block-kbd-font);
- border-radius: var(--v-block-kbd-border-radius);
- margin: 0px;
- padding: 6px 4px;
- color: var(--v-block-kbd-color);
- border: 2px solid var(--v-block-kbd-border-shadow);
- border-left-color: var(--v-block-kbd-border-color);
- border-top-color: var(--v-block-kbd-border-color);
- background: var(--key-bg);
- box-shadow: 0 0 0 1px var(--v-block-kbd-border-shadow);
-
-}
-
-.protyle-wysiwyg *[data-node-id] .vk-todo button
-{
- font: var(--v-block-kbd-font);
- border-radius: var(--v-block-kbd-border-radius);
- margin: 1px;
- padding: 0 4px;
- color: var(--v-block-kbd-color);
- border: 2px solid var(--v-block-kbd-border-shadow);
- border-left-color: var(--v-block-kbd-border-color);
- border-top-color: var(--v-block-kbd-border-color);
- background: var(--key-bg);
- box-shadow: 0 0 0 1px var(--v-block-kbd-border-shadow);
- transform: translate(-3px,-2px)
-}
-
-.protyle-wysiwyg *[data-node-id] .vk-todo button:hover{
-
- box-shadow: 0 0 0 1px var(--v-theme2);
- transform: translate(-4px,-3px) scale(1.1)
-}
-
-.protyle-wysiwyg *[data-node-id] .vk-todo button::after{
- content: attr(custom-codelabel-todo-count);
-}
-
diff --git a/style/customs/codelabel-wz copy.css b/style/customs/codelabel-wz copy.css
deleted file mode 100644
index 98a8068..0000000
--- a/style/customs/codelabel-wz copy.css
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 1. 单独安装指南:
- a. 在 css 文件中添加 codelabel-wz.css 的路径
- @import url(path/to/codelabel-wz.css);
- b. 把 script 文件夹复制到主题的根目录中
- c.如果没有 themes.js ,就把压缩包的 themes.js 直接复制过去;如果有,就添加:
-
- loadScript("/appearance/themes/minilook/script/module/codelabel.js");
-
- 注意把其中的 minilook 改成对应的主题文件夹名称。
-
- * 2. 新建属性 f=wz 可以屏蔽该效果的渲染。
- *
- * 制作人: 路人二
- * 版本: V0.0.1-dev
- * 时间: 2022-04-02
- * 更新日志:
- * 1. 渲染 `#微章标题|微章内容#(颜色)`
- */
-
-/* ****************** custom-codelabel-wz *******************/
-.protyle-wysiwyg *[data-node-id] :not([custom-f~=wz]) code.custom-codelabel-wz {
- /* background-color: #2aa899; */
- background-color: var(--theme-wz-bgcolor);
- padding: 5px;
- border-radius: 3px;
-}
-
-.custom-codelabel-wz:hover {
- filter: brightness(1.1)
-}
-
-.protyle-wysiwyg *[data-node-id] .custom-codelabel-wz span{
- display: none;
-}
-
-.protyle-wysiwyg *[data-node-id] .custom-codelabel-wz::before{
- color: var(--theme-wz-title-color);
- content: attr(custom-codelabel-wz-title);
-}
-
-.protyle-wysiwyg *[data-node-id] .custom-codelabel-wz:not([custom-codelabel-wz-msg='']):after{
- /* color: #1f2e3b; */
- color: var(--theme-wz-msg-color);
- background-color: var(--theme-wz-msg-bgcolor);
- content: attr(custom-codelabel-wz-msg);
- padding: 3px;
- margin-left: 3px;
- border-radius: 3px;
-}
diff --git a/style/customs/codelabel-wz-line.css b/style/customs/codelabel-wz-line.css
index 03b4426..e7dde58 100644
--- a/style/customs/codelabel-wz-line.css
+++ b/style/customs/codelabel-wz-line.css
@@ -1,5 +1,5 @@
/* ****************** custom-codelabel-wzline *******************/
-.protyle-wysiwyg *[data-node-id] :not([custom-f~=wzline]) code.custom-codelabel-wzline {
+.protyle-wysiwyg *[data-node-id] :not([custom-f~=wzline]) span[data-type='code'].custom-codelabel-wzline {
/* background-color: #2aa899; */
background-color: var(--theme-wzline-bgcolor);
padding: 5px;
diff --git a/style/customs/codelabel-wz.css b/style/customs/codelabel-wz.css
index 5f82b1d..6c7349b 100644
--- a/style/customs/codelabel-wz.css
+++ b/style/customs/codelabel-wz.css
@@ -19,7 +19,7 @@
*/
/* ****************** custom-codelabel-wz *******************/
-.protyle-wysiwyg *[data-node-id] :not([custom-f~=wz]) span[data-type="code"].custom-codelabel-wz,
+.protyle-wysiwyg *[data-node-id] :not([custom-f~=wz]) span[data-type~="code"].custom-codelabel-wz,
.protyle-wysiwyg *[data-node-id] :not([custom-f~=wz]) span[data-type=" code"].custom-codelabel-wz {
/* background-color: #2aa899; */
background-color: var(--theme-wz-bgcolor);
diff --git a/style/customs/conver-style.css b/style/customs/conver-style.css
index 6f851e8..0a4df45 100644
--- a/style/customs/conver-style.css
+++ b/style/customs/conver-style.css
@@ -40,34 +40,34 @@
/*------------------------------ 封面 ------------------------------*/
-.protyle-wysiwyg[custom-theme] > .h6:first-child sub{
+.protyle-wysiwyg[custom-theme] > .h6:first-child span[data-type='sub']{
display: block;
color: var(--conver-sub-color);
font: var(--v-f-w-bd) 75% var(--v-f-fm-subtitle)
}
-.protyle-wysiwyg[custom-theme] > .h6:first-child sup{
+.protyle-wysiwyg[custom-theme] > .h6:first-child span[data-type='sup']{
display: block;
color: var(--conver-sup-color);
font: var(--v-f-w-bd) 75% var(--v-f-fm-subtitle)
}
-.protyle-wysiwyg[custom-theme] > .h6:first-child code{
+.protyle-wysiwyg[custom-theme] > .h6:first-child span[data-type='code']{
display: inline-block;
font-size: .5em !important;
color: var(--conver-code-color);
background: var(--conver-code-bg-color);
}
-.protyle-wysiwyg[custom-theme] > .h6:first-child strong,
-.protyle-wysiwyg[custom-theme] > .h6:first-child strong::before
+.protyle-wysiwyg[custom-theme] > .h6:first-child span[data-type='strong'],
+.protyle-wysiwyg[custom-theme] > .h6:first-child span[data-type='strong']::before
{
color: var(--conver-strong-color);
font: .625em var(--v-f-fm-subtitle)
}
-.protyle-wysiwyg[custom-theme] > .h6:first-child strong::before
+.protyle-wysiwyg[custom-theme] > .h6:first-child span[data-type='strong']::before
{
content: "By ";
opacity: .6;
@@ -76,7 +76,7 @@
}
-.protyle-wysiwyg[custom-theme] > .h6:first-child em{
+.protyle-wysiwyg[custom-theme] > .h6:first-child span[data-type='em']{
display:block;
color: var(--conver-em-color);
font: var(--v-f-w-bd) .4em var(--v-f-fm-subtitle)
@@ -116,7 +116,7 @@
}
.b3-list-item[data-treetype="outline"][data-subtype="h6"]:first-child~.b3-list-item[data-treetype="outline"][data-subtype="h1"]:last-child code,
-.b3-list-item[data-treetype="outline"][data-subtype="h6"]:first-child .b3-list-item__text code{
+.b3-list-item[data-treetype="outline"][data-subtype="h6"]:first-child .b3-list-item__text span[data-type='code']{
color: inherit;
background: 0 0;
font-family: var(--v-f-fm-tag);
diff --git a/style/customs/list-to-kanban-by-line.css b/style/customs/list-to-kanban-by-line.css
index e051f55..aa2a1b2 100644
--- a/style/customs/list-to-kanban-by-line.css
+++ b/style/customs/list-to-kanban-by-line.css
@@ -65,9 +65,9 @@
margin-bottom: var(--kbline-boarditem-margin-bottom);
}
-.protyle-wysiwyg *[custom-f~=kb] .li .p code,
-.protyle-wysiwyg .hr:first-child+.hr+*:not(.bq) .li .p code,
-.protyle-wysiwyg :not(.hr)+.hr+.hr+*:not(.bq) .li .p code{
+.protyle-wysiwyg *[custom-f~=kb] .li .p span[data-type='code'],
+.protyle-wysiwyg .hr:first-child+.hr+*:not(.bq) .li .p span[data-type='code'],
+.protyle-wysiwyg :not(.hr)+.hr+.hr+*:not(.bq) .li .p span[data-type='code']{
color: var(--kbline-board-item-code-color);
}
diff --git a/style/customs/tab-bqe copy 2.css b/style/customs/tab-bqe copy 2.css
deleted file mode 100644
index 295f0b8..0000000
--- a/style/customs/tab-bqe copy 2.css
+++ /dev/null
@@ -1,173 +0,0 @@
-
-[custom-type~="bq-wrap"],
-[custom-type~="bq-wrap"] .list,
-[custom-type~="bq-wrap"] .li,
-[custom-type~="bq-wrap"] .bq,
-[custom-type~="bq-wrap"] button,
-[custom-type~="bq-wrap"] code.bq-tab-button-mode{
- margin: 0 !important;
- padding: 0 !important;
-}
-
-[custom-type~="bq-wrap"] code.bq-tab-button-mode{
- /* display: none; */
- /* background-color: ;
- color: #f6eef3;
- padding: 5px !important;
- font-size: 1em !important;
-
- margin-right: 2px; */
-
- font: var(--v-block-kbd-font);
- border-radius: var(--v-block-kbd-border-radius);
- margin: 3px !important;
- padding-left: 2px !important;
- padding-right: 2px !important;
- padding-top: 2px !important;
- padding-bottom: 6px !important;
- color: var(--v-block-kbd-color);
- border: 2px solid var(--v-block-kbd-border-shadow);
- border-left-color: var(--v-block-kbd-border-color);
- border-top-color: var(--v-block-kbd-border-color);
- background: var(--key-bg);
- box-shadow: 0 0 0 1px var(--v-block-kbd-border-shadow);
-
-}
-
-
-[custom-type~="bq-wrap"] code.bq-tab-button-mode:hover {
- filter: brightness(1.1);
- font-size: 1em !important;
- margin: 3px !important;
-}
-
-[custom-type~="bq-wrap"] code.bq-tab-button-mode::after{
- content: "刷新";
- color: #2b1c29;
- background-color: rgba(255,255,255,0.9);
- border-radius: 3px;
- /* margin-left: 5px;
- margin-right: 2px; */
- font-size: 1em !important;
-
- padding-left: 2px !important;
- padding-right: 2px !important;
- padding-top: 2px !important;
- padding-bottom: 6px !important;
-}
-
-[custom-type~="bq-wrap"] button{
- display: inline;
- margin-left: 3px !important;
- margin-right: 2px !important;
-
- padding-left: 2px !important;
- padding-right: 2px !important;
- padding-bottom: 1px !important;
-
- font: var(--v-block-kbd-font);
- border-radius: var(--v-block-kbd-border-radius);
- margin: 0;
- color: var(--v-block-kbd-color);
- border: 2px solid var(--v-block-kbd-border-shadow);
- border-left-color: var(--v-block-kbd-border-color);
- border-top-color: var(--v-block-kbd-border-color);
- background: var(--key-bg);
- box-shadow: 0 0 0 1px var(--v-block-kbd-border-shadow);
-}
-
-[custom-type~="bq-wrap"] button::after{
- font-family: var(--v-black-code-font-family);
- font-size: 1em !important;
- content: attr(bq-button-value);
-}
-
-[custom-type~="bq-wrap"] {
- color: var(--b3-theme-on-background) !important;
- background: var(--b3-theme-background)!important;
- border-width: 5px !important;
- border-style: solid !important;
- border-color: var(--theme-bq-bgcolor)!important;
- border-radius: var(--v-block-bq-border-radius)!important;
- padding: 5px 5px !important;
- margin-top: 0!important;
- margin-bottom: 20px!important;
-}
-
-[custom-type~="bq-wrap"]:not([custom-type~="bq-none"]) [custom-type~="bq-hide"] {
- display: none;
-}
-
-[custom-type~="bq-wrap"] [custom-type~="bq-tab_t"] {
- height: 40px;
- border-bottom: 1px solid #ccc;
-}
-
-[custom-type~="bq-wrap"] [custom-type~="bq-tab_t"] .li {
- float: left;
-
- cursor: pointer;
- padding-right: 20px !important;
- color: var(--v-theme2) !important;
- background: var(--b3-theme-background)!important;
- border-width: 5px !important;
- border-style: solid !important;
- border-color: var(--theme-bq-bgcolor)!important;
- border-radius: 0 !important;
-}
-
-[custom-type~="bq-wrap"] [custom-type~="bq-tab_t"] [custom-type~="bq-act"] {
- position: relative;
-
- cursor: pointer;
- padding-right: 20px !important;
- color: var(--v-theme1) !important;
- background: var(--b3-theme-background)!important;
- border-width: 5px !important;
- border-style: solid !important;
- border-color: var(--theme-bq-bgcolor)!important;
- border-radius: 0 !important;
-}
-
-[custom-type~="bq-wrap"] [custom-type~="bq-tab_c"] {
- color: var(--v-theme2) !important;
- background: var(--b3-theme-background)!important;
- border-width: 5px !important;
- border-style: solid !important;
- border-color: var(--theme-bq-bgcolor)!important;
- border-radius: 0 !important;
- padding: 0.75em 1em!important;
- margin-top: 0!important;
- margin-bottom: 20px!important;
-}
-
-[custom-type~="bq-wrap"]:not([custom-type~="bq-none"]) [custom-type~="bq-tab_c"] .bq
-{
- border-left: 5px solid rgba(148, 152, 160, .2);
- background: 0 0;
- border-radius: 0;
- padding: 0 01.5em;
- margin-bottom: 15px !important;
-}
-
-
-
-
-[custom-type~="bq-wrap"][custom-type~="bq-none"] [custom-type~="bq-tab_c"] .bq
-{
- border: 5px solid rgba(var(--v-theme1-rgb-value), .2);
- background: 0 0;
- border-radius: 0;
- padding: 0 01.5em;
- margin-bottom: 15px !important;
-}
-
-
-[custom-type~="bq-wrap"][custom-type~="bq-none"] .bq[custom-type~="bq-hide"] {
-
- border: 5px dashed rgba(148, 152, 160, .2);
- background: 0 0;
- border-radius: 0;
- padding: 0 01.5em;
- margin-bottom: 15px !important;
-}
\ No newline at end of file
diff --git a/theme.css b/theme.css
index 9079a2d..2b75435 100644
--- a/theme.css
+++ b/theme.css
@@ -5,7 +5,7 @@
@import url(/appearance/themes/mini-vlook/style/menus/four-menu-splite.css);
-/* 修改文档树样式 */
+/* 修改文档树样式 */
@import url(/appearance/themes/mini-vlook/style/customs/file-tree.css);
/* ——————————————————————————— 基础块设置 ——————————————————————————— */
@@ -15,7 +15,7 @@
/*标题-配色*/
@import url(/appearance/themes/mini-vlook/style/blocks/v-block-heading.css);
-/*分隔线-配色*/
+/*分隔线-配色 */
@import url(/appearance/themes/mini-vlook/style/blocks/v-block-hr.css);
/*行内样式配色-配色*/
diff --git a/theme.json b/theme.json
index b410a6a..c8cbf18 100644
--- a/theme.json
+++ b/theme.json
@@ -2,7 +2,7 @@
"name": "mini-vlook",
"author": "路人二",
"url": "https://github.com/idea-zone/mini-vlook",
- "version": "1.10.0",
+ "version": "2.0.0",
"modes": [
"dark",
"light"