feat: consolidate local-first mnote web runtime
This commit is contained in:
@@ -11,6 +11,25 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
searchText,
|
||||
} = context;
|
||||
const MNOTE_GLOBAL_SHOW_HEADING_NUMBERS_KEY = context.globalShowHeadingNumbersKey || 'mnote.global.showHeadingNumbers';
|
||||
const PAGE_WIDTH_TYPES = ['default', 'markdown', 'word', 'pdf', 'excel', 'ppt', 'mindmap'];
|
||||
const PAGE_WIDTH_LABELS = {
|
||||
default: '默认',
|
||||
markdown: 'Markdown',
|
||||
word: 'Word',
|
||||
pdf: 'PDF',
|
||||
excel: 'Excel',
|
||||
ppt: 'PPT',
|
||||
mindmap: 'Mindmap'
|
||||
};
|
||||
const DEFAULT_PAGE_WIDTH_PREFERENCES = {
|
||||
default: { mode: 'comfortable', resolvedMode: 'comfortable', cssMaxWidth: '980px', source: 'system' },
|
||||
markdown: { mode: 'readable', resolvedMode: 'readable', cssMaxWidth: '760px', source: 'system' },
|
||||
word: { mode: 'wide', resolvedMode: 'wide', cssMaxWidth: '1180px', source: 'system' },
|
||||
pdf: { mode: 'wide', resolvedMode: 'wide', cssMaxWidth: '1180px', source: 'system' },
|
||||
excel: { mode: 'full', resolvedMode: 'full', cssMaxWidth: 'none', source: 'system' },
|
||||
ppt: { mode: 'wide', resolvedMode: 'wide', cssMaxWidth: '1180px', source: 'system' },
|
||||
mindmap: { mode: 'full', resolvedMode: 'full', cssMaxWidth: 'none', source: 'system' }
|
||||
};
|
||||
|
||||
function defaultPageOptions() {
|
||||
return {
|
||||
@@ -44,6 +63,93 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
return pageUiState.pageOptions;
|
||||
}
|
||||
|
||||
function currentPageWidthPreferences() {
|
||||
return Object.assign({}, DEFAULT_PAGE_WIDTH_PREFERENCES, pageUiState.pageWidthPreferences || {});
|
||||
}
|
||||
|
||||
function pageWidthModeLabel(mode) {
|
||||
if (mode === 'inherit') return '继承默认';
|
||||
if (mode === 'readable') return '阅读';
|
||||
if (mode === 'comfortable') return '舒适';
|
||||
if (mode === 'wide') return '宽版';
|
||||
if (mode === 'full') return '全宽';
|
||||
return '舒适';
|
||||
}
|
||||
|
||||
function normalizePageWidthPreferences(value) {
|
||||
var input = value && typeof value === 'object' ? value : {};
|
||||
var output = {};
|
||||
PAGE_WIDTH_TYPES.forEach(function(type) {
|
||||
var base = DEFAULT_PAGE_WIDTH_PREFERENCES[type];
|
||||
var current = input[type] && typeof input[type] === 'object' ? input[type] : {};
|
||||
var mode = String(current.mode || base.mode || 'comfortable');
|
||||
var resolvedMode = String(current.resolvedMode || current.resolved_mode || (mode === 'inherit' ? (input.default && input.default.resolvedMode) || 'comfortable' : mode));
|
||||
output[type] = {
|
||||
mode: mode,
|
||||
custom: current.custom || null,
|
||||
resolvedMode: resolvedMode,
|
||||
resolvedCustom: current.resolvedCustom || current.resolved_custom || null,
|
||||
cssMaxWidth: String(current.cssMaxWidth || current.css_max_width || pageWidthCssMaxWidth(resolvedMode)),
|
||||
source: String(current.source || base.source || 'system')
|
||||
};
|
||||
});
|
||||
return output;
|
||||
}
|
||||
|
||||
function pageWidthCssMaxWidth(mode) {
|
||||
if (mode === 'readable') return '760px';
|
||||
if (mode === 'comfortable') return '980px';
|
||||
if (mode === 'wide') return '1180px';
|
||||
if (mode === 'full') return 'none';
|
||||
return '980px';
|
||||
}
|
||||
|
||||
function markdownPageWidthPreference(options) {
|
||||
var preferences = currentPageWidthPreferences();
|
||||
var markdown = preferences.markdown || DEFAULT_PAGE_WIDTH_PREFERENCES.markdown;
|
||||
if (markdown.source === 'system' && options && options.wideLayout) {
|
||||
return {
|
||||
mode: 'comfortable',
|
||||
resolvedMode: 'comfortable',
|
||||
cssMaxWidth: '980px',
|
||||
source: 'wideLayout'
|
||||
};
|
||||
}
|
||||
return markdown;
|
||||
}
|
||||
|
||||
function activeResourceWidthType() {
|
||||
var raw = document.documentElement.getAttribute('data-mnote-active-resource-width-type') || '';
|
||||
var type = String(raw).trim();
|
||||
if (type === 'sheet') return 'excel';
|
||||
if (PAGE_WIDTH_TYPES.indexOf(type) >= 0 && type !== 'default') return type;
|
||||
return '';
|
||||
}
|
||||
|
||||
function activeResourceWidthPreference(options) {
|
||||
var type = activeResourceWidthType();
|
||||
if (!type || type === 'markdown') return markdownPageWidthPreference(options);
|
||||
var preferences = currentPageWidthPreferences();
|
||||
return preferences[type] || DEFAULT_PAGE_WIDTH_PREFERENCES[type] || DEFAULT_PAGE_WIDTH_PREFERENCES.default;
|
||||
}
|
||||
|
||||
function notifyPageWidthPreferenceChanged(type) {
|
||||
var detail = {
|
||||
type: String(type || ''),
|
||||
preferences: currentPageWidthPreferences()
|
||||
};
|
||||
window.dispatchEvent(new CustomEvent('mnote:page-width-preference-changed', { detail: detail }));
|
||||
document.querySelectorAll('iframe.mnote-resource-tab-frame').forEach(function(frame) {
|
||||
if (!(frame instanceof HTMLIFrameElement) || !frame.contentWindow) return;
|
||||
try {
|
||||
frame.contentWindow.postMessage({
|
||||
type: 'mnote:page-width-preference-changed',
|
||||
contentType: detail.type
|
||||
}, window.location.origin);
|
||||
} catch (_) {}
|
||||
});
|
||||
}
|
||||
|
||||
function readGlobalShowHeadingNumbers() {
|
||||
return Boolean(currentPageOptions().showHeadingNumbers);
|
||||
}
|
||||
@@ -157,13 +263,19 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
var editorRoot = document.querySelector('[data-testid="mnote-leptos-tiptap-island-editor-root"]');
|
||||
var editorSurface = editorRoot && editorRoot.querySelector('.editor-surface');
|
||||
if (shell instanceof HTMLElement) {
|
||||
var widthPreference = activeResourceWidthPreference(options);
|
||||
var cssMaxWidth = String(widthPreference.cssMaxWidth || pageWidthCssMaxWidth(widthPreference.resolvedMode));
|
||||
shell.setAttribute('data-page-wide-layout', String(Boolean(options.wideLayout)));
|
||||
shell.setAttribute('data-page-width-content-type', activeResourceWidthType() || 'markdown');
|
||||
shell.setAttribute('data-page-width-mode', String(widthPreference.mode || 'readable'));
|
||||
shell.setAttribute('data-page-width-resolved-mode', String(widthPreference.resolvedMode || widthPreference.mode || 'readable'));
|
||||
shell.setAttribute('data-page-width-source', String(widthPreference.source || 'system'));
|
||||
shell.setAttribute('data-page-small-text', String(Boolean(options.smallText)));
|
||||
shell.setAttribute('data-layout-density', String(options.layoutDensity || 'normal'));
|
||||
shell.setAttribute('data-page-font', String(options.pageFont || 'default'));
|
||||
shell.setAttribute('data-page-show-heading-numbers', String(showHeadingNumbers));
|
||||
shell.style.width = '100%';
|
||||
shell.style.maxWidth = options.wideLayout ? '980px' : '760px';
|
||||
shell.style.maxWidth = cssMaxWidth === 'none' ? 'none' : cssMaxWidth;
|
||||
}
|
||||
if (editorRoot instanceof HTMLElement) {
|
||||
editorRoot.setAttribute('data-page-wide-layout', String(Boolean(options.wideLayout)));
|
||||
@@ -177,6 +289,10 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
editorSurface.setAttribute('data-show-heading-numbers', String(showHeadingNumbers));
|
||||
}
|
||||
document.documentElement.setAttribute('data-page-wide-layout', String(Boolean(options.wideLayout)));
|
||||
var rootWidthPreference = activeResourceWidthPreference(options);
|
||||
document.documentElement.setAttribute('data-page-width-content-type', activeResourceWidthType() || 'markdown');
|
||||
document.documentElement.setAttribute('data-page-width-mode', String(rootWidthPreference.mode || 'readable'));
|
||||
document.documentElement.setAttribute('data-page-width-resolved-mode', String(rootWidthPreference.resolvedMode || rootWidthPreference.mode || 'readable'));
|
||||
document.documentElement.setAttribute('data-page-small-text', String(Boolean(options.smallText)));
|
||||
document.documentElement.setAttribute('data-layout-density', String(options.layoutDensity || 'normal'));
|
||||
document.documentElement.setAttribute('data-page-font', String(options.pageFont || 'default'));
|
||||
@@ -235,11 +351,58 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
'</label>';
|
||||
}
|
||||
|
||||
function createPageWidthSelectRow(type) {
|
||||
var options = type === 'default'
|
||||
? [
|
||||
['readable', '阅读'],
|
||||
['comfortable', '舒适'],
|
||||
['wide', '宽版'],
|
||||
['full', '全宽']
|
||||
]
|
||||
: [
|
||||
['inherit', '继承默认'],
|
||||
['readable', '阅读'],
|
||||
['comfortable', '舒适'],
|
||||
['wide', '宽版'],
|
||||
['full', '全宽']
|
||||
];
|
||||
return '' +
|
||||
'<label class="wolai-page-setting-row" data-page-width-row="' + type + '">' +
|
||||
'<span class="wolai-page-setting-copy">' +
|
||||
'<span class="wolai-page-setting-label">' + escapeHtml(PAGE_WIDTH_LABELS[type] || type) + '</span>' +
|
||||
'<span class="wolai-page-setting-hint" data-page-width-hint="' + type + '"></span>' +
|
||||
'</span>' +
|
||||
'<select class="wolai-page-setting-select" data-page-width-select="' + type + '">' +
|
||||
options.map(function(option) {
|
||||
return '<option value="' + option[0] + '">' + option[1] + '</option>';
|
||||
}).join('') +
|
||||
'</select>' +
|
||||
'</label>';
|
||||
}
|
||||
|
||||
function createPageWidthRows() {
|
||||
return '' +
|
||||
'<div class="wolai-page-settings-global-note">页面宽度</div>' +
|
||||
PAGE_WIDTH_TYPES.map(createPageWidthSelectRow).join('');
|
||||
}
|
||||
|
||||
function renderGlobalOptions(popover) {
|
||||
var globalHeadingNumbers = readGlobalShowHeadingNumbers();
|
||||
popover.querySelectorAll('[data-global-option-checkbox="showHeadingNumbers"]').forEach(function(input) {
|
||||
input.checked = globalHeadingNumbers;
|
||||
});
|
||||
var preferences = currentPageWidthPreferences();
|
||||
popover.querySelectorAll('[data-page-width-select]').forEach(function(select) {
|
||||
var type = select.getAttribute('data-page-width-select') || '';
|
||||
var preference = preferences[type] || DEFAULT_PAGE_WIDTH_PREFERENCES[type] || DEFAULT_PAGE_WIDTH_PREFERENCES.default;
|
||||
select.value = String(preference.mode || 'comfortable');
|
||||
});
|
||||
popover.querySelectorAll('[data-page-width-hint]').forEach(function(node) {
|
||||
var type = node.getAttribute('data-page-width-hint') || '';
|
||||
var preference = preferences[type] || DEFAULT_PAGE_WIDTH_PREFERENCES[type] || DEFAULT_PAGE_WIDTH_PREFERENCES.default;
|
||||
var resolvedMode = String(preference.resolvedMode || preference.mode || 'comfortable');
|
||||
node.textContent = pageWidthModeLabel(resolvedMode) + ' · ' + String(preference.cssMaxWidth || pageWidthCssMaxWidth(resolvedMode));
|
||||
});
|
||||
}
|
||||
|
||||
function createPageFontRow() {
|
||||
@@ -410,6 +573,7 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
'</div>' +
|
||||
'<div class="wolai-page-settings-section" data-page-settings-panel="global" hidden>' +
|
||||
createGlobalHeadingNumbersRow() +
|
||||
createPageWidthRows() +
|
||||
'</div>' +
|
||||
'<div class="wolai-page-settings-actions">' +
|
||||
'<button type="button" class="wolai-page-settings-action" data-page-settings-action="history">页面历史...</button>' +
|
||||
@@ -657,6 +821,71 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadPageWidthPreferences() {
|
||||
if (!currentDocumentId()) return;
|
||||
var params = new URLSearchParams();
|
||||
params.set('documentId', currentDocumentId());
|
||||
params.set('workspaceId', resolveWorkspaceId(document.body));
|
||||
var sourcePayload = currentWorkspaceSourcePayload();
|
||||
Object.keys(sourcePayload || {}).forEach(function(key) {
|
||||
if (sourcePayload[key] != null && sourcePayload[key] !== '') params.set(key, sourcePayload[key]);
|
||||
});
|
||||
try {
|
||||
var response = await fetch('/api/ui/preferences/effective?' + params.toString(), {
|
||||
headers: { accept: 'application/json' }
|
||||
});
|
||||
var payload = await response.json().catch(function(){ return null; });
|
||||
if (!response.ok || !payload || payload.ok !== true) return;
|
||||
pageUiState.pageWidthPreferences = normalizePageWidthPreferences(payload.result && payload.result.pageWidthPreferences);
|
||||
applyPageOptionsToShell();
|
||||
if (isPageSettingsOpen()) renderPageSettingsPopover();
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
async function persistPageWidthPreference(type, mode) {
|
||||
if (PAGE_WIDTH_TYPES.indexOf(type) < 0) return;
|
||||
var previous = pageUiState.pageWidthPreferences;
|
||||
var next = normalizePageWidthPreferences(previous);
|
||||
next[type] = Object.assign({}, next[type], {
|
||||
mode: mode,
|
||||
resolvedMode: mode === 'inherit' ? next.default.resolvedMode : mode,
|
||||
cssMaxWidth: pageWidthCssMaxWidth(mode === 'inherit' ? next.default.resolvedMode : mode),
|
||||
source: 'optimistic'
|
||||
});
|
||||
pageUiState.pageWidthPreferences = next;
|
||||
applyPageOptionsToShell();
|
||||
renderPageSettingsPopover();
|
||||
notifyPageWidthPreferenceChanged(type);
|
||||
try {
|
||||
var updates = {};
|
||||
updates['pageWidth.' + type] = { mode: mode, custom: null };
|
||||
var response = await fetch('/api/ui/preferences', {
|
||||
method: 'PUT',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
documentId: currentDocumentId(),
|
||||
workspaceId: resolveWorkspaceId(document.body),
|
||||
...currentWorkspaceSourcePayload(),
|
||||
updates: updates
|
||||
})
|
||||
});
|
||||
var payload = await response.json().catch(function(){ return null; });
|
||||
if (!response.ok || !payload || payload.ok !== true) {
|
||||
throw new Error(payload && payload.error && payload.error.message ? payload.error.message : 'page_width_save_failed_' + response.status);
|
||||
}
|
||||
pageUiState.pageWidthPreferences = normalizePageWidthPreferences(payload.result && payload.result.pageWidthPreferences);
|
||||
applyPageOptionsToShell();
|
||||
renderPageSettingsPopover();
|
||||
notifyPageWidthPreferenceChanged(type);
|
||||
document.documentElement.setAttribute('data-mnote-page-width-saved', 'true');
|
||||
} catch (error) {
|
||||
pageUiState.pageWidthPreferences = previous;
|
||||
applyPageOptionsToShell();
|
||||
renderPageSettingsPopover();
|
||||
document.documentElement.setAttribute('data-mnote-page-width-error', error instanceof Error ? error.message : String(error));
|
||||
}
|
||||
}
|
||||
|
||||
function isPageSettingsOpen() {
|
||||
var popover = document.querySelector('[data-testid="wolai-page-settings-popover"]');
|
||||
return popover instanceof HTMLElement && !popover.hidden;
|
||||
@@ -684,6 +913,10 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
else openPageSettingsPopover();
|
||||
}
|
||||
|
||||
window.addEventListener('mnote:active-resource-tab-changed', function() {
|
||||
applyPageOptionsToShell();
|
||||
});
|
||||
|
||||
return {
|
||||
applyPageOptionsToShell,
|
||||
closePageHistoryDrawer,
|
||||
@@ -700,11 +933,13 @@ export function createSidebarPageSettingsRuntime(context) {
|
||||
openPageShareDialog,
|
||||
pageOptionIsSupported,
|
||||
persistPageOptionsPatch,
|
||||
persistPageWidthPreference,
|
||||
recordPageHistorySnapshot,
|
||||
renderPageSettingsPopover,
|
||||
setActivePageSettingsTab,
|
||||
togglePageSettingsPopover,
|
||||
updatePageSettingsTriggerState,
|
||||
writeGlobalShowHeadingNumbers,
|
||||
loadPageWidthPreferences,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user