artalk 2.4.3 → 2.5.0

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.
Files changed (145) hide show
  1. package/README.md +27 -0
  2. package/dist/Artalk.css +1 -1
  3. package/dist/Artalk.es.js +4895 -4067
  4. package/dist/Artalk.iife.js +1 -1
  5. package/dist/Artalk.js +1 -1
  6. package/dist/ArtalkLite.css +1 -1
  7. package/dist/ArtalkLite.es.js +2496 -1779
  8. package/dist/ArtalkLite.iife.js +1 -1
  9. package/dist/ArtalkLite.js +1 -1
  10. package/dist/i18n/jp.js +1 -1
  11. package/dist/i18n/zh-TW.js +1 -1
  12. package/dist/src/api/index.d.ts +2 -2
  13. package/dist/src/api/system.d.ts +5 -2
  14. package/dist/src/artalk.d.ts +42 -34
  15. package/dist/src/comment/comment.d.ts +2 -2
  16. package/dist/src/comment/height-limit.d.ts +30 -0
  17. package/dist/src/comment/render-ctx.d.ts +23 -0
  18. package/dist/src/comment/render.d.ts +2 -50
  19. package/dist/src/comment/renders/actions.d.ts +5 -0
  20. package/dist/src/comment/renders/avatar.d.ts +5 -0
  21. package/dist/src/comment/renders/content.d.ts +5 -0
  22. package/dist/src/comment/renders/header.d.ts +5 -0
  23. package/dist/src/comment/renders/index.d.ts +2 -0
  24. package/dist/src/comment/renders/pending.d.ts +5 -0
  25. package/dist/src/comment/renders/reply-at.d.ts +5 -0
  26. package/dist/src/comment/renders/reply-to.d.ts +5 -0
  27. package/dist/src/components/dialog.d.ts +1 -1
  28. package/dist/src/config.d.ts +8 -0
  29. package/dist/src/context.d.ts +15 -29
  30. package/dist/src/editor/edit-mode.d.ts +9 -0
  31. package/dist/src/editor/editor-ui.d.ts +25 -0
  32. package/dist/src/editor/editor.d.ts +37 -45
  33. package/dist/src/editor/funcs.d.ts +2 -0
  34. package/dist/src/editor/mover.d.ts +8 -0
  35. package/dist/src/editor/plug-manager.d.ts +17 -0
  36. package/dist/src/editor/plugs/editor-plug.d.ts +13 -1
  37. package/dist/src/editor/reply.d.ts +9 -0
  38. package/dist/src/editor/submit.d.ts +16 -0
  39. package/dist/src/editor/ui.d.ts +25 -0
  40. package/dist/src/i18n/index.d.ts +11 -7
  41. package/dist/src/layer/index.d.ts +2 -2
  42. package/dist/src/layer/layer.d.ts +1 -1
  43. package/dist/src/lib/checker/captcha-renders.d.ts +5 -0
  44. package/dist/src/lib/checker/index.d.ts +18 -10
  45. package/dist/src/lib/dark-mode.d.ts +4 -0
  46. package/dist/src/lib/marked.d.ts +9 -0
  47. package/dist/src/lib/utils.d.ts +1 -5
  48. package/dist/src/list/list-lite.d.ts +15 -11
  49. package/dist/src/list/list-nest.d.ts +2 -2
  50. package/dist/src/list/list-ui.d.ts +7 -0
  51. package/dist/src/list/paginator/adaptors/index.d.ts +12 -0
  52. package/dist/src/list/paginator/adaptors/pagination.d.ts +9 -0
  53. package/dist/src/list/paginator/adaptors/read-more.d.ts +10 -0
  54. package/dist/src/list/paginator/holder.d.ts +16 -0
  55. package/dist/src/list/paginator/index.d.ts +10 -0
  56. package/dist/src/service.d.ts +40 -0
  57. package/dist/types/artalk-config.d.ts +5 -2
  58. package/dist/types/artalk-data.d.ts +3 -3
  59. package/dist/types/context.d.ts +11 -17
  60. package/dist/types/global.d.ts +1 -0
  61. package/package.json +6 -7
  62. package/src/api/system.ts +3 -12
  63. package/src/artalk.ts +101 -158
  64. package/src/comment/comment.ts +7 -6
  65. package/src/comment/height-limit.ts +100 -0
  66. package/src/comment/render-ctx.ts +30 -0
  67. package/src/comment/render.ts +31 -375
  68. package/src/comment/renders/actions.ts +114 -0
  69. package/src/comment/renders/avatar.ts +22 -0
  70. package/src/comment/renders/content.ts +39 -0
  71. package/src/comment/renders/header.ts +84 -0
  72. package/src/comment/renders/index.ts +19 -0
  73. package/src/comment/renders/pending.ts +12 -0
  74. package/src/comment/renders/reply-at.ts +16 -0
  75. package/src/comment/renders/reply-to.ts +24 -0
  76. package/src/config.ts +65 -0
  77. package/src/context.ts +31 -88
  78. package/src/defaults.ts +1 -0
  79. package/src/editor/edit-mode.ts +102 -0
  80. package/src/editor/editor.ts +100 -394
  81. package/src/editor/funcs.ts +96 -0
  82. package/src/editor/mover.ts +43 -0
  83. package/src/editor/plug-manager.ts +172 -0
  84. package/src/editor/plugs/editor-plug.ts +20 -2
  85. package/src/editor/plugs/{emoticons-plug.less → emoticons-plug.scss} +5 -4
  86. package/src/editor/plugs/emoticons-plug.ts +2 -1
  87. package/src/editor/plugs/header-input-plug.ts +5 -5
  88. package/src/editor/plugs/{preview-plug.less → preview-plug.scss} +1 -1
  89. package/src/editor/plugs/preview-plug.ts +8 -4
  90. package/src/editor/plugs/upload-plug.ts +10 -13
  91. package/src/editor/reply.ts +62 -0
  92. package/src/editor/submit.ts +98 -0
  93. package/src/editor/ui.ts +36 -0
  94. package/src/i18n/index.ts +21 -10
  95. package/src/layer/index.ts +2 -2
  96. package/src/layer/layer.ts +4 -4
  97. package/src/layer/sidebar-layer.ts +2 -1
  98. package/src/lib/checker/admin.ts +36 -0
  99. package/src/lib/checker/captcha-renders.ts +68 -0
  100. package/src/lib/checker/captcha.ts +26 -0
  101. package/src/lib/checker/index.ts +38 -20
  102. package/src/lib/dark-mode.ts +48 -0
  103. package/src/lib/marked.ts +133 -0
  104. package/src/lib/utils.ts +1 -123
  105. package/src/list/list-lite.ts +59 -162
  106. package/src/list/list-ui.ts +80 -0
  107. package/src/list/list.ts +16 -8
  108. package/src/list/paginator/adaptors/index.ts +21 -0
  109. package/src/list/paginator/adaptors/pagination.ts +42 -0
  110. package/src/list/paginator/adaptors/read-more.ts +56 -0
  111. package/src/list/paginator/holder.ts +55 -0
  112. package/src/list/paginator/index.ts +13 -0
  113. package/src/service.ts +118 -0
  114. package/src/style/{_color.less → _color.scss} +3 -3
  115. package/src/style/{_extend.less → _extend.scss} +1 -1
  116. package/src/style/_variables.scss +3 -0
  117. package/src/style/{comment.less → comment.scss} +36 -12
  118. package/src/style/{editor.less → editor.scss} +11 -11
  119. package/src/style/{list.less → list.scss} +8 -6
  120. package/src/style/{main.less → main.scss} +24 -23
  121. package/src/style/{sidebar-layer.less → sidebar-layer.scss} +8 -8
  122. package/types/artalk-config.d.ts +5 -2
  123. package/types/artalk-data.d.ts +3 -3
  124. package/types/context.d.ts +11 -17
  125. package/types/global.d.ts +1 -0
  126. package/CNAME +0 -1
  127. package/LICENSE +0 -165
  128. package/index.html +0 -157
  129. package/postcss.config.js +0 -5
  130. package/scripts/build-i18n.mjs +0 -46
  131. package/src/lib/checker/admin-checker.ts +0 -36
  132. package/src/lib/checker/captcha-checker.ts +0 -84
  133. package/src/style/_variables.less +0 -3
  134. package/test/ArtalkCount.html +0 -16
  135. package/test/ArtalkI18n.html +0 -16
  136. package/test/ArtalkLite.html +0 -15
  137. package/test/CSRF.html +0 -26
  138. package/tsconfig.json +0 -12
  139. package/vite-base.config.ts +0 -21
  140. package/vite-demo.config.ts +0 -12
  141. package/vite-lite.config.ts +0 -22
  142. package/vite.config.ts +0 -27
  143. /package/dist/src/lib/checker/{admin-checker.d.ts → admin.d.ts} +0 -0
  144. /package/dist/src/lib/checker/{captcha-checker.d.ts → captcha.d.ts} +0 -0
  145. /package/src/style/{pagination.less → pagination.scss} +0 -0
