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.
@@ -468,6 +468,21 @@ let Api$1 = class Api extends HttpClient {
468
468
  path: `/conf`,
469
469
  method: "GET",
470
470
  format: "json"
471
+ }, params)),
472
+ /**
473
+ * @description Get Domain Info
474
+ *
475
+ * @tags System
476
+ * @name GetDomain
477
+ * @summary Get Domain Info
478
+ * @request GET:/conf/domain
479
+ * @response `200` `HandlerResponseConfDomain` OK
480
+ */
481
+ getDomain: (query, params = {}) => this.request(__spreadValues({
482
+ path: `/conf/domain`,
483
+ method: "GET",
484
+ query,
485
+ format: "json"
471
486
  }, params))
472
487
  });
473
488
  __publicField(this, "notifies", {
@@ -1036,7 +1051,7 @@ let Api$1 = class Api extends HttpClient {
1036
1051
  /**
1037
1052
  * @description Get user info to prepare for login or check current user status
1038
1053
  *
1039
- * @tags Account
1054
+ * @tags Auth
1040
1055
  * @name GetUser
1041
1056
  * @summary Get User Info
1042
1057
  * @request GET:/user
@@ -1057,23 +1072,23 @@ let Api$1 = class Api extends HttpClient {
1057
1072
  /**
1058
1073
  * @description Login user by name or email
1059
1074
  *
1060
- * @tags Account
1075
+ * @tags Auth
1061
1076
  * @name Login
1062
1077
  * @summary Get Access Token
1063
1078
  * @request POST:/user/access_token
1064
1079
  * @response `200` `HandlerResponseUserLogin` OK
1065
1080
  * @response `400` `(HandlerMap & {
1066
- " data"?: {
1081
+ data?: {
1067
1082
  need_name_select?: (string)[],
1068
1083
 
1069
1084
  },
1070
1085
  msg?: string,
1071
1086
 
1072
1087
  })` Multiple users with the same email address are matched
1073
- * @response `403` `(HandlerMap & {
1088
+ * @response `401` `(HandlerMap & {
1074
1089
  msg?: string,
1075
1090
 
1076
- })` Forbidden
1091
+ })` Unauthorized
1077
1092
  * @response `500` `(HandlerMap & {
1078
1093
  msg?: string,
1079
1094
 
@@ -1089,7 +1104,7 @@ let Api$1 = class Api extends HttpClient {
1089
1104
  /**
1090
1105
  * @description Get user login status by header Authorization
1091
1106
  *
1092
- * @tags Account
1107
+ * @tags Auth
1093
1108
  * @name GetUserStatus
1094
1109
  * @summary Get Login Status
1095
1110
  * @request GET:/user/status
@@ -1701,16 +1716,16 @@ const Fetch$1 = (opts, input, init2) => __async(void 0, null, function* () {
1701
1716
  if (!resp.ok) {
1702
1717
  const json = (yield resp.json().catch(() => {
1703
1718
  })) || {};
1704
- if (json.need_captcha) {
1705
- opts.onNeedCheckCaptcha && (yield opts.onNeedCheckCaptcha({
1706
- data: { imgData: json.img_data, iframe: json.iframe }
1707
- }));
1708
- return Fetch$1(opts, input, init2);
1709
- }
1710
- if (json.need_login) {
1711
- opts.onNeedCheckAdmin && (yield opts.onNeedCheckAdmin({}));
1719
+ let retry = false;
1720
+ opts.handlers && (yield opts.handlers.get().reduce((promise, item) => __async(void 0, null, function* () {
1721
+ yield promise;
1722
+ if (json[item.action] === true) {
1723
+ yield item.handler(json);
1724
+ retry = true;
1725
+ }
1726
+ }), Promise.resolve()));
1727
+ if (retry)
1712
1728
  return Fetch$1(opts, input, init2);
1713
- }
1714
1729
  throw createError(resp.status, json);
1715
1730
  }
1716
1731
  return resp;
@@ -1731,6 +1746,15 @@ function createError(code, data) {
1731
1746
  console.error(err);
1732
1747
  return err;
1733
1748
  }
1749
+ function createApiHandlers() {
1750
+ const handlers = [];
1751
+ return {
1752
+ add: (action, handler) => {
1753
+ handlers.push({ action, handler });
1754
+ },
1755
+ get: () => handlers
1756
+ };
1757
+ }
1734
1758
  class Api2 extends Api$1 {
1735
1759
  constructor(opts) {
1736
1760
  super({
@@ -2472,7 +2496,7 @@ const en = {
2472
2496
  link: "Website",
2473
2497
  emoticon: "Emoji",
2474
2498
  preview: "Preview",
2475
- image: "Image",
2499
+ uploadImage: "Upload Image",
2476
2500
  uploadFail: "Upload Failed",
2477
2501
  commentFail: "Failed to comment",
2478
2502
  restoredMsg: "Content has been restored",
@@ -2524,6 +2548,7 @@ const en = {
2524
2548
  /* Sidebar */
2525
2549
  msgCenter: "Messages",
2526
2550
  ctrlCenter: "Admin",
2551
+ emailVerified: "Email has been verified",
2527
2552
  /* General */
2528
2553
  frontend: "Frontend",
2529
2554
  backend: "Backend",
@@ -2545,14 +2570,14 @@ const zhCN = {
2545
2570
  /* Editor */
2546
2571
  placeholder: "键入内容...",
2547
2572
  noComment: "「此时无声胜有声」",
2548
- send: "发送评论",
2549
- save: "保存评论",
2573
+ send: "发送",
2574
+ save: "保存",
2550
2575
  nick: "昵称",
2551
2576
  email: "邮箱",
2552
2577
  link: "网址",
2553
2578
  emoticon: "表情",
2554
2579
  preview: "预览",
2555
- image: "图片",
2580
+ uploadImage: "上传图片",
2556
2581
  uploadFail: "上传失败",
2557
2582
  commentFail: "评论失败",
2558
2583
  restoredMsg: "内容已自动恢复",
@@ -2604,6 +2629,7 @@ const zhCN = {
2604
2629
  /* Sidebar */
2605
2630
  msgCenter: "通知中心",
2606
2631
  ctrlCenter: "控制中心",
2632
+ emailVerified: "邮箱已验证",
2607
2633
  /* General */
2608
2634
  frontend: "前端",
2609
2635
  backend: "后端",
@@ -2954,21 +2980,15 @@ function convertApiOptions(conf, ctx) {
2954
2980
  name: ctx == null ? void 0 : ctx.get("user").getData().nick,
2955
2981
  email: ctx == null ? void 0 : ctx.get("user").getData().email
2956
2982
  } : void 0,
2957
- onNeedCheckAdmin: (payload) => {
2958
- if (!ctx)
2959
- throw new Error("`ctx` is required when `onNeedCheckAdmin` is called.");
2960
- return ctx.checkAdmin({});
2961
- },
2962
- onNeedCheckCaptcha: (payload) => {
2963
- if (!ctx)
2964
- throw new Error("`ctx` is required when `onNeedCheckCaptcha` is called.");
2965
- return ctx.checkCaptcha({
2966
- imgData: payload.data.imgData,
2967
- iframe: payload.data.iframe
2968
- });
2969
- }
2983
+ handlers: ctx == null ? void 0 : ctx.getApiHandlers()
2970
2984
  };
2971
2985
  }
2986
+ function createNewApiHandlers(ctx) {
2987
+ const h = createApiHandlers();
2988
+ h.add("need_captcha", (res) => ctx.checkCaptcha(res));
2989
+ h.add("need_login", () => ctx.checkAdmin({}));
2990
+ return h;
2991
+ }
2972
2992
  function watchConf(ctx, keys, effect) {
2973
2993
  const deepEqual = (a, b) => JSON.stringify(a) === JSON.stringify(b);
2974
2994
  const val = () => {
@@ -3000,6 +3020,7 @@ class Context {
3000
3020
  /* Event Manager */
3001
3021
  __publicField(this, "events", new EventManager());
3002
3022
  __publicField(this, "mounted", false);
3023
+ __publicField(this, "apiHandlers", null);
3003
3024
  __publicField(this, "getCommentList", this.getCommentNodes);
3004
3025
  __publicField(this, "getCommentDataList", this.getComments);
3005
3026
  this.conf = conf;
@@ -3020,6 +3041,11 @@ class Context {
3020
3041
  getApi() {
3021
3042
  return new Api2(convertApiOptions(this.conf, this));
3022
3043
  }
3044
+ getApiHandlers() {
3045
+ if (!this.apiHandlers)
3046
+ this.apiHandlers = createNewApiHandlers(this);
3047
+ return this.apiHandlers;
3048
+ }
3023
3049
  getData() {
3024
3050
  return this.data;
3025
3051
  }
@@ -3412,7 +3438,7 @@ class CheckerLauncher {
3412
3438
  constructor(opts) {
3413
3439
  __publicField(this, "checkCaptcha", wrapPromise((p) => {
3414
3440
  this.fire(CaptchaChecker, p, (ctx) => {
3415
- ctx.set("img_data", p.imgData);
3441
+ ctx.set("img_data", p.img_data);
3416
3442
  ctx.set("iframe", p.iframe);
3417
3443
  });
3418
3444
  }));
@@ -3490,7 +3516,7 @@ class CheckerLauncher {
3490
3516
  if (payload.onSuccess)
3491
3517
  payload.onSuccess();
3492
3518
  }).catch((err) => {
3493
- btnTextSet(String(err.msg || String(err)));
3519
+ btnTextSet(String(err.message || String(err)));
3494
3520
  if (checker.onError)
3495
3521
  checker.onError(checkerCtx, err, inputVal, formEl);
3496
3522
  const tf = setTimeout(() => btnTextRestore(), 3e3);
@@ -3528,7 +3554,7 @@ class Component {
3528
3554
  return this.$el;
3529
3555
  }
3530
3556
  }
3531
- 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';
3557
+ 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';
3532
3558
  const Sel = {
3533
3559
  $header: ".atk-header",
3534
3560
  $nick: '.atk-header [name="nick"]',
@@ -3539,6 +3565,8 @@ const Sel = {
3539
3565
  $bottom: ".atk-bottom",
3540
3566
  $submitBtn: ".atk-send-btn",
3541
3567
  $notifyWrap: ".atk-notify-wrap",
3568
+ $bottomLeft: ".atk-bottom-left",
3569
+ $stateWrap: ".atk-state-wrap",
3542
3570
  $plugBtnWrap: ".atk-plug-btn-wrap",
3543
3571
  $plugPanelWrap: ".atk-plug-panel-wrap"
3544
3572
  };
@@ -3736,10 +3764,15 @@ class Editor extends Component {
3736
3764
  hideLoading(this.ui.$el);
3737
3765
  }
3738
3766
  submit() {
3739
- this.ctx.trigger("editor-submit");
3767
+ const next = () => this.ctx.trigger("editor-submit");
3768
+ if (this.ctx.conf.beforeSubmit) {
3769
+ this.ctx.conf.beforeSubmit(this, next);
3770
+ } else {
3771
+ next();
3772
+ }
3740
3773
  }
3741
3774
  }
3742
- 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';
3775
+ 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';
3743
3776
  class SidebarLayer extends Component {
3744
3777
  constructor(ctx) {
3745
3778
  super(ctx);
@@ -4294,6 +4327,9 @@ function renderVerifyBadge(ctx) {
4294
4327
  $badge.innerText = badgeText.replace("管理员", t("admin"));
4295
4328
  $badge.style.backgroundColor = badgeColor || "";
4296
4329
  ctx.$headerBadgeWrap.append($badge);
4330
+ } else if (ctx.data.is_verified) {
4331
+ const $verifiedBadge = createElement(`<span class="atk-verified-icon" title="${t("emailVerified")}"></span>`);
4332
+ ctx.$headerBadgeWrap.append($verifiedBadge);
4297
4333
  }
4298
4334
  if (ctx.data.is_pinned) {
4299
4335
  const $pinnedBadge = createElement(`<span class="atk-pinned-badge">${t("pin")}</span>`);
@@ -5548,18 +5584,8 @@ class LayerWrap {
5548
5584
  if (this.items.map((l) => l.getEl()).filter((e) => e !== $el && e.isConnected && e.style.display !== "none").length > 0) {
5549
5585
  return;
5550
5586
  }
5551
- const onHide = () => {
5552
- this.$wrap.style.display = "none";
5553
- this.$wrap.classList.remove("atk-fade-out");
5554
- getScrollbarHelper().unlock();
5555
- this.$wrap.onanimationend = null;
5556
- };
5557
- this.$wrap.classList.add("atk-fade-out");
5558
- if (window.getComputedStyle(this.$wrap)["animation-name"] !== "none") {
5559
- this.$wrap.onanimationend = () => onHide();
5560
- } else {
5561
- onHide();
5562
- }
5587
+ this.$wrap.style.display = "none";
5588
+ getScrollbarHelper().unlock();
5563
5589
  }
5564
5590
  }
5565
5591
  class LayerManager {
@@ -5678,6 +5704,70 @@ const services = {
5678
5704
  return void 0;
5679
5705
  }
5680
5706
  };
5707
+ const PvCountWidget = (ctx) => {
5708
+ ctx.watchConf([
5709
+ "site",
5710
+ "pageKey",
5711
+ "pageTitle",
5712
+ "countEl",
5713
+ "pvEl"
5714
+ ], (conf) => {
5715
+ initCountWidget({
5716
+ getApi: () => ctx.getApi(),
5717
+ siteName: conf.site,
5718
+ pageKey: conf.pageKey,
5719
+ pageTitle: conf.pageTitle,
5720
+ countEl: conf.countEl,
5721
+ pvEl: conf.pvEl,
5722
+ pvAdd: typeof ctx.conf.pvAdd === "boolean" ? ctx.conf.pvAdd : true
5723
+ });
5724
+ });
5725
+ };
5726
+ function initCountWidget(opt) {
5727
+ return __async(this, null, function* () {
5728
+ if (opt.countEl && document.querySelector(opt.countEl)) {
5729
+ refreshStatCount(opt, { query: "page_comment", numEl: opt.countEl });
5730
+ }
5731
+ const initialData = opt.pvAdd && opt.pageKey ? {
5732
+ [opt.pageKey]: (yield opt.getApi().pages.logPv({
5733
+ page_key: opt.pageKey,
5734
+ page_title: opt.pageTitle,
5735
+ site_name: opt.siteName
5736
+ })).data.pv
5737
+ // pv+1 and get pv count
5738
+ } : void 0;
5739
+ if (opt.pvEl && document.querySelector(opt.pvEl)) {
5740
+ refreshStatCount(opt, {
5741
+ query: "page_pv",
5742
+ numEl: opt.pvEl,
5743
+ data: initialData
5744
+ });
5745
+ }
5746
+ });
5747
+ }
5748
+ function refreshStatCount(opt, args) {
5749
+ return __async(this, null, function* () {
5750
+ let data = args.data || {};
5751
+ let queryPageKeys = Array.from(document.querySelectorAll(args.numEl)).map((e) => e.getAttribute("data-page-key") || opt.pageKey).filter((k) => k && typeof data[k] !== "number");
5752
+ queryPageKeys = [...new Set(queryPageKeys)];
5753
+ if (queryPageKeys.length > 0) {
5754
+ const res = (yield opt.getApi().stats.getStats(args.query, {
5755
+ page_keys: queryPageKeys.join(","),
5756
+ site_name: opt.siteName
5757
+ })).data.data;
5758
+ data = __spreadValues(__spreadValues({}, data), res);
5759
+ }
5760
+ const defaultCount = opt.pageKey ? data[opt.pageKey] : 0;
5761
+ applyCountData(args.numEl, data, defaultCount);
5762
+ });
5763
+ }
5764
+ function applyCountData(selector, data, defaultCount) {
5765
+ document.querySelectorAll(selector).forEach((el) => {
5766
+ const pageKey = el.getAttribute("data-page-key");
5767
+ const count = Number(pageKey ? data[pageKey] : defaultCount);
5768
+ el.innerHTML = `${count}`;
5769
+ });
5770
+ }
5681
5771
  function showErrorDialog(opts) {
5682
5772
  const errEl = createElement(`<span><span class="error-message"></span><br/><br/></span>`);
5683
5773
  errEl.querySelector(".error-message").innerText = `${t("listLoadFailMsg")}
@@ -5694,58 +5784,6 @@ ${opts.errMsg}`;
5694
5784
  }
5695
5785
  setError(opts.$err, errEl);
5696
5786
  }
5697
- const ConfRemoter = (ctx) => {
5698
- ctx.on("created", () => {
5699
- if (ctx.conf.immediateFetch !== false)
5700
- ctx.trigger("conf-fetch");
5701
- });
5702
- ctx.on("conf-fetch", () => {
5703
- loadConf(ctx);
5704
- });
5705
- };
5706
- function loadConf(ctx) {
5707
- ctx.getApi().conf.conf().then((res) => {
5708
- var _a;
5709
- let conf = {
5710
- apiVersion: (_a = res.data.version) == null ? void 0 : _a.version
5711
- // version info
5712
- };
5713
- if (ctx.conf.useBackendConf) {
5714
- if (!res.data.frontend_conf)
5715
- throw new Error("The remote backend does not respond to the frontend conf, but `useBackendConf` conf is enabled");
5716
- conf = __spreadValues(__spreadValues({}, conf), handleConfFormServer(res.data.frontend_conf));
5717
- }
5718
- ctx.conf.remoteConfModifier && ctx.conf.remoteConfModifier(conf);
5719
- ctx.updateConf(conf);
5720
- }).catch((err) => {
5721
- var _a;
5722
- ctx.updateConf({});
5723
- let sidebarOpenView = "";
5724
- if ((_a = err.data) == null ? void 0 : _a.err_no_site) {
5725
- const viewLoadParam = { create_name: ctx.conf.site, create_urls: `${window.location.protocol}//${window.location.host}` };
5726
- sidebarOpenView = `sites|${JSON.stringify(viewLoadParam)}`;
5727
- }
5728
- showErrorDialog({
5729
- $err: ctx.get("list").$el,
5730
- errMsg: err.msg || String(err),
5731
- errData: err.data,
5732
- retryFn: () => loadConf(ctx),
5733
- onOpenSidebar: ctx.get("user").getData().isAdmin ? () => ctx.showSidebar({
5734
- view: sidebarOpenView
5735
- }) : void 0
5736
- // only show open sidebar button when user is admin
5737
- });
5738
- console.error(err);
5739
- throw err;
5740
- }).then(() => {
5741
- ctx.trigger("mounted");
5742
- }).then(() => {
5743
- if (ctx.conf.remoteConfModifier)
5744
- return;
5745
- ctx.fetch({ offset: 0 });
5746
- }).catch(() => {
5747
- });
5748
- }
5749
5787
  const Markdown = (ctx) => {
5750
5788
  initMarked();
5751
5789
  ctx.on("updated", (conf) => {
@@ -5901,7 +5939,7 @@ class Submit extends EditorPlug {
5901
5939
  this.defaultPreset.postSubmitAdd(nComment);
5902
5940
  } catch (err) {
5903
5941
  console.error(err);
5904
- this.kit.useEditor().showNotify(`${t("commentFail")}: ${err.msg || String(err)}`, "e");
5942
+ this.kit.useEditor().showNotify(`${t("commentFail")}: ${err.message || String(err)}`, "e");
5905
5943
  return;
5906
5944
  } finally {
5907
5945
  this.kit.useEditor().hideLoading();
@@ -5952,15 +5990,16 @@ class StateReply extends EditorPlug {
5952
5990
  }
5953
5991
  setReply(commentData) {
5954
5992
  const ui = this.kit.useUI();
5955
- if (!ui.$sendReply) {
5956
- ui.$sendReply = createElement(
5957
- `<div class="atk-send-reply">${t("reply")} <span class="atk-text"></span><span class="atk-cancel">×</span></div>`
5993
+ if (!ui.$sendReplyBtn) {
5994
+ const $btn = createElement(
5995
+ `<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>`
5958
5996
  );
5959
- ui.$sendReply.querySelector(".atk-text").innerText = `@${commentData.nick}`;
5960
- ui.$sendReply.addEventListener("click", () => {
5997
+ $btn.querySelector(".atk-text").innerText = `@${commentData.nick}`;
5998
+ $btn.addEventListener("click", () => {
5961
5999
  this.kit.useEditor().resetState();
5962
6000
  });
5963
- ui.$textareaWrap.append(ui.$sendReply);
6001
+ ui.$stateWrap.append($btn);
6002
+ ui.$sendReplyBtn = $btn;
5964
6003
  }
5965
6004
  this.comment = commentData;
5966
6005
  ui.$textarea.focus();
@@ -5969,9 +6008,9 @@ class StateReply extends EditorPlug {
5969
6008
  if (!this.comment)
5970
6009
  return;
5971
6010
  const ui = this.kit.useUI();
5972
- if (ui.$sendReply) {
5973
- ui.$sendReply.remove();
5974
- ui.$sendReply = void 0;
6011
+ if (ui.$sendReplyBtn) {
6012
+ ui.$sendReplyBtn.remove();
6013
+ ui.$sendReplyBtn = void 0;
5975
6014
  }
5976
6015
  this.comment = void 0;
5977
6016
  }
@@ -6018,12 +6057,12 @@ class StateEdit extends EditorPlug {
6018
6057
  const ui = this.kit.useUI();
6019
6058
  if (!ui.$editCancelBtn) {
6020
6059
  const $btn = createElement(
6021
- `<div class="atk-send-reply">${t("editCancel")} <span class="atk-cancel">×</span></div>`
6060
+ `<span class="atk-state-btn"><span class="atk-text-wrap">${t("editCancel")}</span><span class="atk-cancel atk-icon-close atk-icon"></span></span>`
6022
6061
  );
6023
6062
  $btn.onclick = () => {
6024
6063
  this.kit.useEditor().resetState();
6025
6064
  };
6026
- ui.$textareaWrap.append($btn);
6065
+ ui.$stateWrap.append($btn);
6027
6066
  ui.$editCancelBtn = $btn;
6028
6067
  }
6029
6068
  this.comment = comment;
@@ -6220,7 +6259,7 @@ class Emoticons extends EditorPlug {
6220
6259
  __publicField(this, "isImgLoaded", false);
6221
6260
  this.kit.useMounted(() => {
6222
6261
  this.usePanel(`<div class="atk-editor-plug-emoticons"></div>`);
6223
- this.useBtn(t("emoticon"));
6262
+ 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>`);
6224
6263
  });
6225
6264
  this.kit.useUnmounted(() => {
6226
6265
  });
@@ -6471,7 +6510,7 @@ class Upload extends EditorPlug {
6471
6510
  this.$imgUploadInput.type = "file";
6472
6511
  this.$imgUploadInput.style.display = "none";
6473
6512
  this.$imgUploadInput.accept = AllowImgExts.map((o) => `.${o}`).join(",");
6474
- const $btn = this.useBtn(`${t("image")}`);
6513
+ 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>`);
6475
6514
  $btn.after(this.$imgUploadInput);
6476
6515
  $btn.onclick = () => {
6477
6516
  const $input = this.$imgUploadInput;
@@ -6553,7 +6592,7 @@ class Upload extends EditorPlug {
6553
6592
  }
6554
6593
  } catch (err) {
6555
6594
  console.error(err);
6556
- this.kit.useEditor().showNotify(`${t("uploadFail")}: ${err.msg}`, "e");
6595
+ this.kit.useEditor().showNotify(`${t("uploadFail")}: ${err.message}`, "e");
6557
6596
  }
6558
6597
  if (!!resp && resp.public_url) {
6559
6598
  let imgURL = resp.public_url;
@@ -6575,10 +6614,7 @@ class Preview extends EditorPlug {
6575
6614
  __publicField(this, "isPlugPanelShow", false);
6576
6615
  this.kit.useMounted(() => {
6577
6616
  this.usePanel(`<div class="atk-editor-plug-preview"></div>`);
6578
- let btnText = t("preview");
6579
- if (getInstance())
6580
- 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>`;
6581
- this.useBtn(btnText);
6617
+ 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>`);
6582
6618
  });
6583
6619
  this.kit.useUnmounted(() => {
6584
6620
  });
@@ -6719,9 +6755,6 @@ class PlugManager {
6719
6755
  this.editor.getUI().$plugPanelWrap.innerHTML = "";
6720
6756
  this.editor.getUI().$plugPanelWrap.style.display = "none";
6721
6757
  this.editor.getUI().$plugBtnWrap.innerHTML = "";
6722
- this.editor.getUI().$el.addEventListener("click", (e) => {
6723
- e.stopPropagation();
6724
- });
6725
6758
  this.plugs.forEach((plug) => this.loadPluginItem(plug));
6726
6759
  }
6727
6760
  /** Load the plug btn and plug panel on editor ui */
@@ -6817,7 +6850,7 @@ function adminPageEditSave(ctx, page) {
6817
6850
  ctx.getApi().pages.updatePage(page.id, page).then(({ data }) => {
6818
6851
  ctx.getData().updatePage(data);
6819
6852
  }).catch((err) => {
6820
- ctx.editorShowNotify(`${t("editFail")}: ${err.msg || String(err)}`, "e");
6853
+ ctx.editorShowNotify(`${t("editFail")}: ${err.message || String(err)}`, "e");
6821
6854
  }).finally(() => {
6822
6855
  ctx.editorHideLoading();
6823
6856
  });
@@ -6963,7 +6996,7 @@ const GotoFocus = (ctx) => {
6963
6996
  comment.focus();
6964
6997
  }));
6965
6998
  };
6966
- const version = "2.8.1";
6999
+ const version = "2.8.3";
6967
7000
  const Copyright = (ctx) => {
6968
7001
  ctx.on("mounted", () => {
6969
7002
  const list = ctx.get("list");
@@ -7247,70 +7280,6 @@ const Notifies = (ctx) => {
7247
7280
  });
7248
7281
  });
7249
7282
  };
7250
- const PvCountWidget = (ctx) => {
7251
- ctx.watchConf([
7252
- "site",
7253
- "pageKey",
7254
- "pageTitle",
7255
- "countEl",
7256
- "pvEl"
7257
- ], (conf) => {
7258
- initCountWidget({
7259
- getApi: () => ctx.getApi(),
7260
- siteName: conf.site,
7261
- pageKey: conf.pageKey,
7262
- pageTitle: conf.pageTitle,
7263
- countEl: conf.countEl,
7264
- pvEl: conf.pvEl,
7265
- pvAdd: typeof ctx.conf.pvAdd === "boolean" ? ctx.conf.pvAdd : true
7266
- });
7267
- });
7268
- };
7269
- function initCountWidget(opt) {
7270
- return __async(this, null, function* () {
7271
- if (opt.countEl && document.querySelector(opt.countEl)) {
7272
- refreshStatCount(opt, { query: "page_comment", numEl: opt.countEl });
7273
- }
7274
- const initialData = opt.pvAdd && opt.pageKey ? {
7275
- [opt.pageKey]: (yield opt.getApi().pages.logPv({
7276
- page_key: opt.pageKey,
7277
- page_title: opt.pageTitle,
7278
- site_name: opt.siteName
7279
- })).data.pv
7280
- // pv+1 and get pv count
7281
- } : void 0;
7282
- if (opt.pvEl && document.querySelector(opt.pvEl)) {
7283
- refreshStatCount(opt, {
7284
- query: "page_pv",
7285
- numEl: opt.pvEl,
7286
- data: initialData
7287
- });
7288
- }
7289
- });
7290
- }
7291
- function refreshStatCount(opt, args) {
7292
- return __async(this, null, function* () {
7293
- let data = args.data || {};
7294
- let queryPageKeys = Array.from(document.querySelectorAll(args.numEl)).map((e) => e.getAttribute("data-page-key") || opt.pageKey).filter((k) => k && typeof data[k] !== "number");
7295
- queryPageKeys = [...new Set(queryPageKeys)];
7296
- if (queryPageKeys.length > 0) {
7297
- const res = (yield opt.getApi().stats.getStats(args.query, {
7298
- page_keys: queryPageKeys.join(","),
7299
- site_name: opt.siteName
7300
- })).data.data;
7301
- data = __spreadValues(__spreadValues({}, data), res);
7302
- }
7303
- const defaultCount = opt.pageKey ? data[opt.pageKey] : 0;
7304
- applyCountData(args.numEl, data, defaultCount);
7305
- });
7306
- }
7307
- function applyCountData(selector, data, defaultCount) {
7308
- document.querySelectorAll(selector).forEach((el) => {
7309
- const pageKey = el.getAttribute("data-page-key");
7310
- const count = Number(pageKey ? data[pageKey] : defaultCount);
7311
- el.innerHTML = `${count}`;
7312
- });
7313
- }
7314
7283
  let IgnoreVersionCheck = false;
7315
7284
  const VersionCheck = (ctx) => {
7316
7285
  ctx.watchConf(["apiVersion", "versionCheck"], (conf) => {
@@ -7404,7 +7373,6 @@ const DarkMode = (ctx) => {
7404
7373
  });
7405
7374
  };
7406
7375
  const DefaultPlugins = [
7407
- ConfRemoter,
7408
7376
  Markdown,
7409
7377
  EditorKit,
7410
7378
  AdminOnlyElem,
@@ -7415,23 +7383,98 @@ const DefaultPlugins = [
7415
7383
  DarkMode
7416
7384
  ];
7417
7385
  const GlobalPlugins = [...DefaultPlugins];
7386
+ function load(ctx) {
7387
+ return __async(this, null, function* () {
7388
+ var _a;
7389
+ const loadedPlugins = [];
7390
+ const loadPlugins = (plugins) => {
7391
+ plugins.forEach((plugin) => {
7392
+ if (typeof plugin === "function" && !loadedPlugins.includes(plugin)) {
7393
+ plugin(ctx);
7394
+ loadedPlugins.push(plugin);
7395
+ }
7396
+ });
7397
+ };
7398
+ loadPlugins(GlobalPlugins);
7399
+ const { data } = yield ctx.getApi().conf.conf().catch((err) => {
7400
+ onLoadErr(ctx, err);
7401
+ throw err;
7402
+ });
7403
+ let conf = {
7404
+ apiVersion: (_a = data.version) == null ? void 0 : _a.version
7405
+ // version info
7406
+ };
7407
+ if (ctx.conf.useBackendConf) {
7408
+ if (!data.frontend_conf)
7409
+ throw new Error("The remote backend does not respond to the frontend conf, but `useBackendConf` conf is enabled");
7410
+ conf = __spreadValues(__spreadValues({}, conf), handleConfFormServer(data.frontend_conf));
7411
+ }
7412
+ ctx.conf.remoteConfModifier && ctx.conf.remoteConfModifier(conf);
7413
+ conf.pluginURLs && (yield loadNetworkPlugins(conf.pluginURLs, ctx.conf.server).then((plugins) => {
7414
+ loadPlugins(plugins);
7415
+ }).catch((err) => {
7416
+ console.error("Failed to load plugin", err);
7417
+ }));
7418
+ ctx.trigger("created");
7419
+ ctx.updateConf(conf);
7420
+ ctx.trigger("mounted");
7421
+ if (!ctx.conf.remoteConfModifier) {
7422
+ ctx.fetch({ offset: 0 });
7423
+ }
7424
+ });
7425
+ }
7426
+ function loadNetworkPlugins(scripts, apiBase) {
7427
+ return __async(this, null, function* () {
7428
+ if (!scripts || !Array.isArray(scripts))
7429
+ return [];
7430
+ const tasks = [];
7431
+ scripts.forEach((url) => {
7432
+ if (!/^(http|https):\/\//.test(url))
7433
+ url = `${apiBase.replace(/\/$/, "")}/${url.replace(/^\//, "")}`;
7434
+ tasks.push(new Promise((resolve, reject) => {
7435
+ if (document.querySelector(`script[src="${url}"]`)) {
7436
+ resolve();
7437
+ return;
7438
+ }
7439
+ const script = document.createElement("script");
7440
+ script.src = url;
7441
+ document.head.appendChild(script);
7442
+ script.onload = () => resolve();
7443
+ script.onerror = (err) => reject(err);
7444
+ }));
7445
+ });
7446
+ yield Promise.all(tasks);
7447
+ return Object.values(window.ArtalkPlugins || {});
7448
+ });
7449
+ }
7450
+ function onLoadErr(ctx, err) {
7451
+ var _a;
7452
+ let sidebarOpenView = "";
7453
+ if ((_a = err.data) == null ? void 0 : _a.err_no_site) {
7454
+ const viewLoadParam = { create_name: ctx.conf.site, create_urls: `${window.location.protocol}//${window.location.host}` };
7455
+ sidebarOpenView = `sites|${JSON.stringify(viewLoadParam)}`;
7456
+ }
7457
+ showErrorDialog({
7458
+ $err: ctx.get("list").$el,
7459
+ errMsg: err.msg || String(err),
7460
+ errData: err.data,
7461
+ retryFn: () => load(ctx),
7462
+ onOpenSidebar: ctx.get("user").getData().isAdmin ? () => ctx.showSidebar({
7463
+ view: sidebarOpenView
7464
+ }) : void 0
7465
+ // only show open sidebar button when user is admin
7466
+ });
7467
+ }
7418
7468
  class Artalk {
7419
7469
  constructor(conf) {
7420
7470
  __publicField(this, "ctx");
7421
- /** Plugins */
7422
- __publicField(this, "plugins", [...GlobalPlugins]);
7423
7471
  const handledConf = handelCustomConf(conf, true);
7424
7472
  this.ctx = new Context(handledConf);
7425
7473
  Object.entries(services).forEach(([name, initService]) => {
7426
7474
  const obj = initService(this.ctx);
7427
- if (obj)
7428
- this.ctx.inject(name, obj);
7429
- });
7430
- this.plugins.forEach((plugin) => {
7431
- if (typeof plugin === "function")
7432
- plugin(this.ctx);
7475
+ obj && this.ctx.inject(name, obj);
7433
7476
  });
7434
- this.ctx.trigger("created");
7477
+ load(this.ctx);
7435
7478
  }
7436
7479
  /** Get the config of Artalk */
7437
7480
  getConf() {