Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
Vanessa 2024-06-12 22:32:24 +08:00
commit 249f836d9e
54 changed files with 3772 additions and 430 deletions

View file

@ -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", () => {

View file

@ -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
View 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>`;
}
},
}

View file

@ -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 = () => {

View file

@ -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();
});

View file

@ -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();
});
}
});
}
});

View file

@ -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,

View file

@ -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
*/

View file

@ -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);
}
});
});
};

View file

@ -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") {