@@ -0,0 +1,80 @@
1
+ import ListLite from './list-lite'
2
+ import * as Utils from '../lib/utils'
3
+ import * as Ui from '../lib/ui'
4
+ import $t from '../i18n'
5
+
6
+ /** 界面刷新 */
7
+ export function refreshUI(list: ListLite) {
8
+ // 无评论
9
+ const isNoComment = list.ctx.getCommentList().length <= 0
10
+ let $noComment = list.getCommentsWrapEl().querySelector<HTMLElement>('.atk-list-no-comment')
11
+
12
+ if (isNoComment) {
13
+ if (!$noComment) {
14
+ $noComment = Utils.createElement('<div class="atk-list-no-comment"></div>')
15
+ $noComment.innerHTML = list.noCommentText || list.ctx.conf.noComment || list.ctx.$t('noComment')
16
+ list.getCommentsWrapEl().appendChild($noComment)
17
+ }
18
+ } else {
19
+ $noComment?.remove()
20
+ }
21
+
22
+ // 仅管理员显示控制
23
+ list.ctx.checkAdminShowEl()
24
+ }
25
+
26
+ /** 构建错误提示界面 */
27
+ export function renderErrorDialog(list: ListLite, errMsg: string, errData?: any): HTMLElement {
28
+ const errEl = Utils.createElement(`<span>${errMsg},${$t('listLoadFailMsg')}<br/></span>`)
29
+
30
+ const $retryBtn = Utils.createElement(`<span style="cursor:pointer;">${$t('listRetry')}</span>`)
31
+ $retryBtn.onclick = () => (list.fetchComments(0))
32
+ errEl.appendChild($retryBtn)
33
+
34
+ const adminBtn = Utils.createElement('<span atk-only-admin-show> | <span style="cursor:pointer;">打开控制台</span></span>')
35
+ errEl.appendChild(adminBtn)
36
+ if (!list.ctx.user.data.isAdmin) adminBtn.classList.add('atk-hide')
37
+
38
+ let sidebarView = ''
39
+
40
+ // 找不到站点错误,打开侧边栏并填入创建站点表单
41
+ if (errData?.err_no_site) {
42
+ const viewLoadParam = {
43
+ create_name: list.ctx.conf.site,
44
+ create_urls: `${window.location.protocol}//${window.location.host}`
45
+ }
46
+ // TODO 真的是飞鸽传书啊
47
+ sidebarView = `sites|${JSON.stringify(viewLoadParam)}`
48
+ }
49
+
50
+ adminBtn.onclick = () => list.ctx.showSidebar({
51
+ view: sidebarView as any
52
+ })
53
+
54
+ return errEl
55
+ }
56
+
57
+ /** 版本检测弹窗 */
58
+ export function versionCheckDialog(list: ListLite, feVer: string, beVer: string): boolean {
59
+ const comp = Utils.versionCompare(feVer, beVer)
60
+ const notSameVer = (comp !== 0)
61
+ if (notSameVer) {
62
+ const errEl = Utils.createElement(
63
+ `<div>请更新 Artalk ${comp < 0 ? $t('frontend') : $t('backend')}以获得完整体验 ` +
64
+ `(<a href="https://artalk.js.org/" target="_blank">帮助文档</a>)` +
65
+ `<br/><br/><span style="color: var(--at-color-meta);">` +
66
+ `当前版本:${$t('frontend')} ${feVer} / ${$t('backend')} ${beVer}` +
67
+ `</span><br/><br/></div>`)
68
+ const ignoreBtn = Utils.createElement('<span style="cursor:pointer">忽略</span>')
69
+ ignoreBtn.onclick = () => {
70
+ Ui.setError(list.$el.parentElement!, null)
71
+ list.ctx.conf.versionCheck = false
72
+ list.fetchComments(0)
73
+ }
74
+ errEl.append(ignoreBtn)
75
+ Ui.setError(list.$el.parentElement!, errEl, '<span class="atk-warn-title">Artalk Warn</span>')
76
+ return true
77
+ }
78
+
79
+ return false
80
+ }
package/src/list/list.ts CHANGED
@@ -29,16 +29,26 @@ export default class List extends ListLite {
29
29
  // 操作按钮
30
30
  this.initListActionBtn()
31
31
 
32
+ const countNumHTML = '<span class="atk-comment-count-num">0</span>'
32
33
  this.$commentCount = this.$el.querySelector('.atk-comment-count')!
33
- this.$commentCount.innerHTML = this.$t('counter', {
34
- count: '<span class="atk-comment-count-num">0</span>',
35
- })
36
- this.$commentCountNum = this.$commentCount.querySelector('.atk-comment-count-num')!
34
+
35
+ const refreshCountNumEl = () => {
36
+ this.$commentCount.innerHTML = this.$t('counter', {
37
+ count: countNumHTML,
38
+ })
39
+ this.$commentCountNum = this.$commentCount.querySelector('.atk-comment-count-num')!
40
+ }
41
+ refreshCountNumEl()
37
42
 
38
43
  // copyright
39
44
  this.$el.querySelector<HTMLElement>('.atk-copyright')!.innerHTML = `Powered By <a href="https://artalk.js.org" target="_blank" title="Artalk v${ARTALK_VERSION}">Artalk</a>`
40
45
 
41
46
  // event listen
47
+ this.ctx.on('conf-loaded', () => {
48
+ // i18n support when locale changed
49
+ refreshCountNumEl()
50
+ this.refreshUI()
51
+ })
42
52
  }
