mirror of
https://github.com/siyuan-note/siyuan.git
synced 2026-02-20 14:04:06 +01:00
Merge remote-tracking branch 'origin/dev' into dev
This commit is contained in:
commit
249f836d9e
54 changed files with 3772 additions and 430 deletions
|
|
@ -310,10 +310,12 @@ export const editor = {
|
|||
if (fontFamilyElement.tagName === "SELECT") {
|
||||
let fontFamilyHTML = `<option value="">${window.siyuan.languages.default}</option>`;
|
||||
fetchPost("/api/system/getSysFonts", {}, (response) => {
|
||||
response.data.forEach((item: string) => {
|
||||
fontFamilyHTML += `<option value="${item}"${window.siyuan.config.editor.fontFamily === item ? " selected" : ""}>${item}</option>`;
|
||||
});
|
||||
fontFamilyElement.innerHTML = fontFamilyHTML;
|
||||
if (response.code === 0) {
|
||||
response.data.forEach((item: string) => {
|
||||
fontFamilyHTML += `<option value="${item}"${window.siyuan.config.editor.fontFamily === item ? " selected" : ""}>${item}</option>`;
|
||||
});
|
||||
fontFamilyElement.innerHTML = fontFamilyHTML;
|
||||
}
|
||||
});
|
||||
}
|
||||
editor.element.querySelector("#clearHistory").addEventListener("click", () => {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import {query} from "./query";
|
|||
import {Dialog} from "../dialog";
|
||||
import {ai} from "./ai";
|
||||
import {flashcard} from "./flashcard";
|
||||
import {publish} from "./publish";
|
||||
import {App} from "../index";
|
||||
import {isHuawei} from "../protyle/util/compatibility";
|
||||
import {Constants} from "../constants";
|
||||
|
|
@ -79,6 +80,11 @@ export const genItemPanel = (type: string, containerElement: Element, app: App)
|
|||
query.element = containerElement;
|
||||
query.bindEvent();
|
||||
break;
|
||||
case "publish":
|
||||
containerElement.innerHTML = publish.genHTML();
|
||||
publish.element = containerElement;
|
||||
publish.bindEvent();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -115,6 +121,7 @@ export const openSetting = (app: App) => {
|
|||
<li data-name="keymap" class="b3-list-item"><svg class="b3-list-item__graphic"><use xlink:href="#iconKeymap"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.keymap}</span></li>
|
||||
<li data-name="account" class="b3-list-item"><svg class="b3-list-item__graphic"><use xlink:href="#iconAccount"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.account}</span></li>
|
||||
<li data-name="repos" class="b3-list-item"><svg class="b3-list-item__graphic"><use xlink:href="#iconCloud"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.cloud}</span></li>
|
||||
<li data-name="publish" class="b3-list-item"><svg class="b3-list-item__graphic"><use xlink:href="#iconLanguage"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.publish}</span></li>
|
||||
<li data-name="about" class="b3-list-item"><svg class="b3-list-item__graphic"><use xlink:href="#iconInfo"></use></svg><span class="b3-list-item__text">${window.siyuan.languages.about}</span></li>
|
||||
</ul>
|
||||
<div class="config__tab-wrap">
|
||||
|
|
@ -131,6 +138,7 @@ export const openSetting = (app: App) => {
|
|||
<div class="config__tab-container fn__none" style="overflow: scroll" data-name="keymap"></div>
|
||||
<div class="config__tab-container config__tab-container--full fn__none" data-name="account"></div>
|
||||
<div class="config__tab-container fn__none" data-name="repos"></div>
|
||||
<div class="config__tab-container fn__none" data-name="publish"></div>
|
||||
<div class="config__tab-container fn__none" data-name="about"></div>
|
||||
<div class="fn__hr--b"></div>
|
||||
</div>
|
||||
|
|
|
|||
213
app/src/config/publish.ts
Normal file
213
app/src/config/publish.ts
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
import {hasClosestByMatchTag} from "../protyle/util/hasClosest";
|
||||
import {fetchPost} from "../util/fetch";
|
||||
|
||||
export const publish = {
|
||||
element: undefined as Element,
|
||||
genHTML: () => {
|
||||
return `
|
||||
<!-- publish service -->
|
||||
<label class="fn__flex b3-label">
|
||||
<div class="fn__flex-1">
|
||||
${window.siyuan.languages.publishService}
|
||||
<div class="b3-label__text">${window.siyuan.languages.publishServiceTip}</div>
|
||||
</div>
|
||||
<span class="fn__space"></span>
|
||||
<input class="b3-switch fn__flex-center" id="publishEnable" type="checkbox"${window.siyuan.config.publish.enable ? " checked" : ""}/>
|
||||
</label>
|
||||
|
||||
<div class="b3-label">
|
||||
<!-- publish service port -->
|
||||
<div class="fn__flex">
|
||||
<div class="fn__flex-1">
|
||||
${window.siyuan.languages.publishServicePort}
|
||||
<div class="b3-label__text">${window.siyuan.languages.publishServicePortTip}</div>
|
||||
</div>
|
||||
<span class="fn__space"></span>
|
||||
<input class="b3-text-field fn__flex-center fn__size200" id="publishPort" type="number" min="0" max="65535" value="${window.siyuan.config.publish.port}">
|
||||
</div>
|
||||
|
||||
<!-- publish service address list -->
|
||||
<div class="b3-label">
|
||||
<div class="fn__flex config__item">
|
||||
<div class="fn__flex-1">
|
||||
${window.siyuan.languages.publishServiceAddresses}
|
||||
<div class="b3-label__text">${window.siyuan.languages.publishServiceAddressesTip}</div>
|
||||
</div>
|
||||
<div class="fn__space"></div>
|
||||
</div>
|
||||
<div class="b3-label" id="publishAddresses">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="b3-label">
|
||||
<!-- publish service auth -->
|
||||
<label class="fn__flex">
|
||||
<div class="fn__flex-1">
|
||||
${window.siyuan.languages.publishServiceAuth}
|
||||
<div class="b3-label__text">${window.siyuan.languages.publishServiceAuthTip}</div>
|
||||
</div>
|
||||
<span class="fn__space"></span>
|
||||
<input class="b3-switch fn__flex-center" id="publishAuthEnable" type="checkbox"${window.siyuan.config.publish.auth.enable ? " checked" : ""}/>
|
||||
</label>
|
||||
|
||||
<!-- publish service auth accounts -->
|
||||
<div class="b3-label">
|
||||
<div class="fn__flex config__item">
|
||||
<div class="fn__flex-1">
|
||||
${window.siyuan.languages.publishServiceAuthAccounts}
|
||||
<div class="b3-label__text">${window.siyuan.languages.publishServiceAuthAccountsTip}</div>
|
||||
</div>
|
||||
<div class="fn__space"></div>
|
||||
<button class="b3-button b3-button--outline fn__size200 fn__flex-center" id="publishAuthAccountAdd">
|
||||
<svg><use xlink:href="#iconAdd"></use></svg>${window.siyuan.languages.publishServiceAuthAccountAdd}
|
||||
</button>
|
||||
</div>
|
||||
<div class="fn__flex" id="publishAuthAccounts">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
bindEvent: () => {
|
||||
const publishAuthAccountAdd = publish.element.querySelector<HTMLButtonElement>("#publishAuthAccountAdd");
|
||||
|
||||
/* add account */
|
||||
publishAuthAccountAdd.addEventListener("click", () => {
|
||||
window.siyuan.config.publish.auth.accounts.push({
|
||||
username: "",
|
||||
password: "",
|
||||
memo: "",
|
||||
});
|
||||
publish._renderPublishAuthAccounts(publish.element);
|
||||
});
|
||||
|
||||
/* input change */
|
||||
publish.element.querySelectorAll("input").forEach(item => {
|
||||
item.addEventListener("change", () => {
|
||||
publish._savePublish();
|
||||
});
|
||||
});
|
||||
|
||||
publish._refreshPublish();
|
||||
},
|
||||
_refreshPublish: () => {
|
||||
fetchPost("/api/setting/getPublish", {}, publish._updatePublishConfig.bind(null, true));
|
||||
},
|
||||
_savePublish: (reloadAccounts = true) => {
|
||||
const publishEnable = publish.element.querySelector<HTMLInputElement>("#publishEnable");
|
||||
const publishPort = publish.element.querySelector<HTMLInputElement>("#publishPort");
|
||||
const publishAuthEnable = publish.element.querySelector<HTMLInputElement>("#publishAuthEnable");
|
||||
|
||||
fetchPost("/api/setting/setPublish", {
|
||||
enable: publishEnable.checked,
|
||||
port: publishPort.valueAsNumber,
|
||||
auth: {
|
||||
enable: publishAuthEnable.checked,
|
||||
accounts: window.siyuan.config.publish.auth.accounts,
|
||||
} as Config.IPublishAuth,
|
||||
} as Config.IPublish, publish._updatePublishConfig.bind(null, reloadAccounts));
|
||||
},
|
||||
_updatePublishConfig: (
|
||||
reloadAccounts: boolean,
|
||||
response: IWebSocketData,
|
||||
) => {
|
||||
if (response.code === 0) {
|
||||
window.siyuan.config.publish = response.data.publish;
|
||||
|
||||
reloadAccounts && publish._renderPublishAuthAccounts(publish.element);
|
||||
publish._renderPublishAddressList(publish.element, response.data.port);
|
||||
} else {
|
||||
publish._renderPublishAddressList(publish.element, 0);
|
||||
}
|
||||
},
|
||||
_renderPublishAuthAccounts: (
|
||||
element: Element,
|
||||
accounts: Config.IPublishAuthAccount[] = window.siyuan.config.publish.auth.accounts,
|
||||
) => {
|
||||
const publishAuthAccounts = element.querySelector<HTMLDivElement>("#publishAuthAccounts");
|
||||
publishAuthAccounts.innerHTML = `<ul class="fn__flex-1" style="margin-top: 16px">${
|
||||
accounts
|
||||
.map((account, index) => `
|
||||
<li class="b3-label--inner fn__flex" data-index="${index}">
|
||||
<input class="b3-text-field fn__block" data-name="username" value="${account.username}" placeholder="${window.siyuan.languages.userName}">
|
||||
<span class="fn__space"></span>
|
||||
<div class="b3-form__icona fn__block">
|
||||
<input class="b3-text-field fn__block b3-form__icona-input" type="password" data-name="password" value="${account.password}" placeholder="${window.siyuan.languages.password}">
|
||||
<svg class="b3-form__icona-icon" data-action="togglePassword"><use xlink:href="#iconEye"></use></svg>
|
||||
</div>
|
||||
<span class="fn__space"></span>
|
||||
<input class="b3-text-field fn__block" data-name="memo" value="${account.memo}" placeholder="${window.siyuan.languages.memo}">
|
||||
<span class="fn__space"></span>
|
||||
<span data-action="remove" class="block__icon block__icon--show">
|
||||
<svg><use xlink:href="#iconTrashcan"></use></svg>
|
||||
</span>
|
||||
</li>`
|
||||
)
|
||||
.join("")
|
||||
}</ul>`;
|
||||
|
||||
/* account field changed */
|
||||
publishAuthAccounts
|
||||
.querySelectorAll("input")
|
||||
.forEach(input => {
|
||||
input.addEventListener("change", () => {
|
||||
const li = hasClosestByMatchTag(input, "LI");
|
||||
if (li) {
|
||||
const index = parseInt(li.dataset.index);
|
||||
const name = input.dataset.name as keyof Config.IPublishAuthAccount;
|
||||
if (name in window.siyuan.config.publish.auth.accounts[index]) {
|
||||
window.siyuan.config.publish.auth.accounts[index][name] = input.value;
|
||||
publish._savePublish(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/* delete account */
|
||||
publishAuthAccounts
|
||||
.querySelectorAll('.block__icon[data-action="remove"]')
|
||||
.forEach(remove => {
|
||||
remove.addEventListener("click", () => {
|
||||
const li = hasClosestByMatchTag(remove, "LI");
|
||||
if (li) {
|
||||
const index = parseInt(li.dataset.index);
|
||||
window.siyuan.config.publish.auth.accounts.splice(index, 1);
|
||||
publish._savePublish();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/* Toggle the password display status */
|
||||
publishAuthAccounts
|
||||
.querySelectorAll('.b3-form__icona-icon[data-action="togglePassword"]')
|
||||
.forEach(togglePassword => {
|
||||
togglePassword.addEventListener("click", () => {
|
||||
const isEye = togglePassword.firstElementChild.getAttribute("xlink:href") === "#iconEye";
|
||||
togglePassword.firstElementChild.setAttribute("xlink:href", isEye ? "#iconEyeoff" : "#iconEye");
|
||||
togglePassword.previousElementSibling.setAttribute("type", isEye ? "text" : "password");
|
||||
});
|
||||
});
|
||||
},
|
||||
_renderPublishAddressList: (
|
||||
element: Element,
|
||||
port: number,
|
||||
) => {
|
||||
const publishAddresses = element.querySelector<HTMLDivElement>("#publishAddresses");
|
||||
if (port === 0) {
|
||||
publishAddresses.innerText = window.siyuan.languages.publishServiceNotStarted;
|
||||
} else {
|
||||
publishAddresses.innerHTML = `<ul class="b3-list b3-list--background fn__flex-1">${
|
||||
window.siyuan.config.localIPs
|
||||
.filter(ip => !(ip.startsWith("[") && ip.endsWith("]")))
|
||||
.map(ip => `<li><code class="fn__code">${ip}:${port}</code></li>`)
|
||||
.join("")
|
||||
}${
|
||||
window.siyuan.config.localIPs
|
||||
.filter(ip => (ip.startsWith("[") && ip.endsWith("]")))
|
||||
.map(ip => `<li><code class="fn__code">${ip}:${port}</code></li>`)
|
||||
.join("")
|
||||
}</ul>`;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
@ -169,13 +169,17 @@ const dockToJSON = (dock: Dock) => {
|
|||
};
|
||||
|
||||
export const resetLayout = () => {
|
||||
fetchPost("/api/system/setUILayout", {layout: {}}, () => {
|
||||
window.siyuan.storage[Constants.LOCAL_FILEPOSITION] = {};
|
||||
setStorageVal(Constants.LOCAL_FILEPOSITION, window.siyuan.storage[Constants.LOCAL_FILEPOSITION]);
|
||||
window.siyuan.storage[Constants.LOCAL_DIALOGPOSITION] = {};
|
||||
setStorageVal(Constants.LOCAL_DIALOGPOSITION, window.siyuan.storage[Constants.LOCAL_DIALOGPOSITION]);
|
||||
if (window.siyuan.config.readonly) {
|
||||
window.location.reload();
|
||||
});
|
||||
} else {
|
||||
fetchPost("/api/system/setUILayout", {layout: {}}, () => {
|
||||
window.siyuan.storage[Constants.LOCAL_FILEPOSITION] = {};
|
||||
setStorageVal(Constants.LOCAL_FILEPOSITION, window.siyuan.storage[Constants.LOCAL_FILEPOSITION]);
|
||||
window.siyuan.storage[Constants.LOCAL_DIALOGPOSITION] = {};
|
||||
setStorageVal(Constants.LOCAL_DIALOGPOSITION, window.siyuan.storage[Constants.LOCAL_DIALOGPOSITION]);
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let saveCount = 0;
|
||||
|
|
@ -211,10 +215,12 @@ export const saveLayout = () => {
|
|||
if (isWindow()) {
|
||||
sessionStorage.setItem("layout", JSON.stringify(layoutJSON));
|
||||
} else {
|
||||
fetchPost("/api/system/setUILayout", {
|
||||
layout: layoutJSON,
|
||||
errorExit: false // 后台不接受该参数,用于请求发生错误时退出程序
|
||||
});
|
||||
if (!window.siyuan.config.readonly) {
|
||||
fetchPost("/api/system/setUILayout", {
|
||||
layout: layoutJSON,
|
||||
errorExit: false // 后台不接受该参数,用于请求发生错误时退出程序
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -250,12 +256,17 @@ export const exportLayout = (options: {
|
|||
getAllModels().editor.forEach(item => {
|
||||
saveScroll(item.editor.protyle);
|
||||
});
|
||||
fetchPost("/api/system/setUILayout", {
|
||||
layout: layoutJSON,
|
||||
errorExit: options.errorExit // 后台不接受该参数,用于请求发生错误时退出程序
|
||||
}, () => {
|
||||
|
||||
if (window.siyuan.config.readonly) {
|
||||
options.cb();
|
||||
});
|
||||
} else {
|
||||
fetchPost("/api/system/setUILayout", {
|
||||
layout: layoutJSON,
|
||||
errorExit: options.errorExit // 后台不接受该参数,用于请求发生错误时退出程序
|
||||
}, () => {
|
||||
options.cb();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const getAllLayout = () => {
|
||||
|
|
|
|||
|
|
@ -507,8 +507,9 @@ export const exportMd = (id: string) => {
|
|||
});
|
||||
});
|
||||
return;
|
||||
} else if (response.code === 0) {
|
||||
showMessage(window.siyuan.languages.exportTplSucc);
|
||||
}
|
||||
showMessage(window.siyuan.languages.exportTplSucc);
|
||||
});
|
||||
dialog.destroy();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -340,9 +340,13 @@ export const workspaceMenu = (app: App, rect: DOMRect) => {
|
|||
window.siyuan.menus.menu.remove();
|
||||
return;
|
||||
}
|
||||
fetchPost("/api/system/setUILayout", {layout: item.layout}, () => {
|
||||
if (window.siyuan.config.readonly) {
|
||||
window.location.reload();
|
||||
});
|
||||
} else {
|
||||
fetchPost("/api/system/setUILayout", {layout: item.layout}, () => {
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -287,6 +287,10 @@ export const getLocalStorage = (cb: () => void) => {
|
|||
};
|
||||
|
||||
export const setStorageVal = (key: string, val: any) => {
|
||||
if (window.siyuan.config.readonly) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetchPost("/api/storage/setLocalStorageVal", {
|
||||
app: Constants.SIYUAN_APPID,
|
||||
key,
|
||||
|
|
|
|||
55
app/src/types/config.d.ts
vendored
55
app/src/types/config.d.ts
vendored
|
|
@ -63,6 +63,11 @@ declare namespace Config {
|
|||
* Whether to open the user guide after startup
|
||||
*/
|
||||
openHelp: boolean;
|
||||
/**
|
||||
* Publishing service
|
||||
* 发布服务
|
||||
*/
|
||||
publish: IPublish;
|
||||
/**
|
||||
* Whether it is running in read-only mode
|
||||
* 全局只读
|
||||
|
|
@ -1030,6 +1035,56 @@ declare namespace Config {
|
|||
*/
|
||||
export type TLogLevel = "off" | "trace" | "debug" | "info" | "warn" | "error" | "fatal";
|
||||
|
||||
/**
|
||||
* Publishing service
|
||||
*/
|
||||
export interface IPublish {
|
||||
/**
|
||||
* Whether to open the publishing service
|
||||
*/
|
||||
enable: boolean;
|
||||
/**
|
||||
* The basic authentication settings of publishing service
|
||||
*/
|
||||
auth: IPublishAuth;
|
||||
/**
|
||||
* Port on which the publishing service listens
|
||||
*/
|
||||
port: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishing service authentication settings
|
||||
*/
|
||||
export interface IPublishAuth {
|
||||
/**
|
||||
* Whether to enable basic authentication for publishing services
|
||||
*/
|
||||
enable: boolean;
|
||||
/**
|
||||
* List of basic verified accounts
|
||||
*/
|
||||
accounts: IPublishAuthAccount[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic authentication account
|
||||
*/
|
||||
export interface IPublishAuthAccount {
|
||||
/**
|
||||
* Account username
|
||||
*/
|
||||
username: string;
|
||||
/**
|
||||
* Account password
|
||||
*/
|
||||
password: string;
|
||||
/**
|
||||
* The memo text of the account
|
||||
*/
|
||||
memo: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Snapshot repository related configuration
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -150,8 +150,10 @@ export const initAssets = () => {
|
|||
return;
|
||||
}
|
||||
}
|
||||
window.siyuan.config.appearance = response.data.appearance;
|
||||
loadAssets(response.data.appearance);
|
||||
if (response.code === 0) {
|
||||
window.siyuan.config.appearance = response.data.appearance;
|
||||
loadAssets(response.data.appearance);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -32,18 +32,20 @@ export const fetchPost = (url: string, data?: any, cb?: (response: IWebSocketDat
|
|||
init.headers = headers;
|
||||
}
|
||||
fetch(url, init).then((response) => {
|
||||
if (response.status === 404) {
|
||||
return {
|
||||
data: null,
|
||||
msg: response.statusText,
|
||||
code: response.status,
|
||||
};
|
||||
} else {
|
||||
if (response.headers.get("content-type")?.indexOf("application/json") > -1) {
|
||||
return response.json();
|
||||
} else {
|
||||
return response.text();
|
||||
}
|
||||
switch (response.status) {
|
||||
case 403:
|
||||
case 404:
|
||||
return {
|
||||
data: null,
|
||||
msg: response.statusText,
|
||||
code: response.status,
|
||||
};
|
||||
default:
|
||||
if (response.headers.get("content-type")?.indexOf("application/json") > -1) {
|
||||
return response.json();
|
||||
} else {
|
||||
return response.text();
|
||||
}
|
||||
}
|
||||
}).then((response: IWebSocketData) => {
|
||||
if (typeof response === "string") {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue