artalk 2.8.1 → 2.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/Artalk.mjs CHANGED
@@ -481,6 +481,21 @@ let Api$1 = class Api extends HttpClient {
481
481
  path: `/conf`,
482
482
  method: "GET",
483
483
  format: "json"
484
+ }, params)),
485
+ /**
486
+ * @description Get Domain Info
487
+ *
488
+ * @tags System
489
+ * @name GetDomain
490
+ * @summary Get Domain Info
491
+ * @request GET:/conf/domain
492
+ * @response `200` `HandlerResponseConfDomain` OK
493
+ */
494
+ getDomain: (query, params = {}) => this.request(__spreadValues({
495
+ path: `/conf/domain`,
496
+ method: "GET",
497
+ query,
498
+ format: "json"
484
499
  }, params))
485
500
  });
486
501
  __publicField(this, "notifies", {
@@ -1049,7 +1064,7 @@ let Api$1 = class Api extends HttpClient {
1049
1064
  /**
1050
1065
  * @description Get user info to prepare for login or check current user status
1051
1066
  *
1052
- * @tags Account
1067
+ * @tags Auth
1053
1068
  * @name GetUser
1054
1069
  * @summary Get User Info
1055
1070
  * @request GET:/user
@@ -1070,23 +1085,23 @@ let Api$1 = class Api extends HttpClient {
1070
1085
  /**
1071
1086
  * @description Login user by name or email
1072
1087
  *
1073
- * @tags Account
1088
+ * @tags Auth
1074
1089
  * @name Login
1075
1090
  * @summary Get Access Token
1076
1091
  * @request POST:/user/access_token
1077
1092
  * @response `200` `HandlerResponseUserLogin` OK
1078
1093
  * @response `400` `(HandlerMap & {
1079
- " data"?: {
1094
+ data?: {
1080
1095
  need_name_select?: (string)[],
1081
1096
 
1082
1097
  },
1083
1098
  msg?: string,
1084
1099
 
1085
1100
  })` Multiple users with the same email address are matched
1086
- * @response `403` `(HandlerMap & {
1101
+ * @response `401` `(HandlerMap & {
1087
1102
  msg?: string,
1088
1103
 
1089
- })` Forbidden
1104
+ })` Unauthorized
1090
1105
  * @response `500` `(HandlerMap & {
1091
1106
  msg?: string,
1092
1107
 
@@ -1102,7 +1117,7 @@ let Api$1 = class Api extends HttpClient {
1102
1117
  /**
1103
1118
  * @description Get user login status by header Authorization
1104
1119
  *
1105
- * @tags Account
1120
+ * @tags Auth
1106
1121
  * @name GetUserStatus
1107
1122
  * @summary Get Login Status
1108
1123
  * @request GET:/user/status
@@ -1714,16 +1729,16 @@ const Fetch$1 = (opts, input, init2) => __async(void 0, null, function* () {
1714
1729
  if (!resp.ok) {
1715
1730
  const json = (yield resp.json().catch(() => {
1716
1731
  })) || {};
1717
- if (json.need_captcha) {
1718
- opts.onNeedCheckCaptcha && (yield opts.onNeedCheckCaptcha({
1719
- data: { imgData: json.img_data, iframe: json.iframe }
1720
- }));
1732
+ let retry = false;
1733
+ opts.handlers && (yield opts.handlers.get().reduce((promise, item) => __async(void 0, null, function* () {
1734
+ yield promise;
1735
+ if (json[item.action] === true) {
1736
+ yield item.handler(json);
1737
+ retry = true;
1738
+ }
1739
+ }), Promise.resolve()));
1740
+ if (retry)
1721
1741
  return Fetch$1(opts, input, init2);
1722
- }
1723
- if (json.need_login) {
1724
- opts.onNeedCheckAdmin && (yield opts.onNeedCheckAdmin({}));
1725
- return Fetch$1(opts, input, init2);
1726
- }
1727
1742
  throw createError(resp.status, json);
1728
1743
  }
1729
1744
  return resp;
@@ -1744,6 +1759,15 @@ function createError(code, data) {
1744
1759
  console.error(err);
1745
1760
  return err;
1746
1761
  }
1762
+ function createApiHandlers() {
1763
+ const handlers = [];
1764
+ return {
1765
+ add: (action, handler) => {
1766
+ handlers.push({ action, handler });
1767
+ },
1768
+ get: () => handlers
1769
+ };
1770
+ }
1747
1771
  class Api2 extends Api$1 {
1748
1772
  constructor(opts) {
1749
1773
  super({
@@ -4493,7 +4517,7 @@ const en = {
4493
4517
  link: "Website",
4494
4518
  emoticon: "Emoji",
4495
4519
  preview: "Preview",
4496
- image: "Image",
4520
+ uploadImage: "Upload Image",
4497
4521
  uploadFail: "Upload Failed",
4498
4522
  commentFail: "Failed to comment",
4499
4523
  restoredMsg: "Content has been restored",
@@ -4545,6 +4569,7 @@ const en = {
4545
4569
  /* Sidebar */
4546
4570
  msgCenter: "Messages",
4547
4571
  ctrlCenter: "Admin",
4572
+ emailVerified: "Email has been verified",
4548
4573
  /* General */
4549
4574
  frontend: "Frontend",
4550
4575
  backend: "Backend",
@@ -4566,14 +4591,14 @@ const zhCN = {
4566
4591
  /* Editor */
4567
4592
  placeholder: "键入内容...",
4568
4593
  noComment: "「此时无声胜有声」",
4569
- send: "发送评论",
4570
- save: "保存评论",
4594
+ send: "发送",
4595
+ save: "保存",
4571
4596
  nick: "昵称",
4572
4597
  email: "邮箱",
4573
4598
  link: "网址",
4574
4599
  emoticon: "表情",
4575
4600
  preview: "预览",
4576
- image: "图片",
4601
+ uploadImage: "上传图片",
4577
4602
  uploadFail: "上传失败",
4578
4603
  commentFail: "评论失败",
4579
4604
  restoredMsg: "内容已自动恢复",
@@ -4625,6 +4650,7 @@ const zhCN = {
4625
4650
  /* Sidebar */
4626
4651
  msgCenter: "通知中心",
4627
4652
  ctrlCenter: "控制中心",
4653
+ emailVerified: "邮箱已验证",
4628
4654
  /* General */
4629
4655
  frontend: "前端",
4630
4656
  backend: "后端",
@@ -4975,21 +5001,15 @@ function convertApiOptions(conf, ctx) {
4975
5001
  name: ctx == null ? void 0 : ctx.get("user").getData().nick,
4976
5002
  email: ctx == null ? void 0 : ctx.get("user").getData().email
4977
5003
  } : void 0,
4978
- onNeedCheckAdmin: (payload) => {
4979
- if (!ctx)
4980
- throw new Error("`ctx` is required when `onNeedCheckAdmin` is called.");
4981
- return ctx.checkAdmin({});
4982
- },
4983
- onNeedCheckCaptcha: (payload) => {
4984
- if (!ctx)
4985
- throw new Error("`ctx` is required when `onNeedCheckCaptcha` is called.");
4986
- return ctx.checkCaptcha({
4987
- imgData: payload.data.imgData,
4988
- iframe: payload.data.iframe
4989
- });
4990
- }
5004
+ handlers: ctx == null ? void 0 : ctx.getApiHandlers()
4991
5005
  };
4992
5006
  }
5007
+ function createNewApiHandlers(ctx) {
5008
+ const h = createApiHandlers();
5009
+ h.add("need_captcha", (res) => ctx.checkCaptcha(res));
5010
+ h.add("need_login", () => ctx.checkAdmin({}));
5011
+ return h;
5012
+ }
4993
5013
  function watchConf(ctx, keys, effect) {
4994
5014
  const deepEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b);
4995
5015
  const val = () => {
@@ -5021,6 +5041,7 @@ class Context {
5021
5041
  /* Event Manager */
5022
5042
  __publicField(this, "events", new EventManager());
5023
5043
  __publicField(this, "mounted", false);
5044
+ __publicField(this, "apiHandlers", null);
5024
5045
  __publicField(this, "getCommentList", this.getCommentNodes);
5025
5046
  __publicField(this, "getCommentDataList", this.getComments);
5026
5047
  this.conf = conf;
@@ -5041,6 +5062,11 @@ class Context {
5041
5062
  getApi() {
5042
5063
  return new Api2(convertApiOptions(this.conf, this));
5043
5064
  }
5065
+ getApiHandlers() {
5066
+ if (!this.apiHandlers)
5067
+ this.apiHandlers = createNewApiHandlers(this);
5068
+ return this.apiHandlers;
5069
+ }
5044
5070
  getData() {
5045
5071
  return this.data;
5046
5072
  }
@@ -5433,7 +5459,7 @@ class CheckerLauncher {
5433
5459
  constructor(opts) {
5434
5460
  __publicField(this, "checkCaptcha", wrapPromise((p) => {
5435
5461
  this.fire(CaptchaChecker, p, (ctx) => {
5436
- ctx.set("img_data", p.imgData);
5462
+ ctx.set("img_data", p.img_data);
5437
5463
  ctx.set("iframe", p.iframe);
5438
5464
  });
5439
5465
  }));
@@ -5511,7 +5537,7 @@ class CheckerLauncher {
5511
5537
  if (payload.onSuccess)
5512
5538
  payload.onSuccess();
5513
5539
  }).catch((err) => {
5514
- btnTextSet(String(err.msg || String(err)));
5540
+ btnTextSet(String(err.message || String(err)));
5515
5541
  if (checker.onError)
5516
5542
  checker.onError(checkerCtx, err, inputVal, formEl);
5517
5543
  const tf = setTimeout(() => btnTextRestore(), 3e3);
@@ -5549,7 +5575,7 @@ class Component {
5549
5575
  return this.$el;
5550
5576
  }
5551
5577
  }
5552
- const EditorHTML = '<div class="atk-main-editor">\n <div class="atk-header">\n <input name="nick" class="atk-nick" type="text" required="required">\n <input name="email" class="atk-email" type="email" required="required">\n <input name="link" class="atk-link" type="url">\n </div>\n <div class="atk-textarea-wrap">\n <textarea class="atk-textarea"></textarea>\n </div>\n <div class="atk-plug-panel-wrap" style="display: none;"></div>\n <div class="atk-bottom">\n <div class="atk-item atk-plug-btn-wrap"></div>\n <div class="atk-item">\n <button type="button" class="atk-send-btn"></button>\n </div>\n </div>\n <div class="atk-notify-wrap"></div>\n</div>\n';
5578
+ const EditorHTML = '<div class="atk-main-editor">\n <div class="atk-header">\n <input name="nick" class="atk-nick" type="text" required="required">\n <input name="email" class="atk-email" type="email" required="required">\n <input name="link" class="atk-link" type="url">\n </div>\n <div class="atk-textarea-wrap">\n <textarea class="atk-textarea"></textarea>\n </div>\n <div class="atk-plug-panel-wrap" style="display: none;"></div>\n <div class="atk-bottom">\n <div class="atk-item atk-bottom-left">\n <span class="atk-state-wrap"></span>\n <span class="atk-plug-btn-wrap"></span>\n </div>\n <div class="atk-item">\n <button type="button" class="atk-send-btn"></button>\n </div>\n </div>\n <div class="atk-notify-wrap"></div>\n</div>\n';
5553
5579
  const Sel = {
5554
5580
  $header: ".atk-header",
5555
5581
  $nick: '.atk-header [name="nick"]',
@@ -5560,6 +5586,8 @@ const Sel = {
5560
5586
  $bottom: ".atk-bottom",
5561
5587
  $submitBtn: ".atk-send-btn",
5562
5588
  $notifyWrap: ".atk-notify-wrap",
5589
+ $bottomLeft: ".atk-bottom-left",
5590
+ $stateWrap: ".atk-state-wrap",
5563
5591
  $plugBtnWrap: ".atk-plug-btn-wrap",
5564
5592
  $plugPanelWrap: ".atk-plug-panel-wrap"
5565
5593
  };
@@ -5757,10 +5785,15 @@ class Editor extends Component {
5757
5785
  hideLoading(this.ui.$el);
5758
5786
  }
5759
5787
  submit() {
5760
- this.ctx.trigger("editor-submit");
5788
+ const next = () => this.ctx.trigger("editor-submit");
5789
+ if (this.ctx.conf.beforeSubmit) {
5790
+ this.ctx.conf.beforeSubmit(this, next);
5791
+ } else {
5792
+ next();
5793
+ }
5761
5794
  }
5762
5795
  }
5763
- const SidebarHTML = '<div class="atk-sidebar-layer">\n <div class="atk-sidebar-inner">\n <div class="atk-sidebar-header">\n <div class="atk-sidebar-close"><i class="atk-icon atk-icon-close"></i></div>\n </div>\n <div class="atk-sidebar-iframe-wrap"></div>\n </div>\n</div>\n';
5796
+ const SidebarHTML = '<div class="atk-sidebar-layer">\n <div class="atk-sidebar-inner">\n <div class="atk-sidebar-header">\n <div class="atk-sidebar-close"><i class="atk-icon atk-icon-close-slim"></i></div>\n </div>\n <div class="atk-sidebar-iframe-wrap"></div>\n </div>\n</div>\n';
5764
5797
  class SidebarLayer extends Component {
5765
5798
  constructor(ctx) {
5766
5799
  super(ctx);
@@ -6315,6 +6348,9 @@ function renderVerifyBadge(ctx) {
6315
6348
  $badge.innerText = badgeText.replace("管理员", t("admin"));
6316
6349
  $badge.style.backgroundColor = badgeColor || "";
6317
6350
  ctx.$headerBadgeWrap.append($badge);
6351
+ } else if (ctx.data.is_verified) {
6352
+ const $verifiedBadge = createElement(`<span class="atk-verified-icon" title="${t("emailVerified")}"></span>`);
6353
+ ctx.$headerBadgeWrap.append($verifiedBadge);
6318
6354
  }
6319
6355
  if (ctx.data.is_pinned) {
6320
6356
  const $pinnedBadge = createElement(`<span class="atk-pinned-badge">${t("pin")}</span>`);
@@ -7569,18 +7605,8 @@ class LayerWrap {
7569
7605
  if (this.items.map((l) => l.getEl()).filter((e) => e !== $el && e.isConnected && e.style.display !== "none").length > 0) {
7570
7606
  return;
7571
7607
  }
7572
- const onHide = () => {
7573
- this.$wrap.style.display = "none";
7574
- this.$wrap.classList.remove("atk-fade-out");
7575
- getScrollbarHelper().unlock();
7576
- this.$wrap.onanimationend = null;
7577
- };
7578
- this.$wrap.classList.add("atk-fade-out");
7579
- if (window.getComputedStyle(this.$wrap)["animation-name"] !== "none") {
7580
- this.$wrap.onanimationend = () => onHide();
7581
- } else {
7582
- onHide();
7583
- }
7608
+ this.$wrap.style.display = "none";
7609
+ getScrollbarHelper().unlock();
7584
7610
  }
7585
7611
  }
7586
7612
  class LayerManager {
@@ -7699,6 +7725,70 @@ const services = {
7699
7725
  return void 0;
7700
7726
  }
7701
7727
  };
7728
+ const PvCountWidget = (ctx) => {
7729
+ ctx.watchConf([
7730
+ "site",
7731
+ "pageKey",
7732
+ "pageTitle",
7733
+ "countEl",
7734
+ "pvEl"
7735
+ ], (conf) => {
7736
+ initCountWidget({
7737
+ getApi: () => ctx.getApi(),
7738
+ siteName: conf.site,
7739
+ pageKey: conf.pageKey,
7740
+ pageTitle: conf.pageTitle,
7741
+ countEl: conf.countEl,
7742
+ pvEl: conf.pvEl,
7743
+ pvAdd: typeof ctx.conf.pvAdd === "boolean" ? ctx.conf.pvAdd : true
7744
+ });
7745
+ });
7746
+ };
7747
+ function initCountWidget(opt) {
7748
+ return __async(this, null, function* () {
7749
+ if (opt.countEl && document.querySelector(opt.countEl)) {
7750
+ refreshStatCount(opt, { query: "page_comment", numEl: opt.countEl });
7751
+ }
7752
+ const initialData = opt.pvAdd && opt.pageKey ? {
7753
+ [opt.pageKey]: (yield opt.getApi().pages.logPv({
7754
+ page_key: opt.pageKey,
7755
+ page_title: opt.pageTitle,
7756
+ site_name: opt.siteName
7757
+ })).data.pv
7758
+ // pv+1 and get pv count
7759
+ } : void 0;
7760
+ if (opt.pvEl && document.querySelector(opt.pvEl)) {
7761
+ refreshStatCount(opt, {
7762
+ query: "page_pv",
7763
+ numEl: opt.pvEl,
7764
+ data: initialData
7765
+ });
7766
+ }
7767
+ });
7768
+ }
7769
+ function refreshStatCount(opt, args) {
7770
+ return __async(this, null, function* () {
7771
+ let data = args.data || {};
7772
+ let queryPageKeys = Array.from(document.querySelectorAll(args.numEl)).map((e) => e.getAttribute("data-page-key") || opt.pageKey).filter((k) => k && typeof data[k] !== "number");
7773
+ queryPageKeys = [...new Set(queryPageKeys)];
7774
+ if (queryPageKeys.length > 0) {
7775
+ const res = (yield opt.getApi().stats.getStats(args.query, {
7776
+ page_keys: queryPageKeys.join(","),
7777
+ site_name: opt.siteName
7778
+ })).data.data;
7779
+ data = __spreadValues(__spreadValues({}, data), res);
7780
+ }
7781
+ const defaultCount = opt.pageKey ? data[opt.pageKey] : 0;
7782
+ applyCountData(args.numEl, data, defaultCount);
7783
+ });
7784
+ }
7785
+ function applyCountData(selector, data, defaultCount) {
7786
+ document.querySelectorAll(selector).forEach((el) => {
7787
+ const pageKey = el.getAttribute("data-page-key");
7788
+ const count = Number(pageKey ? data[pageKey] : defaultCount);
7789
+ el.innerHTML = `${count}`;
7790
+ });
7791
+ }
7702
7792
  function showErrorDialog(opts) {
7703
7793
  const errEl = createElement(`<span><span class="error-message"></span><br/><br/></span>`);
7704
7794
  errEl.querySelector(".error-message").innerText = `${t("listLoadFailMsg")}
@@ -7715,58 +7805,6 @@ ${opts.errMsg}`;
7715
7805
  }
7716
7806
  setError(opts.$err, errEl);
7717
7807
  }
7718
- const ConfRemoter = (ctx) => {
7719
- ctx.on("created", () => {
7720
- if (ctx.conf.immediateFetch !== false)
7721
- ctx.trigger("conf-fetch");
7722
- });
7723
- ctx.on("conf-fetch", () => {
7724
- loadConf(ctx);
7725
- });
7726
- };
7727
- function loadConf(ctx) {
7728
- ctx.getApi().conf.conf().then((res) => {
7729
- var _a;
7730
- let conf = {
7731
- apiVersion: (_a = res.data.version) == null ? void 0 : _a.version
7732
- // version info
7733
- };
7734
- if (ctx.conf.useBackendConf) {
7735
- if (!res.data.frontend_conf)
7736
- throw new Error("The remote backend does not respond to the frontend conf, but `useBackendConf` conf is enabled");
7737
- conf = __spreadValues(__spreadValues({}, conf), handleConfFormServer(res.data.frontend_conf));
7738
- }
7739
- ctx.conf.remoteConfModifier && ctx.conf.remoteConfModifier(conf);
7740
- ctx.updateConf(conf);
7741
- }).catch((err) => {
7742
- var _a;
7743
- ctx.updateConf({});
7744
- let sidebarOpenView = "";
7745
- if ((_a = err.data) == null ? void 0 : _a.err_no_site) {
7746
- const viewLoadParam = { create_name: ctx.conf.site, create_urls: `${window.location.protocol}//${window.location.host}` };
7747
- sidebarOpenView = `sites|${JSON.stringify(viewLoadParam)}`;
7748
- }
7749
- showErrorDialog({
7750
- $err: ctx.get("list").$el,
7751
- errMsg: err.msg || String(err),
7752
- errData: err.data,
7753
- retryFn: () => loadConf(ctx),
7754
- onOpenSidebar: ctx.get("user").getData().isAdmin ? () => ctx.showSidebar({
7755
- view: sidebarOpenView
7756
- }) : void 0
7757
- // only show open sidebar button when user is admin
7758
- });
7759
- console.error(err);
7760
- throw err;
7761
- }).then(() => {
7762
- ctx.trigger("mounted");
7763
- }).then(() => {
7764
- if (ctx.conf.remoteConfModifier)
7765
- return;
7766
- ctx.fetch({ offset: 0 });
7767
- }).catch(() => {
7768
- });
7769
- }
7770
7808
  const Markdown = (ctx) => {
7771
7809
  initMarked();
7772
7810
  ctx.on("updated", (conf) => {
@@ -7922,7 +7960,7 @@ class Submit extends EditorPlug {
7922
7960
  this.defaultPreset.postSubmitAdd(nComment);
7923
7961
  } catch (err) {
7924
7962
  console.error(err);
7925
- this.kit.useEditor().showNotify(`${t("commentFail")}: ${err.msg || String(err)}`, "e");
7963
+ this.kit.useEditor().showNotify(`${t("commentFail")}: ${err.message || String(err)}`, "e");
7926
7964
  return;
7927
7965
  } finally {
7928
7966
  this.kit.useEditor().hideLoading();
@@ -7973,15 +8011,16 @@ class StateReply extends EditorPlug {
7973
8011
  }
7974
8012
  setReply(commentData) {
7975
8013
  const ui = this.kit.useUI();
7976
- if (!ui.$sendReply) {
7977
- ui.$sendReply = createElement(
7978
- `<div class="atk-send-reply">${t("reply")} <span class="atk-text"></span><span class="atk-cancel">×</span></div>`
8014
+ if (!ui.$sendReplyBtn) {
8015
+ const $btn = createElement(
8016
+ `<span class="atk-state-btn"><span class="atk-text-wrap">${t("reply")} <span class="atk-text"></span></span><span class="atk-cancel atk-icon-close atk-icon"></span></span>`
7979
8017
  );
7980
- ui.$sendReply.querySelector(".atk-text").innerText = `@${commentData.nick}`;
7981
- ui.$sendReply.addEventListener("click", () => {
8018
+ $btn.querySelector(".atk-text").innerText = `@${commentData.nick}`;
8019
+ $btn.addEventListener("click", () => {
7982
8020
  this.kit.useEditor().resetState();
7983
8021
  });
7984
- ui.$textareaWrap.append(ui.$sendReply);
8022
+ ui.$stateWrap.append($btn);
8023
+ ui.$sendReplyBtn = $btn;
7985
8024
  }
7986
8025
  this.comment = commentData;
7987
8026
  ui.$textarea.focus();
@@ -7990,9 +8029,9 @@ class StateReply extends EditorPlug {
7990
8029
  if (!this.comment)
7991
8030
  return;
7992
8031
  const ui = this.kit.useUI();
7993
- if (ui.$sendReply) {
7994
- ui.$sendReply.remove();
7995
- ui.$sendReply = void 0;
8032
+ if (ui.$sendReplyBtn) {
8033
+ ui.$sendReplyBtn.remove();
8034
+ ui.$sendReplyBtn = void 0;
7996
8035
  }
7997
8036
  this.comment = void 0;
7998
8037
  }
@@ -8039,12 +8078,12 @@ class StateEdit extends EditorPlug {
8039
8078
  const ui = this.kit.useUI();
8040
8079
  if (!ui.$editCancelBtn) {
8041
8080
  const $btn = createElement(
8042
- `<div class="atk-send-reply">${t("editCancel")} <span class="atk-cancel">×</span></div>`
8081
+ `<span class="atk-state-btn"><span class="atk-text-wrap">${t("editCancel")}</span><span class="atk-cancel atk-icon-close atk-icon"></span></span>`
8043
8082
  );
8044
8083
  $btn.onclick = () => {
8045
8084
  this.kit.useEditor().resetState();
8046
8085
  };
8047
- ui.$textareaWrap.append($btn);
8086
+ ui.$stateWrap.append($btn);
8048
8087
  ui.$editCancelBtn = $btn;
8049
8088
  }
8050
8089
  this.comment = comment;
@@ -8241,7 +8280,7 @@ class Emoticons extends EditorPlug {
8241
8280
  __publicField(this, "isImgLoaded", false);
8242
8281
  this.kit.useMounted(() => {
8243
8282
  this.usePanel(`<div class="atk-editor-plug-emoticons"></div>`);
8244
- this.useBtn(t("emoticon"));
8283
+ this.useBtn(`<i aria-label="${t("emoticon")}"><svg fill="currentColor" aria-hidden="true" height="14" viewBox="0 0 14 14" width="14"><path d="m4.26829 5.29294c0-.94317.45893-1.7074 1.02439-1.7074.56547 0 1.02439.76423 1.02439 1.7074s-.45892 1.7074-1.02439 1.7074c-.56546 0-1.02439-.76423-1.02439-1.7074zm4.43903 1.7074c.56546 0 1.02439-.76423 1.02439-1.7074s-.45893-1.7074-1.02439-1.7074c-.56547 0-1.02439.76423-1.02439 1.7074s.45892 1.7074 1.02439 1.7074zm-1.70732 2.73184c-1.51883 0-2.06312-1.52095-2.08361-1.58173l-1.29551.43231c.03414.10244.868 2.51604 3.3798 2.51604 2.51181 0 3.34502-2.41291 3.37982-2.51604l-1.29484-.43573c-.02254.06488-.56683 1.58583-2.08498 1.58583zm7-2.73252c0 3.86004-3.1401 7.00034-7 7.00034s-7-3.1396-7-6.99966c0-3.86009 3.1401-7.00034 7-7.00034s7 3.14025 7 7.00034zm-1.3659 0c0-3.10679-2.5275-5.63442-5.6341-5.63442-3.10663 0-5.63415 2.52832-5.63415 5.6351 0 3.10676 2.52752 5.63446 5.63415 5.63446 3.1066 0 5.6341-2.5277 5.6341-5.63446z"/></svg></i>`);
8245
8284
  });
8246
8285
  this.kit.useUnmounted(() => {
8247
8286
  });
@@ -8492,7 +8531,7 @@ class Upload extends EditorPlug {
8492
8531
  this.$imgUploadInput.type = "file";
8493
8532
  this.$imgUploadInput.style.display = "none";
8494
8533
  this.$imgUploadInput.accept = AllowImgExts.map((o) => `.${o}`).join(",");
8495
- const $btn = this.useBtn(`${t("image")}`);
8534
+ const $btn = this.useBtn(`<i aria-label="${t("uploadImage")}"><svg fill="currentColor" aria-hidden="true" height="14" viewBox="0 0 14 14" width="14"><path d="m0 1.94444c0-1.074107.870333-1.94444 1.94444-1.94444h10.11116c1.0741 0 1.9444.870333 1.9444 1.94444v10.11116c0 1.0741-.8703 1.9444-1.9444 1.9444h-10.11116c-1.074107 0-1.94444-.8703-1.94444-1.9444zm1.94444-.38888c-.21466 0-.38888.17422-.38888.38888v7.06689l2.33333-2.33333 2.33333 2.33333 3.88888-3.88889 2.3333 2.33334v-5.51134c0-.21466-.1742-.38888-.3888-.38888zm10.49996 8.09977-2.3333-2.33333-3.88888 3.8889-2.33333-2.33334-2.33333 2.33334v.8447c0 .2146.17422.3888.38888.3888h10.11116c.2146 0 .3888-.1742.3888-.3888zm-7.1944-6.54422c-.75133 0-1.36111.60978-1.36111 1.36111 0 .75134.60978 1.36111 1.36111 1.36111s1.36111-.60977 1.36111-1.36111c0-.75133-.60978-1.36111-1.36111-1.36111z"/></svg></i>`);
8496
8535
  $btn.after(this.$imgUploadInput);
8497
8536
  $btn.onclick = () => {
8498
8537
  const $input = this.$imgUploadInput;
@@ -8574,7 +8613,7 @@ class Upload extends EditorPlug {
8574
8613
  }
8575
8614
  } catch (err) {
8576
8615
  console.error(err);
8577
- this.kit.useEditor().showNotify(`${t("uploadFail")}: ${err.msg}`, "e");
8616
+ this.kit.useEditor().showNotify(`${t("uploadFail")}: ${err.message}`, "e");
8578
8617
  }
8579
8618
  if (!!resp && resp.public_url) {
8580
8619
  let imgURL = resp.public_url;
@@ -8596,10 +8635,7 @@ class Preview extends EditorPlug {
8596
8635
  __publicField(this, "isPlugPanelShow", false);
8597
8636
  this.kit.useMounted(() => {
8598
8637
  this.usePanel(`<div class="atk-editor-plug-preview"></div>`);
8599
- let btnText = t("preview");
8600
- if (getInstance())
8601
- btnText += ` <i title="Markdown is supported"><svg class="markdown" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M14.85 3H1.15C.52 3 0 3.52 0 4.15v7.69C0 12.48.52 13 1.15 13h13.69c.64 0 1.15-.52 1.15-1.15v-7.7C16 3.52 15.48 3 14.85 3zM9 11H7V8L5.5 9.92 4 8v3H2V5h2l1.5 2L7 5h2v6zm2.99.5L9.5 8H11V5h2v3h1.5l-2.51 3.5z"></path></svg></i>`;
8602
- this.useBtn(btnText);
8638
+ this.useBtn(`<i aria-label="${t("preview")}"><svg fill="currentColor" aria-hidden="true" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M14.85 3H1.15C.52 3 0 3.52 0 4.15v7.69C0 12.48.52 13 1.15 13h13.69c.64 0 1.15-.52 1.15-1.15v-7.7C16 3.52 15.48 3 14.85 3zM9 11H7V8L5.5 9.92 4 8v3H2V5h2l1.5 2L7 5h2v6zm2.99.5L9.5 8H11V5h2v3h1.5l-2.51 3.5z"></path></svg></i>`);
8603
8639
  });
8604
8640
  this.kit.useUnmounted(() => {
8605
8641
  });
@@ -8740,9 +8776,6 @@ class PlugManager {
8740
8776
  this.editor.getUI().$plugPanelWrap.innerHTML = "";
8741
8777
  this.editor.getUI().$plugPanelWrap.style.display = "none";
8742
8778
  this.editor.getUI().$plugBtnWrap.innerHTML = "";
8743
- this.editor.getUI().$el.addEventListener("click", (e) => {
8744
- e.stopPropagation();
8745
- });
8746
8779
  this.plugs.forEach((plug) => this.loadPluginItem(plug));
8747
8780
  }
8748
8781
  /** Load the plug btn and plug panel on editor ui */
@@ -8838,7 +8871,7 @@ function adminPageEditSave(ctx, page) {
8838
8871
  ctx.getApi().pages.updatePage(page.id, page).then(({ data }) => {
8839
8872
  ctx.getData().updatePage(data);
8840
8873
  }).catch((err) => {
8841
- ctx.editorShowNotify(`${t("editFail")}: ${err.msg || String(err)}`, "e");
8874
+ ctx.editorShowNotify(`${t("editFail")}: ${err.message || String(err)}`, "e");
8842
8875
  }).finally(() => {
8843
8876
  ctx.editorHideLoading();
8844
8877
  });
@@ -8984,7 +9017,7 @@ const GotoFocus = (ctx) => {
8984
9017
  comment.focus();
8985
9018
  }));
8986
9019
  };
8987
- const version = "2.8.1";
9020
+ const version = "2.8.3";
8988
9021
  const Copyright = (ctx) => {
8989
9022
  ctx.on("mounted", () => {
8990
9023
  const list2 = ctx.get("list");
@@ -9268,70 +9301,6 @@ const Notifies = (ctx) => {
9268
9301
  });
9269
9302
  });
9270
9303
  };
9271
- const PvCountWidget = (ctx) => {
9272
- ctx.watchConf([
9273
- "site",
9274
- "pageKey",
9275
- "pageTitle",
9276
- "countEl",
9277
- "pvEl"
9278
- ], (conf) => {
9279
- initCountWidget({
9280
- getApi: () => ctx.getApi(),
9281
- siteName: conf.site,
9282
- pageKey: conf.pageKey,
9283
- pageTitle: conf.pageTitle,
9284
- countEl: conf.countEl,
9285
- pvEl: conf.pvEl,
9286
- pvAdd: typeof ctx.conf.pvAdd === "boolean" ? ctx.conf.pvAdd : true
9287
- });
9288
- });
9289
- };
9290
- function initCountWidget(opt) {
9291
- return __async(this, null, function* () {
9292
- if (opt.countEl && document.querySelector(opt.countEl)) {
9293
- refreshStatCount(opt, { query: "page_comment", numEl: opt.countEl });
9294
- }
9295
- const initialData = opt.pvAdd && opt.pageKey ? {
9296
- [opt.pageKey]: (yield opt.getApi().pages.logPv({
9297
- page_key: opt.pageKey,
9298
- page_title: opt.pageTitle,
9299
- site_name: opt.siteName
9300
- })).data.pv
9301
- // pv+1 and get pv count
9302
- } : void 0;
9303
- if (opt.pvEl && document.querySelector(opt.pvEl)) {
9304
- refreshStatCount(opt, {
9305
- query: "page_pv",
9306
- numEl: opt.pvEl,
9307
- data: initialData
9308
- });
9309
- }
9310
- });
9311
- }
9312
- function refreshStatCount(opt, args) {
9313
- return __async(this, null, function* () {
9314
- let data = args.data || {};
9315
- let queryPageKeys = Array.from(document.querySelectorAll(args.numEl)).map((e) => e.getAttribute("data-page-key") || opt.pageKey).filter((k) => k && typeof data[k] !== "number");
9316
- queryPageKeys = [...new Set(queryPageKeys)];
9317
- if (queryPageKeys.length > 0) {
9318
- const res = (yield opt.getApi().stats.getStats(args.query, {
9319
- page_keys: queryPageKeys.join(","),
9320
- site_name: opt.siteName
9321
- })).data.data;
9322
- data = __spreadValues(__spreadValues({}, data), res);
9323
- }
9324
- const defaultCount = opt.pageKey ? data[opt.pageKey] : 0;
9325
- applyCountData(args.numEl, data, defaultCount);
9326
- });
9327
- }
9328
- function applyCountData(selector, data, defaultCount) {
9329
- document.querySelectorAll(selector).forEach((el) => {
9330
- const pageKey = el.getAttribute("data-page-key");
9331
- const count = Number(pageKey ? data[pageKey] : defaultCount);
9332
- el.innerHTML = `${count}`;
9333
- });
9334
- }
9335
9304
  let IgnoreVersionCheck = false;
9336
9305
  const VersionCheck = (ctx) => {
9337
9306
  ctx.watchConf(["apiVersion", "versionCheck"], (conf) => {
@@ -9425,7 +9394,6 @@ const DarkMode = (ctx) => {
9425
9394
  });
9426
9395
  };
9427
9396
  const DefaultPlugins = [
9428
- ConfRemoter,
9429
9397
  Markdown,
9430
9398
  EditorKit,
9431
9399
  AdminOnlyElem,
@@ -9436,23 +9404,98 @@ const DefaultPlugins = [
9436
9404
  DarkMode
9437
9405
  ];
9438
9406
  const GlobalPlugins = [...DefaultPlugins];
9407
+ function load(ctx) {
9408
+ return __async(this, null, function* () {
9409
+ var _a;
9410
+ const loadedPlugins = [];
9411
+ const loadPlugins = (plugins) => {
9412
+ plugins.forEach((plugin) => {
9413
+ if (typeof plugin === "function" && !loadedPlugins.includes(plugin)) {
9414
+ plugin(ctx);
9415
+ loadedPlugins.push(plugin);
9416
+ }
9417
+ });
9418
+ };
9419
+ loadPlugins(GlobalPlugins);
9420
+ const { data } = yield ctx.getApi().conf.conf().catch((err) => {
9421
+ onLoadErr(ctx, err);
9422
+ throw err;
9423
+ });
9424
+ let conf = {
9425
+ apiVersion: (_a = data.version) == null ? void 0 : _a.version
9426
+ // version info
9427
+ };
9428
+ if (ctx.conf.useBackendConf) {
9429
+ if (!data.frontend_conf)
9430
+ throw new Error("The remote backend does not respond to the frontend conf, but `useBackendConf` conf is enabled");
9431
+ conf = __spreadValues(__spreadValues({}, conf), handleConfFormServer(data.frontend_conf));
9432
+ }
9433
+ ctx.conf.remoteConfModifier && ctx.conf.remoteConfModifier(conf);
9434
+ conf.pluginURLs && (yield loadNetworkPlugins(conf.pluginURLs, ctx.conf.server).then((plugins) => {
9435
+ loadPlugins(plugins);
9436
+ }).catch((err) => {
9437
+ console.error("Failed to load plugin", err);
9438
+ }));
9439
+ ctx.trigger("created");
9440
+ ctx.updateConf(conf);
9441
+ ctx.trigger("mounted");
9442
+ if (!ctx.conf.remoteConfModifier) {
9443
+ ctx.fetch({ offset: 0 });
9444
+ }
9445
+ });
9446
+ }
9447
+ function loadNetworkPlugins(scripts, apiBase) {
9448
+ return __async(this, null, function* () {
9449
+ if (!scripts || !Array.isArray(scripts))
9450
+ return [];
9451
+ const tasks = [];
9452
+ scripts.forEach((url) => {
9453
+ if (!/^(http|https):\/\//.test(url))
9454
+ url = `${apiBase.replace(/\/$/, "")}/${url.replace(/^\//, "")}`;
9455
+ tasks.push(new Promise((resolve, reject) => {
9456
+ if (document.querySelector(`script[src="${url}"]`)) {
9457
+ resolve();
9458
+ return;
9459
+ }
9460
+ const script = document.createElement("script");
9461
+ script.src = url;
9462
+ document.head.appendChild(script);
9463
+ script.onload = () => resolve();
9464
+ script.onerror = (err) => reject(err);
9465
+ }));
9466
+ });
9467
+ yield Promise.all(tasks);
9468
+ return Object.values(window.ArtalkPlugins || {});
9469
+ });
9470
+ }
9471
+ function onLoadErr(ctx, err) {
9472
+ var _a;
9473
+ let sidebarOpenView = "";
9474
+ if ((_a = err.data) == null ? void 0 : _a.err_no_site) {
9475
+ const viewLoadParam = { create_name: ctx.conf.site, create_urls: `${window.location.protocol}//${window.location.host}` };
9476
+ sidebarOpenView = `sites|${JSON.stringify(viewLoadParam)}`;
9477
+ }
9478
+ showErrorDialog({
9479
+ $err: ctx.get("list").$el,
9480
+ errMsg: err.msg || String(err),
9481
+ errData: err.data,
9482
+ retryFn: () => load(ctx),
9483
+ onOpenSidebar: ctx.get("user").getData().isAdmin ? () => ctx.showSidebar({
9484
+ view: sidebarOpenView
9485
+ }) : void 0
9486
+ // only show open sidebar button when user is admin
9487
+ });
9488
+ }
9439
9489
  class Artalk {
9440
9490
  constructor(conf) {
9441
9491
  __publicField(this, "ctx");
9442
- /** Plugins */
9443
- __publicField(this, "plugins", [...GlobalPlugins]);
9444
9492
  const handledConf = handelCustomConf(conf, true);
9445
9493
  this.ctx = new Context(handledConf);
9446
9494
  Object.entries(services).forEach(([name, initService]) => {
9447
9495
  const obj = initService(this.ctx);
9448
- if (obj)
9449
- this.ctx.inject(name, obj);
9450
- });
9451
- this.plugins.forEach((plugin) => {
9452
- if (typeof plugin === "function")
9453
- plugin(this.ctx);
9496
+ obj && this.ctx.inject(name, obj);
9454
9497
  });
9455
- this.ctx.trigger("created");
9498
+ load(this.ctx);
9456
9499
  }
9457
9500
  /** Get the config of Artalk */
9458
9501
  getConf() {