43
53
 
44
54
  private initListActionBtn() {
@@ -73,8 +83,6 @@ export default class List extends ListLite {
73
83
  this.$openSidebarBtn.classList.add('atk-hide')
74
84
  }
75
85
 
76
- // 仅管理员显示控制
77
- this.ctx.checkAdminShowEl()
78
86
  this.$openSidebarBtn.querySelector<HTMLElement>('.atk-text')!
79
87
  .innerText = (!this.ctx.user.data.isAdmin) ? this.$t('msgCenter') : this.$t('ctrlCenter')
80
88
 
@@ -121,8 +129,7 @@ export default class List extends ListLite {
121
129
  const comment = this.ctx.findComment(commentId)
122
130
  if (!comment) { // 若找不到评论
123
131
  // 自动翻页
124
- if (this.readMoreBtn) this.readMoreBtn.click()
125
- else if (this.pagination) this.pagination.next()
132
+ this.pgHolder?.next()
126
133
  return
127
134
  }
128
135
 
@@ -154,6 +161,7 @@ export default class List extends ListLite {
154
161
  else window.setTimeout(() => goTo(), 350)
155
162
 
156
163
  this.goToCommentFounded = true
164
+ this.goToCommentDelay = true // reset
157
165
  }
158
166
 
159
167
  /** 管理员设置页面信息 */
@@ -0,0 +1,21 @@
1
+ import { IPgHolderConf, TPgMode } from '../index'
2
+ import PaginationAdaptor from './pagination'
3
+ import ReadMoreAdaptor from './read-more'
4
+
5
+ // 分页方式适配器
6
+ const Adaptors: Record<TPgMode, IPgAdaptor<any>> = {
7
+ 'pagination': PaginationAdaptor,
8
+ 'read-more': ReadMoreAdaptor
9
+ }
10
+
11
+ export default Adaptors
12
+
13
+ export interface IPgAdaptor<T> {
14
+ instance: T
15
+ el: HTMLElement
16
+ createInstance(conf: IPgHolderConf): [T, HTMLElement]
17
+ setLoading(val: boolean): void
18
+ update(offset: number, total: number): void
19
+ next(): void
20
+ showErr?(msg: string): void
21
+ }
@@ -0,0 +1,42 @@
1
+ import Pagination from '@/components/pagination'
2
+ import * as Utils from '@/lib/utils'
3
+ import { IPgAdaptor } from '.'
4
+
5
+ interface IPaginationAdaptor extends IPgAdaptor<Pagination> {}
6
+
7
+ /**
8
+ * 翻页形式的分页
9
+ */
10
+ export default <IPaginationAdaptor>{
11
+ createInstance(conf) {
12
+ const instance = new Pagination(conf.total, {
13
+ pageSize: conf.pageSize,
14
+ onChange: async (o) => {
15
+ if (conf.list.conf.editorTravel === true)
16
+ conf.list.ctx.editorTravelBack() // 防止评论框被吞
17
+
18
+ await conf.list.fetchComments(o)
19
+
20
+ // 滚动到第一个评论的位置
21
+ if (conf.list.repositionAt) {
22
+ const at = conf.list.scrollListenerAt || window
23
+ at.scroll({
24
+ top: conf.list.repositionAt ? Utils.getOffset(conf.list.repositionAt).top : 0,
25
+ left: 0,
26
+ })
27
+ }
28
+ }
29
+ })
30
+
31
+ return [instance, instance.$el]
32
+ },
33
+ setLoading(val) {
34
+ this.instance.setLoading(val)
35
+ },
36
+ update(offset, total) {
37
+ this.instance.update(offset, total)
38
+ },
39
+ next() {
40
+ this.instance.next()
41
+ }
42
+ }
@@ -0,0 +1,56 @@
1
+ import ReadMoreBtn from '@/components/read-more-btn'
2
+ import $t from '@/i18n'
3
+ import * as Ui from '@/lib/ui'
4
+ import { IPgAdaptor } from '.'
5
+
6
+ interface IReadMoreAdaptor extends IPgAdaptor<ReadMoreBtn> {
7
+ autoLoadScrollEvent?: () => void
8
+ }
9
+
10
+ /**
11
+ * 阅读更多形式的分页
12
+ */
13
+ export default <IReadMoreAdaptor>{
14
+ createInstance(conf) {
15
+ const readMoreBtn = new ReadMoreBtn({
16
+ pageSize: conf.pageSize,
17
+ onClick: async (o) => {
18
+ await conf.list.fetchComments(o)
19
+ },
20
+ text: $t('loadMore'),
21
+ })
22
+
23
+ // 滚动到底部自动加载
24
+ if (conf.list.conf.pagination.autoLoad) {
25
+ // 添加滚动事件监听
26
+ const at = conf.list.scrollListenerAt || document
27
+ if (this.autoLoadScrollEvent) at.removeEventListener('scroll', this.autoLoadScrollEvent) // 解除原有
28
+ this.autoLoadScrollEvent = () => {
29
+ if (conf.mode !== 'read-more'
30
+ || !readMoreBtn?.hasMore
31
+ || conf.list.getLoading()
32
+ ) return
33
+
34
+ const $target = conf.list.$el.querySelector<HTMLElement>('.atk-list-comments-wrap > .atk-comment-wrap:nth-last-child(3)') // 获取倒数第3个评论元素
35
+ if ($target && Ui.isVisible($target, conf.list.scrollListenerAt)) {
36
+ readMoreBtn.click() // 自动点击加载更多按钮
37
+ }
38
+ }
39
+ at.addEventListener('scroll', this.autoLoadScrollEvent)
40
+ }
41
+
42
+ return [readMoreBtn, readMoreBtn.$el]
43
+ },
44
+ setLoading(val) {
45
+ this.instance.setLoading(val)
46
+ },
47
+ update(offset, total) {
48
+ this.instance.update(offset, total)
49
+ },
50
+ showErr(msg) {
51
+ this.instance.showErr(msg)
52
+ },
53
+ next() {
54
+ this.instance.click()
55
+ }
56
+ }
@@ -0,0 +1,55 @@
1
+ import { IPgHolderConf, TPgMode } from '.'
2
+ import Adaptors from './adaptors'
3
+
4
+ /**
5
+ * 分页方式持有者(调度器)
6
+ */
7
+ export default class PgHolder {
8
+ private conf: IPgHolderConf
9
+
10
+ constructor(conf: IPgHolderConf) {
11
+ this.conf = conf
12
+ this.init()
13
+ }
14
+
15
+ public getAdaptor() {
16
+ return Adaptors[this.conf.mode]
17
+ }
18
+
19
+ public init() {
20
+ const adaptor = this.getAdaptor()
21
+ const [instance, el] = adaptor.createInstance(this.conf)
22
+ adaptor.instance = instance
23
+ adaptor.el = el
24
+ this.conf.list.$el.append(adaptor.el)
25
+ }
26
+
27
+ public setLoading(val: boolean) {
28
+ this.getAdaptor().setLoading(val)
29
+ }
30
+
31
+ public update(offset: number, total: number) {
32
+ this.getAdaptor().update(offset, total)
33
+ }
34
+
35
+ public getEl() {
36
+ return this.getAdaptor().el
37
+ }
38
+
39
+ public showErr(msg: string) {
40
+ const func = this.getAdaptor().showErr
41
+ if (func) func(msg)
42
+ }
43
+
44
+ public setMode(val: TPgMode) {
45
+ if (val !== this.conf.mode) {
46
+ this.getEl().remove()
47
+ this.conf.mode = val
48
+ this.init()
49
+ }
50
+ }
51
+
52
+ public next() {
53
+ this.getAdaptor().next()
54
+ }
55
+ }
@@ -0,0 +1,13 @@
1
+ import ListLite from '../list-lite'
2
+ import PgHolder from './holder'
3
+
4
+ export type TPgMode = 'pagination'|'read-more'
5
+
6
+ export interface IPgHolderConf {
7
+ list: ListLite
8
+ mode: TPgMode
9
+ total: number
10
+ pageSize: number
11
+ }
12
+
13
+ export default PgHolder
package/src/service.ts ADDED
@@ -0,0 +1,118 @@
1
+ import Context from '~/types/context'
2
+ import CheckerLauncher from './lib/checker'
3
+ import Api from './api'
4
+ import Editor from './editor'
5
+ import List from './list'
6
+ import Layer from './layer'
7
+ import SidebarLayer from './layer/sidebar-layer'
8
+ import { initMarked } from './lib/marked'
9
+ import User from './lib/user'
10
+ import ListLite from './list/list-lite'
11
+ import * as DarkMode from './lib/dark-mode'
12
+ import * as I18n from './i18n'
13
+
14
+ /**
15
+ * Services
16
+ *
17
+ * @description
18
+ * 当函数有返回值时,该值将自动添加到 Context 类作为一个 private 成员的值,
19
+ * 这个成员的名称与函数名对应。可在 Context 类中访问该对象(同事类)。
20
+ */
21
+ const services = {
22
+ // I18n
23
+ i18n(ctx: Context) {
24
+ I18n.setLocale(ctx.conf.locale)
25
+
26
+ ctx.on('conf-loaded', () => {
27
+ I18n.setLocale(ctx.conf.locale)
28
+ })
29
+ },
30
+
31
+ // Markdown 组件
32
+ markdown() {
33
+ initMarked()
34
+ },
35
+
36
+ // User Store
37
+ user(ctx: Context) {
38
+ const user = new User(ctx)
39
+ return user
40
+ },
41
+
42
+ // HTTP API client
43
+ api(ctx: Context) {
44
+ const api = new Api(ctx)
45
+ return api
46
+ },
47
+
48
+ // CheckerLauncher
49
+ checkerLauncher(ctx: Context) {
50
+ const checkerLauncher = new CheckerLauncher(ctx)
51
+ return checkerLauncher
52
+ },
53
+
54
+ // 编辑器
55
+ editor(ctx: Context) {
56
+ const editor = new Editor(ctx)
57
+ ctx.$root.appendChild(editor.$el)
58
+ return editor
59
+ },
60
+
61
+ // 评论列表
62
+ list(ctx: Context): ListLite|undefined {
63
+ // 评论列表
64
+ const list = new List(ctx)
65
+ ctx.$root.appendChild(list.$el)
66
+
67
+ // 评论获取
68
+ list.fetchComments(0)
69
+
70
+ return list
71
+ },
72
+
73
+ // 弹出层
74
+ layer(ctx: Context) {
75
+ // 记录页面原始 CSS 属性
76
+ Layer.BodyOrgOverflow = document.body.style.overflow
77
+ Layer.BodyOrgPaddingRight = document.body.style.paddingRight
78
+ },
79
+
80
+ // 侧边栏 Layer
81
+ sidebarLayer(ctx: Context) {
82
+ const sidebarLayer = new SidebarLayer(ctx)
83
+ return sidebarLayer
84
+ },
85
+
86
+ // 默认事件绑定
87
+ eventsDefault(ctx: Context) {
88
+ // 锚点快速跳转评论
89
+ window.addEventListener('hashchange', () => {
90
+ ctx.listHashGotoCheck()
91
+ })
92
+
93
+ // 本地用户数据变更
94
+ ctx.on('user-changed', () => {
95
+ ctx.checkAdminShowEl()
96
+ ctx.listRefreshUI()
97
+ })
98
+ },
99
+
100
+ // 夜间模式
101
+ darkMode(ctx: Context) {
102
+ DarkMode.syncDarkModeConf(ctx)
103
+
104
+ ctx.on('conf-loaded', () => {
105
+ DarkMode.syncDarkModeConf(ctx)
106
+ })
107
+ }
108
+ }
109
+
110
+ export default services
111
+
112
+ // type tricks for dependency injection
113
+ type TServiceImps = typeof services
114
+ type TObjectWithFuncs = {[k: string]: (...args: any) => any}
115
+ type TKeysOnlyReturn<T extends TObjectWithFuncs, V> = {[K in keyof T]: ReturnType<T[K]> extends V ? K : never}[keyof T]
116
+ type TOmitConditions = TKeysOnlyReturn<TServiceImps, void>
117
+ type TServiceInjectors = Omit<TServiceImps, TOmitConditions>
118
+ export type TInjectedServices = {[K in keyof TServiceInjectors]: ReturnType<TServiceInjectors[K]>}
@@ -8,7 +8,7 @@
8
8
  --at-color-light: #4285f4;
9
9
  --at-color-bg: #FFF;
10
10
  --at-color-bg-transl: rgba(255, 255, 255, 0.94);
11
- --at-color-bg-grey: #F4F4F4;
11
+ --at-color-bg-grey: #f3f4f5;
12
12
  --at-color-bg-grey-transl: rgba(244, 244, 244, 0.75);
13
13
  --at-color-bg-light: rgba(29, 161, 242, 0.1);
14
14
  --at-color-main: #0083ff;
@@ -16,7 +16,7 @@
16
16
  --at-color-pink: #fa5a57;
17
17
  --at-color-yellow: #ff7c37;
18
18
  --at-color-green: #4caf50;
19
- --at-color-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0), rgba(255, 255, 255));
19
+ --at-color-gradient: linear-gradient(180deg, transparent, rgba(255, 255, 255));
20
20
 
