mirror of
https://github.com/siyuan-note/siyuan.git
synced 2025-09-22 08:30:42 +02:00
Compare commits
67 commits
master
...
v3.3.3-dev
Author | SHA1 | Date | |
---|---|---|---|
![]() |
639431e70d | ||
![]() |
07259718a8 | ||
![]() |
a62593b2f4 | ||
![]() |
bbd49a7b19 | ||
![]() |
605959ba27 | ||
![]() |
4a3bd72a5e | ||
![]() |
1d6a2c5d33 | ||
![]() |
b50789fa3a | ||
![]() |
2ed6bc0de7 | ||
![]() |
9a605e8692 | ||
![]() |
71ac6d648b | ||
![]() |
4014d1ecdc | ||
![]() |
1344859c1e | ||
![]() |
a8b5c513ae | ||
![]() |
e2f0f241f8 | ||
![]() |
bd5dea7424 | ||
![]() |
812dadb452 | ||
![]() |
66125f4b1d | ||
![]() |
7a249761e2 | ||
![]() |
3f7421a393 | ||
![]() |
b8e67bec2d | ||
![]() |
fe9aba122c | ||
![]() |
07cfc58642 | ||
![]() |
9b62385e86 | ||
![]() |
49e07f92f5 | ||
![]() |
7acf6f5225 | ||
![]() |
3211d331fa | ||
![]() |
2d8f4a3030 | ||
![]() |
b8538772f8 | ||
![]() |
0084a6427b | ||
![]() |
1b077c164c | ||
![]() |
ec77e2bafd | ||
![]() |
f8d1d1958b | ||
![]() |
327c3aaabb | ||
![]() |
c150c16273 | ||
![]() |
9fff044869 | ||
![]() |
be6e92ad7a | ||
![]() |
bcc0472ed0 | ||
![]() |
7faf981b69 | ||
![]() |
370601d277 | ||
![]() |
a99a55b512 | ||
![]() |
ba0c6883e6 | ||
![]() |
41f72e65d3 | ||
![]() |
b70328c7e7 | ||
![]() |
2ff8c148bd | ||
![]() |
5797872b97 | ||
![]() |
9d569ad37b | ||
![]() |
eca318c4ff | ||
![]() |
9f76274747 | ||
![]() |
582f60f574 | ||
![]() |
a2329077e7 | ||
![]() |
a63686cb05 | ||
![]() |
d6e7d0163a | ||
![]() |
6cc6ef66f9 | ||
![]() |
72c84f5f3d | ||
![]() |
043511c2b8 | ||
![]() |
e37ab0db78 | ||
![]() |
fd83286438 | ||
![]() |
24285aae56 | ||
![]() |
8cbefc8788 | ||
![]() |
deb9b933af | ||
![]() |
8476ddd18c | ||
![]() |
ee4ddf89c1 | ||
![]() |
d5e7b27a11 | ||
![]() |
9c37468386 | ||
![]() |
de19d69f99 | ||
![]() |
9a15b466f3 |
86 changed files with 2227 additions and 911 deletions
|
@ -44,6 +44,7 @@
|
||||||
* [Unraid 部署](#unraid-部署)
|
* [Unraid 部署](#unraid-部署)
|
||||||
* [宝塔面板 部署](#宝塔面板部署)
|
* [宝塔面板 部署](#宝塔面板部署)
|
||||||
* [小皮面板 部署](#小皮面板部署)
|
* [小皮面板 部署](#小皮面板部署)
|
||||||
|
* [1Panel面板 部署](#1Panel面板部署)
|
||||||
* [内部预览版](#内部预览版)
|
* [内部预览版](#内部预览版)
|
||||||
* [🏘️ 社区](#️-社区)
|
* [🏘️ 社区](#️-社区)
|
||||||
* [🛠️ 开发指南](#️-开发指南)
|
* [🛠️ 开发指南](#️-开发指南)
|
||||||
|
@ -361,6 +362,42 @@ Publish parameters: --accessAuthCode=******(访问授权码)
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
### 1Panel面板部署
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>1Panel面板 部署文档</summary>
|
||||||
|
|
||||||
|
#### 前提
|
||||||
|
|
||||||
|
- 仅适用于1Panel面板v1.10.32-lts及以上版本
|
||||||
|
- 安装1Panel面板,前往[1Panel](https://1panel.cn/)官网,选择正式版安装脚本下载安装
|
||||||
|
|
||||||
|
#### 部署
|
||||||
|
|
||||||
|
1. 登录1Panel面板,在左侧菜单栏中点击 `应用商店`
|
||||||
|
2. 在 `应用商店-实用工具` 中找到 `思源笔记`,点击`安装`,也可以在搜索框直接搜索
|
||||||
|
3. 配置访问授权码等基本信息,点击 `确定`
|
||||||
|
|
||||||
|
- 名称:应用名称,默认 `siyuan`
|
||||||
|
- 版本:默认最新发行版
|
||||||
|
- 端口:默认 `6806`
|
||||||
|
- 访问授权码:访问笔记时需要使用的`访问密码`
|
||||||
|
- 端口外部访问:如你需通过 `IP+Port` 直接访问,请勾选,同时会开放服务器防火墙端口
|
||||||
|
- CPU限制:默认为0,不限制,可根据实际需要设置
|
||||||
|
- 内存限制:默认为0,不限制,可根据实际需要设置
|
||||||
|
4. 提交后面板会自动进行应用安装启动,应用状态会变为`安装中`,大概需要`1-3`分钟,耐心等待安装完成
|
||||||
|
5. 当应用状态变为`已启动`后,点击左侧的网站,首次使用需要安装`OpenResty`,点击`安装`
|
||||||
|
6. 安装完成后,点击`网站`菜单栏左上角`创建`,在弹出的页面中选择`反向代理`
|
||||||
|
7. 在`主域名`填入你的域名,网站代号会自动生成,代理选择`http`,代理地址填写`127.0.0.1:6806`,点击`确定`
|
||||||
|
8. (可选) 配置你创建的网站,可根据需要配置`https`访问增强访问安全性
|
||||||
|
|
||||||
|
#### 访问思源笔记
|
||||||
|
|
||||||
|
- 如果你通过`OpenResty`反向代理反代了网站,并且填写了域名,请在浏览器输入`域名`访问
|
||||||
|
- 如你选择了 `端口外部访问`,请在浏览器地输入 `http://<1Panel面板IP>:6806` 访问
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
### 内部预览版
|
### 内部预览版
|
||||||
|
|
||||||
我们会在有重大更新前发布内部预览版,请访问 [https://github.com/siyuan-note/insider](https://github.com/siyuan-note/insider)。
|
我们会在有重大更新前发布内部预览版,请访问 [https://github.com/siyuan-note/insider](https://github.com/siyuan-note/insider)。
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
document.body.insertAdjacentHTML('afterbegin', `<svg id="iconsAnt" style="position: absolute; width: 0; height: 0; overflow: hidden;" xmlns="http://www.w3.org/2000/svg">
|
document.body.insertAdjacentHTML('afterbegin', `<svg id="iconsAnt" style="position: absolute; width: 0; height: 0; overflow: hidden;" xmlns="http://www.w3.org/2000/svg">
|
||||||
<defs>
|
<defs>
|
||||||
|
<symbol id="iconInclude" viewBox="0 0 32 32">
|
||||||
|
<path d="M22.091 2.435v7.747h7.582v13.65h-2.637v-11.013h-4.945v9.156h-9.436v5.11h11.036v2.637h-13.674v-7.747h-7.582v-13.65h2.637v11.013h4.945v-9.156h9.436l0-5.11h-11.036v-2.637h13.674zM19.454 12.82h-6.799v6.518l6.799 0v-6.518z"></path>
|
||||||
|
<path d="M1 1h5.604v5.604h-5.604zM25.396 1h5.604v5.604h-5.604zM25.396 25.396h5.604v5.604h-5.604zM1 25.396h5.604v5.604h-5.604z"></path>
|
||||||
|
</symbol>
|
||||||
<symbol id="iconGroups" viewBox="0 0 32 32">
|
<symbol id="iconGroups" viewBox="0 0 32 32">
|
||||||
<path d="M3.074 1c-0.646 0-1.17 0.524-1.17 1.17s0.524 1.17 1.17 1.17h25.851c0.646 0 1.17-0.524 1.17-1.17s-0.524-1.17-1.17-1.17v0zM3.074 17.526c-0.646 0-1.17 0.524-1.17 1.17s0.524 1.17 1.17 1.17h25.851c0.646 0 1.17-0.524 1.17-1.17s-0.524-1.17-1.17-1.17v0zM1.904 8.517c0-1.175 0.953-2.128 2.128-2.128v0h3.83c1.175 0 2.128 0.953 2.128 2.128v0 3.83c0 1.175-0.953 2.128-2.128 2.128v0h-3.83c-1.175 0-2.128-0.953-2.128-2.128v0zM4.245 8.73v3.404h3.404v-3.404zM14.085 6.389c-1.175 0-2.128 0.953-2.128 2.128v0 3.83c0 1.175 0.953 2.128 2.128 2.128v0h3.83c1.175 0 2.128-0.953 2.128-2.128v0-3.83c0-1.175-0.953-2.128-2.128-2.128v0zM14.298 12.134v-3.404h3.404v3.404zM22.011 8.517c0-1.175 0.953-2.128 2.128-2.128v0h3.83c1.175 0 2.128 0.953 2.128 2.128v0 3.83c0 1.175-0.953 2.128-2.128 2.128v0h-3.83c-1.175 0-2.128-0.953-2.128-2.128v0zM24.351 8.73v3.404h3.404v-3.404zM4.032 22.915c-1.175 0-2.128 0.953-2.128 2.128v0 3.83c0 1.175 0.953 2.128 2.128 2.128v0h3.83c1.175 0 2.128-0.953 2.128-2.128v0-3.83c0-1.175-0.953-2.128-2.128-2.128v0zM4.245 28.66v-3.404h3.404v3.404zM11.957 25.043c0-1.175 0.953-2.128 2.128-2.128v0h3.83c1.175 0 2.128 0.953 2.128 2.128v0 3.83c0 1.175-0.953 2.128-2.128 2.128v0h-3.83c-1.175 0-2.128-0.953-2.128-2.128v0zM14.298 25.255v3.404h3.404v-3.404zM24.138 22.915c-1.175 0-2.128 0.953-2.128 2.128v0 3.83c0 1.175 0.953 2.128 2.128 2.128v0h3.83c1.175 0 2.128-0.953 2.128-2.128v0-3.83c0-1.175-0.953-2.128-2.128-2.128v0zM24.351 28.66v-3.404h3.404v3.404z"></path>
|
<path d="M3.074 1c-0.646 0-1.17 0.524-1.17 1.17s0.524 1.17 1.17 1.17h25.851c0.646 0 1.17-0.524 1.17-1.17s-0.524-1.17-1.17-1.17v0zM3.074 17.526c-0.646 0-1.17 0.524-1.17 1.17s0.524 1.17 1.17 1.17h25.851c0.646 0 1.17-0.524 1.17-1.17s-0.524-1.17-1.17-1.17v0zM1.904 8.517c0-1.175 0.953-2.128 2.128-2.128v0h3.83c1.175 0 2.128 0.953 2.128 2.128v0 3.83c0 1.175-0.953 2.128-2.128 2.128v0h-3.83c-1.175 0-2.128-0.953-2.128-2.128v0zM4.245 8.73v3.404h3.404v-3.404zM14.085 6.389c-1.175 0-2.128 0.953-2.128 2.128v0 3.83c0 1.175 0.953 2.128 2.128 2.128v0h3.83c1.175 0 2.128-0.953 2.128-2.128v0-3.83c0-1.175-0.953-2.128-2.128-2.128v0zM14.298 12.134v-3.404h3.404v3.404zM22.011 8.517c0-1.175 0.953-2.128 2.128-2.128v0h3.83c1.175 0 2.128 0.953 2.128 2.128v0 3.83c0 1.175-0.953 2.128-2.128 2.128v0h-3.83c-1.175 0-2.128-0.953-2.128-2.128v0zM24.351 8.73v3.404h3.404v-3.404zM4.032 22.915c-1.175 0-2.128 0.953-2.128 2.128v0 3.83c0 1.175 0.953 2.128 2.128 2.128v0h3.83c1.175 0 2.128-0.953 2.128-2.128v0-3.83c0-1.175-0.953-2.128-2.128-2.128v0zM4.245 28.66v-3.404h3.404v3.404zM11.957 25.043c0-1.175 0.953-2.128 2.128-2.128v0h3.83c1.175 0 2.128 0.953 2.128 2.128v0 3.83c0 1.175-0.953 2.128-2.128 2.128v0h-3.83c-1.175 0-2.128-0.953-2.128-2.128v0zM14.298 25.255v3.404h3.404v-3.404zM24.138 22.915c-1.175 0-2.128 0.953-2.128 2.128v0 3.83c0 1.175 0.953 2.128 2.128 2.128v0h3.83c1.175 0 2.128-0.953 2.128-2.128v0-3.83c0-1.175-0.953-2.128-2.128-2.128v0zM24.351 28.66v-3.404h3.404v3.404z"></path>
|
||||||
</symbol>
|
</symbol>
|
||||||
|
|
|
@ -2,5 +2,5 @@
|
||||||
"name": "ant",
|
"name": "ant",
|
||||||
"author": "Vanessa",
|
"author": "Vanessa",
|
||||||
"url": "https://github.com/Vanessa219",
|
"url": "https://github.com/Vanessa219",
|
||||||
"version": "1.34.0"
|
"version": "1.35.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,12 @@
|
||||||
<body>
|
<body>
|
||||||
<h2>SiYuan</h2>
|
<h2>SiYuan</h2>
|
||||||
<div class="fn__clear">
|
<div class="fn__clear">
|
||||||
|
<div>
|
||||||
|
<svg>
|
||||||
|
<use xlink:href="#iconInclude"></use>
|
||||||
|
</svg>
|
||||||
|
iconGroups
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<svg>
|
<svg>
|
||||||
<use xlink:href="#iconGroups"></use>
|
<use xlink:href="#iconGroups"></use>
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
document.body.insertAdjacentHTML('afterbegin', `<svg id="iconsMaterial" style="position: absolute; width: 0; height: 0; overflow: hidden;" xmlns="http://www.w3.org/2000/svg">
|
document.body.insertAdjacentHTML('afterbegin', `<svg id="iconsMaterial" style="position: absolute; width: 0; height: 0; overflow: hidden;" xmlns="http://www.w3.org/2000/svg">
|
||||||
<defs>
|
<defs>
|
||||||
|
<symbol id="iconInclude" viewBox="0 0 32 32">
|
||||||
|
<path d="M22.091 2.435v7.747h7.582v13.65h-2.637v-11.013h-4.945v9.156h-9.436v5.11h11.036v2.637h-13.674v-7.747h-7.582v-13.65h2.637v11.013h4.945v-9.156h9.436l0-5.11h-11.036v-2.637h13.674zM19.454 12.82h-6.799v6.518l6.799 0v-6.518z"></path>
|
||||||
|
<path d="M1 1h5.604v5.604h-5.604zM25.396 1h5.604v5.604h-5.604zM25.396 25.396h5.604v5.604h-5.604zM1 25.396h5.604v5.604h-5.604z"></path>
|
||||||
|
</symbol>
|
||||||
<symbol id="iconGroups" viewBox="0 0 32 32">
|
<symbol id="iconGroups" viewBox="0 0 32 32">
|
||||||
<path d="M3.074 1c-0.646 0-1.17 0.524-1.17 1.17s0.524 1.17 1.17 1.17h25.851c0.646 0 1.17-0.524 1.17-1.17s-0.524-1.17-1.17-1.17v0zM3.074 17.526c-0.646 0-1.17 0.524-1.17 1.17s0.524 1.17 1.17 1.17h25.851c0.646 0 1.17-0.524 1.17-1.17s-0.524-1.17-1.17-1.17v0zM1.904 8.517c0-1.175 0.953-2.128 2.128-2.128v0h3.83c1.175 0 2.128 0.953 2.128 2.128v0 3.83c0 1.175-0.953 2.128-2.128 2.128v0h-3.83c-1.175 0-2.128-0.953-2.128-2.128v0zM4.245 8.73v3.404h3.404v-3.404zM14.085 6.389c-1.175 0-2.128 0.953-2.128 2.128v0 3.83c0 1.175 0.953 2.128 2.128 2.128v0h3.83c1.175 0 2.128-0.953 2.128-2.128v0-3.83c0-1.175-0.953-2.128-2.128-2.128v0zM14.298 12.134v-3.404h3.404v3.404zM22.011 8.517c0-1.175 0.953-2.128 2.128-2.128v0h3.83c1.175 0 2.128 0.953 2.128 2.128v0 3.83c0 1.175-0.953 2.128-2.128 2.128v0h-3.83c-1.175 0-2.128-0.953-2.128-2.128v0zM24.351 8.73v3.404h3.404v-3.404zM4.032 22.915c-1.175 0-2.128 0.953-2.128 2.128v0 3.83c0 1.175 0.953 2.128 2.128 2.128v0h3.83c1.175 0 2.128-0.953 2.128-2.128v0-3.83c0-1.175-0.953-2.128-2.128-2.128v0zM4.245 28.66v-3.404h3.404v3.404zM11.957 25.043c0-1.175 0.953-2.128 2.128-2.128v0h3.83c1.175 0 2.128 0.953 2.128 2.128v0 3.83c0 1.175-0.953 2.128-2.128 2.128v0h-3.83c-1.175 0-2.128-0.953-2.128-2.128v0zM14.298 25.255v3.404h3.404v-3.404zM24.138 22.915c-1.175 0-2.128 0.953-2.128 2.128v0 3.83c0 1.175 0.953 2.128 2.128 2.128v0h3.83c1.175 0 2.128-0.953 2.128-2.128v0-3.83c0-1.175-0.953-2.128-2.128-2.128v0zM24.351 28.66v-3.404h3.404v3.404z"></path>
|
<path d="M3.074 1c-0.646 0-1.17 0.524-1.17 1.17s0.524 1.17 1.17 1.17h25.851c0.646 0 1.17-0.524 1.17-1.17s-0.524-1.17-1.17-1.17v0zM3.074 17.526c-0.646 0-1.17 0.524-1.17 1.17s0.524 1.17 1.17 1.17h25.851c0.646 0 1.17-0.524 1.17-1.17s-0.524-1.17-1.17-1.17v0zM1.904 8.517c0-1.175 0.953-2.128 2.128-2.128v0h3.83c1.175 0 2.128 0.953 2.128 2.128v0 3.83c0 1.175-0.953 2.128-2.128 2.128v0h-3.83c-1.175 0-2.128-0.953-2.128-2.128v0zM4.245 8.73v3.404h3.404v-3.404zM14.085 6.389c-1.175 0-2.128 0.953-2.128 2.128v0 3.83c0 1.175 0.953 2.128 2.128 2.128v0h3.83c1.175 0 2.128-0.953 2.128-2.128v0-3.83c0-1.175-0.953-2.128-2.128-2.128v0zM14.298 12.134v-3.404h3.404v3.404zM22.011 8.517c0-1.175 0.953-2.128 2.128-2.128v0h3.83c1.175 0 2.128 0.953 2.128 2.128v0 3.83c0 1.175-0.953 2.128-2.128 2.128v0h-3.83c-1.175 0-2.128-0.953-2.128-2.128v0zM24.351 8.73v3.404h3.404v-3.404zM4.032 22.915c-1.175 0-2.128 0.953-2.128 2.128v0 3.83c0 1.175 0.953 2.128 2.128 2.128v0h3.83c1.175 0 2.128-0.953 2.128-2.128v0-3.83c0-1.175-0.953-2.128-2.128-2.128v0zM4.245 28.66v-3.404h3.404v3.404zM11.957 25.043c0-1.175 0.953-2.128 2.128-2.128v0h3.83c1.175 0 2.128 0.953 2.128 2.128v0 3.83c0 1.175-0.953 2.128-2.128 2.128v0h-3.83c-1.175 0-2.128-0.953-2.128-2.128v0zM14.298 25.255v3.404h3.404v-3.404zM24.138 22.915c-1.175 0-2.128 0.953-2.128 2.128v0 3.83c0 1.175 0.953 2.128 2.128 2.128v0h3.83c1.175 0 2.128-0.953 2.128-2.128v0-3.83c0-1.175-0.953-2.128-2.128-2.128v0zM24.351 28.66v-3.404h3.404v3.404z"></path>
|
||||||
</symbol>
|
</symbol>
|
||||||
|
|
|
@ -2,5 +2,5 @@
|
||||||
"name": "material",
|
"name": "material",
|
||||||
"author": "Vanessa",
|
"author": "Vanessa",
|
||||||
"url": "https://github.com/Vanessa219",
|
"url": "https://github.com/Vanessa219",
|
||||||
"version": "1.34.0"
|
"version": "1.35.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
"showAllEntriesIcons": "عرض أيقونات المدخلات",
|
"showAllEntriesIcons": "عرض أيقونات المدخلات",
|
||||||
"wrapAllFields": "التفاف الحقول تلقائيًا",
|
"wrapAllFields": "التفاف الحقول تلقائيًا",
|
||||||
"gallery": "بطاقة",
|
"gallery": "بطاقة",
|
||||||
|
"kanban": "Kanban",
|
||||||
"newTag": "علامة جديدة",
|
"newTag": "علامة جديدة",
|
||||||
"pleaseWait": "يرجى الانتظار...",
|
"pleaseWait": "يرجى الانتظار...",
|
||||||
"reconnectPrompt": "بعد تبديل التطبيقات، سيستغرق الأمر بعض الوقت لاستعادة تشغيل نواة SiYuan. يرجى الانتظار بضع ثوانٍ أو النقر فوق الزر \"إعادة المحاولة\"",
|
"reconnectPrompt": "بعد تبديل التطبيقات، سيستغرق الأمر بعض الوقت لاستعادة تشغيل نواة SiYuan. يرجى الانتظار بضع ثوانٍ أو النقر فوق الزر \"إعادة المحاولة\"",
|
||||||
|
@ -169,7 +170,8 @@
|
||||||
"cloudStoragePurgeConfirm": "سيؤدي تطهير التخزين السحابي إلى حذف جميع اللقطات غير المرجعية وعناصر البيانات ذات الصلة. <ul class='fn__list'><li>الرجاء التأكد من أن الأجهزة الأخرى توقفت عن المزامنة قبل التنفيذ</li><li>عملية التطهير تستغرق وقتاً طويلاً جداً، الرجاء التأكد من أن الشبكة مستقرة</li></ul>هل أنت متأكد من تنفيذها الآن؟",
|
"cloudStoragePurgeConfirm": "سيؤدي تطهير التخزين السحابي إلى حذف جميع اللقطات غير المرجعية وعناصر البيانات ذات الصلة. <ul class='fn__list'><li>الرجاء التأكد من أن الأجهزة الأخرى توقفت عن المزامنة قبل التنفيذ</li><li>عملية التطهير تستغرق وقتاً طويلاً جداً، الرجاء التأكد من أن الشبكة مستقرة</li></ul>هل أنت متأكد من تنفيذها الآن؟",
|
||||||
"dragFill": "اسحب عمودياً لملء القيمة",
|
"dragFill": "اسحب عمودياً لملء القيمة",
|
||||||
"switchReadonly": "تبديل وضع القراءة فقط",
|
"switchReadonly": "تبديل وضع القراءة فقط",
|
||||||
"original": "القيمة الأصلية",
|
"original": "عرض القيمة الأصلية",
|
||||||
|
"uniqueValues": "عرض القيم الفريدة",
|
||||||
"selectRelation": "الرجاء تحديد الحقل ذي الصلة أولاً",
|
"selectRelation": "الرجاء تحديد الحقل ذي الصلة أولاً",
|
||||||
"backRelation": "ثنائي الاتجاه",
|
"backRelation": "ثنائي الاتجاه",
|
||||||
"thisDatabase": "قاعدة البيانات هذه",
|
"thisDatabase": "قاعدة البيانات هذه",
|
||||||
|
@ -1387,6 +1389,7 @@
|
||||||
"_attrView": {
|
"_attrView": {
|
||||||
"table": "جدول",
|
"table": "جدول",
|
||||||
"gallery": "بطاقة",
|
"gallery": "بطاقة",
|
||||||
|
"kanban": "Kanban",
|
||||||
"key": "المفتاح الرئيسي",
|
"key": "المفتاح الرئيسي",
|
||||||
"select": "تحديد"
|
"select": "تحديد"
|
||||||
},
|
},
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
"showAllEntriesIcons": "Eintragssymbole anzeigen",
|
"showAllEntriesIcons": "Eintragssymbole anzeigen",
|
||||||
"wrapAllFields": "Felder automatisch umbrechen",
|
"wrapAllFields": "Felder automatisch umbrechen",
|
||||||
"gallery": "Karte",
|
"gallery": "Karte",
|
||||||
|
"kanban": "Kanban",
|
||||||
"newTag": "Neuer Tag",
|
"newTag": "Neuer Tag",
|
||||||
"pleaseWait": "Bitte warten...",
|
"pleaseWait": "Bitte warten...",
|
||||||
"reconnectPrompt": "Nach dem Wechseln der Anwendungen dauert es einige Zeit, bis der Betrieb des SiYuan-Kernels wiederhergestellt ist. Bitte warten Sie einige Sekunden oder klicken Sie auf die Schaltfläche „Erneut versuchen“",
|
"reconnectPrompt": "Nach dem Wechseln der Anwendungen dauert es einige Zeit, bis der Betrieb des SiYuan-Kernels wiederhergestellt ist. Bitte warten Sie einige Sekunden oder klicken Sie auf die Schaltfläche „Erneut versuchen“",
|
||||||
|
@ -169,7 +170,8 @@
|
||||||
"cloudStoragePurgeConfirm": "Das Bereinigen des Cloud-Speichers löscht alle nicht referenzierten Schnappschüsse und zugehörigen Datenobjekte vollständig. <ul class='fn__list'><li>Bitte stellen Sie sicher, dass andere Geräte die Synchronisation pausiert haben, bevor Sie fortfahren</li><li>Der Bereinigungsvorgang kann sehr zeitaufwendig sein, bitte stellen Sie sicher, dass das Netzwerk stabil ist</li></ul>Sind Sie sicher, dass Sie es jetzt ausführen möchten?",
|
"cloudStoragePurgeConfirm": "Das Bereinigen des Cloud-Speichers löscht alle nicht referenzierten Schnappschüsse und zugehörigen Datenobjekte vollständig. <ul class='fn__list'><li>Bitte stellen Sie sicher, dass andere Geräte die Synchronisation pausiert haben, bevor Sie fortfahren</li><li>Der Bereinigungsvorgang kann sehr zeitaufwendig sein, bitte stellen Sie sicher, dass das Netzwerk stabil ist</li></ul>Sind Sie sicher, dass Sie es jetzt ausführen möchten?",
|
||||||
"dragFill": "Vertikal ziehen, um Werte zu füllen",
|
"dragFill": "Vertikal ziehen, um Werte zu füllen",
|
||||||
"switchReadonly": "In den Nur-Lesen-Modus wechseln",
|
"switchReadonly": "In den Nur-Lesen-Modus wechseln",
|
||||||
"original": "Original",
|
"original": "Originalwert anzeigen",
|
||||||
|
"uniqueValues": "Eindeutige Werte anzeigen",
|
||||||
"selectRelation": "Bitte wählen Sie zuerst die zugehörige Spalte aus",
|
"selectRelation": "Bitte wählen Sie zuerst die zugehörige Spalte aus",
|
||||||
"backRelation": "Bidirektional",
|
"backRelation": "Bidirektional",
|
||||||
"thisDatabase": "Diese Datenbank",
|
"thisDatabase": "Diese Datenbank",
|
||||||
|
@ -1387,6 +1389,7 @@
|
||||||
"_attrView": {
|
"_attrView": {
|
||||||
"table": "Tabelle",
|
"table": "Tabelle",
|
||||||
"gallery": "Karte",
|
"gallery": "Karte",
|
||||||
|
"kanban": "Kanban",
|
||||||
"key": "Primärschlüssel",
|
"key": "Primärschlüssel",
|
||||||
"select": "Auswählen"
|
"select": "Auswählen"
|
||||||
},
|
},
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
"showAllEntriesIcons": "Show entry icons",
|
"showAllEntriesIcons": "Show entry icons",
|
||||||
"wrapAllFields": "Auto-wrap fields",
|
"wrapAllFields": "Auto-wrap fields",
|
||||||
"gallery": "Card",
|
"gallery": "Card",
|
||||||
|
"kanban": "Kanban",
|
||||||
"newTag": "New tag",
|
"newTag": "New tag",
|
||||||
"pleaseWait": "Please wait...",
|
"pleaseWait": "Please wait...",
|
||||||
"reconnectPrompt": "After switching applications, it will take some time to restore the SiYuan kernel operation. Please wait a few seconds or click the \"Retry\" button",
|
"reconnectPrompt": "After switching applications, it will take some time to restore the SiYuan kernel operation. Please wait a few seconds or click the \"Retry\" button",
|
||||||
|
@ -169,7 +170,8 @@
|
||||||
"cloudStoragePurgeConfirm": "Purging the cloud storage will completely delete all unreferenced snapshots and related data objects. <ul class='fn__list'><li>Please ensure that other devices have paused sync before execution</li><li>The purge operation is very time-consuming, please ensure that the network is stable</li></ul>Are you sure to execute it now?",
|
"cloudStoragePurgeConfirm": "Purging the cloud storage will completely delete all unreferenced snapshots and related data objects. <ul class='fn__list'><li>Please ensure that other devices have paused sync before execution</li><li>The purge operation is very time-consuming, please ensure that the network is stable</li></ul>Are you sure to execute it now?",
|
||||||
"dragFill": "Drag vertically to fill value",
|
"dragFill": "Drag vertically to fill value",
|
||||||
"switchReadonly": "Switch read-only mode",
|
"switchReadonly": "Switch read-only mode",
|
||||||
"original": "Original",
|
"original": "Show original values",
|
||||||
|
"uniqueValues": "Show unique values",
|
||||||
"selectRelation": "Please select the related field first",
|
"selectRelation": "Please select the related field first",
|
||||||
"backRelation": "Bidirectional",
|
"backRelation": "Bidirectional",
|
||||||
"thisDatabase": "This database",
|
"thisDatabase": "This database",
|
||||||
|
@ -1387,6 +1389,7 @@
|
||||||
"_attrView": {
|
"_attrView": {
|
||||||
"table": "Table",
|
"table": "Table",
|
||||||
"gallery": "Card",
|
"gallery": "Card",
|
||||||
|
"kanban": "Kanban",
|
||||||
"key": "Primary Key",
|
"key": "Primary Key",
|
||||||
"select": "Select"
|
"select": "Select"
|
||||||
},
|
},
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
"showAllEntriesIcons": "Mostrar íconos de entradas",
|
"showAllEntriesIcons": "Mostrar íconos de entradas",
|
||||||
"wrapAllFields": "Ajuste automático de campos",
|
"wrapAllFields": "Ajuste automático de campos",
|
||||||
"gallery": "Tarjeta",
|
"gallery": "Tarjeta",
|
||||||
|
"kanban": "Kanban",
|
||||||
"newTag": "Nueva etiqueta",
|
"newTag": "Nueva etiqueta",
|
||||||
"pleaseWait": "Por favor, espere...",
|
"pleaseWait": "Por favor, espere...",
|
||||||
"reconnectPrompt": "Después de cambiar de aplicación, tomará algún tiempo restaurar el funcionamiento del núcleo de SiYuan. Espere unos segundos o haga clic en el botón \"Reintentar\"",
|
"reconnectPrompt": "Después de cambiar de aplicación, tomará algún tiempo restaurar el funcionamiento del núcleo de SiYuan. Espere unos segundos o haga clic en el botón \"Reintentar\"",
|
||||||
|
@ -169,7 +170,8 @@
|
||||||
"cloudStoragePurgeConfirm": "Al purgar el almacenamiento en la nube se eliminarán por completo todas las instantáneas sin referencia y los objetos de datos relacionados.<ul class='fn__list'><li>Asegúrese de que otros dispositivos hayan pausado la sincronización antes de la ejecución</li><li>La operación de purga requiere mucho tiempo; asegúrese de que la red esté estable</li></ul>¿Está seguro de ejecutarla ahora?",
|
"cloudStoragePurgeConfirm": "Al purgar el almacenamiento en la nube se eliminarán por completo todas las instantáneas sin referencia y los objetos de datos relacionados.<ul class='fn__list'><li>Asegúrese de que otros dispositivos hayan pausado la sincronización antes de la ejecución</li><li>La operación de purga requiere mucho tiempo; asegúrese de que la red esté estable</li></ul>¿Está seguro de ejecutarla ahora?",
|
||||||
"dragFill": "Arrastra verticalmente para llenar valores",
|
"dragFill": "Arrastra verticalmente para llenar valores",
|
||||||
"switchReadonly": "Cambiar modo de sólo lectura",
|
"switchReadonly": "Cambiar modo de sólo lectura",
|
||||||
"original": "Original",
|
"original": "Mostrar valor original",
|
||||||
|
"uniqueValues": "Mostrar valores únicos",
|
||||||
"selectRelation": "Seleccione primero la columna relacionada",
|
"selectRelation": "Seleccione primero la columna relacionada",
|
||||||
"backRelation": "Bidireccional",
|
"backRelation": "Bidireccional",
|
||||||
"thisDatabase": "Esta base de datos",
|
"thisDatabase": "Esta base de datos",
|
||||||
|
@ -1387,6 +1389,7 @@
|
||||||
"_attrView": {
|
"_attrView": {
|
||||||
"table": "Tabla",
|
"table": "Tabla",
|
||||||
"gallery": "Tarjeta",
|
"gallery": "Tarjeta",
|
||||||
|
"kanban": "Kanban",
|
||||||
"key": "Clave principal",
|
"key": "Clave principal",
|
||||||
"select": "Selección"
|
"select": "Selección"
|
||||||
},
|
},
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
"showAllEntriesIcons": "Afficher les icônes des entrées",
|
"showAllEntriesIcons": "Afficher les icônes des entrées",
|
||||||
"wrapAllFields": "Retour automatique des champs",
|
"wrapAllFields": "Retour automatique des champs",
|
||||||
"gallery": "Carte",
|
"gallery": "Carte",
|
||||||
|
"kanban": "Kanban",
|
||||||
"newTag": "Nouvelle étiquette",
|
"newTag": "Nouvelle étiquette",
|
||||||
"pleaseWait": "Veuillez patienter...",
|
"pleaseWait": "Veuillez patienter...",
|
||||||
"reconnectPrompt": "Après avoir changé d'application, il faudra un certain temps pour rétablir le fonctionnement du noyau SiYuan. Veuillez patienter quelques secondes ou cliquer sur le bouton « Réessayer »",
|
"reconnectPrompt": "Après avoir changé d'application, il faudra un certain temps pour rétablir le fonctionnement du noyau SiYuan. Veuillez patienter quelques secondes ou cliquer sur le bouton « Réessayer »",
|
||||||
|
@ -169,7 +170,8 @@
|
||||||
"cloudStoragePurgeConfirm": "La purge du stockage cloud supprimera complètement tous les instantanés non référencés et les objets de données associés. <ul class='fn__list'><li>Veuillez vous assurer que la synchronisation des autres appareils a été suspendue avant l'exécution</li><li>L'opération de purge prend beaucoup de temps, veuillez vous assurer que le réseau est stable</li></ul>Êtes-vous sûr de l'exécuter maintenant ?",
|
"cloudStoragePurgeConfirm": "La purge du stockage cloud supprimera complètement tous les instantanés non référencés et les objets de données associés. <ul class='fn__list'><li>Veuillez vous assurer que la synchronisation des autres appareils a été suspendue avant l'exécution</li><li>L'opération de purge prend beaucoup de temps, veuillez vous assurer que le réseau est stable</li></ul>Êtes-vous sûr de l'exécuter maintenant ?",
|
||||||
"dragFill": "Faites glisser verticalement pour remplir les valeurs",
|
"dragFill": "Faites glisser verticalement pour remplir les valeurs",
|
||||||
"switchReadonly": "Changer de mode lecture seule",
|
"switchReadonly": "Changer de mode lecture seule",
|
||||||
"original": "Originale",
|
"original": "Afficher la valeur d'origine",
|
||||||
|
"uniqueValues": "Afficher les valeurs uniques",
|
||||||
"selectRelation": "Veuillez d'abord sélectionner la colonne associée",
|
"selectRelation": "Veuillez d'abord sélectionner la colonne associée",
|
||||||
"backRelation": "Bidirectionnel",
|
"backRelation": "Bidirectionnel",
|
||||||
"thisDatabase": "Cette base de données",
|
"thisDatabase": "Cette base de données",
|
||||||
|
@ -1387,6 +1389,7 @@
|
||||||
"_attrView": {
|
"_attrView": {
|
||||||
"table": "Tableau",
|
"table": "Tableau",
|
||||||
"gallery": "Carte",
|
"gallery": "Carte",
|
||||||
|
"kanban": "Kanban",
|
||||||
"key": "Clé primaire",
|
"key": "Clé primaire",
|
||||||
"select": "Sélectionner"
|
"select": "Sélectionner"
|
||||||
},
|
},
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
"showAllEntriesIcons": "הצג סמלי כניסות",
|
"showAllEntriesIcons": "הצג סמלי כניסות",
|
||||||
"wrapAllFields": "עטיפת שדות אוטומטית",
|
"wrapAllFields": "עטיפת שדות אוטומטית",
|
||||||
"gallery": "כרטיס",
|
"gallery": "כרטיס",
|
||||||
|
"kanban": "קאנבן",
|
||||||
"newTag": "תג חדש",
|
"newTag": "תג חדש",
|
||||||
"pleaseWait": "אנא המתן...",
|
"pleaseWait": "אנא המתן...",
|
||||||
"reconnectPrompt": "לאחר מעבר בין יישומים, יידרש זמן מה כדי לשחזר את פעולת ליבת SiYuan. אנא המתן מספר שניות או לחץ על כפתור \"נסה שוב\"",
|
"reconnectPrompt": "לאחר מעבר בין יישומים, יידרש זמן מה כדי לשחזר את פעולת ליבת SiYuan. אנא המתן מספר שניות או לחץ על כפתור \"נסה שוב\"",
|
||||||
|
@ -169,7 +170,8 @@
|
||||||
"cloudStoragePurgeConfirm": "ניקוי האחסון בענן ימחוק לחלוטין את כל הצילומים הלא מתייחסים וכל האובייקטים הנתונים הקשורים. <ul class='fn__list'><li>אנא ודא שהמכשירים האחרים הפסיקו סנכרון לפני הביצוע</li><li>הניקוי מאוד לוקח זמן, אנא ודא שהרשת יציבה</li></ul>האם אתה בטוח לבצע את זה עכשיו?",
|
"cloudStoragePurgeConfirm": "ניקוי האחסון בענן ימחוק לחלוטין את כל הצילומים הלא מתייחסים וכל האובייקטים הנתונים הקשורים. <ul class='fn__list'><li>אנא ודא שהמכשירים האחרים הפסיקו סנכרון לפני הביצוע</li><li>הניקוי מאוד לוקח זמן, אנא ודא שהרשת יציבה</li></ul>האם אתה בטוח לבצע את זה עכשיו?",
|
||||||
"dragFill": "גרור אנכית כדי למלא ערכים",
|
"dragFill": "גרור אנכית כדי למלא ערכים",
|
||||||
"switchReadonly": "עבר למצב קריאה בלבד",
|
"switchReadonly": "עבר למצב קריאה בלבד",
|
||||||
"original": "מקורי",
|
"original": "הצג ערך מקורי",
|
||||||
|
"uniqueValues": "הצג ערכים ייחודיים",
|
||||||
"selectRelation": "אנא בחר קודם את העמודה הקשורה",
|
"selectRelation": "אנא בחר קודם את העמודה הקשורה",
|
||||||
"backRelation": "דו-כיווני",
|
"backRelation": "דו-כיווני",
|
||||||
"thisDatabase": "בסיס הנתונים הזה",
|
"thisDatabase": "בסיס הנתונים הזה",
|
||||||
|
@ -1387,6 +1389,7 @@
|
||||||
"_attrView": {
|
"_attrView": {
|
||||||
"table": "טבלה",
|
"table": "טבלה",
|
||||||
"gallery": "כרטיס",
|
"gallery": "כרטיס",
|
||||||
|
"kanban": "קאנבן",
|
||||||
"key": "מפתח ראשי",
|
"key": "מפתח ראשי",
|
||||||
"select": "בחר"
|
"select": "בחר"
|
||||||
},
|
},
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
"showAllEntriesIcons": "Mostra icone delle voci",
|
"showAllEntriesIcons": "Mostra icone delle voci",
|
||||||
"wrapAllFields": "Avvolgi automaticamente i campi",
|
"wrapAllFields": "Avvolgi automaticamente i campi",
|
||||||
"gallery": "Scheda",
|
"gallery": "Scheda",
|
||||||
|
"kanban": "Kanban",
|
||||||
"newTag": "Nuova etichetta",
|
"newTag": "Nuova etichetta",
|
||||||
"pleaseWait": "Attendere prego...",
|
"pleaseWait": "Attendere prego...",
|
||||||
"reconnectPrompt": "Dopo aver cambiato applicazione, ci vorrà un po' di tempo per ripristinare il funzionamento del kernel SiYuan. Attendere qualche secondo o fare clic sul pulsante \"Riprova\"",
|
"reconnectPrompt": "Dopo aver cambiato applicazione, ci vorrà un po' di tempo per ripristinare il funzionamento del kernel SiYuan. Attendere qualche secondo o fare clic sul pulsante \"Riprova\"",
|
||||||
|
@ -169,7 +170,8 @@
|
||||||
"cloudStoragePurgeConfirm": "La pulizia dello storage cloud eliminerà completamente tutti gli snapshot non referenziati e gli oggetti dati correlati. <ul class='fn__list'><li>Assicurati che gli altri dispositivi abbiano sospeso la sincronizzazione prima dell'esecuzione</li><li>L'operazione di pulizia è molto lunga, assicurati che la rete sia stabile</li></ul>Sei sicuro di voler eseguire ora?",
|
"cloudStoragePurgeConfirm": "La pulizia dello storage cloud eliminerà completamente tutti gli snapshot non referenziati e gli oggetti dati correlati. <ul class='fn__list'><li>Assicurati che gli altri dispositivi abbiano sospeso la sincronizzazione prima dell'esecuzione</li><li>L'operazione di pulizia è molto lunga, assicurati che la rete sia stabile</li></ul>Sei sicuro di voler eseguire ora?",
|
||||||
"dragFill": "Trascina verticalmente per riempire i valori",
|
"dragFill": "Trascina verticalmente per riempire i valori",
|
||||||
"switchReadonly": "Passa alla modalità di sola lettura",
|
"switchReadonly": "Passa alla modalità di sola lettura",
|
||||||
"original": "Originale",
|
"original": "Mostra valore originale",
|
||||||
|
"uniqueValues": "Mostra valori unici",
|
||||||
"selectRelation": "Seleziona prima il campo correlato",
|
"selectRelation": "Seleziona prima il campo correlato",
|
||||||
"backRelation": "Bidirezionale",
|
"backRelation": "Bidirezionale",
|
||||||
"thisDatabase": "Questo database",
|
"thisDatabase": "Questo database",
|
||||||
|
@ -1387,6 +1389,7 @@
|
||||||
"_attrView": {
|
"_attrView": {
|
||||||
"table": "Tabella",
|
"table": "Tabella",
|
||||||
"gallery": "Scheda",
|
"gallery": "Scheda",
|
||||||
|
"kanban": "Kanban",
|
||||||
"key": "Chiave primaria",
|
"key": "Chiave primaria",
|
||||||
"select": "Seleziona"
|
"select": "Seleziona"
|
||||||
},
|
},
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
"showAllEntriesIcons": "エントリアイコンを表示",
|
"showAllEntriesIcons": "エントリアイコンを表示",
|
||||||
"wrapAllFields": "フィールドを自動折り返し",
|
"wrapAllFields": "フィールドを自動折り返し",
|
||||||
"gallery": "カード",
|
"gallery": "カード",
|
||||||
|
"kanban": "カンバン",
|
||||||
"newTag": "新しいタグ",
|
"newTag": "新しいタグ",
|
||||||
"pleaseWait": "しばらくお待ちください...",
|
"pleaseWait": "しばらくお待ちください...",
|
||||||
"reconnectPrompt": "アプリを切り替えた後、思源カーネルの実行を再開するには少し時間がかかります。数秒待つか、「再試行」ボタンをクリックしてください",
|
"reconnectPrompt": "アプリを切り替えた後、思源カーネルの実行を再開するには少し時間がかかります。数秒待つか、「再試行」ボタンをクリックしてください",
|
||||||
|
@ -169,7 +170,8 @@
|
||||||
"cloudStoragePurgeConfirm": "クラウドストレージを消去すると、参照されていないスナップショットと関連データオブジェクトが完全に削除されます。<ul class='fn__list'><li>実行前に他のデバイスが同期を一時停止していることを確認してください</li><li>消去操作は非常に時間がかかるため、ネットワークが安定していることを確認してください</li></ul>今すぐ実行してもよろしいですか?",
|
"cloudStoragePurgeConfirm": "クラウドストレージを消去すると、参照されていないスナップショットと関連データオブジェクトが完全に削除されます。<ul class='fn__list'><li>実行前に他のデバイスが同期を一時停止していることを確認してください</li><li>消去操作は非常に時間がかかるため、ネットワークが安定していることを確認してください</li></ul>今すぐ実行してもよろしいですか?",
|
||||||
"dragFill": "値を埋めるために垂直にドラッグ",
|
"dragFill": "値を埋めるために垂直にドラッグ",
|
||||||
"switchReadonly": "読み取り専用モードの切り替え",
|
"switchReadonly": "読み取り専用モードの切り替え",
|
||||||
"original": "元の値",
|
"original": "元の値を表示",
|
||||||
|
"uniqueValues": "一意の値を表示",
|
||||||
"selectRelation": "最初に関連する列を選択してください",
|
"selectRelation": "最初に関連する列を選択してください",
|
||||||
"backRelation": "双方向の関連付け",
|
"backRelation": "双方向の関連付け",
|
||||||
"thisDatabase": "現在のデータベース",
|
"thisDatabase": "現在のデータベース",
|
||||||
|
@ -1387,6 +1389,7 @@
|
||||||
"_attrView": {
|
"_attrView": {
|
||||||
"table": "テーブル",
|
"table": "テーブル",
|
||||||
"gallery": "カード",
|
"gallery": "カード",
|
||||||
|
"kanban": "カンバン",
|
||||||
"key": "プライマリキー",
|
"key": "プライマリキー",
|
||||||
"select": "選択"
|
"select": "選択"
|
||||||
},
|
},
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
"showAllEntriesIcons": "Pokaż ikony wpisów",
|
"showAllEntriesIcons": "Pokaż ikony wpisów",
|
||||||
"wrapAllFields": "Automatyczne zawijanie pól",
|
"wrapAllFields": "Automatyczne zawijanie pól",
|
||||||
"gallery": "Karta",
|
"gallery": "Karta",
|
||||||
|
"kanban": "Kanban",
|
||||||
"newTag": "Nowy tag",
|
"newTag": "Nowy tag",
|
||||||
"pleaseWait": "Proszę czekać...",
|
"pleaseWait": "Proszę czekać...",
|
||||||
"reconnectPrompt": "Po przełączeniu aplikacji ponowne uruchomienie jądra SiYuan może zająć trochę czasu. Proszę poczekać kilka sekund lub kliknąć przycisk „Ponów próbę”",
|
"reconnectPrompt": "Po przełączeniu aplikacji ponowne uruchomienie jądra SiYuan może zająć trochę czasu. Proszę poczekać kilka sekund lub kliknąć przycisk „Ponów próbę”",
|
||||||
|
@ -169,7 +170,8 @@
|
||||||
"cloudStoragePurgeConfirm": "Oczyszczenie pamięci w chmurze całkowicie usunie wszystkie nieodwołane zrzuty i powiązane obiekty danych. <ul class='fn__list'><li>Proszę upewnić się, że inne urządzenia wstrzymały synchronizację przed aktem</li><li>Operacja oczyszczania jest czasochłonna, proszę upewnić się, że sieć jest stabilna</li></ul>Czy na pewno chcesz to wykonać teraz?",
|
"cloudStoragePurgeConfirm": "Oczyszczenie pamięci w chmurze całkowicie usunie wszystkie nieodwołane zrzuty i powiązane obiekty danych. <ul class='fn__list'><li>Proszę upewnić się, że inne urządzenia wstrzymały synchronizację przed aktem</li><li>Operacja oczyszczania jest czasochłonna, proszę upewnić się, że sieć jest stabilna</li></ul>Czy na pewno chcesz to wykonać teraz?",
|
||||||
"dragFill": "Przeciągnij w pionie, aby wypełnić wartości",
|
"dragFill": "Przeciągnij w pionie, aby wypełnić wartości",
|
||||||
"switchReadonly": "Przełącz tryb tylko do odczytu",
|
"switchReadonly": "Przełącz tryb tylko do odczytu",
|
||||||
"original": "Oryginalny",
|
"original": "Pokaż wartość oryginalną",
|
||||||
|
"uniqueValues": "Pokaż unikalne wartości",
|
||||||
"selectRelation": "Proszę najpierw wybrać powiązaną kolumnę",
|
"selectRelation": "Proszę najpierw wybrać powiązaną kolumnę",
|
||||||
"backRelation": "Dwukierunkowa",
|
"backRelation": "Dwukierunkowa",
|
||||||
"thisDatabase": "Ta baza danych",
|
"thisDatabase": "Ta baza danych",
|
||||||
|
@ -1387,6 +1389,7 @@
|
||||||
"_attrView": {
|
"_attrView": {
|
||||||
"table": "Tabela",
|
"table": "Tabela",
|
||||||
"gallery": "Karta",
|
"gallery": "Karta",
|
||||||
|
"kanban": "Kanban",
|
||||||
"key": "Klucz główny",
|
"key": "Klucz główny",
|
||||||
"select": "Wybierz"
|
"select": "Wybierz"
|
||||||
},
|
},
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
"showAllEntriesIcons": "Mostrar ícones de entradas",
|
"showAllEntriesIcons": "Mostrar ícones de entradas",
|
||||||
"wrapAllFields": "Quebrar automaticamente os campos",
|
"wrapAllFields": "Quebrar automaticamente os campos",
|
||||||
"gallery": "Cartão",
|
"gallery": "Cartão",
|
||||||
|
"kanban": "Kanban",
|
||||||
"newTag": "Nova tag",
|
"newTag": "Nova tag",
|
||||||
"pleaseWait": "Por favor, aguarde...",
|
"pleaseWait": "Por favor, aguarde...",
|
||||||
"reconnectPrompt": "Após alternar aplicativos, levará algum tempo para restaurar a operação do kernel SiYuan. Por favor, aguarde alguns segundos ou clique no botão \"Tentar novamente\"",
|
"reconnectPrompt": "Após alternar aplicativos, levará algum tempo para restaurar a operação do kernel SiYuan. Por favor, aguarde alguns segundos ou clique no botão \"Tentar novamente\"",
|
||||||
|
@ -169,7 +170,8 @@
|
||||||
"cloudStoragePurgeConfirm": "Limpar o armazenamento em nuvem excluirá completamente todos os instantâneos não referenciados e objetos de dados relacionados. <ul class='fn__list'><li>Certifique-se de que outros dispositivos pausaram a sincronização antes da execução</li><li>A operação de limpeza é muito demorada, certifique-se de que a rede está estável</li></ul>Tem certeza que deseja executar agora?",
|
"cloudStoragePurgeConfirm": "Limpar o armazenamento em nuvem excluirá completamente todos os instantâneos não referenciados e objetos de dados relacionados. <ul class='fn__list'><li>Certifique-se de que outros dispositivos pausaram a sincronização antes da execução</li><li>A operação de limpeza é muito demorada, certifique-se de que a rede está estável</li></ul>Tem certeza que deseja executar agora?",
|
||||||
"dragFill": "Arrastar verticalmente para preencher valor",
|
"dragFill": "Arrastar verticalmente para preencher valor",
|
||||||
"switchReadonly": "Alternar modo somente leitura",
|
"switchReadonly": "Alternar modo somente leitura",
|
||||||
"original": "Original",
|
"original": "Exibir valor original",
|
||||||
|
"uniqueValues": "Exibir valores únicos",
|
||||||
"selectRelation": "Por favor, selecione o campo relacionado primeiro",
|
"selectRelation": "Por favor, selecione o campo relacionado primeiro",
|
||||||
"backRelation": "Bidirecional",
|
"backRelation": "Bidirecional",
|
||||||
"thisDatabase": "Este banco de dados",
|
"thisDatabase": "Este banco de dados",
|
||||||
|
@ -1387,6 +1389,7 @@
|
||||||
"_attrView": {
|
"_attrView": {
|
||||||
"table": "Tabela",
|
"table": "Tabela",
|
||||||
"gallery": "Cartão",
|
"gallery": "Cartão",
|
||||||
|
"kanban": "Kanban",
|
||||||
"key": "Chave Primária",
|
"key": "Chave Primária",
|
||||||
"select": "Selecionar"
|
"select": "Selecionar"
|
||||||
},
|
},
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
"showAllEntriesIcons": "Показать значки записей",
|
"showAllEntriesIcons": "Показать значки записей",
|
||||||
"wrapAllFields": "Автоматический перенос полей",
|
"wrapAllFields": "Автоматический перенос полей",
|
||||||
"gallery": "Карточка",
|
"gallery": "Карточка",
|
||||||
|
"kanban": "Канбан",
|
||||||
"newTag": "Новый тег",
|
"newTag": "Новый тег",
|
||||||
"pleaseWait": "Пожалуйста, подождите...",
|
"pleaseWait": "Пожалуйста, подождите...",
|
||||||
"reconnectPrompt": "После переключения приложений потребуется некоторое время, чтобы восстановить работу ядра SiYuan. Пожалуйста, подождите несколько секунд или нажмите кнопку «Повторить»",
|
"reconnectPrompt": "После переключения приложений потребуется некоторое время, чтобы восстановить работу ядра SiYuan. Пожалуйста, подождите несколько секунд или нажмите кнопку «Повторить»",
|
||||||
|
@ -169,7 +170,8 @@
|
||||||
"cloudStoragePurgeConfirm": "Очистка облачного хранилища полностью удалит все неиспользуемые снимки и связанные с ними объекты данных. <ul class='fn__list'><li>Пожалуйста, убедитесь, что другие устройства приостановили синхронизацию перед выполнением</li><li>Операция очистки занимает много времени, пожалуйста, убедитесь, что сеть стабильна</li></ul>Вы уверены, что хотите выполнить её сейчас?",
|
"cloudStoragePurgeConfirm": "Очистка облачного хранилища полностью удалит все неиспользуемые снимки и связанные с ними объекты данных. <ul class='fn__list'><li>Пожалуйста, убедитесь, что другие устройства приостановили синхронизацию перед выполнением</li><li>Операция очистки занимает много времени, пожалуйста, убедитесь, что сеть стабильна</li></ul>Вы уверены, что хотите выполнить её сейчас?",
|
||||||
"dragFill": "Перетащите вертикально, чтобы заполнить значение",
|
"dragFill": "Перетащите вертикально, чтобы заполнить значение",
|
||||||
"switchReadonly": "Переключить режим только для чтения",
|
"switchReadonly": "Переключить режим только для чтения",
|
||||||
"original": "Оригинал",
|
"original": "Показать исходное значение",
|
||||||
|
"uniqueValues": "Показать уникальные значения",
|
||||||
"selectRelation": "Пожалуйста, сначала выберите связанную колонку",
|
"selectRelation": "Пожалуйста, сначала выберите связанную колонку",
|
||||||
"backRelation": "Двустороннее",
|
"backRelation": "Двустороннее",
|
||||||
"thisDatabase": "Эта база данных",
|
"thisDatabase": "Эта база данных",
|
||||||
|
@ -1387,6 +1389,7 @@
|
||||||
"_attrView": {
|
"_attrView": {
|
||||||
"table": "Таблица",
|
"table": "Таблица",
|
||||||
"gallery": "Карточка",
|
"gallery": "Карточка",
|
||||||
|
"kanban": "Канбан",
|
||||||
"key": "Первичный ключ",
|
"key": "Первичный ключ",
|
||||||
"select": "Выбрать"
|
"select": "Выбрать"
|
||||||
},
|
},
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
"showAllEntriesIcons": "顯示條目圖標",
|
"showAllEntriesIcons": "顯示條目圖標",
|
||||||
"wrapAllFields": "欄位自動換行",
|
"wrapAllFields": "欄位自動換行",
|
||||||
"gallery": "卡片",
|
"gallery": "卡片",
|
||||||
|
"kanban": "看板",
|
||||||
"newTag": "新建標籤",
|
"newTag": "新建標籤",
|
||||||
"pleaseWait": "請稍等片刻...",
|
"pleaseWait": "請稍等片刻...",
|
||||||
"reconnectPrompt": "切換應用後再次進入需要一些時間恢復思源內核運行,請稍等幾秒或者點擊“重試”按鈕",
|
"reconnectPrompt": "切換應用後再次進入需要一些時間恢復思源內核運行,請稍等幾秒或者點擊“重試”按鈕",
|
||||||
|
@ -169,7 +170,8 @@
|
||||||
"cloudStoragePurgeConfirm": "清理雲端儲存會徹底刪除所有未引用的快照和相關資料物件。<ul class='fn__list'><li>執行前請確保其他設備已經暫停同步</li><li>清理作業非常耗時,請確保網路穩定</li></ul>確定現在就執行嗎?",
|
"cloudStoragePurgeConfirm": "清理雲端儲存會徹底刪除所有未引用的快照和相關資料物件。<ul class='fn__list'><li>執行前請確保其他設備已經暫停同步</li><li>清理作業非常耗時,請確保網路穩定</li></ul>確定現在就執行嗎?",
|
||||||
"dragFill": "垂直拖動以填充值",
|
"dragFill": "垂直拖動以填充值",
|
||||||
"switchReadonly": "唯讀模式切換",
|
"switchReadonly": "唯讀模式切換",
|
||||||
"original": "原值",
|
"original": "顯示原始值",
|
||||||
|
"uniqueValues": "顯示唯一值",
|
||||||
"selectRelation": "請先選擇關聯欄位",
|
"selectRelation": "請先選擇關聯欄位",
|
||||||
"backRelation": "雙向關聯",
|
"backRelation": "雙向關聯",
|
||||||
"thisDatabase": "目前資料庫",
|
"thisDatabase": "目前資料庫",
|
||||||
|
@ -1387,6 +1389,7 @@
|
||||||
"_attrView": {
|
"_attrView": {
|
||||||
"table": "表格",
|
"table": "表格",
|
||||||
"gallery": "卡片",
|
"gallery": "卡片",
|
||||||
|
"kanban": "看板",
|
||||||
"key": "主鍵",
|
"key": "主鍵",
|
||||||
"select": "單選"
|
"select": "單選"
|
||||||
},
|
},
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
"showAllEntriesIcons": "显示条目图标",
|
"showAllEntriesIcons": "显示条目图标",
|
||||||
"wrapAllFields": "字段自动换行",
|
"wrapAllFields": "字段自动换行",
|
||||||
"gallery": "卡片",
|
"gallery": "卡片",
|
||||||
|
"kanban": "看板",
|
||||||
"newTag": "新建标签",
|
"newTag": "新建标签",
|
||||||
"pleaseWait": "请稍等片刻...",
|
"pleaseWait": "请稍等片刻...",
|
||||||
"reconnectPrompt": "切换应用后再次进入需要一些时间恢复思源内核运行,请稍等几秒或者点击“重试”按钮",
|
"reconnectPrompt": "切换应用后再次进入需要一些时间恢复思源内核运行,请稍等几秒或者点击“重试”按钮",
|
||||||
|
@ -169,7 +170,8 @@
|
||||||
"cloudStoragePurgeConfirm": "清理云端存储会彻底删除所有未引用的快照和相关数据对象。<ul class='fn__list'><li>执行前请确保其他设备已经暂停同步</li><li>清理操作非常耗时,请确保网络稳定</li></ul> 确定现在就执行吗?",
|
"cloudStoragePurgeConfirm": "清理云端存储会彻底删除所有未引用的快照和相关数据对象。<ul class='fn__list'><li>执行前请确保其他设备已经暂停同步</li><li>清理操作非常耗时,请确保网络稳定</li></ul> 确定现在就执行吗?",
|
||||||
"dragFill": "垂直拖动以填充值",
|
"dragFill": "垂直拖动以填充值",
|
||||||
"switchReadonly": "只读模式切换",
|
"switchReadonly": "只读模式切换",
|
||||||
"original": "原值",
|
"original": "显示原始值",
|
||||||
|
"uniqueValues": "显示唯一值",
|
||||||
"selectRelation": "请先选择关联字段",
|
"selectRelation": "请先选择关联字段",
|
||||||
"backRelation": "双向关联",
|
"backRelation": "双向关联",
|
||||||
"thisDatabase": "当前数据库",
|
"thisDatabase": "当前数据库",
|
||||||
|
@ -1387,6 +1389,7 @@
|
||||||
"_attrView": {
|
"_attrView": {
|
||||||
"table": "表格",
|
"table": "表格",
|
||||||
"gallery": "卡片",
|
"gallery": "卡片",
|
||||||
|
"kanban": "看板",
|
||||||
"key": "主键",
|
"key": "主键",
|
||||||
"select": "单选"
|
"select": "单选"
|
||||||
},
|
},
|
||||||
|
|
49
app/appx/AppxManifest-arm64.xml
Normal file
49
app/appx/AppxManifest-arm64.xml
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--suppress XmlUnusedNamespaceDeclaration -->
|
||||||
|
<Package
|
||||||
|
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||||
|
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||||
|
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
|
||||||
|
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities">
|
||||||
|
<!-- use single quotes to avoid double quotes escaping in the publisher value -->
|
||||||
|
<Identity Name="89C2A984.SiYuan"
|
||||||
|
ProcessorArchitecture="arm64"
|
||||||
|
Publisher="CN=087C656E-C1D9-42D8-8807-CED45A74FC0F"
|
||||||
|
Version="3.3.2.0"/>
|
||||||
|
<Properties>
|
||||||
|
<DisplayName>SiYuan</DisplayName>
|
||||||
|
<PublisherDisplayName>云南链滴科技有限公司</PublisherDisplayName>
|
||||||
|
<Description>Refactor your thinking</Description>
|
||||||
|
<Logo>assets\StoreLogo.png</Logo>
|
||||||
|
</Properties>
|
||||||
|
<Resources>
|
||||||
|
<Resource Language="en-US"/>
|
||||||
|
<Resource Language="zh-CN"/>
|
||||||
|
</Resources>
|
||||||
|
<Dependencies>
|
||||||
|
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.14316.0" MaxVersionTested="10.0.14316.0"/>
|
||||||
|
</Dependencies>
|
||||||
|
<Capabilities>
|
||||||
|
<rescap:Capability Name="runFullTrust"/>
|
||||||
|
</Capabilities>
|
||||||
|
<Applications>
|
||||||
|
<Application Id="SiYuan" Executable="app\SiYuan.exe" EntryPoint="Windows.FullTrustApplication">
|
||||||
|
<uap:VisualElements
|
||||||
|
BackgroundColor="transparent"
|
||||||
|
DisplayName="SiYuan"
|
||||||
|
Square150x150Logo="assets\Square150x150Logo.png"
|
||||||
|
Square44x44Logo="assets\Square44x44Logo.png"
|
||||||
|
Description="Refactor your thinking">
|
||||||
|
|
||||||
|
<uap:DefaultTile Wide310x150Logo="assets\Wide310x150Logo.png"/>
|
||||||
|
|
||||||
|
</uap:VisualElements>
|
||||||
|
|
||||||
|
<Extensions>
|
||||||
|
<uap:Extension Category="windows.protocol">
|
||||||
|
<uap:Protocol Name="siyuan"/>
|
||||||
|
</uap:Extension>
|
||||||
|
</Extensions>
|
||||||
|
</Application>
|
||||||
|
</Applications>
|
||||||
|
</Package>
|
|
@ -607,7 +607,7 @@ const initKernel = (workspace, port, lang) => {
|
||||||
resolve(false);
|
resolve(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await sleep(200);
|
await sleep(500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
"build:export": "webpack --mode production --config webpack.export.js",
|
"build:export": "webpack --mode production --config webpack.export.js",
|
||||||
"gen:types": "tsc -d",
|
"gen:types": "tsc -d",
|
||||||
"start": "NODE_ENV=development electron ./electron/main.js",
|
"start": "NODE_ENV=development electron ./electron/main.js",
|
||||||
"dist-appx": "ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ electron-builder --config electron-appx-builder.yml",
|
|
||||||
"dist": "ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ electron-builder --config electron-builder.yml --publish=never",
|
"dist": "ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ electron-builder --config electron-builder.yml --publish=never",
|
||||||
"dist-arm64": "ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ electron-builder --arm64 --config electron-builder-arm64.yml --publish=never",
|
"dist-arm64": "ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ electron-builder --arm64 --config electron-builder-arm64.yml --publish=never",
|
||||||
"dist-darwin": "ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ electron-builder --mac --config electron-builder-darwin.yml --publish=never",
|
"dist-darwin": "ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ electron-builder --mac --config electron-builder-darwin.yml --publish=never",
|
||||||
|
|
|
@ -180,43 +180,43 @@ export class Asset extends Model {
|
||||||
<button id="previous" class="secondaryToolbarButton b3-menu__item pageUp">
|
<button id="previous" class="secondaryToolbarButton b3-menu__item pageUp">
|
||||||
<svg class="b3-menu__icon"><use xlink:href="#iconUp"></use></svg>
|
<svg class="b3-menu__icon"><use xlink:href="#iconUp"></use></svg>
|
||||||
<span class="b3-menu__label">${window.siyuan.languages.previousLabel}</span>
|
<span class="b3-menu__label">${window.siyuan.languages.previousLabel}</span>
|
||||||
<span class="b3-menu__accelerator">${updateHotkeyTip("P")}/${updateHotkeyTip("K")}</span>
|
<span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip("P")}/${updateHotkeyTip("K")}</span>
|
||||||
</button>
|
</button>
|
||||||
<button id="next" class="secondaryToolbarButton b3-menu__item pageDown">
|
<button id="next" class="secondaryToolbarButton b3-menu__item pageDown">
|
||||||
<svg class="b3-menu__icon"><use xlink:href="#iconDown"></use></svg>
|
<svg class="b3-menu__icon"><use xlink:href="#iconDown"></use></svg>
|
||||||
<span class="b3-menu__label">${window.siyuan.languages.nextLabel}</span>
|
<span class="b3-menu__label">${window.siyuan.languages.nextLabel}</span>
|
||||||
<span class="b3-menu__accelerator">${updateHotkeyTip("J")}/${updateHotkeyTip("N")}</span>
|
<span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip("J")}/${updateHotkeyTip("N")}</span>
|
||||||
</button>
|
</button>
|
||||||
<button id="firstPage" class="secondaryToolbarButton b3-menu__item firstPage">
|
<button id="firstPage" class="secondaryToolbarButton b3-menu__item firstPage">
|
||||||
<svg class="b3-menu__icon"><use xlink:href="#iconBack"></use></svg>
|
<svg class="b3-menu__icon"><use xlink:href="#iconBack"></use></svg>
|
||||||
<span class="b3-menu__label">${window.siyuan.languages.firstPage}</span>
|
<span class="b3-menu__label">${window.siyuan.languages.firstPage}</span>
|
||||||
<span class="b3-menu__accelerator">Home</span>
|
<span class="b3-menu__accelerator b3-menu__accelerator--hotkey">Home</span>
|
||||||
</button>
|
</button>
|
||||||
<button id="lastPage" class="secondaryToolbarButton b3-menu__item lastPage">
|
<button id="lastPage" class="secondaryToolbarButton b3-menu__item lastPage">
|
||||||
<svg class="b3-menu__icon"><use xlink:href="#iconForward"></use></svg>
|
<svg class="b3-menu__icon"><use xlink:href="#iconForward"></use></svg>
|
||||||
<span class="b3-menu__label">${window.siyuan.languages.lastPage}</span>
|
<span class="b3-menu__label">${window.siyuan.languages.lastPage}</span>
|
||||||
<span class="b3-menu__accelerator">End</span>
|
<span class="b3-menu__accelerator b3-menu__accelerator--hotkey">End</span>
|
||||||
</button>
|
</button>
|
||||||
<div class="horizontalToolbarSeparator b3-menu__separator"></div>
|
<div class="horizontalToolbarSeparator b3-menu__separator"></div>
|
||||||
<button id="zoomOutButton" class="secondaryToolbarButton b3-menu__item zoomOut">
|
<button id="zoomOutButton" class="secondaryToolbarButton b3-menu__item zoomOut">
|
||||||
<svg class="b3-menu__icon"><use xlink:href="#iconLine"></use></svg>
|
<svg class="b3-menu__icon"><use xlink:href="#iconLine"></use></svg>
|
||||||
<span class="b3-menu__label">${window.siyuan.languages.zoomOut}</span>
|
<span class="b3-menu__label">${window.siyuan.languages.zoomOut}</span>
|
||||||
<span class="b3-menu__accelerator">${updateHotkeyTip("⌘-")}</span>
|
<span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip("⌘-")}</span>
|
||||||
</button>
|
</button>
|
||||||
<button id="zoomInButton" class="secondaryToolbarButton b3-menu__item zoomIn">
|
<button id="zoomInButton" class="secondaryToolbarButton b3-menu__item zoomIn">
|
||||||
<svg class="b3-menu__icon"><use xlink:href="#iconAdd"></use></svg>
|
<svg class="b3-menu__icon"><use xlink:href="#iconAdd"></use></svg>
|
||||||
<span class="b3-menu__label">${window.siyuan.languages.zoomIn}</span>
|
<span class="b3-menu__label">${window.siyuan.languages.zoomIn}</span>
|
||||||
<span class="b3-menu__accelerator">${updateHotkeyTip("⌘=")}</span>
|
<span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip("⌘=")}</span>
|
||||||
</button>
|
</button>
|
||||||
<button id="pageRotateCw" class="secondaryToolbarButton b3-menu__item rotateCw">
|
<button id="pageRotateCw" class="secondaryToolbarButton b3-menu__item rotateCw">
|
||||||
<svg class="b3-menu__icon"><use xlink:href="#iconRedo"></use></svg>
|
<svg class="b3-menu__icon"><use xlink:href="#iconRedo"></use></svg>
|
||||||
<span class="b3-menu__label">${window.siyuan.languages.rotateCw}</span>
|
<span class="b3-menu__label">${window.siyuan.languages.rotateCw}</span>
|
||||||
<span class="b3-menu__accelerator">R</span>
|
<span class="b3-menu__accelerator b3-menu__accelerator--hotkey">R</span>
|
||||||
</button>
|
</button>
|
||||||
<button id="pageRotateCcw" class="secondaryToolbarButton b3-menu__item rotateCcw">
|
<button id="pageRotateCcw" class="secondaryToolbarButton b3-menu__item rotateCcw">
|
||||||
<svg class="b3-menu__icon"><use xlink:href="#iconUndo"></use></svg>
|
<svg class="b3-menu__icon"><use xlink:href="#iconUndo"></use></svg>
|
||||||
<span class="b3-menu__label">${window.siyuan.languages.rotateCcw}</span>
|
<span class="b3-menu__label">${window.siyuan.languages.rotateCcw}</span>
|
||||||
<span class="b3-menu__accelerator">⇧R</span>
|
<span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip("⇧R")}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="horizontalToolbarSeparator b3-menu__separator"></div>
|
<div class="horizontalToolbarSeparator b3-menu__separator"></div>
|
||||||
|
@ -224,12 +224,12 @@ export class Asset extends Model {
|
||||||
<button id="cursorSelectTool" class="secondaryToolbarButton b3-menu__item selectTool toggled">
|
<button id="cursorSelectTool" class="secondaryToolbarButton b3-menu__item selectTool toggled">
|
||||||
<svg class="b3-menu__icon"><use xlink:href="#iconSelectText"></use></svg>
|
<svg class="b3-menu__icon"><use xlink:href="#iconSelectText"></use></svg>
|
||||||
<span class="b3-menu__label">${window.siyuan.languages.cursorText}</span>
|
<span class="b3-menu__label">${window.siyuan.languages.cursorText}</span>
|
||||||
<span class="b3-menu__accelerator">S</span>
|
<span class="b3-menu__accelerator b3-menu__accelerator--hotkey">S</span>
|
||||||
</button>
|
</button>
|
||||||
<button id="cursorHandTool" class="secondaryToolbarButton b3-menu__item handTool">
|
<button id="cursorHandTool" class="secondaryToolbarButton b3-menu__item handTool">
|
||||||
<svg class="b3-menu__icon"><use xlink:href="#iconHand"></use></svg>
|
<svg class="b3-menu__icon"><use xlink:href="#iconHand"></use></svg>
|
||||||
<span class="b3-menu__label">${window.siyuan.languages.cursorHand}</span>
|
<span class="b3-menu__label">${window.siyuan.languages.cursorHand}</span>
|
||||||
<span class="b3-menu__accelerator">H</span>
|
<span class="b3-menu__accelerator b3-menu__accelerator--hotkey">H</span>
|
||||||
</button>
|
</button>
|
||||||
<div class="horizontalToolbarSeparator b3-menu__separator"></div>
|
<div class="horizontalToolbarSeparator b3-menu__separator"></div>
|
||||||
<button id="scrollVertical" class="secondaryToolbarButton b3-menu__item scrollModeButtons scrollVertical toggled">
|
<button id="scrollVertical" class="secondaryToolbarButton b3-menu__item scrollModeButtons scrollVertical toggled">
|
||||||
|
@ -262,7 +262,7 @@ export class Asset extends Model {
|
||||||
<button id="presentationMode" class="secondaryToolbarButton b3-menu__item presentationMode">
|
<button id="presentationMode" class="secondaryToolbarButton b3-menu__item presentationMode">
|
||||||
<svg class="b3-menu__icon"><use xlink:href="#iconPlay"></use></svg>
|
<svg class="b3-menu__icon"><use xlink:href="#iconPlay"></use></svg>
|
||||||
<span class="b3-menu__label">${window.siyuan.languages.presentationMode}</span>
|
<span class="b3-menu__label">${window.siyuan.languages.presentationMode}</span>
|
||||||
<span class="b3-menu__accelerator">${updateHotkeyTip("⌥⌘P")}</span>
|
<span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip("⌥⌘P")}</span>
|
||||||
</button>
|
</button>
|
||||||
<div class="horizontalToolbarSeparator b3-menu__separator spreadModeButtons"></div>
|
<div class="horizontalToolbarSeparator b3-menu__separator spreadModeButtons"></div>
|
||||||
<button id="documentProperties" class="secondaryToolbarButton b3-menu__item documentProperties">
|
<button id="documentProperties" class="secondaryToolbarButton b3-menu__item documentProperties">
|
||||||
|
|
|
@ -74,4 +74,8 @@
|
||||||
left: -8px;
|
left: -8px;
|
||||||
bottom: 8px;
|
bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__move {
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -607,11 +607,15 @@
|
||||||
& > span {
|
& > span {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
display: block;
|
display: block;
|
||||||
|
color: var(--b3-theme-on-surface);
|
||||||
|
|
||||||
|
&:not(:empty)::before {
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
counter-increment: linenumber;
|
counter-increment: linenumber;
|
||||||
content: counter(linenumber);
|
content: counter(linenumber);
|
||||||
color: var(--b3-theme-on-surface);
|
|
||||||
display: block;
|
display: block;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
|
@ -432,7 +432,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.b3-menu {
|
.b3-menu {
|
||||||
&__accelerator {
|
&__accelerator--hotkey {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,12 @@
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&--5 {
|
||||||
|
width: 5px;
|
||||||
|
display: inline-block;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__hr {
|
&__hr {
|
||||||
|
|
|
@ -150,7 +150,16 @@ export const insertEmptyBlock = (protyle: IProtyle, position: InsertPosition, id
|
||||||
if (blockElement.getAttribute("data-type") === "NodeListItem") {
|
if (blockElement.getAttribute("data-type") === "NodeListItem") {
|
||||||
newElement = genListItemElement(blockElement, 0, true) as HTMLDivElement;
|
newElement = genListItemElement(blockElement, 0, true) as HTMLDivElement;
|
||||||
orderIndex = parseInt(blockElement.parentElement.firstElementChild.getAttribute("data-marker"));
|
orderIndex = parseInt(blockElement.parentElement.firstElementChild.getAttribute("data-marker"));
|
||||||
|
} else if (position === "beforebegin" && blockElement.previousElementSibling &&
|
||||||
|
blockElement.previousElementSibling.getAttribute("data-type") === "NodeHeading" &&
|
||||||
|
blockElement.previousElementSibling.getAttribute("fold") === "1") {
|
||||||
|
newElement = genHeadingElement(blockElement.previousElementSibling, false, true) as HTMLDivElement;
|
||||||
|
} else if (position === "afterend" && blockElement &&
|
||||||
|
blockElement.getAttribute("data-type") === "NodeHeading" &&
|
||||||
|
blockElement.getAttribute("fold") === "1") {
|
||||||
|
newElement = genHeadingElement(blockElement, false, true) as HTMLDivElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parentOldHTML = blockElement.parentElement.outerHTML;
|
const parentOldHTML = blockElement.parentElement.outerHTML;
|
||||||
const newId = newElement.getAttribute("data-node-id");
|
const newId = newElement.getAttribute("data-node-id");
|
||||||
blockElement.insertAdjacentElement(position, newElement);
|
blockElement.insertAdjacentElement(position, newElement);
|
||||||
|
@ -216,6 +225,17 @@ export const genEmptyElement = (zwsp = true, wbr = true, id?: string) => {
|
||||||
return element;
|
return element;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const genHeadingElement = (headElement: Element, getHTML = false, addWbr = false) => {
|
||||||
|
const html = `<div data-subtype="${headElement.getAttribute("data-subtype")}" data-node-id="${Lute.NewNodeID()}" data-type="NodeHeading" class="${headElement.className}"><div contenteditable="true" spellcheck="false">${addWbr ? "<wbr>" : ""}</div><div class="protyle-attr" contenteditable="false">${Constants.ZWSP}</div></div>`;
|
||||||
|
if (getHTML) {
|
||||||
|
return html;
|
||||||
|
} else {
|
||||||
|
const tempElement = document.createElement("template");
|
||||||
|
tempElement.innerHTML = html;
|
||||||
|
return tempElement.content.firstElementChild;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const getLangByType = (type: string) => {
|
export const getLangByType = (type: string) => {
|
||||||
let lang = type;
|
let lang = type;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
|
|
@ -13,13 +13,7 @@ import {showMessage} from "../../dialog/message";
|
||||||
import {fetchPost, fetchSyncPost} from "../../util/fetch";
|
import {fetchPost, fetchSyncPost} from "../../util/fetch";
|
||||||
import {openEmojiPanel, unicode2Emoji} from "../../emoji";
|
import {openEmojiPanel, unicode2Emoji} from "../../emoji";
|
||||||
import {mountHelp, newNotebook} from "../../util/mount";
|
import {mountHelp, newNotebook} from "../../util/mount";
|
||||||
import {confirmDialog} from "../../dialog/confirmDialog";
|
import {isNotCtrl, isOnlyMeta, setStorageVal, updateHotkeyAfterTip} from "../../protyle/util/compatibility";
|
||||||
import {
|
|
||||||
isNotCtrl,
|
|
||||||
isOnlyMeta,
|
|
||||||
setStorageVal,
|
|
||||||
updateHotkeyAfterTip
|
|
||||||
} from "../../protyle/util/compatibility";
|
|
||||||
import {openFileById} from "../../editor/util";
|
import {openFileById} from "../../editor/util";
|
||||||
import {
|
import {
|
||||||
hasClosestByAttribute,
|
hasClosestByAttribute,
|
||||||
|
@ -178,18 +172,6 @@ export class Files extends Model {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
break;
|
break;
|
||||||
} else if (type === "remove") {
|
|
||||||
confirmDialog(window.siyuan.languages.deleteOpConfirm,
|
|
||||||
`${window.siyuan.languages.confirmDelete} <b>${escapeHtml(target.parentElement.querySelector(".b3-list-item__text").textContent)}</b>?`, () => {
|
|
||||||
fetchPost("/api/notebook/removeNotebook", {
|
|
||||||
notebook: target.getAttribute("data-url"),
|
|
||||||
callback: Constants.CB_MOUNT_REMOVE
|
|
||||||
});
|
|
||||||
}, undefined, true);
|
|
||||||
window.siyuan.menus.menu.remove();
|
|
||||||
event.stopPropagation();
|
|
||||||
event.preventDefault();
|
|
||||||
break;
|
|
||||||
} else if (type === "open") {
|
} else if (type === "open") {
|
||||||
fetchPost("/api/notebook/openNotebook", {
|
fetchPost("/api/notebook/openNotebook", {
|
||||||
notebook: target.getAttribute("data-url")
|
notebook: target.getAttribute("data-url")
|
||||||
|
@ -813,14 +795,14 @@ export class Files extends Model {
|
||||||
private genNotebook(item: INotebook) {
|
private genNotebook(item: INotebook) {
|
||||||
const emojiHTML = `<span class="b3-list-item__icon b3-tooltips b3-tooltips__e" aria-label="${window.siyuan.languages.changeIcon}">${unicode2Emoji(item.icon || window.siyuan.storage[Constants.LOCAL_IMAGES].note)}</span>`;
|
const emojiHTML = `<span class="b3-list-item__icon b3-tooltips b3-tooltips__e" aria-label="${window.siyuan.languages.changeIcon}">${unicode2Emoji(item.icon || window.siyuan.storage[Constants.LOCAL_IMAGES].note)}</span>`;
|
||||||
if (item.closed) {
|
if (item.closed) {
|
||||||
return `<li data-type="open" data-url="${item.id}" class="b3-list-item b3-list-item--hide-action">
|
return `<li data-url="${item.id}" class="b3-list-item b3-list-item--hide-action">
|
||||||
<span class="b3-list-item__toggle fn__hidden">
|
<span class="b3-list-item__toggle fn__hidden">
|
||||||
<svg class="b3-list-item__arrow"><use xlink:href="#iconRight"></use></svg>
|
<svg class="b3-list-item__arrow"><use xlink:href="#iconRight"></use></svg>
|
||||||
</span>
|
</span>
|
||||||
${emojiHTML}
|
${emojiHTML}
|
||||||
<span class="b3-list-item__text">${escapeHtml(item.name)}</span>
|
<span class="b3-list-item__text" style="cursor: default;">${escapeHtml(item.name)}</span>
|
||||||
<span data-type="remove" data-url="${item.id}" class="b3-list-item__action b3-tooltips b3-tooltips__w${(window.siyuan.config.readonly) ? " fn__none" : ""}" aria-label="${window.siyuan.languages.delete}">
|
<span data-type="open" data-url="${item.id}" class="b3-list-item__action b3-tooltips b3-tooltips__w${(window.siyuan.config.readonly) ? " fn__none" : ""}" aria-label="${window.siyuan.languages.openBy}">
|
||||||
<svg><use xlink:href="#iconTrashcan"></use></svg>
|
<svg><use xlink:href="#iconOpen"></use></svg>
|
||||||
</span>
|
</span>
|
||||||
</li>`;
|
</li>`;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1005,9 +987,12 @@ data-type="navigation-root" data-path="/">
|
||||||
}
|
}
|
||||||
if (sourceElement.parentElement.childElementCount === 1) {
|
if (sourceElement.parentElement.childElementCount === 1) {
|
||||||
if (sourceElement.parentElement.previousElementSibling) {
|
if (sourceElement.parentElement.previousElementSibling) {
|
||||||
sourceElement.parentElement.previousElementSibling.querySelector(".b3-list-item__toggle").classList.add("fn__hidden");
|
const parentLiElement = sourceElement.parentElement.previousElementSibling;
|
||||||
sourceElement.parentElement.previousElementSibling.querySelector(".b3-list-item__arrow").classList.remove("b3-list-item__arrow--open");
|
if (parentLiElement.getAttribute("data-type") !== "navigation-root") {
|
||||||
const emojiElement = sourceElement.parentElement.previousElementSibling.querySelector(".b3-list-item__icon");
|
parentLiElement.querySelector(".b3-list-item__toggle").classList.add("fn__hidden");
|
||||||
|
}
|
||||||
|
parentLiElement.querySelector(".b3-list-item__arrow").classList.remove("b3-list-item__arrow--open");
|
||||||
|
const emojiElement = parentLiElement.querySelector(".b3-list-item__icon");
|
||||||
if (emojiElement.innerHTML === unicode2Emoji(window.siyuan.storage[Constants.LOCAL_IMAGES].folder)) {
|
if (emojiElement.innerHTML === unicode2Emoji(window.siyuan.storage[Constants.LOCAL_IMAGES].folder)) {
|
||||||
emojiElement.innerHTML = unicode2Emoji(window.siyuan.storage[Constants.LOCAL_IMAGES].file);
|
emojiElement.innerHTML = unicode2Emoji(window.siyuan.storage[Constants.LOCAL_IMAGES].file);
|
||||||
}
|
}
|
||||||
|
@ -1016,6 +1001,12 @@ data-type="navigation-root" data-path="/">
|
||||||
} else {
|
} else {
|
||||||
sourceElement.remove();
|
sourceElement.remove();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
const parentElement = this.element.querySelector(`ul[data-url="${response.data.fromNotebook}"] li[data-path="${pathPosix().dirname(response.data.fromPath)}.sy"]`) as HTMLElement;
|
||||||
|
if (parentElement && parentElement.getAttribute("data-count") === "1") {
|
||||||
|
parentElement.querySelector(".b3-list-item__toggle").classList.add("fn__hidden");
|
||||||
|
parentElement.querySelector(".b3-list-item__arrow").classList.remove("b3-list-item__arrow--open");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const newElement = this.element.querySelector(`[data-url="${response.data.toNotebook}"] li[data-path="${response.data.toPath}"]`) as HTMLElement;
|
const newElement = this.element.querySelector(`[data-url="${response.data.toNotebook}"] li[data-path="${response.data.toPath}"]`) as HTMLElement;
|
||||||
// 更新移动到的新文件夹
|
// 更新移动到的新文件夹
|
||||||
|
|
|
@ -750,7 +750,7 @@ export const newModelByInitData = (app: App, tab: Tab, json: any) => {
|
||||||
rootId: json.rootId,
|
rootId: json.rootId,
|
||||||
blockId: json.blockId,
|
blockId: json.blockId,
|
||||||
mode: json.mode,
|
mode: json.mode,
|
||||||
action: typeof json.action === "string" ? [json.action] : json.action,
|
action: typeof json.action === "string" ? [json.action, Constants.CB_GET_FOCUS] : json.action.concat(Constants.CB_GET_FOCUS),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return model;
|
return model;
|
||||||
|
|
|
@ -236,7 +236,7 @@ export class MenuItem {
|
||||||
html = `<svg class="b3-menu__icon ${options.iconClass || ""}" style="${options.icon === "iconClose" ? "height:10px;" : ""}"><use xlink:href="#${options.icon || ""}"></use></svg>${html}`;
|
html = `<svg class="b3-menu__icon ${options.iconClass || ""}" style="${options.icon === "iconClose" ? "height:10px;" : ""}"><use xlink:href="#${options.icon || ""}"></use></svg>${html}`;
|
||||||
}
|
}
|
||||||
if (options.accelerator) {
|
if (options.accelerator) {
|
||||||
html += `<span class="b3-menu__accelerator">${updateHotkeyTip(options.accelerator)}</span>`;
|
html += `<span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip(options.accelerator)}</span>`;
|
||||||
}
|
}
|
||||||
if (options.action) {
|
if (options.action) {
|
||||||
html += `<svg class="b3-menu__action${options.action === "iconCloseRound" ? " b3-menu__action--close" : ""}"><use xlink:href="#${options.action}"></use></svg>`;
|
html += `<svg class="b3-menu__action${options.action === "iconCloseRound" ? " b3-menu__action--close" : ""}"><use xlink:href="#${options.action}"></use></svg>`;
|
||||||
|
|
|
@ -9,7 +9,6 @@ import {genUUID} from "../../util/genID";
|
||||||
import {openMobileFileById} from "../editor";
|
import {openMobileFileById} from "../editor";
|
||||||
import {unicode2Emoji} from "../../emoji";
|
import {unicode2Emoji} from "../../emoji";
|
||||||
import {mountHelp, newNotebook} from "../../util/mount";
|
import {mountHelp, newNotebook} from "../../util/mount";
|
||||||
import {confirmDialog} from "../../dialog/confirmDialog";
|
|
||||||
import {newFile} from "../../util/newFile";
|
import {newFile} from "../../util/newFile";
|
||||||
import {MenuItem} from "../../menus/Menu";
|
import {MenuItem} from "../../menus/Menu";
|
||||||
import {App} from "../../index";
|
import {App} from "../../index";
|
||||||
|
@ -176,17 +175,6 @@ export class MobileFiles extends Model {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
break;
|
break;
|
||||||
} else if (type === "remove") {
|
|
||||||
confirmDialog(window.siyuan.languages.deleteOpConfirm,
|
|
||||||
`${window.siyuan.languages.confirmDelete} <b>${escapeHtml(target.parentElement.querySelector(".b3-list-item__text").textContent)}</b>?`, () => {
|
|
||||||
fetchPost("/api/notebook/removeNotebook", {
|
|
||||||
notebook: target.getAttribute("data-url"),
|
|
||||||
callback: Constants.CB_MOUNT_REMOVE
|
|
||||||
});
|
|
||||||
}, undefined, true);
|
|
||||||
event.stopPropagation();
|
|
||||||
event.preventDefault();
|
|
||||||
break;
|
|
||||||
} else if (type === "open") {
|
} else if (type === "open") {
|
||||||
fetchPost("/api/notebook/openNotebook", {
|
fetchPost("/api/notebook/openNotebook", {
|
||||||
notebook: target.getAttribute("data-url")
|
notebook: target.getAttribute("data-url")
|
||||||
|
@ -309,14 +297,14 @@ export class MobileFiles extends Model {
|
||||||
private genNotebook(item: INotebook) {
|
private genNotebook(item: INotebook) {
|
||||||
const emojiHTML = `<span class="b3-list-item__icon b3-tooltips b3-tooltips__e" aria-label="${window.siyuan.languages.changeIcon}">${unicode2Emoji(item.icon || window.siyuan.storage[Constants.LOCAL_IMAGES].note)}</span>`;
|
const emojiHTML = `<span class="b3-list-item__icon b3-tooltips b3-tooltips__e" aria-label="${window.siyuan.languages.changeIcon}">${unicode2Emoji(item.icon || window.siyuan.storage[Constants.LOCAL_IMAGES].note)}</span>`;
|
||||||
if (item.closed) {
|
if (item.closed) {
|
||||||
return `<li data-type="open" data-url="${item.id}" class="b3-list-item">
|
return `<li data-url="${item.id}" class="b3-list-item">
|
||||||
<span class="b3-list-item__toggle fn__hidden">
|
<span class="b3-list-item__toggle fn__hidden">
|
||||||
<svg class="b3-list-item__arrow"><use xlink:href="#iconRight"></use></svg>
|
<svg class="b3-list-item__arrow"><use xlink:href="#iconRight"></use></svg>
|
||||||
</span>
|
</span>
|
||||||
${emojiHTML}
|
${emojiHTML}
|
||||||
<span class="b3-list-item__text">${escapeHtml(item.name)}</span>
|
<span class="b3-list-item__text">${escapeHtml(item.name)}</span>
|
||||||
<span data-type="remove" data-url="${item.id}" class="b3-list-item__action${(window.siyuan.config.readonly) ? " fn__none" : ""}">
|
<span data-type="open" data-url="${item.id}" class="b3-list-item__action${(window.siyuan.config.readonly) ? " fn__none" : ""}">
|
||||||
<svg><use xlink:href="#iconTrashcan"></use></svg>
|
<svg><use xlink:href="#iconOpen"></use></svg>
|
||||||
</span>
|
</span>
|
||||||
</li>`;
|
</li>`;
|
||||||
} else {
|
} else {
|
||||||
|
@ -402,6 +390,12 @@ export class MobileFiles extends Model {
|
||||||
} else {
|
} else {
|
||||||
sourceElement.remove();
|
sourceElement.remove();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
const parentElement = this.element.querySelector(`ul[data-url="${data.fromNotebook}"] li[data-path="${pathPosix().dirname(data.fromPath)}.sy"]`) as HTMLElement;
|
||||||
|
if (parentElement && parentElement.getAttribute("data-count") === "1") {
|
||||||
|
parentElement.querySelector(".b3-list-item__toggle").classList.add("fn__hidden");
|
||||||
|
parentElement.querySelector(".b3-list-item__arrow").classList.remove("b3-list-item__arrow--open");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const newElement = this.element.querySelector(`[data-url="${data.toNotebook}"] li[data-path="${data.toPath}"]`) as HTMLElement;
|
const newElement = this.element.querySelector(`[data-url="${data.toNotebook}"] li[data-path="${data.toPath}"]`) as HTMLElement;
|
||||||
// 重新展开移动到的新文件夹
|
// 重新展开移动到的新文件夹
|
||||||
|
|
|
@ -752,7 +752,7 @@ export const popSearch = (app: App, searchConfig?: any) => {
|
||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
<span class="fn__flex-1"></span>
|
<span class="fn__flex-1"></span>
|
||||||
<svg data-type="toggle-replace" class="toolbar__icon${config.hasReplace ? " toolbar__icon--active" : ""}"><use xlink:href="#iconReplace"></use></svg>
|
<svg data-type="toggle-replace" class="toolbar__icon${config.hasReplace ? " toolbar__icon--active" : ""}"><use xlink:href="#iconReplace"></use></svg>
|
||||||
<svg ${enableIncludeChild ? "" : "disabled"} data-type="include" class="toolbar__icon${includeChild ? " toolbar__icon--active" : ""}"><use xlink:href="#iconCopy"></use></svg>
|
<svg ${enableIncludeChild ? "" : "disabled"} data-type="include" class="toolbar__icon${includeChild ? " toolbar__icon--active" : ""}"><use xlink:href="#iconInclude"></use></svg>
|
||||||
<svg data-type="path" class="toolbar__icon"><use xlink:href="#iconFolder"></use></svg>
|
<svg data-type="path" class="toolbar__icon"><use xlink:href="#iconFolder"></use></svg>
|
||||||
<svg ${document.querySelector("#empty").classList.contains("fn__none") ? "" : "disabled"} data-type="currentPath" class="toolbar__icon"><use xlink:href="#iconFocus"></use></svg>
|
<svg ${document.querySelector("#empty").classList.contains("fn__none") ? "" : "disabled"} data-type="currentPath" class="toolbar__icon"><use xlink:href="#iconFocus"></use></svg>
|
||||||
<svg data-type="expand" class="toolbar__icon${config.group === 0 ? " fn__none" : ""}"><use xlink:href="#iconExpand"></use></svg>
|
<svg data-type="expand" class="toolbar__icon${config.group === 0 ? " fn__none" : ""}"><use xlink:href="#iconExpand"></use></svg>
|
||||||
|
|
|
@ -58,7 +58,7 @@ export class Breadcrumb {
|
||||||
<span class="protyle-breadcrumb__space"></span>
|
<span class="protyle-breadcrumb__space"></span>
|
||||||
<button class="protyle-breadcrumb__icon fn__none ariaLabel" aria-label="${updateHotkeyTip(window.siyuan.config.keymap.editor.general.exitFocus.custom)}" data-type="exit-focus">${window.siyuan.languages.exitFocus}</button>
|
<button class="protyle-breadcrumb__icon fn__none ariaLabel" aria-label="${updateHotkeyTip(window.siyuan.config.keymap.editor.general.exitFocus.custom)}" data-type="exit-focus">${window.siyuan.languages.exitFocus}</button>
|
||||||
${padHTML}
|
${padHTML}
|
||||||
<button class="block__icon fn__flex-center ariaLabel${window.siyuan.config.readonly ? " fn__none" : ""}" aria-label="${window.siyuan.languages.lockEdit}" data-type="readonly"><svg><use xlink:href="#iconUnlock"></use></svg></button>
|
<button class="block__icon fn__flex-center ariaLabel${window.siyuan.config.readonly ? " fn__none" : ""}" aria-label="${window.siyuan.languages.lockEdit}" data-type="readonly" data-subtype="unlock"><svg><use xlink:href="#iconUnlock"></use></svg></button>
|
||||||
<button class="block__icon fn__flex-center ariaLabel" data-type="doc" aria-label="${isMac() ? window.siyuan.languages.gutterTip2 : window.siyuan.languages.gutterTip2.replace("⇧", "Shift+")}"><svg><use xlink:href="#iconFile"></use></svg></button>
|
<button class="block__icon fn__flex-center ariaLabel" data-type="doc" aria-label="${isMac() ? window.siyuan.languages.gutterTip2 : window.siyuan.languages.gutterTip2.replace("⇧", "Shift+")}"><svg><use xlink:href="#iconFile"></use></svg></button>
|
||||||
<button class="block__icon fn__flex-center ariaLabel" data-type="more" aria-label="${window.siyuan.languages.more}"><svg><use xlink:href="#iconMore"></use></svg></button>
|
<button class="block__icon fn__flex-center ariaLabel" data-type="more" aria-label="${window.siyuan.languages.more}"><svg><use xlink:href="#iconMore"></use></svg></button>
|
||||||
<button class="block__icon fn__flex-center fn__none ariaLabel" data-type="context" aria-label="${window.siyuan.languages.context}"><svg><use xlink:href="#iconAlignCenter"></use></svg></button>`;
|
<button class="block__icon fn__flex-center fn__none ariaLabel" data-type="context" aria-label="${window.siyuan.languages.context}"><svg><use xlink:href="#iconAlignCenter"></use></svg></button>`;
|
||||||
|
|
|
@ -80,6 +80,9 @@ export const exportImage = (id: string) => {
|
||||||
objectElement.remove();
|
objectElement.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
previewElement.querySelectorAll(".protyle-linenumber__rows span").forEach((item, index) => {
|
||||||
|
item.textContent = (index + 1).toString();
|
||||||
|
});
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
addScript("/stage/protyle/js/html-to-image.min.js?v=1.11.13", "protyleHtml2image").then(async () => {
|
addScript("/stage/protyle/js/html-to-image.min.js?v=1.11.13", "protyleHtml2image").then(async () => {
|
||||||
let blob = await window.htmlToImage.toBlob(exportDialog.element.querySelector(".b3-dialog__content"));
|
let blob = await window.htmlToImage.toBlob(exportDialog.element.querySelector(".b3-dialog__content"));
|
||||||
|
|
|
@ -1802,15 +1802,6 @@ export class Gutter {
|
||||||
transferBlockRef(id);
|
transferBlockRef(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
window.siyuan.menus.menu.append(new MenuItem({
|
|
||||||
id: "jumpToParentNext",
|
|
||||||
label: window.siyuan.languages.jumpToParentNext,
|
|
||||||
accelerator: window.siyuan.config.keymap.editor.general.jumpToParentNext.custom,
|
|
||||||
click() {
|
|
||||||
hideElements(["select"], protyle);
|
|
||||||
jumpToParent(protyle, nodeElement, "next");
|
|
||||||
}
|
|
||||||
}).element);
|
|
||||||
window.siyuan.menus.menu.append(new MenuItem({
|
window.siyuan.menus.menu.append(new MenuItem({
|
||||||
id: "jumpToParentPrev",
|
id: "jumpToParentPrev",
|
||||||
label: window.siyuan.languages.jumpToParentPrev,
|
label: window.siyuan.languages.jumpToParentPrev,
|
||||||
|
@ -1820,6 +1811,15 @@ export class Gutter {
|
||||||
jumpToParent(protyle, nodeElement, "previous");
|
jumpToParent(protyle, nodeElement, "previous");
|
||||||
}
|
}
|
||||||
}).element);
|
}).element);
|
||||||
|
window.siyuan.menus.menu.append(new MenuItem({
|
||||||
|
id: "jumpToParentNext",
|
||||||
|
label: window.siyuan.languages.jumpToParentNext,
|
||||||
|
accelerator: window.siyuan.config.keymap.editor.general.jumpToParentNext.custom,
|
||||||
|
click() {
|
||||||
|
hideElements(["select"], protyle);
|
||||||
|
jumpToParent(protyle, nodeElement, "next");
|
||||||
|
}
|
||||||
|
}).element);
|
||||||
window.siyuan.menus.menu.append(new MenuItem({
|
window.siyuan.menus.menu.append(new MenuItem({
|
||||||
id: "jumpToParent",
|
id: "jumpToParent",
|
||||||
label: window.siyuan.languages.jumpToParent,
|
label: window.siyuan.languages.jumpToParent,
|
||||||
|
|
|
@ -23,7 +23,7 @@ import {avRender} from "../render/av/render";
|
||||||
|
|
||||||
const getHotkeyOrMarker = (hotkey: string, marker: string) => {
|
const getHotkeyOrMarker = (hotkey: string, marker: string) => {
|
||||||
if (hotkey) {
|
if (hotkey) {
|
||||||
return `<span class="b3-menu__accelerator">${updateHotkeyTip(hotkey)}</span>`;
|
return `<span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip(hotkey)}</span>`;
|
||||||
} else if (marker) {
|
} else if (marker) {
|
||||||
return `<span class="b3-list-item__meta">${marker}</span>`;
|
return `<span class="b3-list-item__meta">${marker}</span>`;
|
||||||
}
|
}
|
||||||
|
@ -139,7 +139,7 @@ export const hintSlash = (key: string, protyle: IProtyle) => {
|
||||||
filter: [window.siyuan.languages.table, "table", "表格", "biaoge", "bg"],
|
filter: [window.siyuan.languages.table, "table", "表格", "biaoge", "bg"],
|
||||||
id: "table",
|
id: "table",
|
||||||
value: `| ${Lute.Caret} | | |\n| --- | --- | --- |\n| | | |\n| | | |`,
|
value: `| ${Lute.Caret} | | |\n| --- | --- | --- |\n| | | |\n| | | |`,
|
||||||
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconTable"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.table}</span><span class="b3-menu__accelerator">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.table.custom))}</span></div>`,
|
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconTable"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.table}</span><span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.table.custom))}</span></div>`,
|
||||||
}, {
|
}, {
|
||||||
filter: [window.siyuan.languages.line, "thematic break", "divider", "分隔线", "分割线", "fengexian", "fgx"],
|
filter: [window.siyuan.languages.line, "thematic break", "divider", "分隔线", "分割线", "fengexian", "fgx"],
|
||||||
id: "line",
|
id: "line",
|
||||||
|
@ -168,62 +168,62 @@ export const hintSlash = (key: string, protyle: IProtyle) => {
|
||||||
filter: [window.siyuan.languages.link, "link", "a", "链接", "lianjie", "lj"],
|
filter: [window.siyuan.languages.link, "link", "a", "链接", "lianjie", "lj"],
|
||||||
id: "link",
|
id: "link",
|
||||||
value: "a",
|
value: "a",
|
||||||
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconLink"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.link}</span><span class="b3-menu__accelerator">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.link.custom))}</span></div>`,
|
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconLink"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.link}</span><span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.link.custom))}</span></div>`,
|
||||||
}, {
|
}, {
|
||||||
filter: [window.siyuan.languages.bold, "bold", "strong", "粗体", "cuti", "ct", "加粗", "jiacu", "jc"],
|
filter: [window.siyuan.languages.bold, "bold", "strong", "粗体", "cuti", "ct", "加粗", "jiacu", "jc"],
|
||||||
id: "bold",
|
id: "bold",
|
||||||
value: "strong",
|
value: "strong",
|
||||||
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconBold"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.bold}</span><span class="b3-menu__accelerator">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.bold.custom))}</span></div>`,
|
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconBold"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.bold}</span><span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.bold.custom))}</span></div>`,
|
||||||
}, {
|
}, {
|
||||||
filter: [window.siyuan.languages.italic, "italic", "em", "斜体", "xieti", "xt"],
|
filter: [window.siyuan.languages.italic, "italic", "em", "斜体", "xieti", "xt"],
|
||||||
id: "italic",
|
id: "italic",
|
||||||
value: "em",
|
value: "em",
|
||||||
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconItalic"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.italic}</span><span class="b3-menu__accelerator">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.italic.custom))}</span></div>`,
|
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconItalic"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.italic}</span><span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.italic.custom))}</span></div>`,
|
||||||
}, {
|
}, {
|
||||||
filter: [window.siyuan.languages.underline, "underline", "下划线", "xiahuaxian", "xhx"],
|
filter: [window.siyuan.languages.underline, "underline", "下划线", "xiahuaxian", "xhx"],
|
||||||
id: "underline",
|
id: "underline",
|
||||||
value: "u",
|
value: "u",
|
||||||
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconUnderline"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.underline}</span><span class="b3-menu__accelerator">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.underline.custom))}</span></div>`,
|
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconUnderline"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.underline}</span><span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.underline.custom))}</span></div>`,
|
||||||
}, {
|
}, {
|
||||||
filter: [window.siyuan.languages.strike, "strike", "delete", "删除线", "shanchuxian", "scx"],
|
filter: [window.siyuan.languages.strike, "strike", "delete", "删除线", "shanchuxian", "scx"],
|
||||||
id: "strike",
|
id: "strike",
|
||||||
value: "s",
|
value: "s",
|
||||||
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconStrike"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.strike}</span><span class="b3-menu__accelerator">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.strike.custom))}</span></div>`,
|
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconStrike"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.strike}</span><span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.strike.custom))}</span></div>`,
|
||||||
}, {
|
}, {
|
||||||
filter: [window.siyuan.languages.mark, "mark", "标记", "biaoji", "bj", "高亮", "gaoliang", "gl"],
|
filter: [window.siyuan.languages.mark, "mark", "标记", "biaoji", "bj", "高亮", "gaoliang", "gl"],
|
||||||
id: "mark",
|
id: "mark",
|
||||||
value: "mark",
|
value: "mark",
|
||||||
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconMark"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.mark}</span><span class="b3-menu__accelerator">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.mark.custom))}</span></div>`,
|
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconMark"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.mark}</span><span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.mark.custom))}</span></div>`,
|
||||||
}, {
|
}, {
|
||||||
filter: [window.siyuan.languages.sup, "superscript", "上标", "shangbiao", "sb"],
|
filter: [window.siyuan.languages.sup, "superscript", "上标", "shangbiao", "sb"],
|
||||||
id: "sup",
|
id: "sup",
|
||||||
value: "sup",
|
value: "sup",
|
||||||
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconSup"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.sup}</span><span class="b3-menu__accelerator">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.sup.custom))}</span></div>`,
|
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconSup"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.sup}</span><span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.sup.custom))}</span></div>`,
|
||||||
}, {
|
}, {
|
||||||
filter: [window.siyuan.languages.sub, "subscript", "下标", "xiaobiao", "xb"],
|
filter: [window.siyuan.languages.sub, "subscript", "下标", "xiaobiao", "xb"],
|
||||||
id: "sub",
|
id: "sub",
|
||||||
value: "sub",
|
value: "sub",
|
||||||
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconSub"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.sub}</span><span class="b3-menu__accelerator">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.sub.custom))}</span></div>`,
|
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconSub"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.sub}</span><span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.sub.custom))}</span></div>`,
|
||||||
}, {
|
}, {
|
||||||
filter: [window.siyuan.languages["inline-code"], "inline code", "行级代码", "hangjidaima", "hjdm"],
|
filter: [window.siyuan.languages["inline-code"], "inline code", "行级代码", "hangjidaima", "hjdm"],
|
||||||
id: "inlineCode",
|
id: "inlineCode",
|
||||||
value: "code",
|
value: "code",
|
||||||
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconInlineCode"></use></svg><span class="b3-list-item__text">${window.siyuan.languages["inline-code"]}</span><span class="b3-menu__accelerator">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert["inline-code"].custom))}</span></div>`,
|
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconInlineCode"></use></svg><span class="b3-list-item__text">${window.siyuan.languages["inline-code"]}</span><span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert["inline-code"].custom))}</span></div>`,
|
||||||
}, {
|
}, {
|
||||||
filter: [window.siyuan.languages.kbd, "kbd", "键盘", "jianpan", "jp"],
|
filter: [window.siyuan.languages.kbd, "kbd", "键盘", "jianpan", "jp"],
|
||||||
id: "kbd",
|
id: "kbd",
|
||||||
value: "kbd",
|
value: "kbd",
|
||||||
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconKeymap"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.kbd}</span><span class="b3-menu__accelerator">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.kbd.custom))}</span></div>`,
|
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconKeymap"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.kbd}</span><span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.kbd.custom))}</span></div>`,
|
||||||
}, {
|
}, {
|
||||||
filter: [window.siyuan.languages.tag, "tags", "标签", "biaoqian", "bq"],
|
filter: [window.siyuan.languages.tag, "tags", "标签", "biaoqian", "bq"],
|
||||||
id: "tag",
|
id: "tag",
|
||||||
value: "tag",
|
value: "tag",
|
||||||
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconTags"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.tag}</span><span class="b3-menu__accelerator">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.tag.custom))}</span></div>`,
|
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconTags"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.tag}</span><span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert.tag.custom))}</span></div>`,
|
||||||
}, {
|
}, {
|
||||||
filter: [window.siyuan.languages["inline-math"], "inline formulas", "inline math", "行级公式", "hangjigongshi", "hjgs", "行级数学公式", "hangjishuxvegongshi", "hangjishuxuegongshi", "hjsxgs"],
|
filter: [window.siyuan.languages["inline-math"], "inline formulas", "inline math", "行级公式", "hangjigongshi", "hjgs", "行级数学公式", "hangjishuxvegongshi", "hangjishuxuegongshi", "hjsxgs"],
|
||||||
id: "inlineMath",
|
id: "inlineMath",
|
||||||
value: "inline-math",
|
value: "inline-math",
|
||||||
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconMath"></use></svg><span class="b3-list-item__text">${window.siyuan.languages["inline-math"]}</span><span class="b3-menu__accelerator">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert["inline-math"].custom))}</span></div>`,
|
html: `<div class="b3-list-item__first"><svg class="b3-list-item__graphic"><use xlink:href="#iconMath"></use></svg><span class="b3-list-item__text">${window.siyuan.languages["inline-math"]}</span><span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${updateHotkeyTip((window.siyuan.config.keymap.editor.insert["inline-math"].custom))}</span></div>`,
|
||||||
}, {
|
}, {
|
||||||
value: "",
|
value: "",
|
||||||
id: "separator_3",
|
id: "separator_3",
|
||||||
|
|
|
@ -138,7 +138,7 @@ export const genAVValueHTML = (value: IAVCellValue) => {
|
||||||
if (item && item.block) {
|
if (item && item.block) {
|
||||||
const rowID = value.relation.blockIDs[index];
|
const rowID = value.relation.blockIDs[index];
|
||||||
if (item?.isDetached) {
|
if (item?.isDetached) {
|
||||||
html += `<span data-row-id="${rowID}" class="av__cell--relation"><span class="b3-menu__avemoji">➖</span><span class="av__celltext">${Lute.EscapeHTMLStr(item.block.content || window.siyuan.languages.untitled)}</span></span>`;
|
html += `<span data-row-id="${rowID}" class="av__cell--relation"><span>➖<span class="fn__space--5"></span></span><span class="av__celltext">${Lute.EscapeHTMLStr(item.block.content || window.siyuan.languages.untitled)}</span></span>`;
|
||||||
} else {
|
} else {
|
||||||
// data-block-id 用于更新 emoji
|
// data-block-id 用于更新 emoji
|
||||||
html += `<span data-row-id="${rowID}" class="av__cell--relation" data-block-id="${item.block.id}"><span class="b3-menu__avemoji" data-unicode="${item.block.icon || ""}">${unicode2Emoji(item.block.icon || window.siyuan.storage[Constants.LOCAL_IMAGES].file)}</span><span data-type="block-ref" data-id="${item.block.id}" data-subtype="s" class="av__celltext av__celltext--ref">${Lute.EscapeHTMLStr(item.block.content || window.siyuan.languages.untitled)}</span></span>`;
|
html += `<span data-row-id="${rowID}" class="av__cell--relation" data-block-id="${item.block.id}"><span class="b3-menu__avemoji" data-unicode="${item.block.icon || ""}">${unicode2Emoji(item.block.icon || window.siyuan.storage[Constants.LOCAL_IMAGES].file)}</span><span data-type="block-ref" data-id="${item.block.id}" data-subtype="s" class="av__celltext av__celltext--ref">${Lute.EscapeHTMLStr(item.block.content || window.siyuan.languages.untitled)}</span></span>`;
|
||||||
|
|
|
@ -131,6 +131,20 @@ export const openCalcMenu = async (protyle: IProtyle, calcElement: HTMLElement,
|
||||||
blockID,
|
blockID,
|
||||||
target: calcElement
|
target: calcElement
|
||||||
});
|
});
|
||||||
|
if (panelData?.data && type !== "checkbox") {
|
||||||
|
// 汇总字段汇总方式中才有“显示唯一值”选项 Add "Show unique values" to the calculation of the database rollup field https://github.com/siyuan-note/siyuan/issues/15852
|
||||||
|
calcItem({
|
||||||
|
menu,
|
||||||
|
protyle,
|
||||||
|
colId,
|
||||||
|
avId,
|
||||||
|
oldOperator,
|
||||||
|
operator: "Unique values",
|
||||||
|
data: panelData?.data,
|
||||||
|
blockID,
|
||||||
|
target: calcElement
|
||||||
|
});
|
||||||
|
}
|
||||||
calcItem({
|
calcItem({
|
||||||
menu,
|
menu,
|
||||||
protyle,
|
protyle,
|
||||||
|
@ -488,6 +502,8 @@ export const getNameByOperator = (operator: string, isRollup: boolean) => {
|
||||||
case undefined:
|
case undefined:
|
||||||
case "":
|
case "":
|
||||||
return isRollup ? window.siyuan.languages.original : window.siyuan.languages.calcOperatorNone;
|
return isRollup ? window.siyuan.languages.original : window.siyuan.languages.calcOperatorNone;
|
||||||
|
case "Unique values": // 仅汇总字段的汇总方式在使用
|
||||||
|
return window.siyuan.languages.uniqueValues;
|
||||||
case "Count all":
|
case "Count all":
|
||||||
return window.siyuan.languages.calcOperatorCountAll;
|
return window.siyuan.languages.calcOperatorCountAll;
|
||||||
case "Count values":
|
case "Count values":
|
||||||
|
|
|
@ -335,7 +335,7 @@ export const genCellValue = (colType: TAVCol, value: string | any) => {
|
||||||
cellValue = {
|
cellValue = {
|
||||||
type: colType,
|
type: colType,
|
||||||
number: {
|
number: {
|
||||||
content: null,
|
content: 0,
|
||||||
isNotEmpty: false
|
isNotEmpty: false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -863,6 +863,10 @@ export const updateCellsValue = (protyle: IProtyle, nodeElement: HTMLElement, va
|
||||||
doOperations.push(...operations.doOperations);
|
doOperations.push(...operations.doOperations);
|
||||||
undoOperations.push(...operations.undoOperations);
|
undoOperations.push(...operations.undoOperations);
|
||||||
}
|
}
|
||||||
|
// formattedContent 在单元格渲染时没有用到,需对比保持一致
|
||||||
|
if (type === "date") {
|
||||||
|
cellValue.date.formattedContent = oldValue.date.formattedContent;
|
||||||
|
}
|
||||||
if (objEquals(cellValue, oldValue)) {
|
if (objEquals(cellValue, oldValue)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -989,21 +993,27 @@ export const renderCell = (cellValue: IAVCellValue, rowIndex = 0, showIcon = tru
|
||||||
}
|
}
|
||||||
text += "</div>";
|
text += "</div>";
|
||||||
} else if (cellValue.type === "rollup") {
|
} else if (cellValue.type === "rollup") {
|
||||||
|
let rollupType;
|
||||||
cellValue?.rollup?.contents?.forEach((item) => {
|
cellValue?.rollup?.contents?.forEach((item) => {
|
||||||
const rollupText = ["template", "select", "mSelect", "mAsset", "checkbox", "relation"].includes(item.type) ? renderCell(item, rowIndex, showIcon, type) : renderRollup(item);
|
const rollupText = ["template", "select", "mSelect", "mAsset", "relation"].includes(item.type) ? renderCell(item, rowIndex, showIcon, type) : renderRollup(item, showIcon);
|
||||||
if (rollupText) {
|
if (rollupText) {
|
||||||
text += rollupText + ", ";
|
text += rollupText + (item.type === "checkbox" ? "" : ", ");
|
||||||
}
|
}
|
||||||
|
rollupType = item.type;
|
||||||
});
|
});
|
||||||
if (text && text.endsWith(", ")) {
|
if (text) {
|
||||||
text = text.substring(0, text.length - 2);
|
if (rollupType === "checkbox") {
|
||||||
|
text = `<div class="fn__flex">${text}</div>`;
|
||||||
|
} else if (text.endsWith(", ")) {
|
||||||
|
text = text.substring(0, text.length - 2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (cellValue.type === "relation") {
|
} else if (cellValue.type === "relation") {
|
||||||
cellValue?.relation?.contents?.forEach((item, index) => {
|
cellValue?.relation?.contents?.forEach((item, index) => {
|
||||||
if (item && item.block) {
|
if (item && item.block) {
|
||||||
const rowID = cellValue.relation.blockIDs[index];
|
const rowID = cellValue.relation.blockIDs[index];
|
||||||
if (item?.isDetached) {
|
if (item?.isDetached) {
|
||||||
text += `<span data-row-id="${rowID}" class="av__cell--relation"><span class="b3-menu__avemoji${showIcon ? "" : " fn__none"}">➖</span><span class="av__celltext">${Lute.EscapeHTMLStr(item.block.content || window.siyuan.languages.untitled)}</span></span>`;
|
text += `<span data-row-id="${rowID}" class="av__cell--relation"><span class="${showIcon ? "" : " fn__none"}">➖<span class="fn__space--5"></span></span><span class="av__celltext">${Lute.EscapeHTMLStr(item.block.content || window.siyuan.languages.untitled)}</span></span>`;
|
||||||
} else {
|
} else {
|
||||||
// data-block-id 用于更新 emoji
|
// data-block-id 用于更新 emoji
|
||||||
text += `<span data-row-id="${rowID}" class="av__cell--relation" data-block-id="${item.block.id}"><span class="b3-menu__avemoji${showIcon ? "" : " fn__none"}" data-unicode="${item.block.icon || ""}">${unicode2Emoji(item.block.icon || window.siyuan.storage[Constants.LOCAL_IMAGES].file)}</span><span data-type="block-ref" data-id="${item.block.id}" data-subtype="s" class="av__celltext av__celltext--ref">${Lute.EscapeHTMLStr(item.block.content || window.siyuan.languages.untitled)}</span></span>`;
|
text += `<span data-row-id="${rowID}" class="av__cell--relation" data-block-id="${item.block.id}"><span class="b3-menu__avemoji${showIcon ? "" : " fn__none"}" data-unicode="${item.block.icon || ""}">${unicode2Emoji(item.block.icon || window.siyuan.storage[Constants.LOCAL_IMAGES].file)}</span><span data-type="block-ref" data-id="${item.block.id}" data-subtype="s" class="av__celltext av__celltext--ref">${Lute.EscapeHTMLStr(item.block.content || window.siyuan.languages.untitled)}</span></span>`;
|
||||||
|
@ -1024,7 +1034,7 @@ export const renderCell = (cellValue: IAVCellValue, rowIndex = 0, showIcon = tru
|
||||||
return text;
|
return text;
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderRollup = (cellValue: IAVCellValue) => {
|
const renderRollup = (cellValue: IAVCellValue, showIcon: boolean) => {
|
||||||
let text = "";
|
let text = "";
|
||||||
if (["text"].includes(cellValue.type)) {
|
if (["text"].includes(cellValue.type)) {
|
||||||
text = cellValue ? (cellValue[cellValue.type as "text"].content || "") : "";
|
text = cellValue ? (cellValue[cellValue.type as "text"].content || "") : "";
|
||||||
|
@ -1042,10 +1052,12 @@ const renderRollup = (cellValue: IAVCellValue) => {
|
||||||
if (cellValue?.isDetached) {
|
if (cellValue?.isDetached) {
|
||||||
text = `<span class="av__celltext">${Lute.EscapeHTMLStr(cellValue.block?.content || window.siyuan.languages.untitled)}</span>`;
|
text = `<span class="av__celltext">${Lute.EscapeHTMLStr(cellValue.block?.content || window.siyuan.languages.untitled)}</span>`;
|
||||||
} else {
|
} else {
|
||||||
text = `<span data-type="block-ref" data-id="${cellValue.block?.id}" data-subtype="s" class="av__celltext av__celltext--ref">${Lute.EscapeHTMLStr(cellValue.block?.content || window.siyuan.languages.untitled)}</span>`;
|
text = `<span class="b3-menu__avemoji${showIcon ? "" : " fn__none"}" data-unicode="${cellValue.block.icon || ""}">${unicode2Emoji(cellValue.block.icon || window.siyuan.storage[Constants.LOCAL_IMAGES].file)}</span><span data-type="block-ref" data-id="${cellValue.block?.id}" data-subtype="s" class="av__celltext av__celltext--ref">${Lute.EscapeHTMLStr(cellValue.block?.content || window.siyuan.languages.untitled)}</span>`;
|
||||||
}
|
}
|
||||||
} else if (cellValue.type === "number") {
|
} else if (cellValue.type === "number") {
|
||||||
text = cellValue?.number.formattedContent || cellValue?.number.content.toString() || "";
|
text = cellValue?.number.formattedContent || cellValue?.number.content.toString() || "";
|
||||||
|
} else if (cellValue.type === "checkbox") {
|
||||||
|
text += `<svg class="av__checkbox"><use xlink:href="#icon${cellValue?.checkbox?.checked ? "Check" : "Uncheck"}"></use></svg><span class="fn__space"></span>`;
|
||||||
} else if (["date", "updated", "created"].includes(cellValue.type)) {
|
} else if (["date", "updated", "created"].includes(cellValue.type)) {
|
||||||
const dataValue = cellValue ? cellValue[cellValue.type as "date"] : null;
|
const dataValue = cellValue ? cellValue[cellValue.type as "date"] : null;
|
||||||
if (dataValue.formattedContent) {
|
if (dataValue.formattedContent) {
|
||||||
|
|
|
@ -932,11 +932,12 @@ export const showColMenu = (protyle: IProtyle, blockElement: Element, cellElemen
|
||||||
});
|
});
|
||||||
menu.addItem({
|
menu.addItem({
|
||||||
icon: "iconSoftWrap",
|
icon: "iconSoftWrap",
|
||||||
label: `<label class="fn__flex" style="margin-bottom: 4px"><span>${window.siyuan.languages.wrap}</span><span class="fn__space fn__flex-1"></span>
|
label: `<label class="fn__flex fn__pointer"><span>${window.siyuan.languages.wrap}</span><span class="fn__space fn__flex-1"></span>
|
||||||
<input type="checkbox" class="b3-switch b3-switch--menu"${cellElement.dataset.wrap === "true" ? " checked" : ""}></label>`,
|
<input type="checkbox" class="b3-switch b3-switch--menu"${cellElement.dataset.wrap === "true" ? " checked" : ""}></label>`,
|
||||||
bind(element) {
|
bind(element) {
|
||||||
const wrapElement = element.querySelector(".b3-switch") as HTMLInputElement;
|
const wrapElement = element.querySelector(".b3-switch") as HTMLInputElement;
|
||||||
wrapElement.addEventListener("change", () => {
|
wrapElement.addEventListener("change", () => {
|
||||||
|
cellElement.dataset.wrap = wrapElement.checked.toString();
|
||||||
transaction(protyle, [{
|
transaction(protyle, [{
|
||||||
action: "setAttrViewColWrap",
|
action: "setAttrViewColWrap",
|
||||||
id: colId,
|
id: colId,
|
||||||
|
|
|
@ -2,7 +2,7 @@ import * as dayjs from "dayjs";
|
||||||
import {genCellValueByElement, updateCellsValue} from "./cell";
|
import {genCellValueByElement, updateCellsValue} from "./cell";
|
||||||
|
|
||||||
export const getDateHTML = (cellElements: HTMLElement[]) => {
|
export const getDateHTML = (cellElements: HTMLElement[]) => {
|
||||||
const cellValue = genCellValueByElement("date", cellElements[0]).date;
|
const cellValue = genCellValueByElement("date", cellElements[0]).date;
|
||||||
const isNotTime = cellValue.isNotTime;
|
const isNotTime = cellValue.isNotTime;
|
||||||
let value = "";
|
let value = "";
|
||||||
const currentDate = new Date().getTime();
|
const currentDate = new Date().getTime();
|
||||||
|
@ -64,9 +64,9 @@ export const bindDateEvent = (options: {
|
||||||
}
|
}
|
||||||
if (event.key === "Enter") {
|
if (event.key === "Enter") {
|
||||||
updateCellsValue(options.protyle, options.blockElement as HTMLElement, {
|
updateCellsValue(options.protyle, options.blockElement as HTMLElement, {
|
||||||
content: getFullYearTime(inputElements[0].dataset.value),
|
content: getFullYearTime(inputElements[0].dataset.value) || 0,
|
||||||
isNotEmpty: inputElements[0].value !== "",
|
isNotEmpty: inputElements[0].value !== "",
|
||||||
content2: getFullYearTime(inputElements[1].dataset.value),
|
content2: getFullYearTime(inputElements[1].dataset.value) || 0,
|
||||||
isNotEmpty2: inputElements[1].value !== "",
|
isNotEmpty2: inputElements[1].value !== "",
|
||||||
hasEndDate: inputElements[2].checked,
|
hasEndDate: inputElements[2].checked,
|
||||||
isNotTime: !inputElements[3].checked,
|
isNotTime: !inputElements[3].checked,
|
||||||
|
@ -114,9 +114,9 @@ export const bindDateEvent = (options: {
|
||||||
});
|
});
|
||||||
return () => {
|
return () => {
|
||||||
updateCellsValue(options.protyle, options.blockElement as HTMLElement, {
|
updateCellsValue(options.protyle, options.blockElement as HTMLElement, {
|
||||||
content: getFullYearTime(inputElements[0].dataset.value),
|
content: getFullYearTime(inputElements[0].dataset.value) || 0,
|
||||||
isNotEmpty: inputElements[0].value !== "",
|
isNotEmpty: inputElements[0].value !== "",
|
||||||
content2: getFullYearTime(inputElements[1].dataset.value),
|
content2: getFullYearTime(inputElements[1].dataset.value) || 0,
|
||||||
isNotEmpty2: inputElements[1].value !== "",
|
isNotEmpty2: inputElements[1].value !== "",
|
||||||
hasEndDate: inputElements[2].checked,
|
hasEndDate: inputElements[2].checked,
|
||||||
isNotTime: !inputElements[3].checked,
|
isNotTime: !inputElements[3].checked,
|
||||||
|
|
|
@ -154,6 +154,10 @@ const afterRenderGallery = (options: ITableOptions) => {
|
||||||
if (typeof options.resetData.oldOffset === "number") {
|
if (typeof options.resetData.oldOffset === "number") {
|
||||||
options.protyle.contentElement.scrollTop = options.resetData.oldOffset;
|
options.protyle.contentElement.scrollTop = options.resetData.oldOffset;
|
||||||
}
|
}
|
||||||
|
if (options.blockElement.getAttribute("data-need-focus") === "true") {
|
||||||
|
focusBlock(options.blockElement);
|
||||||
|
options.blockElement.removeAttribute("data-need-focus");
|
||||||
|
}
|
||||||
options.blockElement.setAttribute("data-render", "true");
|
options.blockElement.setAttribute("data-render", "true");
|
||||||
if (options.resetData.alignSelf) {
|
if (options.resetData.alignSelf) {
|
||||||
options.blockElement.style.alignSelf = options.resetData.alignSelf;
|
options.blockElement.style.alignSelf = options.resetData.alignSelf;
|
||||||
|
|
|
@ -39,7 +39,7 @@ import {
|
||||||
openViewMenu
|
openViewMenu
|
||||||
} from "./view";
|
} from "./view";
|
||||||
import {focusBlock} from "../../util/selection";
|
import {focusBlock} from "../../util/selection";
|
||||||
import {setPageSize} from "./row";
|
import {getFieldIdByCellElement, setPageSize} from "./row";
|
||||||
import {bindRelationEvent, getRelationHTML, openSearchAV, setRelationCell, updateRelation} from "./relation";
|
import {bindRelationEvent, getRelationHTML, openSearchAV, setRelationCell, updateRelation} from "./relation";
|
||||||
import {bindRollupData, getRollupHTML, goSearchRollupCol} from "./rollup";
|
import {bindRollupData, getRollupHTML, goSearchRollupCol} from "./rollup";
|
||||||
import {updateCellsValue} from "./cell";
|
import {updateCellsValue} from "./cell";
|
||||||
|
@ -157,7 +157,18 @@ export const openMenuPanel = (options: {
|
||||||
const menuElement = avPanelElement.lastElementChild as HTMLElement;
|
const menuElement = avPanelElement.lastElementChild as HTMLElement;
|
||||||
let tabRect = options.blockElement.querySelector(`.av__views, .av__row[data-col-id="${options.colId}"] > .block__logo`)?.getBoundingClientRect();
|
let tabRect = options.blockElement.querySelector(`.av__views, .av__row[data-col-id="${options.colId}"] > .block__logo`)?.getBoundingClientRect();
|
||||||
if (["select", "date", "asset", "relation", "rollup"].includes(options.type)) {
|
if (["select", "date", "asset", "relation", "rollup"].includes(options.type)) {
|
||||||
const cellRect = options.cellElements[options.cellElements.length - 1].getBoundingClientRect();
|
let lastElement = options.cellElements[options.cellElements.length - 1];
|
||||||
|
if (!options.blockElement.contains(lastElement)) {
|
||||||
|
// https://github.com/siyuan-note/siyuan/issues/15839
|
||||||
|
const rowID = getFieldIdByCellElement(lastElement, data.viewType);
|
||||||
|
if (data.viewType === "table") {
|
||||||
|
lastElement = options.blockElement.querySelector(`.av__row[data-id="${rowID}"] .av__cell[data-col-id="${lastElement.dataset.colId}"]`);
|
||||||
|
} else {
|
||||||
|
lastElement = options.blockElement.querySelector(`.av__gallery-item[data-id="${rowID}"] .av__cell[data-field-id="${lastElement.dataset.fieldId}"]`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const cellRect = (lastElement || options.cellElements[options.cellElements.length - 1]).getBoundingClientRect();
|
||||||
|
|
||||||
if (options.type === "select") {
|
if (options.type === "select") {
|
||||||
bindSelectEvent(options.protyle, data, menuElement, options.cellElements, options.blockElement);
|
bindSelectEvent(options.protyle, data, menuElement, options.cellElements, options.blockElement);
|
||||||
} else if (options.type === "date") {
|
} else if (options.type === "date") {
|
||||||
|
@ -1201,10 +1212,10 @@ export const openMenuPanel = (options: {
|
||||||
title: isTwoWay ? window.siyuan.languages.removeColConfirm : window.siyuan.languages.deleteOpConfirm,
|
title: isTwoWay ? window.siyuan.languages.removeColConfirm : window.siyuan.languages.deleteOpConfirm,
|
||||||
content: `<div class="b3-dialog__content">
|
content: `<div class="b3-dialog__content">
|
||||||
${isTwoWay ? window.siyuan.languages.confirmRemoveRelationField
|
${isTwoWay ? window.siyuan.languages.confirmRemoveRelationField
|
||||||
.replace("${x}", menuElement.querySelector("input").value || window.siyuan.languages._kernel[272])
|
.replace("${x}", menuElement.querySelector("input").value || window.siyuan.languages._kernel[272])
|
||||||
.replace("${y}", menuElement.querySelector('.b3-menu__item[data-type="goSearchAV"] .b3-menu__accelerator').textContent)
|
.replace("${y}", menuElement.querySelector('.b3-menu__item[data-type="goSearchAV"] .b3-menu__accelerator').textContent)
|
||||||
.replace("${z}", (menuElement.querySelector('input[data-type="colName"]') as HTMLInputElement).value || window.siyuan.languages._kernel[272])
|
.replace("${z}", (menuElement.querySelector('input[data-type="colName"]') as HTMLInputElement).value || window.siyuan.languages._kernel[272])
|
||||||
: window.siyuan.languages.removeCol.replace("${x}", menuElement.querySelector("input").value || window.siyuan.languages._kernel[272])}
|
: window.siyuan.languages.removeCol.replace("${x}", menuElement.querySelector("input").value || window.siyuan.languages._kernel[272])}
|
||||||
<div class="fn__hr--b"></div>
|
<div class="fn__hr--b"></div>
|
||||||
<button class="fn__block b3-button b3-button--remove" data-action="delete">${isTwoWay ? window.siyuan.languages.removeBothRelationField : window.siyuan.languages.delete}</button>
|
<button class="fn__block b3-button b3-button--remove" data-action="delete">${isTwoWay ? window.siyuan.languages.removeBothRelationField : window.siyuan.languages.delete}</button>
|
||||||
<div class="fn__hr"></div>
|
<div class="fn__hr"></div>
|
||||||
|
|
|
@ -282,6 +282,10 @@ const renderGroupTable = (options: ITableOptions) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const afterRenderTable = (options: ITableOptions) => {
|
const afterRenderTable = (options: ITableOptions) => {
|
||||||
|
if (options.blockElement.getAttribute("data-need-focus") === "true") {
|
||||||
|
focusBlock(options.blockElement);
|
||||||
|
options.blockElement.removeAttribute("data-need-focus");
|
||||||
|
}
|
||||||
options.blockElement.setAttribute("data-render", "true");
|
options.blockElement.setAttribute("data-render", "true");
|
||||||
options.blockElement.querySelector(".av__scroll").scrollLeft = options.resetData.left;
|
options.blockElement.querySelector(".av__scroll").scrollLeft = options.resetData.left;
|
||||||
options.blockElement.style.alignSelf = options.resetData.alignSelf;
|
options.blockElement.style.alignSelf = options.resetData.alignSelf;
|
||||||
|
@ -761,6 +765,7 @@ export const refreshAV = (protyle: IProtyle, operation: IOperation) => {
|
||||||
const attrElement = document.querySelector(`.b3-dialog--open[data-key="${Constants.DIALOG_ATTR}"] .custom-attr > [data-av-id="${avID}"]`) as HTMLElement;
|
const attrElement = document.querySelector(`.b3-dialog--open[data-key="${Constants.DIALOG_ATTR}"] .custom-attr > [data-av-id="${avID}"]`) as HTMLElement;
|
||||||
if (attrElement) {
|
if (attrElement) {
|
||||||
// 更新属性面板
|
// 更新属性面板
|
||||||
|
attrElement.removeAttribute("data-rendering");
|
||||||
renderAVAttribute(attrElement.parentElement, attrElement.dataset.nodeId, protyle);
|
renderAVAttribute(attrElement.parentElement, attrElement.dataset.nodeId, protyle);
|
||||||
} else {
|
} else {
|
||||||
if (operation.action === "insertAttrViewBlock" && operation.context?.ignoreTip !== "true") {
|
if (operation.action === "insertAttrViewBlock" && operation.context?.ignoreTip !== "true") {
|
||||||
|
|
|
@ -81,7 +81,7 @@ const genSearchList = (element: Element, keyword: string, avId: string, isRelati
|
||||||
showMessage(window.siyuan.languages.selectRelation);
|
showMessage(window.siyuan.languages.selectRelation);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fetchPost(isRelation ? "/api/av/searchAttributeViewRelationKey" : "/api/av/searchAttributeViewNonRelationKey", {
|
fetchPost(isRelation ? "/api/av/searchAttributeViewRelationKey" : "/api/av/searchAttributeViewRollupDestKeys", {
|
||||||
avID: avId,
|
avID: avId,
|
||||||
keyword
|
keyword
|
||||||
}, (response) => {
|
}, (response) => {
|
||||||
|
|
|
@ -59,7 +59,7 @@ const filterSelectHTML = (key: string, options: {
|
||||||
<span class="fn__ellipsis">${escapeHtml(key)}</span>
|
<span class="fn__ellipsis">${escapeHtml(key)}</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="b3-menu__accelerator">${window.siyuan.languages.enterKey}</span>
|
<span class="b3-menu__accelerator b3-menu__accelerator--hotkey">${window.siyuan.languages.enterKey}</span>
|
||||||
</button>${html}`;
|
</button>${html}`;
|
||||||
} else if (html.indexOf("b3-menu__item--current") === -1) {
|
} else if (html.indexOf("b3-menu__item--current") === -1) {
|
||||||
html = html.replace('class="b3-menu__item"', 'class="b3-menu__item b3-menu__item--current"');
|
html = html.replace('class="b3-menu__item"', 'class="b3-menu__item b3-menu__item--current"');
|
||||||
|
|
|
@ -14,7 +14,7 @@ export const mermaidRender = (element: Element, cdn = Constants.PROTYLE_CDN) =>
|
||||||
if (mermaidElements.length === 0) {
|
if (mermaidElements.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
addScript(`${cdn}/js/mermaid/mermaid.min.js?v=11.6.0`, "protyleMermaidScript").then(() => {
|
addScript(`${cdn}/js/mermaid/mermaid.min.js?v=11.11.0`, "protyleMermaidScript").then(() => {
|
||||||
const config: any = {
|
const config: any = {
|
||||||
securityLevel: "loose", // 升级后无 https://github.com/siyuan-note/siyuan/issues/3587,可使用该选项
|
securityLevel: "loose", // 升级后无 https://github.com/siyuan-note/siyuan/issues/3587,可使用该选项
|
||||||
altFontFamily: "sans-serif",
|
altFontFamily: "sans-serif",
|
||||||
|
|
|
@ -901,7 +901,7 @@ export class Toolbar {
|
||||||
this.subElement.style.padding = "0";
|
this.subElement.style.padding = "0";
|
||||||
}
|
}
|
||||||
this.subElement.innerHTML = `<div ${(isPin && this.subElement.firstElementChild.getAttribute("data-drag") === "true") ? 'data-drag="true"' : ""}><div class="block__icons block__icons--menu fn__flex" style="border-radius: var(--b3-border-radius-b) var(--b3-border-radius-b) 0 0;">
|
this.subElement.innerHTML = `<div ${(isPin && this.subElement.firstElementChild.getAttribute("data-drag") === "true") ? 'data-drag="true"' : ""}><div class="block__icons block__icons--menu fn__flex" style="border-radius: var(--b3-border-radius-b) var(--b3-border-radius-b) 0 0;">
|
||||||
<span class="fn__flex-1 resize__move">
|
<span class="fn__flex-1 resize__move" style="line-height: 24px;">
|
||||||
${title}
|
${title}
|
||||||
</span>
|
</span>
|
||||||
<span class="fn__space"></span>
|
<span class="fn__space"></span>
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {lineNumberRender} from "../render/highlightRender";
|
||||||
import {hideMessage, showMessage} from "../../dialog/message";
|
import {hideMessage, showMessage} from "../../dialog/message";
|
||||||
import {genUUID} from "../../util/genID";
|
import {genUUID} from "../../util/genID";
|
||||||
import {getContenteditableElement, getLastBlock} from "../wysiwyg/getBlock";
|
import {getContenteditableElement, getLastBlock} from "../wysiwyg/getBlock";
|
||||||
import {genEmptyElement} from "../../block/util";
|
import {genEmptyElement, genHeadingElement} from "../../block/util";
|
||||||
import {transaction} from "../wysiwyg/transaction";
|
import {transaction} from "../wysiwyg/transaction";
|
||||||
import {focusByRange} from "../util/selection";
|
import {focusByRange} from "../util/selection";
|
||||||
/// #if !MOBILE
|
/// #if !MOBILE
|
||||||
|
@ -137,16 +137,22 @@ export const initUI = (protyle: IProtyle) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const lastRect = protyle.wysiwyg.element.lastElementChild.getBoundingClientRect();
|
const lastElement = protyle.wysiwyg.element.lastElementChild;
|
||||||
|
const lastRect = lastElement.getBoundingClientRect();
|
||||||
const range = document.createRange();
|
const range = document.createRange();
|
||||||
if (event.y > lastRect.bottom) {
|
if (event.y > lastRect.bottom) {
|
||||||
const lastEditElement = getContenteditableElement(getLastBlock(protyle.wysiwyg.element.lastElementChild));
|
const lastEditElement = getContenteditableElement(getLastBlock(lastElement));
|
||||||
if (!protyle.options.click.preventInsetEmptyBlock && (
|
if (!protyle.options.click.preventInsetEmptyBlock && (
|
||||||
!lastEditElement ||
|
!lastEditElement ||
|
||||||
(protyle.wysiwyg.element.lastElementChild.getAttribute("data-type") !== "NodeParagraph" && protyle.wysiwyg.element.getAttribute("data-doc-type") !== "NodeListItem") ||
|
(lastElement.getAttribute("data-type") !== "NodeParagraph" && protyle.wysiwyg.element.getAttribute("data-doc-type") !== "NodeListItem") ||
|
||||||
(protyle.wysiwyg.element.lastElementChild.getAttribute("data-type") === "NodeParagraph" && getContenteditableElement(lastEditElement).innerHTML !== ""))
|
(lastElement.getAttribute("data-type") === "NodeParagraph" && getContenteditableElement(lastEditElement).innerHTML !== ""))
|
||||||
) {
|
) {
|
||||||
const emptyElement = genEmptyElement(false, false);
|
let emptyElement:Element;
|
||||||
|
if (lastElement.getAttribute("data-type") === "NodeHeading" && lastElement.getAttribute("fold") === "1") {
|
||||||
|
emptyElement = genHeadingElement(lastElement) as Element;
|
||||||
|
} else {
|
||||||
|
emptyElement = genEmptyElement(false, false);
|
||||||
|
}
|
||||||
protyle.wysiwyg.element.insertAdjacentElement("beforeend", emptyElement);
|
protyle.wysiwyg.element.insertAdjacentElement("beforeend", emptyElement);
|
||||||
transaction(protyle, [{
|
transaction(protyle, [{
|
||||||
action: "insert",
|
action: "insert",
|
||||||
|
|
|
@ -44,10 +44,25 @@ const moveToNew = (protyle: IProtyle, sourceElements: Element[], targetElement:
|
||||||
const newSourceId = newSourceElement.getAttribute("data-node-id");
|
const newSourceId = newSourceElement.getAttribute("data-node-id");
|
||||||
const doOperations: IOperation[] = [];
|
const doOperations: IOperation[] = [];
|
||||||
const undoOperations: IOperation[] = [];
|
const undoOperations: IOperation[] = [];
|
||||||
targetElement.insertAdjacentElement(isBottom ? "afterend" : "beforebegin", newSourceElement);
|
let ignoreInsert = false;
|
||||||
|
if (isBottom &&
|
||||||
|
targetElement.getAttribute("data-type") === "NodeHeading" &&
|
||||||
|
targetElement.getAttribute("fold") === "1") {
|
||||||
|
ignoreInsert = true;
|
||||||
|
} else if (!isBottom && targetElement.previousElementSibling &&
|
||||||
|
targetElement.previousElementSibling.getAttribute("data-type") === "NodeHeading" &&
|
||||||
|
targetElement.previousElementSibling.getAttribute("fold") === "1") {
|
||||||
|
ignoreInsert = true;
|
||||||
|
}
|
||||||
|
if (!ignoreInsert) {
|
||||||
|
targetElement.insertAdjacentElement(isBottom ? "afterend" : "beforebegin", newSourceElement);
|
||||||
|
}
|
||||||
if (isBottom) {
|
if (isBottom) {
|
||||||
doOperations.push({
|
doOperations.push({
|
||||||
action: "insert",
|
action: "insert",
|
||||||
|
context: {
|
||||||
|
ignoreProcess: ignoreInsert.toString(),
|
||||||
|
},
|
||||||
data: newSourceElement.outerHTML,
|
data: newSourceElement.outerHTML,
|
||||||
id: newSourceId,
|
id: newSourceId,
|
||||||
previousID: targetId,
|
previousID: targetId,
|
||||||
|
@ -55,6 +70,9 @@ const moveToNew = (protyle: IProtyle, sourceElements: Element[], targetElement:
|
||||||
} else {
|
} else {
|
||||||
doOperations.push({
|
doOperations.push({
|
||||||
action: "insert",
|
action: "insert",
|
||||||
|
context: {
|
||||||
|
ignoreProcess: ignoreInsert.toString(),
|
||||||
|
},
|
||||||
data: newSourceElement.outerHTML,
|
data: newSourceElement.outerHTML,
|
||||||
id: newSourceId,
|
id: newSourceId,
|
||||||
nextID: targetId,
|
nextID: targetId,
|
||||||
|
@ -139,6 +157,7 @@ const moveToNew = (protyle: IProtyle, sourceElements: Element[], targetElement:
|
||||||
id: newSourceId,
|
id: newSourceId,
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
|
ignoreInsert,
|
||||||
doOperations,
|
doOperations,
|
||||||
undoOperations,
|
undoOperations,
|
||||||
topSourceElement,
|
topSourceElement,
|
||||||
|
@ -150,9 +169,32 @@ const moveTo = async (protyle: IProtyle, sourceElements: Element[], targetElemen
|
||||||
let topSourceElement;
|
let topSourceElement;
|
||||||
const doOperations: IOperation[] = [];
|
const doOperations: IOperation[] = [];
|
||||||
const undoOperations: IOperation[] = [];
|
const undoOperations: IOperation[] = [];
|
||||||
const foldHeadingIds: { id: string, parentID: string }[] = [];
|
const copyFoldHeadingIds: { newId: string, oldId: string }[] = [];
|
||||||
const targetId = targetElement.getAttribute("data-node-id");
|
const targetId = targetElement.getAttribute("data-node-id");
|
||||||
let tempTargetElement = targetElement;
|
let tempTargetElement = targetElement;
|
||||||
|
let ignoreInsert = "";
|
||||||
|
const targetPreviousId = targetElement.previousElementSibling?.getAttribute("data-node-id");
|
||||||
|
if (position === "afterend" &&
|
||||||
|
targetElement.getAttribute("data-type") === "NodeHeading" &&
|
||||||
|
targetElement.getAttribute("fold") === "1") {
|
||||||
|
ignoreInsert = targetElement.getAttribute("data-subtype").replace("h", "");
|
||||||
|
} else if (position === "beforebegin" && targetElement.previousElementSibling &&
|
||||||
|
targetElement.previousElementSibling.getAttribute("data-type") === "NodeHeading" &&
|
||||||
|
targetElement.previousElementSibling.getAttribute("fold") === "1") {
|
||||||
|
ignoreInsert = targetElement.getAttribute("data-subtype").replace("h", "");
|
||||||
|
}
|
||||||
|
if (ignoreInsert) {
|
||||||
|
let breakIgnore = false;
|
||||||
|
sourceElements.forEach(item => {
|
||||||
|
if (item.getAttribute("data-type") === "NodeHeading" &&
|
||||||
|
parseInt(item.getAttribute("data-subtype").replace("h", "")) >= parseInt(ignoreInsert)) {
|
||||||
|
breakIgnore = true;
|
||||||
|
}
|
||||||
|
if (!breakIgnore) {
|
||||||
|
item.setAttribute("data-remove", "true");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
sourceElements.reverse().forEach((item, index) => {
|
sourceElements.reverse().forEach((item, index) => {
|
||||||
const id = item.getAttribute("data-node-id");
|
const id = item.getAttribute("data-node-id");
|
||||||
const parentID = item.parentElement.getAttribute("data-node-id") || protyle.block.rootID;
|
const parentID = item.parentElement.getAttribute("data-node-id") || protyle.block.rootID;
|
||||||
|
@ -165,17 +207,19 @@ const moveTo = async (protyle: IProtyle, sourceElements: Element[], targetElemen
|
||||||
topSourceElement = targetElement;
|
topSourceElement = targetElement;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const copyNewId = Lute.NewNodeID();
|
||||||
if (isCopy && item.getAttribute("data-type") === "NodeHeading" && item.getAttribute("fold") === "1") {
|
if (isCopy && item.getAttribute("data-type") === "NodeHeading" && item.getAttribute("fold") === "1") {
|
||||||
item.removeAttribute("fold");
|
copyFoldHeadingIds.push({
|
||||||
foldHeadingIds.push({id, parentID});
|
newId: copyNewId,
|
||||||
|
oldId: id
|
||||||
|
});
|
||||||
}
|
}
|
||||||
let copyId;
|
|
||||||
let copyElement;
|
let copyElement;
|
||||||
if (isCopy) {
|
if (isCopy) {
|
||||||
copyId = Lute.NewNodeID();
|
|
||||||
undoOperations.push({
|
undoOperations.push({
|
||||||
action: "delete",
|
action: "delete",
|
||||||
id: copyId,
|
id: copyNewId,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
undoOperations.push({
|
undoOperations.push({
|
||||||
|
@ -193,28 +237,41 @@ const moveTo = async (protyle: IProtyle, sourceElements: Element[], targetElemen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const needInset = !ignoreInsert || (ignoreInsert && !item.hasAttribute("data-remove"));
|
||||||
|
|
||||||
if (isCopy) {
|
if (isCopy) {
|
||||||
|
item.removeAttribute("data-remove");
|
||||||
copyElement = item.cloneNode(true) as HTMLElement;
|
copyElement = item.cloneNode(true) as HTMLElement;
|
||||||
copyElement.setAttribute("data-node-id", copyId);
|
copyElement.setAttribute("data-node-id", copyNewId);
|
||||||
copyElement.querySelectorAll("[data-node-id]").forEach((e) => {
|
copyElement.querySelectorAll("[data-node-id]").forEach((e) => {
|
||||||
const newId = Lute.NewNodeID();
|
const newId = Lute.NewNodeID();
|
||||||
e.setAttribute("data-node-id", newId);
|
e.setAttribute("data-node-id", newId);
|
||||||
e.setAttribute("updated", newId.split("-")[0]);
|
e.setAttribute("updated", newId.split("-")[0]);
|
||||||
});
|
});
|
||||||
tempTargetElement.insertAdjacentElement(position, copyElement);
|
if (needInset) {
|
||||||
|
tempTargetElement.insertAdjacentElement(position, copyElement);
|
||||||
|
}
|
||||||
doOperations.push({
|
doOperations.push({
|
||||||
action: "insert",
|
action: "insert",
|
||||||
id: copyId,
|
context: {
|
||||||
|
ignoreProcess: (!needInset).toString(),
|
||||||
|
},
|
||||||
|
id: copyNewId,
|
||||||
data: copyElement.outerHTML,
|
data: copyElement.outerHTML,
|
||||||
previousID: position === "afterend" ? targetId : copyElement.previousElementSibling?.getAttribute("data-node-id"), // 不能使用常量,移动后会被修改
|
previousID: position === "afterend" ? targetId : (!needInset ? targetPreviousId : copyElement.previousElementSibling?.getAttribute("data-node-id")), // 不能使用常量,移动后会被修改
|
||||||
parentID: copyElement.parentElement?.getAttribute("data-node-id") || protyle.block.parentID || protyle.block.rootID,
|
parentID: copyElement.parentElement?.getAttribute("data-node-id") || protyle.block.parentID || protyle.block.rootID,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
tempTargetElement.insertAdjacentElement(position, item);
|
if (needInset) {
|
||||||
|
tempTargetElement.insertAdjacentElement(position, item);
|
||||||
|
}
|
||||||
doOperations.push({
|
doOperations.push({
|
||||||
action: "move",
|
action: "move",
|
||||||
|
context: {
|
||||||
|
ignoreProcess: (!needInset).toString(),
|
||||||
|
},
|
||||||
id,
|
id,
|
||||||
previousID: position === "afterend" ? targetId : item.previousElementSibling?.getAttribute("data-node-id"), // 不能使用常量,移动后会被修改
|
previousID: position === "afterend" ? targetId : (!needInset ? targetPreviousId : item.previousElementSibling?.getAttribute("data-node-id")), // 不能使用常量,移动后会被修改
|
||||||
parentID: item.parentElement?.getAttribute("data-node-id") || protyle.block.parentID || protyle.block.rootID,
|
parentID: item.parentElement?.getAttribute("data-node-id") || protyle.block.parentID || protyle.block.rootID,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -222,29 +279,26 @@ const moveTo = async (protyle: IProtyle, sourceElements: Element[], targetElemen
|
||||||
tempTargetElement = isCopy ? copyElement : item;
|
tempTargetElement = isCopy ? copyElement : item;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
undoOperations.reverse();
|
if (ignoreInsert) {
|
||||||
for (let j = 0; j < foldHeadingIds.length; j++) {
|
// 不能在上一个循环中移除,否则会影响位置的判断和 tempTargetElement
|
||||||
const childrenItem = foldHeadingIds[j];
|
sourceElements.forEach(item => {
|
||||||
const headingIds = await fetchSyncPost("/api/block/getHeadingChildrenIDs", {id: childrenItem.id});
|
if (item.hasAttribute("data-remove")) {
|
||||||
headingIds.data.reverse().forEach((headingId: string) => {
|
item.remove();
|
||||||
undoOperations.push({
|
}
|
||||||
action: "move",
|
|
||||||
id: headingId,
|
|
||||||
previousID: childrenItem.id,
|
|
||||||
parentID: childrenItem.parentID,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
undoOperations.push({
|
|
||||||
action: "foldHeading",
|
|
||||||
id: childrenItem.id,
|
|
||||||
data: "remove"
|
|
||||||
});
|
|
||||||
doOperations.push({
|
|
||||||
action: "unfoldHeading",
|
|
||||||
id: childrenItem.id,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
undoOperations.reverse();
|
||||||
|
for (let j = 0; j < copyFoldHeadingIds.length; j++) {
|
||||||
|
const childrenItem = copyFoldHeadingIds[j];
|
||||||
|
const responseTransaction = await fetchSyncPost("/api/block/getHeadingInsertTransaction", {id: childrenItem.oldId});
|
||||||
|
responseTransaction.data.doOperations.splice(0, 1);
|
||||||
|
responseTransaction.data.doOperations[0].previousID = childrenItem.newId;
|
||||||
|
responseTransaction.data.undoOperations.splice(0, 1);
|
||||||
|
doOperations.push(...responseTransaction.data.doOperations);
|
||||||
|
undoOperations.push(...responseTransaction.data.undoOperations);
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
|
ignoreInsert: ignoreInsert ? true : false,
|
||||||
doOperations,
|
doOperations,
|
||||||
undoOperations,
|
undoOperations,
|
||||||
topSourceElement,
|
topSourceElement,
|
||||||
|
@ -367,7 +421,7 @@ const dragSb = async (protyle: IProtyle, sourceElements: Element[], targetElemen
|
||||||
id: newSourceId,
|
id: newSourceId,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const foldHeadingIds: { id: string, parentID: string }[] = [];
|
const copyFoldHeadingIds: { newId: string, oldId: string }[] = [];
|
||||||
let afterPreviousID;
|
let afterPreviousID;
|
||||||
sourceElements.reverse().forEach((item, index) => {
|
sourceElements.reverse().forEach((item, index) => {
|
||||||
const id = item.getAttribute("data-node-id");
|
const id = item.getAttribute("data-node-id");
|
||||||
|
@ -384,8 +438,7 @@ const dragSb = async (protyle: IProtyle, sourceElements: Element[], targetElemen
|
||||||
}
|
}
|
||||||
if (item.getAttribute("data-type") === "NodeHeading" && item.getAttribute("fold") === "1") {
|
if (item.getAttribute("data-type") === "NodeHeading" && item.getAttribute("fold") === "1") {
|
||||||
if (isCopy) {
|
if (isCopy) {
|
||||||
item.removeAttribute("fold");
|
copyFoldHeadingIds.push({oldId: id, newId: copyId});
|
||||||
foldHeadingIds.push({id, parentID});
|
|
||||||
}
|
}
|
||||||
hasFoldHeading = true;
|
hasFoldHeading = true;
|
||||||
}
|
}
|
||||||
|
@ -434,29 +487,17 @@ const dragSb = async (protyle: IProtyle, sourceElements: Element[], targetElemen
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
undoOperations.reverse();
|
undoOperations.reverse();
|
||||||
for (let j = 0; j < foldHeadingIds.length; j++) {
|
for (let j = 0; j < copyFoldHeadingIds.length; j++) {
|
||||||
const childrenItem = foldHeadingIds[j];
|
const childrenItem = copyFoldHeadingIds[j];
|
||||||
const headingIds = await fetchSyncPost("/api/block/getHeadingChildrenIDs", {id: childrenItem.id});
|
const responseTransaction = await fetchSyncPost("/api/block/getHeadingInsertTransaction", {id: childrenItem.oldId});
|
||||||
headingIds.data.reverse().forEach((headingId: string) => {
|
responseTransaction.data.doOperations.splice(0, 1);
|
||||||
undoOperations.push({
|
responseTransaction.data.doOperations[0].previousID = childrenItem.newId;
|
||||||
action: "move",
|
responseTransaction.data.undoOperations.splice(0, 1);
|
||||||
id: headingId,
|
doOperations.push(...responseTransaction.data.doOperations);
|
||||||
previousID: childrenItem.id,
|
undoOperations.push(...responseTransaction.data.undoOperations);
|
||||||
parentID: childrenItem.parentID,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
if (j === 0) {
|
if (j === 0) {
|
||||||
afterPreviousID = headingIds.data[0];
|
afterPreviousID = copyFoldHeadingIds[0].newId;
|
||||||
}
|
}
|
||||||
undoOperations.push({
|
|
||||||
action: "foldHeading",
|
|
||||||
id: childrenItem.id,
|
|
||||||
data: "remove"
|
|
||||||
});
|
|
||||||
doOperations.push({
|
|
||||||
action: "unfoldHeading",
|
|
||||||
id: childrenItem.id,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
if (isBottom) {
|
if (isBottom) {
|
||||||
sbElement.insertAdjacentElement("afterbegin", targetElement);
|
sbElement.insertAdjacentElement("afterbegin", targetElement);
|
||||||
|
@ -595,7 +636,7 @@ const dragSb = async (protyle: IProtyle, sourceElements: Element[], targetElemen
|
||||||
level: "row"
|
level: "row"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if ((sourceElements.length > 1 || hasFoldHeading)) {
|
if (sourceElements.length > 1 || hasFoldHeading) {
|
||||||
turnsIntoOneTransaction({
|
turnsIntoOneTransaction({
|
||||||
protyle,
|
protyle,
|
||||||
selectsElement: sourceElements.reverse(),
|
selectsElement: sourceElements.reverse(),
|
||||||
|
@ -622,6 +663,7 @@ const dragSame = async (protyle: IProtyle, sourceElements: Element[], targetElem
|
||||||
newSourceElement.insertAdjacentHTML("beforeend", `<div class="protyle-attr" contenteditable="false">${Constants.ZWSP}</div>`);
|
newSourceElement.insertAdjacentHTML("beforeend", `<div class="protyle-attr" contenteditable="false">${Constants.ZWSP}</div>`);
|
||||||
}
|
}
|
||||||
let topSourceElement: Element;
|
let topSourceElement: Element;
|
||||||
|
let ignoreInsert = false;
|
||||||
let oldSourceParentElement = sourceElements[0].parentElement;
|
let oldSourceParentElement = sourceElements[0].parentElement;
|
||||||
if (isBottom) {
|
if (isBottom) {
|
||||||
if (newSourceElement) {
|
if (newSourceElement) {
|
||||||
|
@ -629,11 +671,13 @@ const dragSame = async (protyle: IProtyle, sourceElements: Element[], targetElem
|
||||||
doOperations.push(...moveToResult.doOperations);
|
doOperations.push(...moveToResult.doOperations);
|
||||||
undoOperations.push(...moveToResult.undoOperations);
|
undoOperations.push(...moveToResult.undoOperations);
|
||||||
topSourceElement = moveToResult.topSourceElement;
|
topSourceElement = moveToResult.topSourceElement;
|
||||||
|
ignoreInsert = moveToResult.ignoreInsert;
|
||||||
} else {
|
} else {
|
||||||
const moveToResult = await moveTo(protyle, sourceElements, targetElement, isSameDoc, "afterend", isCopy);
|
const moveToResult = await moveTo(protyle, sourceElements, targetElement, isSameDoc, "afterend", isCopy);
|
||||||
doOperations.push(...moveToResult.doOperations);
|
doOperations.push(...moveToResult.doOperations);
|
||||||
undoOperations.push(...moveToResult.undoOperations);
|
undoOperations.push(...moveToResult.undoOperations);
|
||||||
topSourceElement = moveToResult.topSourceElement;
|
topSourceElement = moveToResult.topSourceElement;
|
||||||
|
ignoreInsert = moveToResult.ignoreInsert;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (newSourceElement) {
|
if (newSourceElement) {
|
||||||
|
@ -641,11 +685,13 @@ const dragSame = async (protyle: IProtyle, sourceElements: Element[], targetElem
|
||||||
doOperations.push(...moveToResult.doOperations);
|
doOperations.push(...moveToResult.doOperations);
|
||||||
undoOperations.push(...moveToResult.undoOperations);
|
undoOperations.push(...moveToResult.undoOperations);
|
||||||
topSourceElement = moveToResult.topSourceElement;
|
topSourceElement = moveToResult.topSourceElement;
|
||||||
|
ignoreInsert = moveToResult.ignoreInsert;
|
||||||
} else {
|
} else {
|
||||||
const moveToResult = await moveTo(protyle, sourceElements, targetElement, isSameDoc, "beforebegin", isCopy);
|
const moveToResult = await moveTo(protyle, sourceElements, targetElement, isSameDoc, "beforebegin", isCopy);
|
||||||
doOperations.push(...moveToResult.doOperations);
|
doOperations.push(...moveToResult.doOperations);
|
||||||
undoOperations.push(...moveToResult.undoOperations);
|
undoOperations.push(...moveToResult.undoOperations);
|
||||||
topSourceElement = moveToResult.topSourceElement;
|
topSourceElement = moveToResult.topSourceElement;
|
||||||
|
ignoreInsert = moveToResult.ignoreInsert;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (targetElement.getAttribute("data-type") === "NodeListItem" && targetElement.getAttribute("data-subtype") === "o") {
|
if (targetElement.getAttribute("data-type") === "NodeListItem" && targetElement.getAttribute("data-subtype") === "o") {
|
||||||
|
@ -783,9 +829,9 @@ const dragSame = async (protyle: IProtyle, sourceElements: Element[], targetElem
|
||||||
/// #endif
|
/// #endif
|
||||||
}
|
}
|
||||||
if (isSameDoc || isCopy) {
|
if (isSameDoc || isCopy) {
|
||||||
transaction(protyle, doOperations, undoOperations);
|
transaction(protyle, doOperations, ignoreInsert ? undefined : undoOperations);
|
||||||
} else {
|
} else {
|
||||||
// 跨文档不支持撤销
|
// 跨文档或插入折叠标题下不支持撤销
|
||||||
transaction(protyle, doOperations);
|
transaction(protyle, doOperations);
|
||||||
}
|
}
|
||||||
let hasFoldHeading = false;
|
let hasFoldHeading = false;
|
||||||
|
@ -805,7 +851,11 @@ const dragSame = async (protyle: IProtyle, sourceElements: Element[], targetElem
|
||||||
level: "row"
|
level: "row"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
focusBlock(sourceElements[0]);
|
if (document.contains(sourceElements[0])) {
|
||||||
|
focusBlock(sourceElements[0]);
|
||||||
|
} else {
|
||||||
|
focusBlock(targetElement);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const dropEvent = (protyle: IProtyle, editorElement: HTMLElement) => {
|
export const dropEvent = (protyle: IProtyle, editorElement: HTMLElement) => {
|
||||||
|
|
|
@ -368,8 +368,10 @@ export const disabledProtyle = (protyle: IProtyle) => {
|
||||||
item.setAttribute("draggable", "false");
|
item.setAttribute("draggable", "false");
|
||||||
});
|
});
|
||||||
if (protyle.breadcrumb) {
|
if (protyle.breadcrumb) {
|
||||||
protyle.breadcrumb.element.parentElement.querySelector('[data-type="readonly"] use').setAttribute("xlink:href", "#iconLock");
|
const readonlyButton = protyle.breadcrumb.element.parentElement.querySelector('[data-type="readonly"]');
|
||||||
protyle.breadcrumb.element.parentElement.querySelector('[data-type="readonly"]').setAttribute("aria-label", window.siyuan.config.editor.readOnly ? window.siyuan.languages.tempUnlock : window.siyuan.languages.unlockEdit);
|
readonlyButton.querySelector("use").setAttribute("xlink:href", "#iconLock");
|
||||||
|
readonlyButton.setAttribute("aria-label", window.siyuan.config.editor.readOnly ? window.siyuan.languages.tempUnlock : window.siyuan.languages.unlockEdit);
|
||||||
|
readonlyButton.setAttribute("data-subtype", "lock");
|
||||||
const undoElement = protyle.breadcrumb.element.parentElement.querySelector('[data-type="undo"]');
|
const undoElement = protyle.breadcrumb.element.parentElement.querySelector('[data-type="undo"]');
|
||||||
if (undoElement && !undoElement.classList.contains("fn__none")) {
|
if (undoElement && !undoElement.classList.contains("fn__none")) {
|
||||||
undoElement.classList.add("fn__none");
|
undoElement.classList.add("fn__none");
|
||||||
|
@ -426,8 +428,10 @@ export const enableProtyle = (protyle: IProtyle) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (protyle.breadcrumb) {
|
if (protyle.breadcrumb) {
|
||||||
protyle.breadcrumb.element.parentElement.querySelector('[data-type="readonly"] use').setAttribute("xlink:href", "#iconUnlock");
|
const readonlyButton = protyle.breadcrumb.element.parentElement.querySelector('[data-type="readonly"]');
|
||||||
protyle.breadcrumb.element.parentElement.querySelector('[data-type="readonly"]').setAttribute("aria-label", window.siyuan.config.editor.readOnly ? window.siyuan.languages.cancelTempUnlock : window.siyuan.languages.lockEdit);
|
readonlyButton.querySelector("use").setAttribute("xlink:href", "#iconUnlock");
|
||||||
|
readonlyButton.setAttribute("aria-label", window.siyuan.config.editor.readOnly ? window.siyuan.languages.cancelTempUnlock : window.siyuan.languages.lockEdit);
|
||||||
|
readonlyButton.setAttribute("data-subtype", "unlock");
|
||||||
const undoElement = protyle.breadcrumb.element.parentElement.querySelector('[data-type="undo"]');
|
const undoElement = protyle.breadcrumb.element.parentElement.querySelector('[data-type="undo"]');
|
||||||
if (undoElement && undoElement.classList.contains("fn__none")) {
|
if (undoElement && undoElement.classList.contains("fn__none")) {
|
||||||
undoElement.classList.remove("fn__none");
|
undoElement.classList.remove("fn__none");
|
||||||
|
|
|
@ -610,6 +610,7 @@ export const focusBlock = (element: Element, parentElement?: HTMLElement, toStar
|
||||||
range.setStart(cursorElement.firstChild, 0);
|
range.setStart(cursorElement.firstChild, 0);
|
||||||
setRange = true;
|
setRange = true;
|
||||||
} else {
|
} else {
|
||||||
|
element.setAttribute("data-need-focus", "true");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
/// #else
|
/// #else
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {genEmptyElement, insertEmptyBlock} from "../../block/util";
|
import {genEmptyElement, genHeadingElement, insertEmptyBlock} from "../../block/util";
|
||||||
import {focusByRange, focusByWbr, getSelectionOffset, setLastNodeRange} from "../util/selection";
|
import {focusByRange, focusByWbr, getSelectionOffset, setLastNodeRange} from "../util/selection";
|
||||||
import {
|
import {
|
||||||
getContenteditableElement,
|
getContenteditableElement,
|
||||||
|
@ -220,7 +220,11 @@ export const enter = (blockElement: HTMLElement, range: Range, protyle: IProtyle
|
||||||
}
|
}
|
||||||
const id = blockElement.getAttribute("data-node-id");
|
const id = blockElement.getAttribute("data-node-id");
|
||||||
const newElement = document.createElement("div");
|
const newElement = document.createElement("div");
|
||||||
newElement.appendChild(genEmptyElement(false, false));
|
if (blockElement.getAttribute("data-type") === "NodeHeading" && blockElement.getAttribute("fold") === "1") {
|
||||||
|
newElement.innerHTML = genHeadingElement(blockElement, true) as string;
|
||||||
|
} else {
|
||||||
|
newElement.appendChild(genEmptyElement(false, false));
|
||||||
|
}
|
||||||
const newEditableElement = newElement.querySelector('[contenteditable="true"]');
|
const newEditableElement = newElement.querySelector('[contenteditable="true"]');
|
||||||
newEditableElement.appendChild(range.extractContents());
|
newEditableElement.appendChild(range.extractContents());
|
||||||
const selectWbrElement = newEditableElement.querySelector("wbr");
|
const selectWbrElement = newEditableElement.querySelector("wbr");
|
||||||
|
|
|
@ -1741,7 +1741,7 @@ export class WYSIWYG {
|
||||||
}
|
}
|
||||||
const range = getSelection().getRangeAt(0);
|
const range = getSelection().getRangeAt(0);
|
||||||
if (this.element === range.startContainer || this.element.contains(range.startContainer)) {
|
if (this.element === range.startContainer || this.element.contains(range.startContainer)) {
|
||||||
protyle.toolbar.range = range;
|
protyle.toolbar.range = range.cloneRange();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -300,6 +300,14 @@ export const removeBlock = async (protyle: IProtyle, blockElement: Element, rang
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (blockType === "NodeHeading") {
|
if (blockType === "NodeHeading") {
|
||||||
|
if ((blockElement.previousElementSibling &&
|
||||||
|
blockElement.previousElementSibling.getAttribute("data-type") === "NodeHeading" &&
|
||||||
|
blockElement.previousElementSibling.getAttribute("fold") === "1") ||
|
||||||
|
(blockElement.getAttribute("data-type") === "NodeHeading" &&
|
||||||
|
blockElement.getAttribute("fold") === "1")) {
|
||||||
|
focusBlock(blockElement.previousElementSibling, undefined, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
turnsIntoTransaction({
|
turnsIntoTransaction({
|
||||||
protyle: protyle,
|
protyle: protyle,
|
||||||
selectsElement: [blockElement],
|
selectsElement: [blockElement],
|
||||||
|
|
|
@ -643,6 +643,9 @@ export const onTransaction = (protyle: IProtyle, operation: IOperation, isUndo:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (operation.action === "move") {
|
if (operation.action === "move") {
|
||||||
|
if (operation.context?.ignoreProcess === "true") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
/// #if !MOBILE
|
/// #if !MOBILE
|
||||||
if (updateElements.length === 0) {
|
if (updateElements.length === 0) {
|
||||||
// 打开两个相同的文档 A、A1,从 A 拖拽块 B 到 A1,在后续 ws 处理中,无法获取到拖拽出去的 B
|
// 打开两个相同的文档 A、A1,从 A 拖拽块 B 到 A1,在后续 ws 处理中,无法获取到拖拽出去的 B
|
||||||
|
|
|
@ -106,7 +106,7 @@ export const genSearch = (app: App, config: Config.IUILayoutTabSearchConfig, ele
|
||||||
</span>
|
</span>
|
||||||
<span class="fn__space"></span>
|
<span class="fn__space"></span>
|
||||||
<span data-position="9south" id="searchInclude" ${enableIncludeChild ? "" : "disabled"} aria-label="${window.siyuan.languages.includeChildDoc}" class="block__icon block__icon--show ariaLabel">
|
<span data-position="9south" id="searchInclude" ${enableIncludeChild ? "" : "disabled"} aria-label="${window.siyuan.languages.includeChildDoc}" class="block__icon block__icon--show ariaLabel">
|
||||||
<svg${includeChild ? ' class="ft__primary"' : ""}><use xlink:href="#iconCopy"></use></svg>
|
<svg${includeChild ? ' class="ft__primary"' : ""}><use xlink:href="#iconInclude"></use></svg>
|
||||||
</span>
|
</span>
|
||||||
<span class="fn__space"></span>
|
<span class="fn__space"></span>
|
||||||
<span id="searchPath" aria-label="${window.siyuan.languages.specifyPath}" class="block__icon block__icon--show ariaLabel" data-position="9south">
|
<span id="searchPath" aria-label="${window.siyuan.languages.specifyPath}" class="block__icon block__icon--show ariaLabel" data-position="9south">
|
||||||
|
@ -494,6 +494,11 @@ export const genSearch = (app: App, config: Config.IUILayoutTabSearchConfig, ele
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
break;
|
break;
|
||||||
} else if (target.id === "searchInclude") {
|
} else if (target.id === "searchInclude") {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
if (target.hasAttribute("disabled")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const svgElement = target.firstElementChild;
|
const svgElement = target.firstElementChild;
|
||||||
svgElement.classList.toggle("ft__primary");
|
svgElement.classList.toggle("ft__primary");
|
||||||
if (!svgElement.classList.contains("ft__primary")) {
|
if (!svgElement.classList.contains("ft__primary")) {
|
||||||
|
@ -511,8 +516,6 @@ export const genSearch = (app: App, config: Config.IUILayoutTabSearchConfig, ele
|
||||||
}
|
}
|
||||||
config.page = 1;
|
config.page = 1;
|
||||||
inputEvent(element, config, edit, true);
|
inputEvent(element, config, edit, true);
|
||||||
event.stopPropagation();
|
|
||||||
event.preventDefault();
|
|
||||||
break;
|
break;
|
||||||
} else if (target.id === "searchReplace") {
|
} else if (target.id === "searchReplace") {
|
||||||
// ctrl+P 不需要保存
|
// ctrl+P 不需要保存
|
||||||
|
|
|
@ -51,11 +51,19 @@ export const scrollCenter = (protyle: IProtyle, nodeElement?: Element, top = fal
|
||||||
brElement.remove();
|
brElement.remove();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// undo 时禁止数据库滚动
|
|
||||||
if (blockElement.classList.contains("av") && blockElement.dataset.render === "true" && (
|
if (blockElement.classList.contains("av") && blockElement.dataset.render === "true") {
|
||||||
blockElement.querySelector(".av__row--header")?.getAttribute("style")?.indexOf("transform") > -1 ||
|
// undo 时禁止数据库滚动
|
||||||
blockElement.querySelector(".av__row--footer")?.getAttribute("style")?.indexOf("transform") > -1
|
if (blockElement.querySelector(".av__row--header")?.getAttribute("style")?.indexOf("transform") > -1 ||
|
||||||
)) {
|
blockElement.querySelector(".av__row--footer")?.getAttribute("style")?.indexOf("transform") > -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const activeElement = blockElement.querySelector(".av__cell--select, .av__row--select, .av__gallery-item--select");
|
||||||
|
if (activeElement) {
|
||||||
|
activeElement.scrollIntoView({block: "nearest", behavior});
|
||||||
|
} else {
|
||||||
|
blockElement.scrollIntoView({block: "nearest", behavior});
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 撤销时 br 插入删除会导致 rang 被修改 https://github.com/siyuan-note/siyuan/issues/12679
|
// 撤销时 br 插入删除会导致 rang 被修改 https://github.com/siyuan-note/siyuan/issues/12679
|
||||||
|
|
4
app/stage/protyle/js/lute/lute.min.js
vendored
4
app/stage/protyle/js/lute/lute.min.js
vendored
File diff suppressed because one or more lines are too long
1197
app/stage/protyle/js/mermaid/mermaid.min.js
vendored
1197
app/stage/protyle/js/mermaid/mermaid.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -567,6 +567,24 @@ func searchAttributeViewNonRelationKey(c *gin.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func searchAttributeViewRollupDestKeys(c *gin.Context) {
|
||||||
|
ret := gulu.Ret.NewResult()
|
||||||
|
defer c.JSON(http.StatusOK, ret)
|
||||||
|
|
||||||
|
arg, _ := util.JsonArg(c, ret)
|
||||||
|
if nil == arg {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
avID := arg["avID"].(string)
|
||||||
|
keyword := arg["keyword"].(string)
|
||||||
|
|
||||||
|
rollupDestKeys := model.SearchAttributeViewRollupDestKeys(avID, keyword)
|
||||||
|
ret.Data = map[string]interface{}{
|
||||||
|
"keys": rollupDestKeys,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func searchAttributeViewRelationKey(c *gin.Context) {
|
func searchAttributeViewRelationKey(c *gin.Context) {
|
||||||
ret := gulu.Ret.NewResult()
|
ret := gulu.Ret.NewResult()
|
||||||
defer c.JSON(http.StatusOK, ret)
|
defer c.JSON(http.StatusOK, ret)
|
||||||
|
@ -675,7 +693,41 @@ func renderHistoryAttributeView(c *gin.Context) {
|
||||||
|
|
||||||
id := arg["id"].(string)
|
id := arg["id"].(string)
|
||||||
created := arg["created"].(string)
|
created := arg["created"].(string)
|
||||||
view, attrView, err := model.RenderHistoryAttributeView(id, created)
|
blockIDArg := arg["blockID"]
|
||||||
|
var blockID string
|
||||||
|
if nil != blockIDArg {
|
||||||
|
blockID = blockIDArg.(string)
|
||||||
|
}
|
||||||
|
viewIDArg := arg["viewID"]
|
||||||
|
var viewID string
|
||||||
|
if nil != viewIDArg {
|
||||||
|
viewID = viewIDArg.(string)
|
||||||
|
}
|
||||||
|
page := 1
|
||||||
|
pageArg := arg["page"]
|
||||||
|
if nil != pageArg {
|
||||||
|
page = int(pageArg.(float64))
|
||||||
|
}
|
||||||
|
|
||||||
|
pageSize := -1
|
||||||
|
pageSizeArg := arg["pageSize"]
|
||||||
|
if nil != pageSizeArg {
|
||||||
|
pageSize = int(pageSizeArg.(float64))
|
||||||
|
}
|
||||||
|
|
||||||
|
query := ""
|
||||||
|
queryArg := arg["query"]
|
||||||
|
if nil != queryArg {
|
||||||
|
query = queryArg.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
groupPaging := map[string]interface{}{}
|
||||||
|
groupPagingArg := arg["groupPaging"]
|
||||||
|
if nil != groupPagingArg {
|
||||||
|
groupPaging = groupPagingArg.(map[string]interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
view, attrView, err := model.RenderHistoryAttributeView(blockID, id, viewID, query, page, pageSize, groupPaging, created)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ret.Code = -1
|
ret.Code = -1
|
||||||
ret.Msg = err.Error()
|
ret.Msg = err.Error()
|
||||||
|
|
|
@ -243,6 +243,28 @@ func getHeadingDeleteTransaction(c *gin.Context) {
|
||||||
ret.Data = transaction
|
ret.Data = transaction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getHeadingInsertTransaction(c *gin.Context) {
|
||||||
|
ret := gulu.Ret.NewResult()
|
||||||
|
defer c.JSON(http.StatusOK, ret)
|
||||||
|
|
||||||
|
arg, ok := util.JsonArg(c, ret)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
id := arg["id"].(string)
|
||||||
|
|
||||||
|
transaction, err := model.GetHeadingInsertTransaction(id)
|
||||||
|
if err != nil {
|
||||||
|
ret.Code = -1
|
||||||
|
ret.Msg = err.Error()
|
||||||
|
ret.Data = map[string]interface{}{"closeTimeout": 7000}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.Data = transaction
|
||||||
|
}
|
||||||
|
|
||||||
func getHeadingLevelTransaction(c *gin.Context) {
|
func getHeadingLevelTransaction(c *gin.Context) {
|
||||||
ret := gulu.Ret.NewResult()
|
ret := gulu.Ret.NewResult()
|
||||||
defer c.JSON(http.StatusOK, ret)
|
defer c.JSON(http.StatusOK, ret)
|
||||||
|
|
|
@ -214,6 +214,7 @@ func ServeAPI(ginServer *gin.Engine) {
|
||||||
ginServer.Handle("POST", "/api/block/setBlockReminder", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setBlockReminder)
|
ginServer.Handle("POST", "/api/block/setBlockReminder", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, setBlockReminder)
|
||||||
ginServer.Handle("POST", "/api/block/getHeadingLevelTransaction", model.CheckAuth, getHeadingLevelTransaction)
|
ginServer.Handle("POST", "/api/block/getHeadingLevelTransaction", model.CheckAuth, getHeadingLevelTransaction)
|
||||||
ginServer.Handle("POST", "/api/block/getHeadingDeleteTransaction", model.CheckAuth, getHeadingDeleteTransaction)
|
ginServer.Handle("POST", "/api/block/getHeadingDeleteTransaction", model.CheckAuth, getHeadingDeleteTransaction)
|
||||||
|
ginServer.Handle("POST", "/api/block/getHeadingInsertTransaction", model.CheckAuth, getHeadingInsertTransaction)
|
||||||
ginServer.Handle("POST", "/api/block/getHeadingChildrenIDs", model.CheckAuth, getHeadingChildrenIDs)
|
ginServer.Handle("POST", "/api/block/getHeadingChildrenIDs", model.CheckAuth, getHeadingChildrenIDs)
|
||||||
ginServer.Handle("POST", "/api/block/getHeadingChildrenDOM", model.CheckAuth, getHeadingChildrenDOM)
|
ginServer.Handle("POST", "/api/block/getHeadingChildrenDOM", model.CheckAuth, getHeadingChildrenDOM)
|
||||||
ginServer.Handle("POST", "/api/block/swapBlockRef", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, swapBlockRef)
|
ginServer.Handle("POST", "/api/block/swapBlockRef", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, swapBlockRef)
|
||||||
|
@ -447,7 +448,8 @@ func ServeAPI(ginServer *gin.Engine) {
|
||||||
ginServer.Handle("POST", "/api/av/searchAttributeView", model.CheckAuth, model.CheckReadonly, searchAttributeView)
|
ginServer.Handle("POST", "/api/av/searchAttributeView", model.CheckAuth, model.CheckReadonly, searchAttributeView)
|
||||||
ginServer.Handle("POST", "/api/av/getAttributeView", model.CheckAuth, model.CheckReadonly, getAttributeView)
|
ginServer.Handle("POST", "/api/av/getAttributeView", model.CheckAuth, model.CheckReadonly, getAttributeView)
|
||||||
ginServer.Handle("POST", "/api/av/searchAttributeViewRelationKey", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, searchAttributeViewRelationKey)
|
ginServer.Handle("POST", "/api/av/searchAttributeViewRelationKey", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, searchAttributeViewRelationKey)
|
||||||
ginServer.Handle("POST", "/api/av/searchAttributeViewNonRelationKey", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, searchAttributeViewNonRelationKey)
|
ginServer.Handle("POST", "/api/av/searchAttributeViewNonRelationKey", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, searchAttributeViewNonRelationKey) // 请勿使用,该端点计划于 2026 年 6 月 30 日后删除 https://github.com/siyuan-note/siyuan/issues/15727
|
||||||
|
ginServer.Handle("POST", "/api/av/searchAttributeViewRollupDestKeys", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, searchAttributeViewRollupDestKeys)
|
||||||
ginServer.Handle("POST", "/api/av/getAttributeViewFilterSort", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, getAttributeViewFilterSort)
|
ginServer.Handle("POST", "/api/av/getAttributeViewFilterSort", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, getAttributeViewFilterSort)
|
||||||
ginServer.Handle("POST", "/api/av/addAttributeViewKey", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, addAttributeViewKey)
|
ginServer.Handle("POST", "/api/av/addAttributeViewKey", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, addAttributeViewKey)
|
||||||
ginServer.Handle("POST", "/api/av/removeAttributeViewKey", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, removeAttributeViewKey)
|
ginServer.Handle("POST", "/api/av/removeAttributeViewKey", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, removeAttributeViewKey)
|
||||||
|
|
|
@ -207,6 +207,7 @@ type View struct {
|
||||||
LayoutType LayoutType `json:"type"` // 当前布局类型
|
LayoutType LayoutType `json:"type"` // 当前布局类型
|
||||||
Table *LayoutTable `json:"table,omitempty"` // 表格布局
|
Table *LayoutTable `json:"table,omitempty"` // 表格布局
|
||||||
Gallery *LayoutGallery `json:"gallery,omitempty"` // 卡片布局
|
Gallery *LayoutGallery `json:"gallery,omitempty"` // 卡片布局
|
||||||
|
Kanban *LayoutKanban `json:"kanban,omitempty"` // 看板布局
|
||||||
ItemIDs []string `json:"itemIds,omitempty"` // 项目 ID 列表,用于维护所有项目
|
ItemIDs []string `json:"itemIds,omitempty"` // 项目 ID 列表,用于维护所有项目
|
||||||
|
|
||||||
Group *ViewGroup `json:"group,omitempty"` // 分组规则
|
Group *ViewGroup `json:"group,omitempty"` // 分组规则
|
||||||
|
@ -221,6 +222,10 @@ type View struct {
|
||||||
GroupSort int `json:"groupSort"` // 分组排序值,用于手动排序
|
GroupSort int `json:"groupSort"` // 分组排序值,用于手动排序
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (view *View) IsGroupView() bool {
|
||||||
|
return nil != view.Group && "" != view.Group.Field
|
||||||
|
}
|
||||||
|
|
||||||
// GetGroupValue 获取分组视图的分组值。
|
// GetGroupValue 获取分组视图的分组值。
|
||||||
func (view *View) GetGroupValue() string {
|
func (view *View) GetGroupValue() string {
|
||||||
if nil == view.GroupVal {
|
if nil == view.GroupVal {
|
||||||
|
@ -270,7 +275,7 @@ func (view *View) RemoveGroupByID(groupID string) {
|
||||||
|
|
||||||
// GetGroupKey 获取分组视图的分组字段。
|
// GetGroupKey 获取分组视图的分组字段。
|
||||||
func (view *View) GetGroupKey(attrView *AttributeView) (ret *Key) {
|
func (view *View) GetGroupKey(attrView *AttributeView) (ret *Key) {
|
||||||
if nil == view.Group || "" == view.Group.Field {
|
if !view.IsGroupView() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,14 +300,15 @@ type LayoutType string
|
||||||
const (
|
const (
|
||||||
LayoutTypeTable LayoutType = "table" // 属性视图类型 - 表格
|
LayoutTypeTable LayoutType = "table" // 属性视图类型 - 表格
|
||||||
LayoutTypeGallery LayoutType = "gallery" // 属性视图类型 - 卡片
|
LayoutTypeGallery LayoutType = "gallery" // 属性视图类型 - 卡片
|
||||||
|
LayoutTypeKanban LayoutType = "kanban" // 属性视图类型 - 看板
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ViewDefaultPageSize = 50 // 视图默认分页大小
|
ViewDefaultPageSize = 50 // 视图默认分页大小
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewTableView() (ret *View) {
|
func NewTableView() *View {
|
||||||
ret = &View{
|
return &View{
|
||||||
ID: ast.NewNodeID(),
|
ID: ast.NewNodeID(),
|
||||||
Name: GetAttributeViewI18n("table"),
|
Name: GetAttributeViewI18n("table"),
|
||||||
Filters: []*ViewFilter{},
|
Filters: []*ViewFilter{},
|
||||||
|
@ -311,7 +317,6 @@ func NewTableView() (ret *View) {
|
||||||
LayoutType: LayoutTypeTable,
|
LayoutType: LayoutTypeTable,
|
||||||
Table: NewLayoutTable(),
|
Table: NewLayoutTable(),
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTableViewWithBlockKey(blockKeyID string) (view *View, blockKey, selectKey *Key) {
|
func NewTableViewWithBlockKey(blockKeyID string) (view *View, blockKey, selectKey *Key) {
|
||||||
|
@ -334,7 +339,7 @@ func NewTableViewWithBlockKey(blockKeyID string) (view *View, blockKey, selectKe
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGalleryView() (ret *View) {
|
func NewGalleryView() (ret *View) {
|
||||||
ret = &View{
|
return &View{
|
||||||
ID: ast.NewNodeID(),
|
ID: ast.NewNodeID(),
|
||||||
Name: GetAttributeViewI18n("gallery"),
|
Name: GetAttributeViewI18n("gallery"),
|
||||||
Filters: []*ViewFilter{},
|
Filters: []*ViewFilter{},
|
||||||
|
@ -343,7 +348,18 @@ func NewGalleryView() (ret *View) {
|
||||||
LayoutType: LayoutTypeGallery,
|
LayoutType: LayoutTypeGallery,
|
||||||
Gallery: NewLayoutGallery(),
|
Gallery: NewLayoutGallery(),
|
||||||
}
|
}
|
||||||
return
|
}
|
||||||
|
|
||||||
|
func NewKanbanView() (ret *View) {
|
||||||
|
return &View{
|
||||||
|
ID: ast.NewNodeID(),
|
||||||
|
Name: GetAttributeViewI18n("kanban"),
|
||||||
|
Filters: []*ViewFilter{},
|
||||||
|
Sorts: []*ViewSort{},
|
||||||
|
PageSize: ViewDefaultPageSize,
|
||||||
|
LayoutType: LayoutTypeKanban,
|
||||||
|
Kanban: NewLayoutKanban(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Viewable 描述了视图的接口。
|
// Viewable 描述了视图的接口。
|
||||||
|
@ -530,6 +546,15 @@ func SaveAttributeView(av *AttributeView) (err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 清理渲染回填值
|
||||||
|
for _, kv := range av.KeyValues {
|
||||||
|
for i := len(kv.Values) - 1; i >= 0; i-- {
|
||||||
|
if kv.Values[i].IsRenderAutoFill {
|
||||||
|
kv.Values = append(kv.Values[:i], kv.Values[i+1:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var data []byte
|
var data []byte
|
||||||
if util.UseSingleLineSave {
|
if util.UseSingleLineSave {
|
||||||
data, err = gulu.JSON.MarshalJSON(av)
|
data, err = gulu.JSON.MarshalJSON(av)
|
||||||
|
@ -741,6 +766,11 @@ func (av *AttributeView) Clone() (ret *AttributeView) {
|
||||||
for _, cardField := range view.Gallery.CardFields {
|
for _, cardField := range view.Gallery.CardFields {
|
||||||
cardField.ID = keyIDMap[cardField.ID]
|
cardField.ID = keyIDMap[cardField.ID]
|
||||||
}
|
}
|
||||||
|
case LayoutTypeKanban:
|
||||||
|
view.Kanban.ID = ast.NewNodeID()
|
||||||
|
for _, field := range view.Kanban.Fields {
|
||||||
|
field.ID = keyIDMap[field.ID]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
view.ItemIDs = []string{}
|
view.ItemIDs = []string{}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ type CalcOperator string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CalcOperatorNone CalcOperator = ""
|
CalcOperatorNone CalcOperator = ""
|
||||||
|
CalcOperatorUniqueValues CalcOperator = "Unique values"
|
||||||
CalcOperatorCountAll CalcOperator = "Count all"
|
CalcOperatorCountAll CalcOperator = "Count all"
|
||||||
CalcOperatorCountValues CalcOperator = "Count values"
|
CalcOperatorCountValues CalcOperator = "Count values"
|
||||||
CalcOperatorCountUniqueValues CalcOperator = "Count unique values"
|
CalcOperatorCountUniqueValues CalcOperator = "Count unique values"
|
||||||
|
@ -1709,9 +1710,35 @@ func calcFieldRollup(collection Collection, field Field, fieldIndex int) {
|
||||||
values := item.GetValues()
|
values := item.GetValues()
|
||||||
if nil != values[fieldIndex] && nil != values[fieldIndex].Rollup {
|
if nil != values[fieldIndex] && nil != values[fieldIndex].Rollup {
|
||||||
for _, content := range values[fieldIndex].Rollup.Contents {
|
for _, content := range values[fieldIndex].Rollup.Contents {
|
||||||
if !uniqueValues[content.String(true)] {
|
switch content.Type {
|
||||||
uniqueValues[content.String(true)] = true
|
case KeyTypeRelation:
|
||||||
countUniqueValues++
|
for _, relationVal := range content.Relation.Contents {
|
||||||
|
key := relationVal.String(true)
|
||||||
|
if !uniqueValues[key] {
|
||||||
|
uniqueValues[key] = true
|
||||||
|
countUniqueValues++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case KeyTypeMSelect:
|
||||||
|
for _, mSelectVal := range content.MSelect {
|
||||||
|
if !uniqueValues[mSelectVal.Content] {
|
||||||
|
uniqueValues[mSelectVal.Content] = true
|
||||||
|
countUniqueValues++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case KeyTypeMAsset:
|
||||||
|
for _, mAssetVal := range content.MAsset {
|
||||||
|
if !uniqueValues[mAssetVal.Content] {
|
||||||
|
uniqueValues[mAssetVal.Content] = true
|
||||||
|
countUniqueValues++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
key := content.String(true)
|
||||||
|
if !uniqueValues[key] {
|
||||||
|
uniqueValues[key] = true
|
||||||
|
countUniqueValues++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,6 +81,9 @@ func NewViewBaseInstance(view *View) *BaseInstance {
|
||||||
case LayoutTypeGallery:
|
case LayoutTypeGallery:
|
||||||
showIcon = view.Gallery.ShowIcon
|
showIcon = view.Gallery.ShowIcon
|
||||||
wrapField = view.Gallery.WrapField
|
wrapField = view.Gallery.WrapField
|
||||||
|
case LayoutTypeKanban:
|
||||||
|
showIcon = view.Kanban.ShowIcon
|
||||||
|
wrapField = view.Kanban.WrapField
|
||||||
}
|
}
|
||||||
return &BaseInstance{
|
return &BaseInstance{
|
||||||
ID: view.ID,
|
ID: view.ID,
|
||||||
|
|
168
kernel/av/layout_kanban.go
Normal file
168
kernel/av/layout_kanban.go
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
// SiYuan - Refactor your thinking
|
||||||
|
// Copyright (c) 2020-present, b3log.org
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package av
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/88250/lute/ast"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LayoutKanban 描述了看板视图的结构。
|
||||||
|
type LayoutKanban struct {
|
||||||
|
*BaseLayout
|
||||||
|
|
||||||
|
CoverFrom CoverFrom `json:"coverFrom"` // 封面来源,0:无,1:内容图,2:资源字段
|
||||||
|
CoverFromAssetKeyID string `json:"coverFromAssetKeyID,omitempty"` // 资源字段 ID,CoverFrom 为 2 时有效
|
||||||
|
CardAspectRatio CardAspectRatio `json:"cardAspectRatio"` // 卡片宽高比
|
||||||
|
CardSize CardSize `json:"cardSize"` // 卡片大小,0:小卡片,1:中卡片,2:大卡片
|
||||||
|
FitImage bool `json:"fitImage"` // 是否适应封面图片大小
|
||||||
|
DisplayFieldName bool `json:"displayFieldName"` // 是否显示字段名称
|
||||||
|
|
||||||
|
Fields []*ViewKanbanField `json:"fields"` // 字段
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLayoutKanban() *LayoutKanban {
|
||||||
|
return &LayoutKanban{
|
||||||
|
BaseLayout: &BaseLayout{
|
||||||
|
Spec: 0,
|
||||||
|
ID: ast.NewNodeID(),
|
||||||
|
ShowIcon: true,
|
||||||
|
},
|
||||||
|
CoverFrom: CoverFromContentImage,
|
||||||
|
CardAspectRatio: CardAspectRatio16_9,
|
||||||
|
CardSize: CardSizeMedium,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ViewKanbanField 描述了看板字段的结构。
|
||||||
|
type ViewKanbanField struct {
|
||||||
|
*BaseField
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kanban 描述了看板视图实例的结构。
|
||||||
|
type Kanban struct {
|
||||||
|
*BaseInstance
|
||||||
|
|
||||||
|
CoverFrom CoverFrom `json:"coverFrom"` // 封面来源
|
||||||
|
CoverFromAssetKeyID string `json:"coverFromAssetKeyID,omitempty"` // 资源字段 ID,CoverFrom 为 CoverFromAssetField 时有效
|
||||||
|
CardAspectRatio CardAspectRatio `json:"cardAspectRatio"` // 卡片宽高比
|
||||||
|
CardSize CardSize `json:"cardSize"` // 卡片大小
|
||||||
|
FitImage bool `json:"fitImage"` // 是否适应封面图片大小
|
||||||
|
DisplayFieldName bool `json:"displayFieldName"` // 是否显示字段名称
|
||||||
|
Fields []*KanbanField `json:"fields"` // 卡片字段
|
||||||
|
Cards []*KanbanCard `json:"cards"` // 卡片
|
||||||
|
CardCount int `json:"rowCount"` // 总卡片数
|
||||||
|
}
|
||||||
|
|
||||||
|
// KanbanCard 描述了看板实例卡片的结构。
|
||||||
|
type KanbanCard struct {
|
||||||
|
ID string `json:"id"` // 卡片 ID
|
||||||
|
Values []*KanbanFieldValue `json:"values"` // 卡片字段值
|
||||||
|
|
||||||
|
CoverURL string `json:"coverURL"` // 卡片封面超链接
|
||||||
|
CoverContent string `json:"coverContent"` // 卡片封面文本内容
|
||||||
|
}
|
||||||
|
|
||||||
|
// KanbanField 描述了看板实例字段的结构。
|
||||||
|
type KanbanField struct {
|
||||||
|
*BaseInstanceField
|
||||||
|
}
|
||||||
|
|
||||||
|
// KanbanFieldValue 描述了卡片字段实例值的结构。
|
||||||
|
type KanbanFieldValue struct {
|
||||||
|
*BaseValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (card *KanbanCard) GetID() string {
|
||||||
|
return card.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (card *KanbanCard) GetBlockValue() (ret *Value) {
|
||||||
|
for _, v := range card.Values {
|
||||||
|
if KeyTypeBlock == v.ValueType {
|
||||||
|
ret = v.Value
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (card *KanbanCard) GetValues() (ret []*Value) {
|
||||||
|
ret = []*Value{}
|
||||||
|
for _, v := range card.Values {
|
||||||
|
ret = append(ret, v.Value)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (card *KanbanCard) GetValue(keyID string) (ret *Value) {
|
||||||
|
for _, value := range card.Values {
|
||||||
|
if nil != value.Value && keyID == value.Value.KeyID {
|
||||||
|
ret = value.Value
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kanban *Kanban) GetItems() (ret []Item) {
|
||||||
|
ret = []Item{}
|
||||||
|
for _, card := range kanban.Cards {
|
||||||
|
ret = append(ret, card)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kanban *Kanban) SetItems(items []Item) {
|
||||||
|
kanban.Cards = []*KanbanCard{}
|
||||||
|
for _, item := range items {
|
||||||
|
kanban.Cards = append(kanban.Cards, item.(*KanbanCard))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kanban *Kanban) CountItems() int {
|
||||||
|
return len(kanban.Cards)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kanban *Kanban) GetFields() (ret []Field) {
|
||||||
|
ret = []Field{}
|
||||||
|
for _, field := range kanban.Fields {
|
||||||
|
ret = append(ret, field)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kanban *Kanban) GetField(id string) (ret Field, fieldIndex int) {
|
||||||
|
for i, field := range kanban.Fields {
|
||||||
|
if field.ID == id {
|
||||||
|
return field, i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kanban *Kanban) GetValue(itemID, keyID string) (ret *Value) {
|
||||||
|
for _, card := range kanban.Cards {
|
||||||
|
if card.ID == itemID {
|
||||||
|
return card.GetValue(keyID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kanban *Kanban) GetType() LayoutType {
|
||||||
|
return LayoutTypeKanban
|
||||||
|
}
|
|
@ -57,6 +57,8 @@ type Value struct {
|
||||||
Checkbox *ValueCheckbox `json:"checkbox,omitempty"`
|
Checkbox *ValueCheckbox `json:"checkbox,omitempty"`
|
||||||
Relation *ValueRelation `json:"relation,omitempty"`
|
Relation *ValueRelation `json:"relation,omitempty"`
|
||||||
Rollup *ValueRollup `json:"rollup,omitempty"`
|
Rollup *ValueRollup `json:"rollup,omitempty"`
|
||||||
|
|
||||||
|
IsRenderAutoFill bool `json:"-"` // 标识是否是渲染阶段自动填充的值,保存数据的时候要删掉
|
||||||
}
|
}
|
||||||
|
|
||||||
func (value *Value) SetUpdatedAt(mills int64) {
|
func (value *Value) SetUpdatedAt(mills int64) {
|
||||||
|
@ -806,6 +808,11 @@ func (r *ValueRollup) BuildContents(keyValues []*KeyValues, destKey *Key, relati
|
||||||
}
|
}
|
||||||
|
|
||||||
if nil == destVal {
|
if nil == destVal {
|
||||||
|
if KeyTypeCheckbox == destKey.Type {
|
||||||
|
// 没有编辑过复选框的时候没有值,没有值等同于未选中,所以这里补一个未选中的值 https://github.com/siyuan-note/siyuan/issues/15858
|
||||||
|
defaultVal := GetAttributeViewDefaultValue(ast.NewNodeID(), destKey.ID, blockID, destKey.Type, false)
|
||||||
|
r.Contents = append(r.Contents, defaultVal)
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -832,6 +839,40 @@ func (r *ValueRollup) calcContents(calc *RollupCalc, destKey *Key) {
|
||||||
|
|
||||||
switch calc.Operator {
|
switch calc.Operator {
|
||||||
case CalcOperatorNone:
|
case CalcOperatorNone:
|
||||||
|
case CalcOperatorUniqueValues:
|
||||||
|
uniqueValues := map[string]bool{}
|
||||||
|
for _, content := range r.Contents {
|
||||||
|
switch content.Type {
|
||||||
|
case KeyTypeRelation:
|
||||||
|
var newRelationContents []*Value
|
||||||
|
for _, relationVal := range content.Relation.Contents {
|
||||||
|
key := relationVal.String(true)
|
||||||
|
if !uniqueValues[key] {
|
||||||
|
uniqueValues[key] = true
|
||||||
|
newRelationContents = append(newRelationContents, relationVal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
content.Relation.Contents = newRelationContents
|
||||||
|
case KeyTypeMSelect:
|
||||||
|
var newMSelect []*ValueSelect
|
||||||
|
for _, mSelect := range content.MSelect {
|
||||||
|
if !uniqueValues[mSelect.Content] {
|
||||||
|
uniqueValues[mSelect.Content] = true
|
||||||
|
newMSelect = append(newMSelect, mSelect)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
content.MSelect = newMSelect
|
||||||
|
case KeyTypeMAsset:
|
||||||
|
var newMAsset []*ValueAsset
|
||||||
|
for _, mAsset := range content.MAsset {
|
||||||
|
if !uniqueValues[mAsset.Content] {
|
||||||
|
uniqueValues[mAsset.Content] = true
|
||||||
|
newMAsset = append(newMAsset, mAsset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
content.MAsset = newMAsset
|
||||||
|
}
|
||||||
|
}
|
||||||
case CalcOperatorCountAll:
|
case CalcOperatorCountAll:
|
||||||
r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewFormattedValueNumber(float64(len(r.Contents)), NumberFormatNone)}}
|
r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewFormattedValueNumber(float64(len(r.Contents)), NumberFormatNone)}}
|
||||||
case CalcOperatorCountValues:
|
case CalcOperatorCountValues:
|
||||||
|
@ -1199,7 +1240,7 @@ func (r *ValueRollup) calcContents(calc *RollupCalc, destKey *Key) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAttributeViewDefaultValue(valueID, keyID, blockID string, typ KeyType) (ret *Value) {
|
func GetAttributeViewDefaultValue(valueID, keyID, blockID string, typ KeyType, keyDateAutoFill bool) (ret *Value) {
|
||||||
if "" == valueID {
|
if "" == valueID {
|
||||||
valueID = ast.NewNodeID()
|
valueID = ast.NewNodeID()
|
||||||
}
|
}
|
||||||
|
@ -1225,7 +1266,7 @@ func GetAttributeViewDefaultValue(valueID, keyID, blockID string, typ KeyType) (
|
||||||
case KeyTypeNumber:
|
case KeyTypeNumber:
|
||||||
ret.Number = &ValueNumber{}
|
ret.Number = &ValueNumber{}
|
||||||
case KeyTypeDate:
|
case KeyTypeDate:
|
||||||
ret.Date = &ValueDate{IsNotTime: true}
|
ret.Date = &ValueDate{IsNotTime: !keyDateAutoFill}
|
||||||
case KeyTypeSelect:
|
case KeyTypeSelect:
|
||||||
ret.MSelect = []*ValueSelect{}
|
ret.MSelect = []*ValueSelect{}
|
||||||
case KeyTypeMSelect:
|
case KeyTypeMSelect:
|
||||||
|
|
|
@ -206,6 +206,12 @@ func DocIAL(absPath string) (ret map[string]string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TreeSize(tree *parse.Tree) (size uint64) {
|
||||||
|
luteEngine := util.NewLute() // 不关注用户的自定义解析渲染选项
|
||||||
|
renderer := render.NewJSONRenderer(tree, luteEngine.RenderOptions)
|
||||||
|
return uint64(len(renderer.Render()))
|
||||||
|
}
|
||||||
|
|
||||||
func WriteTree(tree *parse.Tree) (size uint64, err error) {
|
func WriteTree(tree *parse.Tree) (size uint64, err error) {
|
||||||
data, filePath, err := prepareWriteTree(tree)
|
data, filePath, err := prepareWriteTree(tree)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -8,7 +8,7 @@ require (
|
||||||
github.com/88250/epub v0.0.0-20230830085737-c19055cd1f48
|
github.com/88250/epub v0.0.0-20230830085737-c19055cd1f48
|
||||||
github.com/88250/go-humanize v0.0.0-20240424102817-4f78fac47ea7
|
github.com/88250/go-humanize v0.0.0-20240424102817-4f78fac47ea7
|
||||||
github.com/88250/gulu v1.2.3-0.20250227144607-7f4570b0d689
|
github.com/88250/gulu v1.2.3-0.20250227144607-7f4570b0d689
|
||||||
github.com/88250/lute v1.7.7-0.20250907110109-efc34e9d52fa
|
github.com/88250/lute v1.7.7-0.20250915120219-5d9cd1b7574b
|
||||||
github.com/88250/vitess-sqlparser v0.0.0-20210205111146-56a2ded2aba1
|
github.com/88250/vitess-sqlparser v0.0.0-20210205111146-56a2ded2aba1
|
||||||
github.com/ClarkThan/ahocorasick v0.0.0-20231011042242-30d1ef1347f4
|
github.com/ClarkThan/ahocorasick v0.0.0-20231011042242-30d1ef1347f4
|
||||||
github.com/ConradIrwin/font v0.2.1
|
github.com/ConradIrwin/font v0.2.1
|
||||||
|
|
|
@ -14,8 +14,8 @@ github.com/88250/go-sqlite3 v1.14.13-0.20231214121541-e7f54c482950 h1:Pa5hMiBceT
|
||||||
github.com/88250/go-sqlite3 v1.14.13-0.20231214121541-e7f54c482950/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
github.com/88250/go-sqlite3 v1.14.13-0.20231214121541-e7f54c482950/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||||
github.com/88250/gulu v1.2.3-0.20250227144607-7f4570b0d689 h1:39y5g7vnFAIcXhTN3IXPk7h2xBhC4a9hBTykDhHJqRY=
|
github.com/88250/gulu v1.2.3-0.20250227144607-7f4570b0d689 h1:39y5g7vnFAIcXhTN3IXPk7h2xBhC4a9hBTykDhHJqRY=
|
||||||
github.com/88250/gulu v1.2.3-0.20250227144607-7f4570b0d689/go.mod h1:c8uVw25vW2W4dhJ/j4iYsX5H1hc19spim266jO5x2hU=
|
github.com/88250/gulu v1.2.3-0.20250227144607-7f4570b0d689/go.mod h1:c8uVw25vW2W4dhJ/j4iYsX5H1hc19spim266jO5x2hU=
|
||||||
github.com/88250/lute v1.7.7-0.20250907110109-efc34e9d52fa h1:kbvW8LD3yJK5hwbLdJDb+7xqj7i68Rbz47xEqnNqf4I=
|
github.com/88250/lute v1.7.7-0.20250915120219-5d9cd1b7574b h1:/x4JPITd6K2Fb6arbs84TsoPKVZqRxXmwR5blMwJD2k=
|
||||||
github.com/88250/lute v1.7.7-0.20250907110109-efc34e9d52fa/go.mod h1:WYyUw//5yVw9BJnoVjx7rI/3szsISxNZCYGOqTIrV0o=
|
github.com/88250/lute v1.7.7-0.20250915120219-5d9cd1b7574b/go.mod h1:WYyUw//5yVw9BJnoVjx7rI/3szsISxNZCYGOqTIrV0o=
|
||||||
github.com/88250/pdfcpu v0.3.14-0.20250424122812-f10e8d9d8d46 h1:Bq1JsDfVbHKUxNL/B2JXd8cC/1h6aFjrlXpGycnh0Hk=
|
github.com/88250/pdfcpu v0.3.14-0.20250424122812-f10e8d9d8d46 h1:Bq1JsDfVbHKUxNL/B2JXd8cC/1h6aFjrlXpGycnh0Hk=
|
||||||
github.com/88250/pdfcpu v0.3.14-0.20250424122812-f10e8d9d8d46/go.mod h1:fVfOloBzs2+W2VJCCbq60XIxc3yJHAZ0Gahv1oO0gyI=
|
github.com/88250/pdfcpu v0.3.14-0.20250424122812-f10e8d9d8d46/go.mod h1:fVfOloBzs2+W2VJCCbq60XIxc3yJHAZ0Gahv1oO0gyI=
|
||||||
github.com/88250/vitess-sqlparser v0.0.0-20210205111146-56a2ded2aba1 h1:48T899JQDwyyRu9yXHePYlPdHtpJfrJEUGBMH3SMBWY=
|
github.com/88250/vitess-sqlparser v0.0.0-20210205111146-56a2ded2aba1 h1:48T899JQDwyyRu9yXHePYlPdHtpJfrJEUGBMH3SMBWY=
|
||||||
|
|
|
@ -101,7 +101,7 @@ func GetAttrViewAddingBlockDefaultValues(avID, viewID, groupID, previousBlockID,
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if 1 > len(view.Filters) && nil == view.Group {
|
if 1 > len(view.Filters) && !view.IsGroupView() {
|
||||||
// 没有过滤条件也没有分组条件时忽略
|
// 没有过滤条件也没有分组条件时忽略
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -128,7 +128,7 @@ func GetAttrViewAddingBlockDefaultValues(avID, viewID, groupID, previousBlockID,
|
||||||
func getAttrViewAddingBlockDefaultValues(attrView *av.AttributeView, view, groupView *av.View, previousItemID, addingItemID string) (ret map[string]*av.Value) {
|
func getAttrViewAddingBlockDefaultValues(attrView *av.AttributeView, view, groupView *av.View, previousItemID, addingItemID string) (ret map[string]*av.Value) {
|
||||||
ret = map[string]*av.Value{}
|
ret = map[string]*av.Value{}
|
||||||
|
|
||||||
if 1 > len(view.Filters) && nil == view.Group {
|
if 1 > len(view.Filters) && !view.IsGroupView() {
|
||||||
// 没有过滤条件也没有分组条件时忽略
|
// 没有过滤条件也没有分组条件时忽略
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -233,7 +233,7 @@ func getAttrViewAddingBlockDefaultValues(attrView *av.AttributeView, view, group
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
newValue = av.GetAttributeViewDefaultValue(ast.NewNodeID(), groupKey.ID, addingItemID, groupKey.Type)
|
newValue = av.GetAttributeViewDefaultValue(ast.NewNodeID(), groupKey.ID, addingItemID, groupKey.Type, false)
|
||||||
newValue.MSelect = append(newValue.MSelect, &av.ValueSelect{Content: opt.Name, Color: opt.Color})
|
newValue.MSelect = append(newValue.MSelect, &av.ValueSelect{Content: opt.Name, Color: opt.Color})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -278,7 +278,7 @@ func getAttrViewAddingBlockDefaultValues(attrView *av.AttributeView, view, group
|
||||||
|
|
||||||
if nil == nearItem && !filterKeyIDs[groupKey.ID] {
|
if nil == nearItem && !filterKeyIDs[groupKey.ID] {
|
||||||
// 没有临近项并且分组字段和过滤字段不同时,使用分组值
|
// 没有临近项并且分组字段和过滤字段不同时,使用分组值
|
||||||
newValue = av.GetAttributeViewDefaultValue(ast.NewNodeID(), groupKey.ID, addingItemID, groupKey.Type)
|
newValue = av.GetAttributeViewDefaultValue(ast.NewNodeID(), groupKey.ID, addingItemID, groupKey.Type, false)
|
||||||
if av.KeyTypeText == groupView.GroupVal.Type {
|
if av.KeyTypeText == groupView.GroupVal.Type {
|
||||||
content := groupView.GroupVal.Text.Content
|
content := groupView.GroupVal.Text.Content
|
||||||
switch newValue.Type {
|
switch newValue.Type {
|
||||||
|
@ -429,7 +429,7 @@ func syncAttrViewTableColWidth(operation *Operation) (err error) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case av.LayoutTypeGallery:
|
case av.LayoutTypeGallery, av.LayoutTypeKanban:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -533,7 +533,7 @@ func foldAttrViewGroup(avID, blockID, groupID string, folded bool) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if nil == view.Group {
|
if !view.IsGroupView() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -667,6 +667,8 @@ func setAttrViewCardAspectRatio(operation *Operation) (err error) {
|
||||||
return
|
return
|
||||||
case av.LayoutTypeGallery:
|
case av.LayoutTypeGallery:
|
||||||
view.Gallery.CardAspectRatio = av.CardAspectRatio(operation.Data.(float64))
|
view.Gallery.CardAspectRatio = av.CardAspectRatio(operation.Data.(float64))
|
||||||
|
case av.LayoutTypeKanban:
|
||||||
|
view.Kanban.CardAspectRatio = av.CardAspectRatio(operation.Data.(float64))
|
||||||
}
|
}
|
||||||
|
|
||||||
err = av.SaveAttributeView(attrView)
|
err = av.SaveAttributeView(attrView)
|
||||||
|
@ -707,7 +709,7 @@ func ChangeAttrViewLayout(blockID, avID string, layout av.LayoutType) (err error
|
||||||
|
|
||||||
switch newLayout {
|
switch newLayout {
|
||||||
case av.LayoutTypeTable:
|
case av.LayoutTypeTable:
|
||||||
if view.Name == av.GetAttributeViewI18n("gallery") {
|
if view.Name == av.GetAttributeViewI18n("gallery") || view.Name == av.GetAttributeViewI18n("kanban") {
|
||||||
view.Name = av.GetAttributeViewI18n("table")
|
view.Name = av.GetAttributeViewI18n("table")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -721,9 +723,13 @@ func ChangeAttrViewLayout(blockID, avID string, layout av.LayoutType) (err error
|
||||||
for _, field := range view.Gallery.CardFields {
|
for _, field := range view.Gallery.CardFields {
|
||||||
view.Table.Columns = append(view.Table.Columns, &av.ViewTableColumn{BaseField: &av.BaseField{ID: field.ID}})
|
view.Table.Columns = append(view.Table.Columns, &av.ViewTableColumn{BaseField: &av.BaseField{ID: field.ID}})
|
||||||
}
|
}
|
||||||
|
case av.LayoutTypeKanban:
|
||||||
|
for _, field := range view.Kanban.Fields {
|
||||||
|
view.Table.Columns = append(view.Table.Columns, &av.ViewTableColumn{BaseField: &av.BaseField{ID: field.ID}})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case av.LayoutTypeGallery:
|
case av.LayoutTypeGallery:
|
||||||
if view.Name == av.GetAttributeViewI18n("table") {
|
if view.Name == av.GetAttributeViewI18n("table") || view.Name == av.GetAttributeViewI18n("kanban") {
|
||||||
view.Name = av.GetAttributeViewI18n("gallery")
|
view.Name = av.GetAttributeViewI18n("gallery")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -737,6 +743,30 @@ func ChangeAttrViewLayout(blockID, avID string, layout av.LayoutType) (err error
|
||||||
for _, col := range view.Table.Columns {
|
for _, col := range view.Table.Columns {
|
||||||
view.Gallery.CardFields = append(view.Gallery.CardFields, &av.ViewGalleryCardField{BaseField: &av.BaseField{ID: col.ID}})
|
view.Gallery.CardFields = append(view.Gallery.CardFields, &av.ViewGalleryCardField{BaseField: &av.BaseField{ID: col.ID}})
|
||||||
}
|
}
|
||||||
|
case av.LayoutTypeKanban:
|
||||||
|
for _, field := range view.Kanban.Fields {
|
||||||
|
view.Gallery.CardFields = append(view.Gallery.CardFields, &av.ViewGalleryCardField{BaseField: &av.BaseField{ID: field.ID}})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case av.LayoutTypeKanban:
|
||||||
|
if view.Name == av.GetAttributeViewI18n("table") || view.Name == av.GetAttributeViewI18n("gallery") {
|
||||||
|
view.Name = av.GetAttributeViewI18n("kanban")
|
||||||
|
}
|
||||||
|
|
||||||
|
if nil != view.Kanban {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
view.Kanban = av.NewLayoutKanban()
|
||||||
|
switch view.LayoutType {
|
||||||
|
case av.LayoutTypeTable:
|
||||||
|
for _, col := range view.Table.Columns {
|
||||||
|
view.Kanban.Fields = append(view.Kanban.Fields, &av.ViewKanbanField{BaseField: &av.BaseField{ID: col.ID}})
|
||||||
|
}
|
||||||
|
case av.LayoutTypeGallery:
|
||||||
|
for _, field := range view.Gallery.CardFields {
|
||||||
|
view.Kanban.Fields = append(view.Kanban.Fields, &av.ViewKanbanField{BaseField: &av.BaseField{ID: field.ID}})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -816,6 +846,11 @@ func setAttrViewWrapField(operation *Operation) (err error) {
|
||||||
for _, field := range view.Gallery.CardFields {
|
for _, field := range view.Gallery.CardFields {
|
||||||
field.Wrap = allFieldWrap
|
field.Wrap = allFieldWrap
|
||||||
}
|
}
|
||||||
|
case av.LayoutTypeKanban:
|
||||||
|
view.Kanban.WrapField = allFieldWrap
|
||||||
|
for _, field := range view.Kanban.Fields {
|
||||||
|
field.Wrap = allFieldWrap
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = av.SaveAttributeView(attrView)
|
err = av.SaveAttributeView(attrView)
|
||||||
|
@ -846,6 +881,8 @@ func setAttrViewShowIcon(operation *Operation) (err error) {
|
||||||
view.Table.ShowIcon = operation.Data.(bool)
|
view.Table.ShowIcon = operation.Data.(bool)
|
||||||
case av.LayoutTypeGallery:
|
case av.LayoutTypeGallery:
|
||||||
view.Gallery.ShowIcon = operation.Data.(bool)
|
view.Gallery.ShowIcon = operation.Data.(bool)
|
||||||
|
case av.LayoutTypeKanban:
|
||||||
|
view.Kanban.ShowIcon = operation.Data.(bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = av.SaveAttributeView(attrView)
|
err = av.SaveAttributeView(attrView)
|
||||||
|
@ -876,6 +913,8 @@ func setAttrViewFitImage(operation *Operation) (err error) {
|
||||||
return
|
return
|
||||||
case av.LayoutTypeGallery:
|
case av.LayoutTypeGallery:
|
||||||
view.Gallery.FitImage = operation.Data.(bool)
|
view.Gallery.FitImage = operation.Data.(bool)
|
||||||
|
case av.LayoutTypeKanban:
|
||||||
|
view.Kanban.FitImage = operation.Data.(bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = av.SaveAttributeView(attrView)
|
err = av.SaveAttributeView(attrView)
|
||||||
|
@ -906,6 +945,8 @@ func setAttrViewDisplayFieldName(operation *Operation) (err error) {
|
||||||
return
|
return
|
||||||
case av.LayoutTypeGallery:
|
case av.LayoutTypeGallery:
|
||||||
view.Gallery.DisplayFieldName = operation.Data.(bool)
|
view.Gallery.DisplayFieldName = operation.Data.(bool)
|
||||||
|
case av.LayoutTypeKanban:
|
||||||
|
view.Kanban.DisplayFieldName = operation.Data.(bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = av.SaveAttributeView(attrView)
|
err = av.SaveAttributeView(attrView)
|
||||||
|
@ -936,6 +977,8 @@ func setAttrViewCardSize(operation *Operation) (err error) {
|
||||||
return
|
return
|
||||||
case av.LayoutTypeGallery:
|
case av.LayoutTypeGallery:
|
||||||
view.Gallery.CardSize = av.CardSize(operation.Data.(float64))
|
view.Gallery.CardSize = av.CardSize(operation.Data.(float64))
|
||||||
|
case av.LayoutTypeKanban:
|
||||||
|
view.Kanban.CardSize = av.CardSize(operation.Data.(float64))
|
||||||
}
|
}
|
||||||
|
|
||||||
err = av.SaveAttributeView(attrView)
|
err = av.SaveAttributeView(attrView)
|
||||||
|
@ -966,6 +1009,8 @@ func setAttrViewCoverFromAssetKeyID(operation *Operation) (err error) {
|
||||||
return
|
return
|
||||||
case av.LayoutTypeGallery:
|
case av.LayoutTypeGallery:
|
||||||
view.Gallery.CoverFromAssetKeyID = operation.KeyID
|
view.Gallery.CoverFromAssetKeyID = operation.KeyID
|
||||||
|
case av.LayoutTypeKanban:
|
||||||
|
view.Kanban.CoverFromAssetKeyID = operation.KeyID
|
||||||
}
|
}
|
||||||
|
|
||||||
err = av.SaveAttributeView(attrView)
|
err = av.SaveAttributeView(attrView)
|
||||||
|
@ -996,6 +1041,8 @@ func setAttrViewCoverFrom(operation *Operation) (err error) {
|
||||||
return
|
return
|
||||||
case av.LayoutTypeGallery:
|
case av.LayoutTypeGallery:
|
||||||
view.Gallery.CoverFrom = av.CoverFrom(operation.Data.(float64))
|
view.Gallery.CoverFrom = av.CoverFrom(operation.Data.(float64))
|
||||||
|
case av.LayoutTypeKanban:
|
||||||
|
view.Kanban.CoverFrom = av.CoverFrom(operation.Data.(float64))
|
||||||
}
|
}
|
||||||
|
|
||||||
err = av.SaveAttributeView(attrView)
|
err = av.SaveAttributeView(attrView)
|
||||||
|
@ -1032,7 +1079,7 @@ func AppendAttributeViewDetachedBlocksWithValues(avID string, blocksValues [][]*
|
||||||
v.IsDetached = true
|
v.IsDetached = true
|
||||||
v.CreatedAt = now
|
v.CreatedAt = now
|
||||||
v.UpdatedAt = now
|
v.UpdatedAt = now
|
||||||
|
v.IsRenderAutoFill = false
|
||||||
keyValues.Values = append(keyValues.Values, v)
|
keyValues.Values = append(keyValues.Values, v)
|
||||||
|
|
||||||
if av.KeyTypeSelect == v.Type || av.KeyTypeMSelect == v.Type {
|
if av.KeyTypeSelect == v.Type || av.KeyTypeMSelect == v.Type {
|
||||||
|
@ -1264,6 +1311,26 @@ func SearchAttributeViewNonRelationKey(avID, keyword string) (ret []*av.Key) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SearchAttributeViewRollupDestKeys(avID, keyword string) (ret []*av.Key) {
|
||||||
|
waitForSyncingStorages()
|
||||||
|
|
||||||
|
ret = []*av.Key{}
|
||||||
|
attrView, err := av.ParseAttributeView(avID)
|
||||||
|
if err != nil {
|
||||||
|
logging.LogErrorf("parse attribute view [%s] failed: %s", avID, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, keyValues := range attrView.KeyValues {
|
||||||
|
if av.KeyTypeRollup != keyValues.Key.Type && av.KeyTypeLineNumber != keyValues.Key.Type {
|
||||||
|
if strings.Contains(strings.ToLower(keyValues.Key.Name), strings.ToLower(keyword)) {
|
||||||
|
ret = append(ret, keyValues.Key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func SearchAttributeViewRelationKey(avID, keyword string) (ret []*av.Key) {
|
func SearchAttributeViewRelationKey(avID, keyword string) (ret []*av.Key) {
|
||||||
waitForSyncingStorages()
|
waitForSyncingStorages()
|
||||||
|
|
||||||
|
@ -1339,6 +1406,10 @@ func SearchAttributeView(keyword string, excludeAvIDs []string) (ret []*AvSearch
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if gulu.Str.Contains(id, excludeAvIDs) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if nil == avBlockRels[id] {
|
if nil == avBlockRels[id] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -1398,7 +1469,8 @@ func SearchAttributeView(keyword string, excludeAvIDs []string) (ret []*AvSearch
|
||||||
}
|
}
|
||||||
|
|
||||||
node = treenode.GetNodeInTree(tree, bID)
|
node = treenode.GetNodeInTree(tree, bID)
|
||||||
if nil == node || "" == node.AttributeViewID {
|
if nil == node || "" == node.AttributeViewID || ast.NodeAttributeView != node.Type {
|
||||||
|
node = nil
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1535,13 +1607,12 @@ func GetBlockAttributeViewKeys(nodeID string) (ret []*BlockAttributeViewKeys) {
|
||||||
keyValues = append(keyValues, kValues)
|
keyValues = append(keyValues, kValues)
|
||||||
} else {
|
} else {
|
||||||
// 如果没有值,那么就补一个默认值
|
// 如果没有值,那么就补一个默认值
|
||||||
kValues.Values = append(kValues.Values, av.GetAttributeViewDefaultValue(itemID[:14]+ast.NewNodeID()[14:], kv.Key.ID, itemID, kv.Key.Type))
|
kValues.Values = append(kValues.Values, av.GetAttributeViewDefaultValue(itemID[:14]+ast.NewNodeID()[14:], kv.Key.ID, itemID, kv.Key.Type, false))
|
||||||
keyValues = append(keyValues, kValues)
|
keyValues = append(keyValues, kValues)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 先渲染主键、创建时间、更新时间
|
// 渲染主键、创建时间、更新时间
|
||||||
|
|
||||||
for _, kv := range keyValues {
|
for _, kv := range keyValues {
|
||||||
switch kv.Key.Type {
|
switch kv.Key.Type {
|
||||||
case av.KeyTypeBlock: // 对于主键可能需要填充静态锚文本 Database-bound block primary key supports setting static anchor text https://github.com/siyuan-note/siyuan/issues/10049
|
case av.KeyTypeBlock: // 对于主键可能需要填充静态锚文本 Database-bound block primary key supports setting static anchor text https://github.com/siyuan-note/siyuan/issues/10049
|
||||||
|
@ -1575,8 +1646,7 @@ func GetBlockAttributeViewKeys(nodeID string) (ret []*BlockAttributeViewKeys) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 再渲染关联和汇总
|
// 渲染关联和汇总
|
||||||
|
|
||||||
rollupFurtherCollections := sql.GetFurtherCollections(attrView, cachedAttrViews)
|
rollupFurtherCollections := sql.GetFurtherCollections(attrView, cachedAttrViews)
|
||||||
for _, kv := range keyValues {
|
for _, kv := range keyValues {
|
||||||
switch kv.Key.Type {
|
switch kv.Key.Type {
|
||||||
|
@ -1633,7 +1703,7 @@ func GetBlockAttributeViewKeys(nodeID string) (ret []*BlockAttributeViewKeys) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 最后渲染模板
|
// 渲染模板
|
||||||
templateKeys, _ := sql.GetTemplateKeysByResolutionOrder(attrView)
|
templateKeys, _ := sql.GetTemplateKeysByResolutionOrder(attrView)
|
||||||
var renderTemplateErr error
|
var renderTemplateErr error
|
||||||
for _, templateKey := range templateKeys {
|
for _, templateKey := range templateKeys {
|
||||||
|
@ -1651,7 +1721,7 @@ func GetBlockAttributeViewKeys(nodeID string) (ret []*BlockAttributeViewKeys) {
|
||||||
ial = map[string]string{}
|
ial = map[string]string{}
|
||||||
}
|
}
|
||||||
if nil == kv.Values[0].Template {
|
if nil == kv.Values[0].Template {
|
||||||
kv.Values[0] = av.GetAttributeViewDefaultValue(kv.Values[0].ID, kv.Key.ID, nodeID, kv.Key.Type)
|
kv.Values[0] = av.GetAttributeViewDefaultValue(kv.Values[0].ID, kv.Key.ID, nodeID, kv.Key.Type, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
var renderErr error
|
var renderErr error
|
||||||
|
@ -1725,7 +1795,7 @@ func GetBlockAttributeViewKeys(nodeID string) (ret []*BlockAttributeViewKeys) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func genAttrViewGroups(view *av.View, attrView *av.AttributeView) {
|
func genAttrViewGroups(view *av.View, attrView *av.AttributeView) {
|
||||||
if nil == view.Group {
|
if !view.IsGroupView() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1912,6 +1982,9 @@ func genAttrViewGroups(view *av.View, attrView *av.AttributeView) {
|
||||||
case av.LayoutTypeGallery:
|
case av.LayoutTypeGallery:
|
||||||
v = av.NewGalleryView()
|
v = av.NewGalleryView()
|
||||||
v.Gallery = av.NewLayoutGallery()
|
v.Gallery = av.NewLayoutGallery()
|
||||||
|
case av.LayoutTypeKanban:
|
||||||
|
v = av.NewKanbanView()
|
||||||
|
v.Kanban = av.NewLayoutKanban()
|
||||||
default:
|
default:
|
||||||
logging.LogWarnf("unknown layout type [%s] for group view", view.LayoutType)
|
logging.LogWarnf("unknown layout type [%s] for group view", view.LayoutType)
|
||||||
return
|
return
|
||||||
|
@ -1969,7 +2042,7 @@ type GroupState struct {
|
||||||
|
|
||||||
func getAttrViewGroupStates(view *av.View) (groupStates map[string]*GroupState) {
|
func getAttrViewGroupStates(view *av.View) (groupStates map[string]*GroupState) {
|
||||||
groupStates = map[string]*GroupState{}
|
groupStates = map[string]*GroupState{}
|
||||||
if nil == view.Group {
|
if !view.IsGroupView() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2308,6 +2381,8 @@ func updateAttributeViewColRelation(operation *Operation) (err error) {
|
||||||
v.Table.Columns = append(v.Table.Columns, &av.ViewTableColumn{BaseField: &av.BaseField{ID: operation.BackRelationKeyID}})
|
v.Table.Columns = append(v.Table.Columns, &av.ViewTableColumn{BaseField: &av.BaseField{ID: operation.BackRelationKeyID}})
|
||||||
case av.LayoutTypeGallery:
|
case av.LayoutTypeGallery:
|
||||||
v.Gallery.CardFields = append(v.Gallery.CardFields, &av.ViewGalleryCardField{BaseField: &av.BaseField{ID: operation.BackRelationKeyID}})
|
v.Gallery.CardFields = append(v.Gallery.CardFields, &av.ViewGalleryCardField{BaseField: &av.BaseField{ID: operation.BackRelationKeyID}})
|
||||||
|
case av.LayoutTypeKanban:
|
||||||
|
v.Kanban.Fields = append(v.Kanban.Fields, &av.ViewKanbanField{BaseField: &av.BaseField{ID: operation.BackRelationKeyID}})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2329,6 +2404,7 @@ func updateAttributeViewColRelation(operation *Operation) (err error) {
|
||||||
destVal.Relation = &av.ValueRelation{}
|
destVal.Relation = &av.ValueRelation{}
|
||||||
}
|
}
|
||||||
destVal.UpdatedAt = now
|
destVal.UpdatedAt = now
|
||||||
|
destVal.IsRenderAutoFill = false
|
||||||
}
|
}
|
||||||
destVal.Relation.BlockIDs = append(destVal.Relation.BlockIDs, srcVal.BlockID)
|
destVal.Relation.BlockIDs = append(destVal.Relation.BlockIDs, srcVal.BlockID)
|
||||||
destVal.Relation.BlockIDs = gulu.Str.RemoveDuplicatedElem(destVal.Relation.BlockIDs)
|
destVal.Relation.BlockIDs = gulu.Str.RemoveDuplicatedElem(destVal.Relation.BlockIDs)
|
||||||
|
@ -2522,6 +2598,8 @@ func (tx *Transaction) doDuplicateAttrViewView(operation *Operation) (ret *TxErr
|
||||||
view = av.NewTableView()
|
view = av.NewTableView()
|
||||||
case av.LayoutTypeGallery:
|
case av.LayoutTypeGallery:
|
||||||
view = av.NewGalleryView()
|
view = av.NewGalleryView()
|
||||||
|
case av.LayoutTypeKanban:
|
||||||
|
view = av.NewKanbanView()
|
||||||
}
|
}
|
||||||
|
|
||||||
view.ID = operation.ID
|
view.ID = operation.ID
|
||||||
|
@ -2590,6 +2668,25 @@ func (tx *Transaction) doDuplicateAttrViewView(operation *Operation) (ret *TxErr
|
||||||
view.Gallery.DisplayFieldName = masterView.Gallery.DisplayFieldName
|
view.Gallery.DisplayFieldName = masterView.Gallery.DisplayFieldName
|
||||||
view.Gallery.ShowIcon = masterView.Gallery.ShowIcon
|
view.Gallery.ShowIcon = masterView.Gallery.ShowIcon
|
||||||
view.Gallery.WrapField = masterView.Gallery.WrapField
|
view.Gallery.WrapField = masterView.Gallery.WrapField
|
||||||
|
case av.LayoutTypeKanban:
|
||||||
|
for _, field := range masterView.Kanban.Fields {
|
||||||
|
view.Kanban.Fields = append(view.Kanban.Fields, &av.ViewKanbanField{
|
||||||
|
BaseField: &av.BaseField{
|
||||||
|
ID: field.ID,
|
||||||
|
Wrap: field.Wrap,
|
||||||
|
Hidden: field.Hidden,
|
||||||
|
Desc: field.Desc,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
view.Kanban.CoverFrom = masterView.Kanban.CoverFrom
|
||||||
|
view.Kanban.CoverFromAssetKeyID = masterView.Kanban.CoverFromAssetKeyID
|
||||||
|
view.Kanban.CardSize = masterView.Kanban.CardSize
|
||||||
|
view.Kanban.FitImage = masterView.Kanban.FitImage
|
||||||
|
view.Kanban.DisplayFieldName = masterView.Kanban.DisplayFieldName
|
||||||
|
view.Kanban.ShowIcon = masterView.Kanban.ShowIcon
|
||||||
|
view.Kanban.WrapField = masterView.Kanban.WrapField
|
||||||
}
|
}
|
||||||
|
|
||||||
view.ItemIDs = masterView.ItemIDs
|
view.ItemIDs = masterView.ItemIDs
|
||||||
|
@ -2655,6 +2752,10 @@ func addAttrViewView(avID, viewID, blockID string, layout av.LayoutType) (err er
|
||||||
for _, field := range firstView.Gallery.CardFields {
|
for _, field := range firstView.Gallery.CardFields {
|
||||||
view.Table.Columns = append(view.Table.Columns, &av.ViewTableColumn{BaseField: &av.BaseField{ID: field.ID}})
|
view.Table.Columns = append(view.Table.Columns, &av.ViewTableColumn{BaseField: &av.BaseField{ID: field.ID}})
|
||||||
}
|
}
|
||||||
|
case av.LayoutTypeKanban:
|
||||||
|
for _, field := range firstView.Kanban.Fields {
|
||||||
|
view.Table.Columns = append(view.Table.Columns, &av.ViewTableColumn{BaseField: &av.BaseField{ID: field.ID}})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case av.LayoutTypeGallery:
|
case av.LayoutTypeGallery:
|
||||||
view = av.NewGalleryView()
|
view = av.NewGalleryView()
|
||||||
|
@ -2667,6 +2768,22 @@ func addAttrViewView(avID, viewID, blockID string, layout av.LayoutType) (err er
|
||||||
for _, field := range firstView.Gallery.CardFields {
|
for _, field := range firstView.Gallery.CardFields {
|
||||||
view.Gallery.CardFields = append(view.Gallery.CardFields, &av.ViewGalleryCardField{BaseField: &av.BaseField{ID: field.ID}})
|
view.Gallery.CardFields = append(view.Gallery.CardFields, &av.ViewGalleryCardField{BaseField: &av.BaseField{ID: field.ID}})
|
||||||
}
|
}
|
||||||
|
case av.LayoutTypeKanban:
|
||||||
|
for _, field := range firstView.Kanban.Fields {
|
||||||
|
view.Gallery.CardFields = append(view.Gallery.CardFields, &av.ViewGalleryCardField{BaseField: &av.BaseField{ID: field.ID}})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case av.LayoutTypeKanban:
|
||||||
|
view = av.NewKanbanView()
|
||||||
|
switch firstView.LayoutType {
|
||||||
|
case av.LayoutTypeTable:
|
||||||
|
for _, col := range firstView.Table.Columns {
|
||||||
|
view.Kanban.Fields = append(view.Kanban.Fields, &av.ViewKanbanField{BaseField: &av.BaseField{ID: col.ID}})
|
||||||
|
}
|
||||||
|
case av.LayoutTypeKanban:
|
||||||
|
for _, field := range firstView.Kanban.Fields {
|
||||||
|
view.Kanban.Fields = append(view.Kanban.Fields, &av.ViewKanbanField{BaseField: &av.BaseField{ID: field.ID}})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
err = av.ErrWrongLayoutType
|
err = av.ErrWrongLayoutType
|
||||||
|
@ -2995,7 +3112,7 @@ func setAttributeViewColumnCalc(operation *Operation) (err error) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case av.LayoutTypeGallery:
|
case av.LayoutTypeGallery, av.LayoutTypeKanban:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3145,12 +3262,19 @@ func addAttributeViewBlock(now int64, avID, dbBlockID, viewID, groupID, previous
|
||||||
// The database date field supports filling the current time by default https://github.com/siyuan-note/siyuan/issues/10823
|
// The database date field supports filling the current time by default https://github.com/siyuan-note/siyuan/issues/10823
|
||||||
for _, keyValues := range attrView.KeyValues {
|
for _, keyValues := range attrView.KeyValues {
|
||||||
if av.KeyTypeDate == keyValues.Key.Type && nil != keyValues.Key.Date && keyValues.Key.Date.AutoFillNow {
|
if av.KeyTypeDate == keyValues.Key.Type && nil != keyValues.Key.Date && keyValues.Key.Date.AutoFillNow {
|
||||||
if nil == keyValues.GetValue(addingItemID) { // 避免覆盖已有值(可能前面已经通过过滤或者分组条件填充了值)
|
val := keyValues.GetValue(addingItemID)
|
||||||
|
if nil == val { // 避免覆盖已有值(可能前面已经通过过滤或者分组条件填充了值)
|
||||||
dateVal := &av.Value{
|
dateVal := &av.Value{
|
||||||
ID: ast.NewNodeID(), KeyID: keyValues.Key.ID, BlockID: addingItemID, Type: av.KeyTypeDate, IsDetached: isDetached, CreatedAt: now, UpdatedAt: now + 1000,
|
ID: ast.NewNodeID(), KeyID: keyValues.Key.ID, BlockID: addingItemID, Type: av.KeyTypeDate, IsDetached: isDetached, CreatedAt: now, UpdatedAt: now + 1000,
|
||||||
Date: &av.ValueDate{Content: now, IsNotEmpty: true},
|
Date: &av.ValueDate{Content: now, IsNotEmpty: true},
|
||||||
}
|
}
|
||||||
keyValues.Values = append(keyValues.Values, dateVal)
|
keyValues.Values = append(keyValues.Values, dateVal)
|
||||||
|
} else {
|
||||||
|
if val.IsRenderAutoFill {
|
||||||
|
val.CreatedAt, val.UpdatedAt = now, now+1000
|
||||||
|
val.Date.Content, val.Date.IsNotEmpty, val.Date.IsNotTime = now, true, false
|
||||||
|
val.IsRenderAutoFill = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3230,11 +3354,13 @@ func fillDefaultValue(attrView *av.AttributeView, view, groupView *av.View, prev
|
||||||
|
|
||||||
existingVal := keyValues.GetValue(addingItemID)
|
existingVal := keyValues.GetValue(addingItemID)
|
||||||
if nil == existingVal {
|
if nil == existingVal {
|
||||||
|
newValue.IsRenderAutoFill = false
|
||||||
keyValues.Values = append(keyValues.Values, newValue)
|
keyValues.Values = append(keyValues.Values, newValue)
|
||||||
} else {
|
} else {
|
||||||
newValueRaw := newValue.GetValByType(keyValues.Key.Type)
|
newValueRaw := newValue.GetValByType(keyValues.Key.Type)
|
||||||
if av.KeyTypeBlock != existingVal.Type || (av.KeyTypeBlock == existingVal.Type && existingVal.IsDetached) {
|
if av.KeyTypeBlock != existingVal.Type || (av.KeyTypeBlock == existingVal.Type && existingVal.IsDetached) {
|
||||||
// 非主键的值直接覆盖,主键的值只覆盖非绑定块
|
// 非主键的值直接覆盖,主键的值只覆盖非绑定块
|
||||||
|
existingVal.IsRenderAutoFill = false
|
||||||
existingVal.SetValByType(keyValues.Key.Type, newValueRaw)
|
existingVal.SetValByType(keyValues.Key.Type, newValueRaw)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3486,6 +3612,22 @@ func duplicateAttributeViewKey(operation *Operation) (err error) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case av.LayoutTypeKanban:
|
||||||
|
for i, field := range view.Kanban.Fields {
|
||||||
|
if field.ID == key.ID {
|
||||||
|
view.Kanban.Fields = append(view.Kanban.Fields[:i+1], append([]*av.ViewKanbanField{
|
||||||
|
{
|
||||||
|
BaseField: &av.BaseField{
|
||||||
|
ID: copyKey.ID,
|
||||||
|
Wrap: field.Wrap,
|
||||||
|
Hidden: field.Hidden,
|
||||||
|
Desc: field.Desc,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, view.Kanban.Fields[i+1:]...)...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3520,7 +3662,7 @@ func setAttributeViewColWidth(operation *Operation) (err error) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case av.LayoutTypeGallery:
|
case av.LayoutTypeGallery, av.LayoutTypeKanban:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3566,6 +3708,14 @@ func setAttributeViewColWrap(operation *Operation) (err error) {
|
||||||
allFieldWrap = allFieldWrap && field.Wrap
|
allFieldWrap = allFieldWrap && field.Wrap
|
||||||
}
|
}
|
||||||
view.Gallery.WrapField = allFieldWrap
|
view.Gallery.WrapField = allFieldWrap
|
||||||
|
case av.LayoutTypeKanban:
|
||||||
|
for _, field := range view.Kanban.Fields {
|
||||||
|
if field.ID == operation.ID {
|
||||||
|
field.Wrap = newWrap
|
||||||
|
}
|
||||||
|
allFieldWrap = allFieldWrap && field.Wrap
|
||||||
|
}
|
||||||
|
view.Kanban.WrapField = allFieldWrap
|
||||||
}
|
}
|
||||||
|
|
||||||
err = av.SaveAttributeView(attrView)
|
err = av.SaveAttributeView(attrView)
|
||||||
|
@ -3606,6 +3756,13 @@ func setAttributeViewColHidden(operation *Operation) (err error) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case av.LayoutTypeKanban:
|
||||||
|
for _, field := range view.Kanban.Fields {
|
||||||
|
if field.ID == operation.ID {
|
||||||
|
field.Hidden = operation.Data.(bool)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = av.SaveAttributeView(attrView)
|
err = av.SaveAttributeView(attrView)
|
||||||
|
@ -3639,7 +3796,7 @@ func setAttributeViewColPin(operation *Operation) (err error) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case av.LayoutTypeGallery:
|
case av.LayoutTypeGallery, av.LayoutTypeKanban:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3867,6 +4024,27 @@ func SortAttributeViewViewKey(avID, blockID, keyID, previousKeyID string) (err e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
view.Gallery.CardFields = util.InsertElem(view.Gallery.CardFields, previousIndex, field)
|
view.Gallery.CardFields = util.InsertElem(view.Gallery.CardFields, previousIndex, field)
|
||||||
|
case av.LayoutTypeKanban:
|
||||||
|
var field *av.ViewKanbanField
|
||||||
|
for i, kanbanField := range view.Kanban.Fields {
|
||||||
|
if kanbanField.ID == keyID {
|
||||||
|
field = kanbanField
|
||||||
|
curIndex = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if nil == field {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
view.Kanban.Fields = append(view.Kanban.Fields[:curIndex], view.Kanban.Fields[curIndex+1:]...)
|
||||||
|
for i, kanbanField := range view.Kanban.Fields {
|
||||||
|
if kanbanField.ID == previousKeyID {
|
||||||
|
previousIndex = i + 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
view.Kanban.Fields = util.InsertElem(view.Kanban.Fields, previousIndex, field)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = av.SaveAttributeView(attrView)
|
err = av.SaveAttributeView(attrView)
|
||||||
|
@ -3990,8 +4168,8 @@ func AddAttributeViewKey(avID, keyID, keyName, keyType, keyIcon, previousKeyID s
|
||||||
newField.Wrap = view.Table.WrapField
|
newField.Wrap = view.Table.WrapField
|
||||||
|
|
||||||
if "" == previousKeyID {
|
if "" == previousKeyID {
|
||||||
if av.LayoutTypeGallery == currentView.LayoutType {
|
if av.LayoutTypeGallery == currentView.LayoutType || av.LayoutTypeKanban == currentView.LayoutType {
|
||||||
// 如果当前视图是卡片视图则添加到最后
|
// 如果当前视图是卡片或看板视图则添加到最后
|
||||||
view.Table.Columns = append(view.Table.Columns, &av.ViewTableColumn{BaseField: newField})
|
view.Table.Columns = append(view.Table.Columns, &av.ViewTableColumn{BaseField: newField})
|
||||||
} else {
|
} else {
|
||||||
view.Table.Columns = append([]*av.ViewTableColumn{{BaseField: newField}}, view.Table.Columns...)
|
view.Table.Columns = append([]*av.ViewTableColumn{{BaseField: newField}}, view.Table.Columns...)
|
||||||
|
@ -4030,6 +4208,26 @@ func AddAttributeViewKey(avID, keyID, keyName, keyType, keyIcon, previousKeyID s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if nil != view.Kanban {
|
||||||
|
newField.Wrap = view.Kanban.WrapField
|
||||||
|
|
||||||
|
if "" == previousKeyID {
|
||||||
|
view.Kanban.Fields = append(view.Kanban.Fields, &av.ViewKanbanField{BaseField: newField})
|
||||||
|
} else {
|
||||||
|
added := false
|
||||||
|
for i, field := range view.Kanban.Fields {
|
||||||
|
if field.ID == previousKeyID {
|
||||||
|
view.Kanban.Fields = append(view.Kanban.Fields[:i+1], append([]*av.ViewKanbanField{{BaseField: newField}}, view.Kanban.Fields[i+1:]...)...)
|
||||||
|
added = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !added {
|
||||||
|
view.Kanban.Fields = append(view.Kanban.Fields, &av.ViewKanbanField{BaseField: newField})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4242,6 +4440,13 @@ func RemoveAttributeViewKey(avID, keyID string, removeRelationDest bool) (err er
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case av.LayoutTypeKanban:
|
||||||
|
for i, field := range view.Kanban.Fields {
|
||||||
|
if field.ID == removedKey.Relation.BackKeyID {
|
||||||
|
view.Kanban.Fields = append(view.Kanban.Fields[:i], view.Kanban.Fields[i+1:]...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4286,6 +4491,15 @@ func RemoveAttributeViewKey(avID, keyID string, removeRelationDest bool) (err er
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if nil != view.Kanban {
|
||||||
|
for i, field := range view.Kanban.Fields {
|
||||||
|
if field.ID == keyID {
|
||||||
|
view.Kanban.Fields = append(view.Kanban.Fields[:i], view.Kanban.Fields[i+1:]...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, view := range attrView.Views {
|
for _, view := range attrView.Views {
|
||||||
|
|
|
@ -97,14 +97,14 @@ func renderAttributeViewGroups(viewable av.Viewable, attrView *av.AttributeView,
|
||||||
if isGroupByDate(view) {
|
if isGroupByDate(view) {
|
||||||
createdDate := time.UnixMilli(view.GroupCreated).Format("2006-01-02")
|
createdDate := time.UnixMilli(view.GroupCreated).Format("2006-01-02")
|
||||||
if time.Now().Format("2006-01-02") != createdDate {
|
if time.Now().Format("2006-01-02") != createdDate {
|
||||||
regenAttrViewGroups(attrView)
|
genAttrViewGroups(view, attrView) // 仅重新生成一个视图的分组以提升性能
|
||||||
av.SaveAttributeView(attrView)
|
av.SaveAttributeView(attrView)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果是按模板分组则需要重新生成分组
|
// 如果是按模板分组则需要重新生成分组
|
||||||
if isGroupByTemplate(attrView, view) {
|
if isGroupByTemplate(attrView, view) {
|
||||||
regenAttrViewGroups(attrView)
|
genAttrViewGroups(view, attrView) // 仅重新生成一个视图的分组以提升性能
|
||||||
av.SaveAttributeView(attrView)
|
av.SaveAttributeView(attrView)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,6 +167,8 @@ func renderAttributeViewGroups(viewable av.Viewable, attrView *av.AttributeView,
|
||||||
groupView.Table.Columns = nil
|
groupView.Table.Columns = nil
|
||||||
case av.LayoutTypeGallery:
|
case av.LayoutTypeGallery:
|
||||||
groupView.Gallery.CardFields = nil
|
groupView.Gallery.CardFields = nil
|
||||||
|
case av.LayoutTypeKanban:
|
||||||
|
groupView.Kanban.Fields = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
viewable.SetGroups(groups)
|
viewable.SetGroups(groups)
|
||||||
|
@ -177,7 +179,7 @@ func renderAttributeViewGroups(viewable av.Viewable, attrView *av.AttributeView,
|
||||||
}
|
}
|
||||||
|
|
||||||
func hideEmptyGroupViews(view *av.View, viewable av.Viewable) {
|
func hideEmptyGroupViews(view *av.View, viewable av.Viewable) {
|
||||||
if nil == view.Group {
|
if !view.IsGroupView() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -343,14 +345,14 @@ func sortGroupsBySelectOption(view *av.View, groupKey *av.Key) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func isGroupByDate(view *av.View) bool {
|
func isGroupByDate(view *av.View) bool {
|
||||||
if nil == view.Group {
|
if !view.IsGroupView() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return av.GroupMethodDateDay == view.Group.Method || av.GroupMethodDateWeek == view.Group.Method || av.GroupMethodDateMonth == view.Group.Method || av.GroupMethodDateYear == view.Group.Method || av.GroupMethodDateRelative == view.Group.Method
|
return av.GroupMethodDateDay == view.Group.Method || av.GroupMethodDateWeek == view.Group.Method || av.GroupMethodDateMonth == view.Group.Method || av.GroupMethodDateYear == view.Group.Method || av.GroupMethodDateRelative == view.Group.Method
|
||||||
}
|
}
|
||||||
|
|
||||||
func isGroupByTemplate(attrView *av.AttributeView, view *av.View) bool {
|
func isGroupByTemplate(attrView *av.AttributeView, view *av.View) bool {
|
||||||
if nil == view.Group {
|
if !view.IsGroupView() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -402,6 +404,19 @@ func renderViewableInstance(viewable av.Viewable, view *av.View, attrView *av.At
|
||||||
end = len(gallery.Cards)
|
end = len(gallery.Cards)
|
||||||
}
|
}
|
||||||
gallery.Cards = gallery.Cards[start:end]
|
gallery.Cards = gallery.Cards[start:end]
|
||||||
|
case av.LayoutTypeKanban:
|
||||||
|
kanban := viewable.(*av.Kanban)
|
||||||
|
kanban.CardCount = 0
|
||||||
|
kanban.PageSize = view.PageSize
|
||||||
|
if 1 > pageSize {
|
||||||
|
pageSize = kanban.PageSize
|
||||||
|
}
|
||||||
|
start := (page - 1) * pageSize
|
||||||
|
end := start + pageSize
|
||||||
|
if len(kanban.Cards) < end {
|
||||||
|
end = len(kanban.Cards)
|
||||||
|
}
|
||||||
|
kanban.Cards = kanban.Cards[start:end]
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -486,7 +501,7 @@ func RenderRepoSnapshotAttributeView(indexID, avID string) (viewable av.Viewable
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func RenderHistoryAttributeView(avID, created string) (viewable av.Viewable, attrView *av.AttributeView, err error) {
|
func RenderHistoryAttributeView(blockID, avID, viewID, query string, page, pageSize int, groupPaging map[string]interface{}, created string) (viewable av.Viewable, attrView *av.AttributeView, err error) {
|
||||||
createdUnix, parseErr := strconv.ParseInt(created, 10, 64)
|
createdUnix, parseErr := strconv.ParseInt(created, 10, 64)
|
||||||
if nil != parseErr {
|
if nil != parseErr {
|
||||||
logging.LogErrorf("parse created [%s] failed: %s", created, parseErr)
|
logging.LogErrorf("parse created [%s] failed: %s", created, parseErr)
|
||||||
|
@ -525,6 +540,6 @@ func RenderHistoryAttributeView(avID, created string) (viewable av.Viewable, att
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
viewable, err = renderAttributeView(attrView, "", "", "", 1, -1, nil)
|
viewable, err = renderAttributeView(attrView, blockID, viewID, query, page, pageSize, groupPaging)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -234,6 +234,22 @@ func GetBlockSiblingID(id string) (parent, previous, next string) {
|
||||||
next = flb.ID
|
next = flb.ID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if "" == previous && "" == next && nil != current {
|
||||||
|
parent = current.ID
|
||||||
|
if nil != current.Previous {
|
||||||
|
previous = current.Previous.ID
|
||||||
|
if flb := treenode.FirstChildBlock(current.Previous); nil != flb {
|
||||||
|
previous = flb.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if nil != current.Next {
|
||||||
|
next = current.Next.ID
|
||||||
|
if flb := treenode.FirstChildBlock(current.Next); nil != flb {
|
||||||
|
next = flb.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -612,6 +628,46 @@ func GetHeadingDeleteTransaction(id string) (transaction *Transaction, err error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetHeadingInsertTransaction(id string) (transaction *Transaction, err error) {
|
||||||
|
tree, err := LoadTreeByBlockID(id)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
node := treenode.GetNodeInTree(tree, id)
|
||||||
|
if nil == node {
|
||||||
|
err = errors.New(fmt.Sprintf(Conf.Language(15), id))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ast.NodeHeading != node.Type {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var nodes []*ast.Node
|
||||||
|
nodes = append(nodes, node)
|
||||||
|
nodes = append(nodes, treenode.HeadingChildren(node)...)
|
||||||
|
|
||||||
|
transaction = &Transaction{}
|
||||||
|
luteEngine := util.NewLute()
|
||||||
|
for _, n := range nodes {
|
||||||
|
n.ID = ast.NewNodeID()
|
||||||
|
n.SetIALAttr("id", n.ID)
|
||||||
|
|
||||||
|
op := &Operation{Context: map[string]any{"ignoreProcess": "true"}}
|
||||||
|
op.ID = n.ID
|
||||||
|
op.Action = "insert"
|
||||||
|
op.Data = luteEngine.RenderNodeBlockDOM(n)
|
||||||
|
transaction.DoOperations = append(transaction.DoOperations, op)
|
||||||
|
|
||||||
|
op = &Operation{}
|
||||||
|
op.ID = n.ID
|
||||||
|
op.Action = "delete"
|
||||||
|
transaction.UndoOperations = append(transaction.UndoOperations, op)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func GetHeadingChildrenIDs(id string) (ret []string) {
|
func GetHeadingChildrenIDs(id string) (ret []string) {
|
||||||
tree, err := LoadTreeByBlockID(id)
|
tree, err := LoadTreeByBlockID(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -473,6 +473,8 @@ func moveTree(tree *parse.Tree) {
|
||||||
|
|
||||||
box := Conf.Box(tree.Box)
|
box := Conf.Box(tree.Box)
|
||||||
box.renameSubTrees(tree)
|
box.renameSubTrees(tree)
|
||||||
|
|
||||||
|
refreshDocInfo(tree)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (box *Box) renameSubTrees(tree *parse.Tree) {
|
func (box *Box) renameSubTrees(tree *parse.Tree) {
|
||||||
|
|
|
@ -2271,11 +2271,11 @@ func exportTree(tree *parse.Tree, wysiwyg, keepFold, avHiddenCol bool,
|
||||||
|
|
||||||
switch blockRefMode {
|
switch blockRefMode {
|
||||||
case 2: // 锚文本块链
|
case 2: // 锚文本块链
|
||||||
blockRefLink := &ast.Node{Type: ast.NodeTextMark, TextMarkType: "a", TextMarkTextContent: linkText, TextMarkAHref: "siyuan://blocks/" + defID}
|
blockRefLink := &ast.Node{Type: ast.NodeTextMark, TextMarkTextContent: linkText, TextMarkAHref: "siyuan://blocks/" + defID}
|
||||||
blockRefLink.KramdownIAL = n.KramdownIAL
|
blockRefLink.KramdownIAL = n.KramdownIAL
|
||||||
if n.IsTextMarkType("inline-memo") {
|
if "block-ref" != n.TextMarkType { // 除了块引还有其他元素 https://github.com/siyuan-note/siyuan/issues/15698
|
||||||
|
blockRefLink.TextMarkType = strings.TrimSpace(strings.ReplaceAll(n.TextMarkType, "block-ref", "a"))
|
||||||
blockRefLink.TextMarkInlineMemoContent = n.TextMarkInlineMemoContent
|
blockRefLink.TextMarkInlineMemoContent = n.TextMarkInlineMemoContent
|
||||||
blockRefLink.TextMarkType = "a inline-memo"
|
|
||||||
}
|
}
|
||||||
n.InsertBefore(blockRefLink)
|
n.InsertBefore(blockRefLink)
|
||||||
unlinks = append(unlinks, n)
|
unlinks = append(unlinks, n)
|
||||||
|
@ -2291,11 +2291,11 @@ func exportTree(tree *parse.Tree, wysiwyg, keepFold, avHiddenCol bool,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
blockRefLink = &ast.Node{Type: ast.NodeText, Tokens: []byte(linkText)}
|
blockRefLink = &ast.Node{Type: ast.NodeText, Tokens: []byte(linkText)}
|
||||||
if n.IsTextMarkType("inline-memo") {
|
if "block-ref" != n.TextMarkType {
|
||||||
blockRefLink.Type = ast.NodeTextMark
|
blockRefLink.Type = ast.NodeTextMark
|
||||||
blockRefLink.TextMarkInlineMemoContent = n.TextMarkInlineMemoContent
|
blockRefLink.TextMarkType = strings.TrimSpace(strings.ReplaceAll(n.TextMarkType, "block-ref", ""))
|
||||||
blockRefLink.TextMarkType = "inline-memo"
|
|
||||||
blockRefLink.TextMarkTextContent = linkText
|
blockRefLink.TextMarkTextContent = linkText
|
||||||
|
blockRefLink.TextMarkInlineMemoContent = n.TextMarkInlineMemoContent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
n.InsertBefore(blockRefLink)
|
n.InsertBefore(blockRefLink)
|
||||||
|
@ -2315,13 +2315,13 @@ func exportTree(tree *parse.Tree, wysiwyg, keepFold, avHiddenCol bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
text := &ast.Node{Type: ast.NodeText, Tokens: []byte(linkText)}
|
text := &ast.Node{Type: ast.NodeText, Tokens: []byte(linkText)}
|
||||||
n.InsertBefore(text)
|
if "block-ref" != n.TextMarkType {
|
||||||
if n.IsTextMarkType("inline-memo") {
|
|
||||||
text.Type = ast.NodeTextMark
|
text.Type = ast.NodeTextMark
|
||||||
text.TextMarkType = "inline-memo"
|
text.TextMarkType = strings.TrimSpace(strings.ReplaceAll(n.TextMarkType, "block-ref", ""))
|
||||||
text.TextMarkTextContent = linkText
|
text.TextMarkTextContent = linkText
|
||||||
text.TextMarkInlineMemoContent = n.TextMarkInlineMemoContent
|
text.TextMarkInlineMemoContent = n.TextMarkInlineMemoContent
|
||||||
}
|
}
|
||||||
|
n.InsertBefore(text)
|
||||||
n.InsertBefore(&ast.Node{Type: ast.NodeFootnotesRef, Tokens: []byte("^" + refFoot.refNum), FootnotesRefId: refFoot.refNum, FootnotesRefLabel: []byte("^" + refFoot.refNum)})
|
n.InsertBefore(&ast.Node{Type: ast.NodeFootnotesRef, Tokens: []byte("^" + refFoot.refNum), FootnotesRefId: refFoot.refNum, FootnotesRefLabel: []byte("^" + refFoot.refNum)})
|
||||||
unlinks = append(unlinks, n)
|
unlinks = append(unlinks, n)
|
||||||
}
|
}
|
||||||
|
@ -3454,6 +3454,11 @@ func getAttrViewTable(attrView *av.AttributeView, view *av.View, query string) (
|
||||||
for _, field := range view.Gallery.CardFields {
|
for _, field := range view.Gallery.CardFields {
|
||||||
view.Table.Columns = append(view.Table.Columns, &av.ViewTableColumn{BaseField: &av.BaseField{ID: field.ID}})
|
view.Table.Columns = append(view.Table.Columns, &av.ViewTableColumn{BaseField: &av.BaseField{ID: field.ID}})
|
||||||
}
|
}
|
||||||
|
case av.LayoutTypeKanban:
|
||||||
|
view.Table = av.NewLayoutTable()
|
||||||
|
for _, field := range view.Kanban.Fields {
|
||||||
|
view.Table.Columns = append(view.Table.Columns, &av.ViewTableColumn{BaseField: &av.BaseField{ID: field.ID}})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
depth := 1
|
depth := 1
|
||||||
|
|
|
@ -933,7 +933,7 @@ func writeTreeUpsertQueue(tree *parse.Tree) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sql.UpsertTreeQueue(tree)
|
sql.UpsertTreeQueue(tree)
|
||||||
refreshDocInfo(tree, size)
|
refreshDocInfoWithSize(tree, size)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -959,7 +959,7 @@ func renameWriteJSONQueue(tree *parse.Tree) (err error) {
|
||||||
}
|
}
|
||||||
sql.RenameTreeQueue(tree)
|
sql.RenameTreeQueue(tree)
|
||||||
treenode.UpsertBlockTree(tree)
|
treenode.UpsertBlockTree(tree)
|
||||||
refreshDocInfo(tree, size)
|
refreshDocInfoWithSize(tree, size)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1371,6 +1371,8 @@ func moveDoc(fromBox *Box, fromPath string, toBox *Box, toPath string, luteEngin
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fromParentTree := loadParentTree(tree)
|
||||||
|
|
||||||
moveToRoot := "/" == toPath
|
moveToRoot := "/" == toPath
|
||||||
toBlockID := tree.ID
|
toBlockID := tree.ID
|
||||||
fromFolder := path.Join(path.Dir(fromPath), tree.ID)
|
fromFolder := path.Join(path.Dir(fromPath), tree.ID)
|
||||||
|
@ -1489,6 +1491,8 @@ func moveDoc(fromBox *Box, fromPath string, toBox *Box, toPath string, luteEngin
|
||||||
}
|
}
|
||||||
evt.Callback = callback
|
evt.Callback = callback
|
||||||
util.PushEvent(evt)
|
util.PushEvent(evt)
|
||||||
|
|
||||||
|
refreshDocInfo(fromParentTree)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,10 @@
|
||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
"github.com/88250/lute/ast"
|
"github.com/88250/lute/ast"
|
||||||
|
"github.com/88250/lute/editor"
|
||||||
"github.com/88250/lute/render"
|
"github.com/88250/lute/render"
|
||||||
"github.com/siyuan-note/logging"
|
"github.com/siyuan-note/logging"
|
||||||
"github.com/siyuan-note/siyuan/kernel/util"
|
"github.com/siyuan-note/siyuan/kernel/util"
|
||||||
|
@ -37,13 +40,18 @@ func AutoSpace(rootID string) (err error) {
|
||||||
|
|
||||||
generateOpTypeHistory(tree, HistoryOpFormat)
|
generateOpTypeHistory(tree, HistoryOpFormat)
|
||||||
luteEngine := NewLute()
|
luteEngine := NewLute()
|
||||||
// 合并相邻的同类行级节点
|
|
||||||
ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
|
ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
|
||||||
if entering {
|
if !entering {
|
||||||
switch n.Type {
|
return ast.WalkContinue
|
||||||
case ast.NodeTextMark:
|
}
|
||||||
luteEngine.MergeSameTextMark(n)
|
|
||||||
}
|
switch n.Type {
|
||||||
|
case ast.NodeTextMark:
|
||||||
|
luteEngine.MergeSameTextMark(n) // 合并相邻的同类行级节点
|
||||||
|
case ast.NodeCodeBlockCode:
|
||||||
|
// 代码块中包含 ``` 时 `优化排版` 异常 `Optimize typography` exception when code block contains ``` https://github.com/siyuan-note/siyuan/issues/15843
|
||||||
|
n.Tokens = bytes.ReplaceAll(n.Tokens, []byte(editor.Zwj+"```"), []byte("```"))
|
||||||
|
n.Tokens = bytes.ReplaceAll(n.Tokens, []byte("```"), []byte(editor.Zwj+"```"))
|
||||||
}
|
}
|
||||||
return ast.WalkContinue
|
return ast.WalkContinue
|
||||||
})
|
})
|
||||||
|
|
|
@ -18,7 +18,6 @@ package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"path"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -38,25 +37,22 @@ import (
|
||||||
"github.com/siyuan-note/siyuan/kernel/util"
|
"github.com/siyuan-note/siyuan/kernel/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func refreshDocInfo(tree *parse.Tree, size uint64) {
|
func refreshDocInfo(tree *parse.Tree) {
|
||||||
|
refreshDocInfoWithSize(tree, filesys.TreeSize(tree))
|
||||||
|
}
|
||||||
|
|
||||||
|
func refreshDocInfoWithSize(tree *parse.Tree, size uint64) {
|
||||||
refreshDocInfo0(tree, size)
|
refreshDocInfo0(tree, size)
|
||||||
refreshParentDocInfo(tree)
|
refreshParentDocInfo(tree)
|
||||||
}
|
}
|
||||||
|
|
||||||
func refreshParentDocInfo(tree *parse.Tree) {
|
func refreshParentDocInfo(tree *parse.Tree) {
|
||||||
|
parentTree := loadParentTree(tree)
|
||||||
|
if nil == parentTree {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
luteEngine := lute.New()
|
luteEngine := lute.New()
|
||||||
boxDir := filepath.Join(util.DataDir, tree.Box)
|
|
||||||
parentDir := path.Dir(tree.Path)
|
|
||||||
if parentDir == boxDir || parentDir == "/" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
parentPath := parentDir + ".sy"
|
|
||||||
parentTree, err := filesys.LoadTree(tree.Box, parentPath, luteEngine)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
renderer := render.NewJSONRenderer(parentTree, luteEngine.RenderOptions)
|
renderer := render.NewJSONRenderer(parentTree, luteEngine.RenderOptions)
|
||||||
data := renderer.Render()
|
data := renderer.Render()
|
||||||
refreshDocInfo0(parentTree, uint64(len(data)))
|
refreshDocInfo0(parentTree, uint64(len(data)))
|
||||||
|
|
|
@ -305,3 +305,16 @@ func indexTreeInFilesystem(rootID string) {
|
||||||
sql.IndexTreeQueue(tree)
|
sql.IndexTreeQueue(tree)
|
||||||
logging.LogInfof("reindexed tree by filesystem [rootID=%s]", rootID)
|
logging.LogInfof("reindexed tree by filesystem [rootID=%s]", rootID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loadParentTree(tree *parse.Tree) (ret *parse.Tree) {
|
||||||
|
boxDir := filepath.Join(util.DataDir, tree.Box)
|
||||||
|
parentDir := path.Dir(tree.Path)
|
||||||
|
if parentDir == boxDir || parentDir == "/" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
luteEngine := lute.New()
|
||||||
|
parentPath := parentDir + ".sy"
|
||||||
|
ret, _ = filesys.LoadTree(tree.Box, parentPath, luteEngine)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
161
kernel/sql/av.go
161
kernel/sql/av.go
|
@ -53,6 +53,17 @@ func RenderGroupView(attrView *av.AttributeView, view, groupView *av.View, query
|
||||||
groupView.Gallery.CardSize = view.Gallery.CardSize
|
groupView.Gallery.CardSize = view.Gallery.CardSize
|
||||||
groupView.Gallery.FitImage = view.Gallery.FitImage
|
groupView.Gallery.FitImage = view.Gallery.FitImage
|
||||||
groupView.Gallery.DisplayFieldName = view.Gallery.DisplayFieldName
|
groupView.Gallery.DisplayFieldName = view.Gallery.DisplayFieldName
|
||||||
|
case av.LayoutTypeKanban:
|
||||||
|
err = copier.CopyWithOption(&groupView.Kanban.Fields, &view.Kanban.Fields, copier.Option{DeepCopy: true})
|
||||||
|
groupView.Kanban.ShowIcon = view.Kanban.ShowIcon
|
||||||
|
groupView.Kanban.WrapField = view.Kanban.WrapField
|
||||||
|
|
||||||
|
groupView.Kanban.CoverFrom = view.Kanban.CoverFrom
|
||||||
|
groupView.Kanban.CoverFromAssetKeyID = view.Kanban.CoverFromAssetKeyID
|
||||||
|
groupView.Kanban.CardAspectRatio = view.Kanban.CardAspectRatio
|
||||||
|
groupView.Kanban.CardSize = view.Kanban.CardSize
|
||||||
|
groupView.Kanban.FitImage = view.Kanban.FitImage
|
||||||
|
groupView.Kanban.DisplayFieldName = view.Kanban.DisplayFieldName
|
||||||
}
|
}
|
||||||
if nil != err {
|
if nil != err {
|
||||||
logging.LogErrorf("copy view fields [%s] to group [%s] failed: %s", view.ID, groupView.ID, err)
|
logging.LogErrorf("copy view fields [%s] to group [%s] failed: %s", view.ID, groupView.ID, err)
|
||||||
|
@ -61,6 +72,8 @@ func RenderGroupView(attrView *av.AttributeView, view, groupView *av.View, query
|
||||||
groupView.Table.Columns = view.Table.Columns
|
groupView.Table.Columns = view.Table.Columns
|
||||||
case av.LayoutTypeGallery:
|
case av.LayoutTypeGallery:
|
||||||
groupView.Gallery.CardFields = view.Gallery.CardFields
|
groupView.Gallery.CardFields = view.Gallery.CardFields
|
||||||
|
case av.LayoutTypeKanban:
|
||||||
|
groupView.Kanban.Fields = view.Kanban.Fields
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,6 +104,8 @@ func renderView(attrView *av.AttributeView, view *av.View, query string, depth *
|
||||||
ret = RenderAttributeViewTable(attrView, view, query, depth, cachedAttrViews)
|
ret = RenderAttributeViewTable(attrView, view, query, depth, cachedAttrViews)
|
||||||
case av.LayoutTypeGallery:
|
case av.LayoutTypeGallery:
|
||||||
ret = RenderAttributeViewGallery(attrView, view, query, depth, cachedAttrViews)
|
ret = RenderAttributeViewGallery(attrView, view, query, depth, cachedAttrViews)
|
||||||
|
case av.LayoutTypeKanban:
|
||||||
|
ret = RenderAttributeViewKanban(attrView, view, query, depth, cachedAttrViews)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -314,7 +329,7 @@ func filterNotFoundAttrViewItems(keyValuesMap map[string][]*av.KeyValues) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func fillAttributeViewBaseValue(baseValue *av.BaseValue, fieldID, itemID string, fieldNumberFormat av.NumberFormat, fieldTemplate string) {
|
func fillAttributeViewBaseValue(baseValue *av.BaseValue, fieldID, itemID string, fieldNumberFormat av.NumberFormat, fieldTemplate string, fieldDateAutoFill bool) {
|
||||||
switch baseValue.ValueType {
|
switch baseValue.ValueType {
|
||||||
case av.KeyTypeNumber: // 格式化数字
|
case av.KeyTypeNumber: // 格式化数字
|
||||||
if nil != baseValue.Value && nil != baseValue.Value.Number && baseValue.Value.Number.IsNotEmpty {
|
if nil != baseValue.Value && nil != baseValue.Value.Number && baseValue.Value.Number.IsNotEmpty {
|
||||||
|
@ -330,14 +345,14 @@ func fillAttributeViewBaseValue(baseValue *av.BaseValue, fieldID, itemID string,
|
||||||
}
|
}
|
||||||
|
|
||||||
if nil == baseValue.Value {
|
if nil == baseValue.Value {
|
||||||
baseValue.Value = av.GetAttributeViewDefaultValue(baseValue.ID, fieldID, itemID, baseValue.ValueType)
|
baseValue.Value = av.GetAttributeViewDefaultValue(baseValue.ID, fieldID, itemID, baseValue.ValueType, fieldDateAutoFill)
|
||||||
} else {
|
} else {
|
||||||
FillAttributeViewNilValue(baseValue.Value, baseValue.ValueType)
|
FillAttributeViewNilValue(baseValue.Value, baseValue.ValueType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func fillAttributeViewAutoGeneratedValues(attrView *av.AttributeView, collection av.Collection, ials map[string]map[string]string, depth *int, cachedAttrViews map[string]*av.AttributeView) {
|
func fillAttributeViewAutoGeneratedValues(attrView *av.AttributeView, collection av.Collection, ials map[string]map[string]string, depth *int, cachedAttrViews map[string]*av.AttributeView) {
|
||||||
// 先渲染主键、创建时间、更新时间
|
// 渲染主键、创建时间、更新时间
|
||||||
|
|
||||||
for _, item := range collection.GetItems() {
|
for _, item := range collection.GetItems() {
|
||||||
for _, value := range item.GetValues() {
|
for _, value := range item.GetValues() {
|
||||||
|
@ -400,8 +415,42 @@ func fillAttributeViewAutoGeneratedValues(attrView *av.AttributeView, collection
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 再渲染关联和汇总
|
// 渲染关联
|
||||||
|
for _, item := range collection.GetItems() {
|
||||||
|
for _, value := range item.GetValues() {
|
||||||
|
if av.KeyTypeRelation != value.Type {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
value.Relation.Contents = nil
|
||||||
|
relKey, _ := attrView.GetKey(value.KeyID)
|
||||||
|
if nil != relKey && nil != relKey.Relation {
|
||||||
|
destAv := cachedAttrViews[relKey.Relation.AvID]
|
||||||
|
if nil == destAv {
|
||||||
|
destAv, _ = av.ParseAttributeView(relKey.Relation.AvID)
|
||||||
|
if nil != destAv {
|
||||||
|
cachedAttrViews[relKey.Relation.AvID] = destAv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if nil != destAv {
|
||||||
|
blocks := map[string]*av.Value{}
|
||||||
|
blockValues := destAv.GetBlockKeyValues()
|
||||||
|
if nil != blockValues {
|
||||||
|
for _, blockValue := range blockValues.Values {
|
||||||
|
blocks[blockValue.BlockID] = blockValue
|
||||||
|
}
|
||||||
|
for _, blockID := range value.Relation.BlockIDs {
|
||||||
|
if val := blocks[blockID]; nil != val {
|
||||||
|
value.Relation.Contents = append(value.Relation.Contents, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 渲染汇总
|
||||||
rollupFurtherCollections := map[string]av.Collection{}
|
rollupFurtherCollections := map[string]av.Collection{}
|
||||||
for _, field := range collection.GetFields() {
|
for _, field := range collection.GetFields() {
|
||||||
if av.KeyTypeRollup != field.GetType() {
|
if av.KeyTypeRollup != field.GetType() {
|
||||||
|
@ -436,7 +485,7 @@ func fillAttributeViewAutoGeneratedValues(attrView *av.AttributeView, collection
|
||||||
|
|
||||||
isSameAv := destAv.ID == attrView.ID
|
isSameAv := destAv.ID == attrView.ID
|
||||||
var furtherCollection av.Collection
|
var furtherCollection av.Collection
|
||||||
if av.KeyTypeTemplate == destKey.Type || (!isSameAv && (av.KeyTypeUpdated == destKey.Type || av.KeyTypeCreated == destKey.Type)) {
|
if av.KeyTypeTemplate == destKey.Type || (!isSameAv && (av.KeyTypeUpdated == destKey.Type || av.KeyTypeCreated == destKey.Type || av.KeyTypeRelation == destKey.Type)) {
|
||||||
viewable := renderView(destAv, destAv.Views[0], "", depth, cachedAttrViews)
|
viewable := renderView(destAv, destAv.Views[0], "", depth, cachedAttrViews)
|
||||||
if nil != viewable {
|
if nil != viewable {
|
||||||
furtherCollection = viewable.(av.Collection)
|
furtherCollection = viewable.(av.Collection)
|
||||||
|
@ -450,70 +499,43 @@ func fillAttributeViewAutoGeneratedValues(attrView *av.AttributeView, collection
|
||||||
|
|
||||||
for _, item := range collection.GetItems() {
|
for _, item := range collection.GetItems() {
|
||||||
for _, value := range item.GetValues() {
|
for _, value := range item.GetValues() {
|
||||||
itemID := item.GetID()
|
if av.KeyTypeRollup != value.Type {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
switch value.Type {
|
rollupKey, _ := attrView.GetKey(value.KeyID)
|
||||||
case av.KeyTypeRollup: // 渲染汇总
|
if nil == rollupKey || nil == rollupKey.Rollup {
|
||||||
rollupKey, _ := attrView.GetKey(value.KeyID)
|
break
|
||||||
if nil == rollupKey || nil == rollupKey.Rollup {
|
}
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
relKey, _ := attrView.GetKey(rollupKey.Rollup.RelationKeyID)
|
relKey, _ := attrView.GetKey(rollupKey.Rollup.RelationKeyID)
|
||||||
if nil == relKey || nil == relKey.Relation {
|
if nil == relKey || nil == relKey.Relation {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
relVal := attrView.GetValue(relKey.ID, itemID)
|
relVal := attrView.GetValue(relKey.ID, item.GetID())
|
||||||
if nil == relVal || nil == relVal.Relation {
|
if nil == relVal || nil == relVal.Relation {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
destAv := cachedAttrViews[relKey.Relation.AvID]
|
destAv := cachedAttrViews[relKey.Relation.AvID]
|
||||||
if nil == destAv {
|
if nil == destAv {
|
||||||
destAv, _ = av.ParseAttributeView(relKey.Relation.AvID)
|
destAv, _ = av.ParseAttributeView(relKey.Relation.AvID)
|
||||||
if nil != destAv {
|
if nil != destAv {
|
||||||
cachedAttrViews[relKey.Relation.AvID] = destAv
|
cachedAttrViews[relKey.Relation.AvID] = destAv
|
||||||
}
|
|
||||||
}
|
|
||||||
if nil == destAv {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
destKey, _ := destAv.GetKey(rollupKey.Rollup.KeyID)
|
|
||||||
if nil == destKey {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
furtherCollection := rollupFurtherCollections[rollupKey.ID]
|
|
||||||
value.Rollup.BuildContents(destAv.KeyValues, destKey, relVal, rollupKey.Rollup.Calc, furtherCollection)
|
|
||||||
case av.KeyTypeRelation: // 渲染关联
|
|
||||||
value.Relation.Contents = nil
|
|
||||||
relKey, _ := attrView.GetKey(value.KeyID)
|
|
||||||
if nil != relKey && nil != relKey.Relation {
|
|
||||||
destAv := cachedAttrViews[relKey.Relation.AvID]
|
|
||||||
if nil == destAv {
|
|
||||||
destAv, _ = av.ParseAttributeView(relKey.Relation.AvID)
|
|
||||||
if nil != destAv {
|
|
||||||
cachedAttrViews[relKey.Relation.AvID] = destAv
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if nil != destAv {
|
|
||||||
blocks := map[string]*av.Value{}
|
|
||||||
blockValues := destAv.GetBlockKeyValues()
|
|
||||||
if nil != blockValues {
|
|
||||||
for _, blockValue := range blockValues.Values {
|
|
||||||
blocks[blockValue.BlockID] = blockValue
|
|
||||||
}
|
|
||||||
for _, blockID := range value.Relation.BlockIDs {
|
|
||||||
if val := blocks[blockID]; nil != val {
|
|
||||||
value.Relation.Contents = append(value.Relation.Contents, val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if nil == destAv {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
destKey, _ := destAv.GetKey(rollupKey.Rollup.KeyID)
|
||||||
|
if nil == destKey {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
furtherCollection := rollupFurtherCollections[rollupKey.ID]
|
||||||
|
value.Rollup.BuildContents(destAv.KeyValues, destKey, relVal, rollupKey.Rollup.Calc, furtherCollection)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -546,7 +568,7 @@ func GetFurtherCollections(attrView *av.AttributeView, cachedAttrViews map[strin
|
||||||
isSameAv := destAv.ID == attrView.ID
|
isSameAv := destAv.ID == attrView.ID
|
||||||
|
|
||||||
var furtherCollection av.Collection
|
var furtherCollection av.Collection
|
||||||
if av.KeyTypeTemplate == destKey.Type || (!isSameAv && (av.KeyTypeUpdated == destKey.Type || av.KeyTypeCreated == destKey.Type)) {
|
if av.KeyTypeTemplate == destKey.Type || (!isSameAv && (av.KeyTypeUpdated == destKey.Type || av.KeyTypeCreated == destKey.Type || av.KeyTypeRelation == destKey.Type)) {
|
||||||
viewable := RenderView(destAv, destAv.Views[0], "")
|
viewable := RenderView(destAv, destAv.Views[0], "")
|
||||||
if nil != viewable {
|
if nil != viewable {
|
||||||
furtherCollection = viewable.(av.Collection)
|
furtherCollection = viewable.(av.Collection)
|
||||||
|
@ -624,6 +646,7 @@ func fillAttributeViewKeyValues(attrView *av.AttributeView, collection av.Collec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !exist {
|
if !exist {
|
||||||
|
val.IsRenderAutoFill = true
|
||||||
keyValues.Values = append(keyValues.Values, val)
|
keyValues.Values = append(keyValues.Values, val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -772,6 +795,16 @@ func removeMissingField(attrView *av.AttributeView, view *av.View, missingKeyID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if nil != view.Kanban {
|
||||||
|
for i, kanbanField := range view.Kanban.Fields {
|
||||||
|
if kanbanField.ID == missingKeyID {
|
||||||
|
view.Kanban.Fields = append(view.Kanban.Fields[:i], view.Kanban.Fields[i+1:]...)
|
||||||
|
changed = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if changed {
|
if changed {
|
||||||
av.SaveAttributeView(attrView)
|
av.SaveAttributeView(attrView)
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,7 +108,11 @@ func RenderAttributeViewGallery(attrView *av.AttributeView, view *av.View, query
|
||||||
}
|
}
|
||||||
galleryCard.ID = cardID
|
galleryCard.ID = cardID
|
||||||
|
|
||||||
fillAttributeViewBaseValue(fieldValue.BaseValue, field.ID, cardID, field.NumberFormat, field.Template)
|
filedDateAutoFill := false
|
||||||
|
if nil != field.Date {
|
||||||
|
filedDateAutoFill = field.Date.AutoFillNow
|
||||||
|
}
|
||||||
|
fillAttributeViewBaseValue(fieldValue.BaseValue, field.ID, cardID, field.NumberFormat, field.Template, filedDateAutoFill)
|
||||||
galleryCard.Values = append(galleryCard.Values, fieldValue)
|
galleryCard.Values = append(galleryCard.Values, fieldValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
219
kernel/sql/av_kanban.go
Normal file
219
kernel/sql/av_kanban.go
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
package sql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/88250/lute"
|
||||||
|
"github.com/88250/lute/ast"
|
||||||
|
"github.com/88250/lute/parse"
|
||||||
|
"github.com/siyuan-note/siyuan/kernel/av"
|
||||||
|
"github.com/siyuan-note/siyuan/kernel/filesys"
|
||||||
|
"github.com/siyuan-note/siyuan/kernel/treenode"
|
||||||
|
"github.com/siyuan-note/siyuan/kernel/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RenderAttributeViewKanban(attrView *av.AttributeView, view *av.View, query string, depth *int, cachedAttrViews map[string]*av.AttributeView) (ret *av.Kanban) {
|
||||||
|
viewable := attrView.RenderedViewables[view.ID]
|
||||||
|
if nil != viewable {
|
||||||
|
ret = viewable.(*av.Kanban)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = &av.Kanban{
|
||||||
|
BaseInstance: av.NewViewBaseInstance(view),
|
||||||
|
CoverFrom: view.Kanban.CoverFrom,
|
||||||
|
CoverFromAssetKeyID: view.Kanban.CoverFromAssetKeyID,
|
||||||
|
CardAspectRatio: view.Kanban.CardAspectRatio,
|
||||||
|
CardSize: view.Kanban.CardSize,
|
||||||
|
FitImage: view.Kanban.FitImage,
|
||||||
|
DisplayFieldName: view.Kanban.DisplayFieldName,
|
||||||
|
Fields: []*av.KanbanField{},
|
||||||
|
Cards: []*av.KanbanCard{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// 组装字段
|
||||||
|
for _, field := range view.Kanban.Fields {
|
||||||
|
key, getErr := attrView.GetKey(field.ID)
|
||||||
|
if nil != getErr {
|
||||||
|
// 找不到字段则在视图中删除
|
||||||
|
removeMissingField(attrView, view, field.ID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.Fields = append(ret.Fields, &av.KanbanField{
|
||||||
|
BaseInstanceField: &av.BaseInstanceField{
|
||||||
|
ID: key.ID,
|
||||||
|
Name: key.Name,
|
||||||
|
Type: key.Type,
|
||||||
|
Icon: key.Icon,
|
||||||
|
Wrap: field.Wrap,
|
||||||
|
Hidden: field.Hidden,
|
||||||
|
Desc: key.Desc,
|
||||||
|
Calc: field.Calc,
|
||||||
|
Options: key.Options,
|
||||||
|
NumberFormat: key.NumberFormat,
|
||||||
|
Template: key.Template,
|
||||||
|
Relation: key.Relation,
|
||||||
|
Rollup: key.Rollup,
|
||||||
|
Date: key.Date,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
cardsValues := generateAttrViewItems(attrView, view) // 生成卡片
|
||||||
|
filterNotFoundAttrViewItems(cardsValues) // 过滤掉不存在的卡片
|
||||||
|
|
||||||
|
// 批量加载绑定块对应的树
|
||||||
|
var ialIDs []string
|
||||||
|
for _, keyValues := range cardsValues {
|
||||||
|
for _, kValues := range keyValues {
|
||||||
|
blockVal := kValues.GetBlockValue()
|
||||||
|
if nil != blockVal && !blockVal.IsDetached {
|
||||||
|
ialIDs = append(ialIDs, blockVal.Block.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
boundTrees := filesys.LoadTrees(ialIDs)
|
||||||
|
|
||||||
|
// 生成卡片字段值
|
||||||
|
for cardID, cardValues := range cardsValues {
|
||||||
|
var kanbanCard av.KanbanCard
|
||||||
|
for _, field := range ret.Fields {
|
||||||
|
var fieldValue *av.KanbanFieldValue
|
||||||
|
for _, keyValues := range cardValues {
|
||||||
|
if keyValues.Key.ID == field.ID {
|
||||||
|
fieldValue = &av.KanbanFieldValue{
|
||||||
|
BaseValue: &av.BaseValue{
|
||||||
|
ID: keyValues.Values[0].ID,
|
||||||
|
Value: keyValues.Values[0],
|
||||||
|
ValueType: field.Type,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if nil == fieldValue {
|
||||||
|
fieldValue = &av.KanbanFieldValue{
|
||||||
|
BaseValue: &av.BaseValue{
|
||||||
|
ID: cardID[:14] + ast.NewNodeID()[14:],
|
||||||
|
ValueType: field.Type,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kanbanCard.ID = cardID
|
||||||
|
|
||||||
|
filedDateAutoFill := false
|
||||||
|
if nil != field.Date {
|
||||||
|
filedDateAutoFill = field.Date.AutoFillNow
|
||||||
|
}
|
||||||
|
fillAttributeViewBaseValue(fieldValue.BaseValue, field.ID, cardID, field.NumberFormat, field.Template, filedDateAutoFill)
|
||||||
|
kanbanCard.Values = append(kanbanCard.Values, fieldValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
fillAttributeViewKanbanCardCover(attrView, view, cardValues, &kanbanCard, cardID, luteEngine, boundTrees)
|
||||||
|
ret.Cards = append(ret.Cards, &kanbanCard)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 回填补全数据
|
||||||
|
fillAttributeViewKeyValues(attrView, ret)
|
||||||
|
|
||||||
|
// 批量获取块属性以提升性能
|
||||||
|
ials := BatchGetBlockAttrsWitTrees(ialIDs, boundTrees)
|
||||||
|
|
||||||
|
// 渲染自动生成的字段值,比如关联、汇总、创建时间和更新时间
|
||||||
|
fillAttributeViewAutoGeneratedValues(attrView, ret, ials, depth, cachedAttrViews)
|
||||||
|
|
||||||
|
// 最后渲染模板字段,这样模板就可以使用汇总、关联、创建时间和更新时间的值了
|
||||||
|
renderTemplateErr := fillAttributeViewTemplateValues(attrView, view, ret, ials)
|
||||||
|
if nil != renderTemplateErr {
|
||||||
|
util.PushErrMsg(fmt.Sprintf(util.Langs[util.Lang][44], util.EscapeHTML(renderTemplateErr.Error())), 30000)
|
||||||
|
}
|
||||||
|
|
||||||
|
filterByQuery(query, ret)
|
||||||
|
manualSort(view, ret)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func fillAttributeViewKanbanCardCover(attrView *av.AttributeView, view *av.View, cardValues []*av.KeyValues, kanbanCard *av.KanbanCard, cardID string, luteEngine *lute.Lute, trees map[string]*parse.Tree) {
|
||||||
|
switch view.Kanban.CoverFrom {
|
||||||
|
case av.CoverFromNone:
|
||||||
|
case av.CoverFromContentImage:
|
||||||
|
blockValue := getBlockValue(cardValues)
|
||||||
|
if blockValue.IsDetached {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
tree := trees[blockValue.Block.ID]
|
||||||
|
if nil == tree {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
node := treenode.GetNodeInTree(tree, blockValue.Block.ID)
|
||||||
|
if nil == node {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if ast.NodeDocument == node.Type {
|
||||||
|
if titleImg := treenode.GetDocTitleImgPath(node); "" != titleImg {
|
||||||
|
kanbanCard.CoverURL = titleImg
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if titleImgCSS := node.IALAttr("title-img"); "" != titleImgCSS {
|
||||||
|
kanbanCard.CoverURL = titleImgCSS
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ast.Walk(node, func(n *ast.Node, entering bool) ast.WalkStatus {
|
||||||
|
if !entering {
|
||||||
|
return ast.WalkContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ast.NodeImage != n.Type {
|
||||||
|
return ast.WalkContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
dest := n.ChildByType(ast.NodeLinkDest)
|
||||||
|
if nil == dest {
|
||||||
|
return ast.WalkContinue
|
||||||
|
}
|
||||||
|
kanbanCard.CoverURL = dest.TokensStr()
|
||||||
|
return ast.WalkStop
|
||||||
|
})
|
||||||
|
|
||||||
|
if "" == kanbanCard.CoverURL {
|
||||||
|
kanbanCard.CoverContent = renderCoverContentBlock(node, luteEngine)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case av.CoverFromAssetField:
|
||||||
|
if "" == view.Kanban.CoverFromAssetKeyID {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
assetValue := attrView.GetValue(view.Kanban.CoverFromAssetKeyID, cardID)
|
||||||
|
if nil == assetValue || 1 > len(assetValue.MAsset) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
p := assetValue.MAsset[0].Content
|
||||||
|
if util.IsAssetsImage(p) {
|
||||||
|
kanbanCard.CoverURL = p
|
||||||
|
}
|
||||||
|
return
|
||||||
|
case av.CoverFromContentBlock:
|
||||||
|
blockValue := getBlockValue(cardValues)
|
||||||
|
if blockValue.IsDetached {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
tree := trees[blockValue.Block.ID]
|
||||||
|
if nil == tree {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
node := treenode.GetNodeInTree(tree, blockValue.Block.ID)
|
||||||
|
if nil == node {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
kanbanCard.CoverContent = renderCoverContentBlock(node, luteEngine)
|
||||||
|
}
|
||||||
|
}
|
|
@ -96,7 +96,11 @@ func RenderAttributeViewTable(attrView *av.AttributeView, view *av.View, query s
|
||||||
}
|
}
|
||||||
tableRow.ID = rowID
|
tableRow.ID = rowID
|
||||||
|
|
||||||
fillAttributeViewBaseValue(tableCell.BaseValue, col.ID, rowID, col.NumberFormat, col.Template)
|
filedDateAutoFill := false
|
||||||
|
if nil != col.Date {
|
||||||
|
filedDateAutoFill = col.Date.AutoFillNow
|
||||||
|
}
|
||||||
|
fillAttributeViewBaseValue(tableCell.BaseValue, col.ID, rowID, col.NumberFormat, col.Template, filedDateAutoFill)
|
||||||
tableRow.Cells = append(tableRow.Cells, tableCell)
|
tableRow.Cells = append(tableRow.Cells, tableCell)
|
||||||
}
|
}
|
||||||
ret.Rows = append(ret.Rows, &tableRow)
|
ret.Rows = append(ret.Rows, &tableRow)
|
||||||
|
|
|
@ -14,9 +14,9 @@ if errorlevel 1 (
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
echo 'Cleaning Builds'
|
echo 'Cleaning Builds'
|
||||||
del /S /Q /F app\build 1>nul
|
rmdir /S /Q app\build 1>nul
|
||||||
del /S /Q /F app\kernel 1>nul
|
rmdir /S /Q app\kernel 1>nul
|
||||||
del /S /Q /F app\kernel-arm64 1>nul
|
rmdir /S /Q app\kernel-arm64 1>nul
|
||||||
|
|
||||||
echo 'Building Kernel'
|
echo 'Building Kernel'
|
||||||
@REM the C compiler "gcc" is necessary https://sourceforge.net/projects/mingw-w64/files/mingw-w64/
|
@REM the C compiler "gcc" is necessary https://sourceforge.net/projects/mingw-w64/files/mingw-w64/
|
||||||
|
@ -68,4 +68,13 @@ cd ..
|
||||||
echo 'Building Appx'
|
echo 'Building Appx'
|
||||||
echo 'Building Appx should be disabled if you do not need it. Not configured correctly will lead to build failures'
|
echo 'Building Appx should be disabled if you do not need it. Not configured correctly will lead to build failures'
|
||||||
cd . > app\build\win-unpacked\resources\ms-store
|
cd . > app\build\win-unpacked\resources\ms-store
|
||||||
electron-windows-store --input-directory app\build\win-unpacked --output-directory app\build\ --package-version 1.0.0.0 --package-name SiYuan --manifest app\appx\AppxManifest.xml --assets app\appx\assets\ --make-pri true
|
call electron-windows-store --input-directory app\build\win-unpacked --output-directory app\build\ --package-version 1.0.0.0 --package-name SiYuan --manifest app\appx\AppxManifest.xml --assets app\appx\assets\ --make-pri true
|
||||||
|
|
||||||
|
rmdir /S /Q app\build\pre-appx 1>nul
|
||||||
|
|
||||||
|
echo 'Building Appx arm64'
|
||||||
|
echo 'Building Appx arm64 should be disabled if you do not need it. Not configured correctly will lead to build failures'
|
||||||
|
cd . > app\build\win-arm64-unpacked\resources\ms-store
|
||||||
|
call electron-windows-store --input-directory app\build\win-arm64-unpacked --output-directory app\build\ --package-version 1.0.0.0 --package-name SiYuan-arm64 --manifest app\appx\AppxManifest-arm64.xml --assets app\appx\assets\ --make-pri true
|
||||||
|
|
||||||
|
rmdir /S /Q app\build\pre-appx 1>nul
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue