feat(ui): redesign public reading and complete glass workspace
Add scoped public navigation, reading progress, focus and print controls without loading the editor. Polish responsive settings, graph and media layouts. Correct share expiry units, preserve math and code during link maintenance, and normalize Markdown destinations for templates and note navigation. Verify browser flows and document the new tools.
This commit is contained in:
1 parent
91a359c2b7
commit
4d1339851b
28 files changed
+472
-66
No files matched your search
@@ -0,0 +1,8 @@
|
||||
// 浏览器在呈现链接属性时会解码实体;源码操作也须按相同规则解析目标。
|
||||
export function decodeLinkDestination(raw: string): string {
|
||||
const field = document.createElement('textarea')
|
||||
return raw.replace(/\\([!-/:-@[-`{-~])/g, '$1').replace(/&(?:#\d{1,7}|#[xX][\da-fA-F]{1,6}|[A-Za-z][A-Za-z\d]{1,31});/g, entity => {
|
||||
field.innerHTML = entity
|
||||
return field.value
|
||||
})
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// @vitest-environment happy-dom
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { localDate, renderTemplate } from './template'
|
||||
|
||||
@@ -12,4 +13,9 @@ describe('Markdown 模板', () => {
|
||||
expect(result).toContain('[ref]: ../docs/guide.md#%E6%A0%87%E9%A2%98')
|
||||
expect(result).toContain('``\n```\n\n```')
|
||||
})
|
||||
it('链接实体和 Markdown 转义按真实文件名重定位', () => {
|
||||
const result = renderTemplate('[文档](a&b.md) [括号](a\\(1\\).md)', 'Templates/日记.md', 'Daily/今天.md')
|
||||
expect(result).toContain('[文档](../Templates/a%26b.md)')
|
||||
expect(result).toContain('[括号](../Templates/a(1).md)')
|
||||
})
|
||||
})
|
||||
@@ -1,4 +1,5 @@
|
||||
import { markdownParser } from './html'
|
||||
import { decodeLinkDestination } from './links'
|
||||
|
||||
export function localDate(now = new Date()): string {
|
||||
return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`
|
||||
@@ -8,9 +9,9 @@ export function renderTemplate(content: string, template: string, target: string
|
||||
const changes: { from: number; to: number; value: string }[] = []
|
||||
markdownParser.parse(content).iterate({ enter(node) {
|
||||
if (node.name !== 'URL' || !['Link', 'Image', 'LinkReference'].includes(node.node.parent?.name ?? '')) return
|
||||
const raw = content.slice(node.from, node.to).replace(/^<|>$/g, '')
|
||||
const raw = decodeLinkDestination(content.slice(node.from, node.to).replace(/^<|>$/g, ''))
|
||||
if (/^(?:[a-z][a-z\d+.-]*:|\/|#)/i.test(raw)) return
|
||||
const url = new URL(raw.replace(/\\([!-/:-@[-`{-~])/g, '$1'), 'https://rawmark.local/' + template.split('/').map(encodeURIComponent).join('/'))
|
||||
const url = new URL(raw, 'https://rawmark.local/' + template.split('/').map(encodeURIComponent).join('/'))
|
||||
const parts = decodeURIComponent(url.pathname.slice(1)).split('/')
|
||||
const base = target.slice(0, Math.max(0, target.lastIndexOf('/'))).split('/').filter(Boolean)
|
||||
while (base.length && base[0] === parts[0]) { base.shift(); parts.shift() }
|
||||
|
||||
Reference in new issue
Block a user