21
21
  &.atk-dark-mode {
22
22
  --at-color-font: #fff;
@@ -36,6 +36,6 @@
36
36
  --at-color-pink: #fa5a57;
37
37
  --at-color-yellow: #ff7c37;
38
38
  --at-color-green: #4caf50;
39
- --at-color-gradient: linear-gradient(180deg, rgba(30, 34, 36, 0), rgba(30, 34, 36, 1));
39
+ --at-color-gradient: linear-gradient(180deg, transparent, rgba(30, 34, 36, 1));
40
40
  }
41
41
  }
@@ -1,4 +1,4 @@
1
- .atk-slim-scrollbar() {
1
+ .atk-slim-scrollbar {
2
2
  &::-webkit-scrollbar {
3
3
  width: 4px;
4
4
  height: 4px;
@@ -0,0 +1,3 @@
1
+ $font-size: 14px;
2
+ $line-height: 1.6em;
3
+ $block-radius: 6px;
@@ -51,7 +51,7 @@
51
51
  img {
52
52
  width: 50px;
53
53
  height: 50px;
54
- border-radius: (@block-radius / 2);
54
+ border-radius: calc($block-radius / 2);
55
55
  }
56
56
  }
57
57
 
@@ -60,7 +60,7 @@
60
60
  margin-left: 63px;
61
61
 
62
62
  & > .atk-header {
63
- line-height: 1;
63
+ line-height: 1.5;
64
64
  font-size: 13px;
65
65
  margin-bottom: .5em;
66
66
  overflow: hidden;
@@ -108,13 +108,13 @@
108
108
  }
109
109
  }
110
110
 
111
- .badge() {
111
+ .badge {
112
112
  display: inline-block;
113
113
  color: var(--at-color-meta);
114
114
  background: var(--at-color-bg-grey);
115
115
  padding: 0px 6px;
116
116
  line-height: 17px;
117
- border-radius: (@block-radius / 2);
117
+ border-radius: calc($block-radius / 2);
118
118
 
119
119
  &:not(:last-child) {
120
120
  margin-right: 6px;
@@ -126,24 +126,27 @@
126
126
  }
127
127
 
128
128
  .atk-badge {
129
- .badge();
129
+ @extend .badge;
130
130
  color: #fff;
131
131
  }
132
132
 
133
+ .atk-region-badge {
134
+ @extend .badge;
135
+ }
136
+
133
137
  .atk-pinned-badge {
134
- .badge();
138
+ @extend .badge;
135
139
  color: #fff;
136
140
  background: #f44336;
137
141
  }
138
142
 
139
143
  .atk-ua {
140
- .badge()
144
+ @extend .badge;
141
145
  }
142
146
 
143
147
  .atk-ua-wrap {
144
148
  @media only screen and (max-width: 768px) {
145
149
  display: block;
146
- margin-top: 5px;
147
150
  }
148
151
  }
149
152
  }
@@ -164,7 +167,7 @@
164
167
  border: 3px solid var(--at-color-bg-grey);
165
168
  border-bottom: 0;
166
169
  padding: 5px 10px;
167
- border-radius: @block-radius @block-radius 0 0;
170
+ border-radius: $block-radius $block-radius 0 0;
168
171
  margin-bottom: -5px;
169
172
  }
170
173
 
@@ -193,7 +196,7 @@
193
196
 
194
197
  & > .atk-reply-to {
195
198
  padding: 5px 15px;
196
- border-left: 3px solid #eee;
199
+ border-left: 3px solid var(--at-color-border);
197
200
  margin-bottom: 10px;
198
201
  position: relative;
199
202
  margin-top: 10px;
@@ -217,7 +220,7 @@
217
220
  padding: 10px 18px;
218
221
  display: block;
219
222
  background: var(--at-color-bg-grey);
220
- border-radius: @block-radius;
223
+ border-radius: $block-radius;
221
224
 
222
225
  .atk-text {
223
226
  color: var(--at-color-meta);
@@ -291,7 +294,7 @@
291
294
  transform: translateX(-50%);
292
295
  cursor: pointer;
293
296
  border: 1px solid var(--at-color-border);
294
- border-radius: @block-radius;
297
+ border-radius: $block-radius;
295
298
  background: var(--at-color-bg);
296
299
  padding: 1px 20px;
297
300
  font-size: 15px;
@@ -307,6 +310,27 @@
307
310
  .atk-height-limit .atk-height-limit .atk-height-limit-btn {
308
311
  display: none;
309
312
  }
313
+
314
+ // 限高可滚动
315
+ .atk-height-limit-scroll {
316
+ // @extend .atk-slim-scrollbar;
317
+ margin-top: 10px;
318
+ overflow-y: auto;
319
+ $cBorder: var(--at-color-border);
320
+ $cOverwrite: var(--at-color-bg);
321
+ background:
322
+ /* Shadow covers */
323
+ linear-gradient($cOverwrite 1px, transparent 1px calc(100% - 1px)) center top,
324
+ linear-gradient(transparent calc(100% - 1px), $cOverwrite calc(100% - 1px) 1px) center bottom,
325
+ /* Shadows */
326
+ linear-gradient($cBorder 1px, transparent 1px calc(100% - 1px)) center top,
327
+ linear-gradient(transparent calc(100% - 1px), $cBorder calc(100% - 1px) 1px) center bottom;
328
+ background-repeat: no-repeat;
329
+ background-color: transparent;
330
+ background-size: 100% 1px, 100% 1px, 100% 1px, 100% 1px;
331
+ /* Opera doesn't support this in the shorthand */
332
+ background-attachment: local, local, scroll, scroll;
333
+ }
310
334
  }
311
335
 
312
336
  /** 子评论区域 **/
@@ -1,11 +1,11 @@
1
1
  .atk-main-editor {
2
- @editor-padding: 10px;
2
+ $editor-padding: 10px;
3
3
  z-index: 5;
4
4
  position: relative;
5
5
  overflow: hidden;
6
6
  background: var(--at-color-bg);
7
7
  border: 1px solid var(--at-color-border);
8
- border-radius: @block-radius;
8
+ border-radius: $block-radius;
9
9
  margin-bottom: 10px;
10
10
 
11
11
  @media only screen and (max-width: 768px) {
@@ -23,7 +23,7 @@
23
23
  font-size: 14px;
24
24
  background: transparent;
25
25
  border: 2px solid transparent;
26
- border-radius: (@block-radius / 2);
26
+ border-radius: calc($block-radius / 2);
27
27
  padding: 6px 5px;
28
28
  resize: none;
29
29
  outline: none;
@@ -44,7 +44,7 @@
44
44
  font-size: 15px;
45
45
  background-color: var(--at-color-bg);
46
46
  border: 2px solid transparent;
47
- border-radius: (@block-radius / 2);
47
+ border-radius: calc($block-radius / 2);
48
48
  width: 100%;
49
49
  min-height: 120px;
50
50
  margin-top: 2px;
@@ -70,7 +70,7 @@
70
70
  position: absolute;
71
71
  right: 7px;
72
72
  bottom: 2px;
73
- max-width: (100% - 20px);
73
+ max-width: calc(100% - 20px);
74
74
  height: 30px;
75
75
  line-height: 30px;
76
76
  font-size: 14px;
@@ -80,7 +80,7 @@
80
80
  text-overflow: ellipsis;
81
81
  white-space: nowrap;
82
82
  cursor: pointer;
83
- border-radius: (@block-radius / 2);
83
+ border-radius: calc($block-radius / 2);
84
84
 
85
85
  .atk-text {
86
86
  }
@@ -129,9 +129,9 @@
129
129
  line-height: 24px;
130
130
  cursor: pointer;
131
131
  color: var(--at-color-grey);
132
- font-size: @font-size;
132
+ font-size: $font-size;
133
133
  user-select: none;
134
- border-radius: (@block-radius / 2);
134
+ border-radius: calc($block-radius / 2);
135
135
 
136
136
  &:not(:last-child) {
137
137
  margin-right: 2px;
@@ -163,15 +163,15 @@
163
163
  .atk-send-btn {
164
164
  background: var(--at-color-main);
165
165
  color: #fff;
166
- font-size: @font-size;
166
+ font-size: $font-size;
167
167
  border: none;
168
- padding: 0 30px;
169
168
  margin: 0;
170
169
  height: 30px;
170
+ width: 8em;
171
171
  cursor: pointer;
172
172
  transition: opacity 0.3s ease-in-out;
173
173
  outline: none;
174
- border-radius: (@block-radius / 2);
174
+ border-radius: calc($block-radius / 2);
175
175
 
176
176
  &:active {
177
177
  opacity: 0.9;