@waline/client 1.5.3 → 2.0.0-alpha.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 (94) hide show
  1. package/dist/component.js +2 -0
  2. package/dist/component.js.map +1 -0
  3. package/dist/pageview.cjs.js +2 -0
  4. package/dist/pageview.cjs.js.map +1 -0
  5. package/dist/pageview.d.ts +33 -0
  6. package/dist/pageview.esm.js +2 -0
  7. package/dist/pageview.esm.js.map +1 -0
  8. package/dist/pageview.js +2 -0
  9. package/dist/pageview.js.map +1 -0
  10. package/dist/{Waline.min.d.ts → shim.d.ts} +192 -261
  11. package/dist/{Waline.noStyle.d.ts → shim.esm.d.ts} +192 -261
  12. package/dist/shim.esm.js +2 -0
  13. package/dist/shim.esm.js.map +1 -0
  14. package/dist/shim.js +2 -0
  15. package/dist/shim.js.map +1 -0
  16. package/dist/waline.cjs.d.ts +388 -0
  17. package/dist/waline.cjs.js +2 -0
  18. package/dist/waline.cjs.js.map +1 -0
  19. package/dist/waline.css +1 -0
  20. package/dist/waline.css.map +1 -0
  21. package/dist/waline.d.ts +388 -0
  22. package/dist/waline.esm.d.ts +388 -0
  23. package/dist/waline.esm.js +2 -0
  24. package/dist/waline.esm.js.map +1 -0
  25. package/dist/waline.js +2 -0
  26. package/dist/waline.js.map +1 -0
  27. package/package.json +33 -18
  28. package/src/comment.ts +39 -0
  29. package/src/components/CommentBox.vue +667 -0
  30. package/src/components/CommentCard.vue +125 -0
  31. package/src/components/Icons.ts +124 -0
  32. package/src/components/Waline.vue +359 -0
  33. package/src/composables/index.ts +3 -0
  34. package/src/composables/inputs.ts +29 -0
  35. package/src/composables/store.ts +38 -0
  36. package/src/composables/userInfo.ts +27 -0
  37. package/src/config/default.ts +21 -0
  38. package/src/config/i18n/en.ts +34 -0
  39. package/src/config/i18n/generate.ts +39 -0
  40. package/src/config/i18n/index.ts +30 -0
  41. package/src/config/i18n/jp.ts +34 -0
  42. package/src/config/i18n/pt-BR.ts +34 -0
  43. package/src/config/i18n/ru.ts +34 -0
  44. package/src/config/i18n/vi-VN.ts +34 -0
  45. package/src/config/i18n/zh-CN.ts +34 -0
  46. package/src/config/i18n/zh-TW.ts +34 -0
  47. package/src/config/index.ts +2 -0
  48. package/src/entrys/components.ts +2 -0
  49. package/src/entrys/full.ts +7 -0
  50. package/src/entrys/init.ts +4 -0
  51. package/src/entrys/pageview.ts +2 -0
  52. package/src/init.ts +92 -0
  53. package/src/pageview.ts +100 -0
  54. package/src/shims-hanabi.d.ts +9 -0
  55. package/src/shims-vue.d.ts +5 -0
  56. package/src/styles/base.scss +67 -0
  57. package/src/styles/card.scss +223 -0
  58. package/src/styles/config.scss +52 -0
  59. package/src/styles/emoji.scss +118 -0
  60. package/src/styles/highlight.scss +135 -0
  61. package/src/styles/index.scss +12 -0
  62. package/src/styles/layout.scss +78 -0
  63. package/src/styles/nomalize.scss +112 -0
  64. package/src/styles/panel.scss +293 -0
  65. package/src/styles/recent.scss +3 -0
  66. package/src/typings/base.ts +54 -0
  67. package/src/typings/comment.ts +25 -0
  68. package/src/typings/index.ts +5 -0
  69. package/src/typings/locale.ts +32 -0
  70. package/src/typings/options.ts +41 -0
  71. package/src/typings/waline.ts +208 -0
  72. package/src/utils/config.ts +99 -0
  73. package/src/utils/darkmode.ts +11 -0
  74. package/src/utils/data.ts +10 -0
  75. package/src/utils/emoji.ts +75 -0
  76. package/src/utils/error.ts +3 -0
  77. package/src/utils/fetch.ts +177 -0
  78. package/src/utils/getRoot.ts +8 -0
  79. package/src/utils/index.ts +13 -0
  80. package/src/utils/markdown.ts +41 -0
  81. package/src/utils/markedMathExtension.ts +52 -0
  82. package/src/utils/path.ts +15 -0
  83. package/src/utils/query.ts +2 -0
  84. package/src/utils/timeAgo.ts +75 -0
  85. package/src/utils/userInfo.ts +26 -0
  86. package/src/utils/wordCount.ts +20 -0
  87. package/src/version.ts +3 -0
  88. package/src/widgets/index.ts +1 -0
  89. package/src/widgets/recentComments.ts +52 -0
  90. package/dist/Waline.min.js +0 -2
  91. package/dist/Waline.min.js.map +0 -1
  92. package/dist/Waline.noStyle.js +0 -2
  93. package/dist/Waline.noStyle.js.map +0 -1
  94. package/dist/index.html +0 -11
@@ -0,0 +1,99 @@
1
+ import {
2
+ defaultLang,
3
+ defaultUploadImage,
4
+ defaultTexRenderer,
5
+ getMeta,
6
+ locales,
7
+ } from '../config';
8
+
9
+ import { decodePath, isLinkHttp, removeEndingSplash } from './path';
10
+ import { getEmojis } from './emoji';
11
+
12
+ import type {
13
+ WalineEmojiInfo,
14
+ WalineEmojiMaps,
15
+ WalineLocale,
16
+ WalineProps,
17
+ } from '../typings';
18
+ import hanabi from 'hanabi';
19
+
20
+ export interface EmojiConfig {
21
+ tabs: Pick<WalineEmojiInfo, 'name' | 'icon' | 'items'>[];
22
+ map: WalineEmojiMaps;
23
+ }
24
+
25
+ export interface Config
26
+ extends Required<
27
+ Pick<
28
+ WalineProps,
29
+ | 'path'
30
+ | 'lang'
31
+ | 'meta'
32
+ | 'pageSize'
33
+ | 'requiredMeta'
34
+ | 'imageUploader'
35
+ | 'highlighter'
36
+ | 'texRenderer'
37
+ | 'copyright'
38
+ | 'login'
39
+ >
40
+ >,
41
+ Pick<WalineProps, 'dark' | 'serverURL'> {
42
+ locale: WalineLocale;
43
+ wordLimit: [number, number] | false;
44
+ emoji: Promise<EmojiConfig>;
45
+ }
46
+
47
+ const getServerURL = (serverURL: string): string => {
48
+ const result = removeEndingSplash(serverURL);
49
+
50
+ return isLinkHttp(result) ? result : `https://${result}`;
51
+ };
52
+
53
+ const fallback = <T = unknown>(
54
+ value: T | false | undefined,
55
+ fallback: T
56
+ ): T | false =>
57
+ typeof value === 'function' ? value : value === false ? false : fallback;
58
+
59
+ export const getConfig = ({
60
+ serverURL,
61
+
62
+ path = location.pathname,
63
+ lang = defaultLang,
64
+ locale,
65
+ emoji = ['https://cdn.jsdelivr.net/gh/walinejs/emojis@1.0.0/weibo'],
66
+ meta = ['nick', 'mail', 'link'],
67
+ requiredMeta = [],
68
+ pageSize = 10,
69
+ wordLimit,
70
+ imageUploader,
71
+ highlighter,
72
+ texRenderer,
73
+ copyright = true,
74
+ login = 'enable',
75
+ ...more
76
+ }: WalineProps): Config => ({
77
+ serverURL: getServerURL(serverURL),
78
+ path: decodePath(path),
79
+ lang,
80
+ locale: {
81
+ ...(locales[lang] || locales[defaultLang]),
82
+ ...(typeof locale === 'object' ? locale : {}),
83
+ },
84
+ emoji: getEmojis(emoji),
85
+ wordLimit: Array.isArray(wordLimit)
86
+ ? wordLimit
87
+ : wordLimit
88
+ ? [0, wordLimit]
89
+ : false,
90
+ meta: getMeta(meta),
91
+ requiredMeta: getMeta(requiredMeta),
92
+ pageSize,
93
+ login,
94
+ imageUploader: fallback(imageUploader, defaultUploadImage),
95
+ highlighter: fallback(highlighter, hanabi),
96
+ texRenderer: fallback(texRenderer, defaultTexRenderer),
97
+ copyright,
98
+ ...more,
99
+ });
@@ -0,0 +1,11 @@
1
+ const style = `{--waline-white:#000;--waline-light-grey:#666;--waline-dark-grey:#999;--waline-color:#888;--waline-bgcolor:#1e1e1e;--waline-bgcolor-light:#272727;--waline-bgcolor-hover: #444;--waline-border-color:#333;--waline-disable-bgcolor:#444;--waline-disable-color:#272727;--waline-bq-color:#272727;--waline-info-bgcolor:#272727;--waline-info-color:#666}`;
2
+
3
+ export const getDarkStyle = (selector?: string | boolean): string => {
4
+ if (typeof selector === 'string') {
5
+ return selector === 'auto'
6
+ ? `@media(prefers-color-scheme:dark){body${style}}`
7
+ : `${selector}${style}`;
8
+ }
9
+
10
+ return selector === true ? `:root${style}` : '';
11
+ };
@@ -0,0 +1,10 @@
1
+ const isImage = (item: DataTransferItem): boolean =>
2
+ item.type.includes('image');
3
+
4
+ export const getImagefromDataTransfer = (
5
+ items: DataTransferItemList
6
+ ): File | null => {
7
+ const image = Array.from(items).find(isImage);
8
+
9
+ return image ? (image.getAsFile() as File) : null;
10
+ };
@@ -0,0 +1,75 @@
1
+ import { Store, useStore } from '../composables/store';
2
+ import { removeEndingSplash } from './path';
3
+
4
+ import type { EmojiConfig } from './config';
5
+ import type { WalineEmojiInfo } from '../typings';
6
+
7
+ let store: Store;
8
+
9
+ const hasVersion = (url: string): boolean =>
10
+ Boolean(/@[0-9]+\.[0-9]+\.[0-9]+/.test(url));
11
+
12
+ const fetchEmoji = (link: string): Promise<WalineEmojiInfo> => {
13
+ if (!store) store = useStore('WALINE_EMOJI');
14
+
15
+ const result = hasVersion(link);
16
+
17
+ if (result) {
18
+ const info = store.get<WalineEmojiInfo>(link);
19
+ if (info) return Promise.resolve(info);
20
+ }
21
+
22
+ return fetch(`${link}/info.json`)
23
+ .then((resp) => resp.json() as Promise<Omit<WalineEmojiInfo, 'folder'>>)
24
+ .then((emojiInfo) => {
25
+ const info = {
26
+ folder: link,
27
+ ...emojiInfo,
28
+ };
29
+
30
+ if (result) store.set(link, info);
31
+
32
+ return info;
33
+ });
34
+ };
35
+
36
+ const getLink = (
37
+ name: string,
38
+ folder: string,
39
+ prefix = '',
40
+ type = ''
41
+ ): string => `${folder}/${prefix}${name}${type ? `.${type}` : ''}`;
42
+
43
+ export const getEmojis = (
44
+ emojis: (string | WalineEmojiInfo)[]
45
+ ): Promise<EmojiConfig> =>
46
+ Promise.all(
47
+ emojis.map((emoji) =>
48
+ typeof emoji === 'string'
49
+ ? fetchEmoji(removeEndingSplash(emoji))
50
+ : Promise.resolve(emoji)
51
+ )
52
+ ).then((emojiInfos) => {
53
+ const emojiConfig: EmojiConfig = {
54
+ tabs: [],
55
+ map: {},
56
+ };
57
+
58
+ emojiInfos.forEach((emojiInfo) => {
59
+ const { name, folder, icon, prefix, type, items } = emojiInfo;
60
+
61
+ emojiConfig.tabs.push({
62
+ name,
63
+ icon: getLink(icon, folder, prefix, type),
64
+ items: items.map((item) => {
65
+ const key = `${prefix || ''}${item}`;
66
+
67
+ emojiConfig.map[key] = getLink(item, folder, prefix, type);
68
+
69
+ return key;
70
+ }),
71
+ });
72
+ });
73
+
74
+ return emojiConfig;
75
+ });
@@ -0,0 +1,3 @@
1
+ export const errorHandler = (err: Error): void => {
2
+ if (err.name !== 'AbortError') console.error(err.message);
3
+ };
@@ -0,0 +1,177 @@
1
+ import type { WalineComment, WalineCommentData } from '../typings';
2
+
3
+ export interface FetchErrorData {
4
+ errno: number;
5
+ errmsg: string;
6
+ }
7
+
8
+ const errorCheck = <T = unknown>(data: T | FetchErrorData, name = ''): T => {
9
+ if (typeof data === 'object' && (data as FetchErrorData).errno)
10
+ throw new TypeError(
11
+ `Fetch ${name} failed with ${(data as FetchErrorData).errno}: ${
12
+ (data as FetchErrorData).errmsg
13
+ }`
14
+ );
15
+
16
+ return data as T;
17
+ };
18
+
19
+ export interface FetchCountOptions {
20
+ serverURL: string;
21
+ paths: string[];
22
+ signal: AbortSignal;
23
+ token?: string;
24
+ }
25
+
26
+ export const fetchCommentCount = ({
27
+ serverURL,
28
+ paths,
29
+ signal,
30
+ token,
31
+ }: FetchCountOptions): Promise<number[]> => {
32
+ const headers: Record<string, string> = {};
33
+ if (token) headers.Authorization = `Bearer ${token}`;
34
+
35
+ return (
36
+ fetch(
37
+ `${serverURL}/comment?type=count&url=${encodeURIComponent(
38
+ paths.join(',')
39
+ )}`,
40
+ { signal, headers }
41
+ )
42
+ .then((resp) => resp.json() as Promise<number | number[]>)
43
+ .then((data) => errorCheck(data, 'comment count'))
44
+ // TODO: Improve this API
45
+ .then((counts) => (Array.isArray(counts) ? counts : [counts]))
46
+ );
47
+ };
48
+ export interface FetchRecentOptions {
49
+ serverURL: string;
50
+ count: number;
51
+ signal: AbortSignal;
52
+ token?: string;
53
+ }
54
+
55
+ export const fetchRecentComment = ({
56
+ serverURL,
57
+ count,
58
+ signal,
59
+ token,
60
+ }: FetchRecentOptions): Promise<WalineComment[]> => {
61
+ const headers: Record<string, string> = {};
62
+ if (token) headers.Authorization = `Bearer ${token}`;
63
+
64
+ return fetch(`${serverURL}/comment?type=recent&count=${count}`, {
65
+ signal,
66
+ headers,
67
+ })
68
+ .then((resp) => resp.json() as Promise<WalineComment[]>)
69
+ .then((data) => errorCheck(data, 'recent comment'));
70
+ };
71
+
72
+ export interface FetchListOptions {
73
+ serverURL: string;
74
+ path: string;
75
+ page: number;
76
+ pageSize: number;
77
+ signal: AbortSignal;
78
+ token?: string;
79
+ }
80
+
81
+ export interface FetchListResult {
82
+ count: number;
83
+ data: WalineComment[];
84
+ totalPages: number;
85
+ }
86
+
87
+ export const fetchCommentList = ({
88
+ serverURL,
89
+ path,
90
+ page,
91
+ pageSize,
92
+ signal,
93
+ token,
94
+ }: FetchListOptions): Promise<FetchListResult> => {
95
+ const headers: Record<string, string> = {};
96
+ if (token) headers.Authorization = `Bearer ${token}`;
97
+
98
+ return fetch(
99
+ `${serverURL}/comment?path=${encodeURIComponent(
100
+ path
101
+ )}&pageSize=${pageSize}&page=${page}`,
102
+ { signal, headers }
103
+ )
104
+ .then((resp) => resp.json() as Promise<FetchListResult>)
105
+ .then((data) => errorCheck(data, 'comment list'));
106
+ };
107
+
108
+ export interface PostCommentOptions {
109
+ serverURL: string;
110
+ lang: string;
111
+ token?: string;
112
+ comment: WalineCommentData;
113
+ }
114
+
115
+ export interface PostCommentResponse {
116
+ data?: WalineComment;
117
+ errmsg?: string;
118
+ }
119
+
120
+ export const postComment = ({
121
+ serverURL,
122
+ lang,
123
+ token,
124
+ comment,
125
+ }: PostCommentOptions): Promise<PostCommentResponse> => {
126
+ const headers: Record<string, string> = {
127
+ // eslint-disable-next-line @typescript-eslint/naming-convention
128
+ 'Content-Type': 'application/json',
129
+ };
130
+
131
+ if (token) headers.Authorization = `Bearer ${token}`;
132
+
133
+ return fetch(`${serverURL}/comment?lang=${lang}`, {
134
+ method: 'POST',
135
+ headers,
136
+ body: JSON.stringify(comment),
137
+ }).then((resp) => resp.json() as Promise<PostCommentResponse>);
138
+ };
139
+
140
+ export interface FetchPageviewsOptions {
141
+ serverURL: string;
142
+ paths: string[];
143
+ signal: AbortSignal;
144
+ }
145
+
146
+ export const fetchPageviews = ({
147
+ serverURL,
148
+ paths,
149
+ signal,
150
+ }: FetchPageviewsOptions): Promise<number[]> =>
151
+ fetch(`${serverURL}/article?path=${encodeURIComponent(paths.join(','))}`, {
152
+ signal,
153
+ })
154
+ .then((resp) => resp.json() as Promise<number[] | number>)
155
+ .then((data) => errorCheck(data, 'visit count'))
156
+ // TODO: Improve this API
157
+ .then((counts) => (Array.isArray(counts) ? counts : [counts]));
158
+
159
+ export interface UpdatePageviewsOptions {
160
+ serverURL: string;
161
+ path: string;
162
+ }
163
+
164
+ export const updatePageviews = ({
165
+ serverURL,
166
+ path,
167
+ }: UpdatePageviewsOptions): Promise<number> =>
168
+ fetch(`${serverURL}/article`, {
169
+ method: 'POST',
170
+ headers: {
171
+ // eslint-disable-next-line @typescript-eslint/naming-convention
172
+ 'Content-Type': 'application/json',
173
+ },
174
+ body: JSON.stringify({ path }),
175
+ })
176
+ .then((resp) => resp.json() as Promise<number>)
177
+ .then((data) => errorCheck(data, 'visit count'));
@@ -0,0 +1,8 @@
1
+ export const getRoot = (
2
+ el: string | HTMLElement | undefined
3
+ ): HTMLElement | null =>
4
+ el instanceof HTMLElement
5
+ ? el
6
+ : typeof el === 'string'
7
+ ? document.querySelector(el)
8
+ : null;
@@ -0,0 +1,13 @@
1
+ export * from './config';
2
+ export * from './darkmode';
3
+ export * from './data';
4
+ export * from './emoji';
5
+ export * from './error';
6
+ export * from './fetch';
7
+ export * from './getRoot';
8
+ export * from './markdown';
9
+ export * from './path';
10
+ export * from './query';
11
+ export * from './timeAgo';
12
+ export * from './userInfo';
13
+ export * from './wordCount';
@@ -0,0 +1,41 @@
1
+ import { marked } from 'marked';
2
+ import { markedTexExtensions } from './markedMathExtension';
3
+
4
+ import type {
5
+ WalineEmojiMaps,
6
+ WalineHighlighter,
7
+ WalineTexRenderer,
8
+ } from '../typings';
9
+
10
+ export const parseEmoji = (text = '', emojiMap: WalineEmojiMaps = {}): string =>
11
+ text.replace(/:(.+?):/g, (placeholder, key: string) =>
12
+ emojiMap[key]
13
+ ? `<img class="vemoji" src="${emojiMap[key]}" alt="${key}">`
14
+ : placeholder
15
+ );
16
+
17
+ export interface ParseMarkdownOptions {
18
+ emojiMap: WalineEmojiMaps;
19
+ highlighter: WalineHighlighter | false;
20
+ texRenderer: WalineTexRenderer | false;
21
+ }
22
+
23
+ export const parseMarkdown = (
24
+ content: string,
25
+ { emojiMap, highlighter, texRenderer }: ParseMarkdownOptions
26
+ ): string => {
27
+ marked.setOptions({
28
+ highlight: highlighter || undefined,
29
+ breaks: true,
30
+ smartLists: true,
31
+ smartypants: true,
32
+ });
33
+
34
+ if (texRenderer) {
35
+ const extensions = markedTexExtensions(texRenderer);
36
+
37
+ marked.use({ extensions });
38
+ }
39
+
40
+ return marked.parse(parseEmoji(content, emojiMap));
41
+ };
@@ -0,0 +1,52 @@
1
+ import type { marked } from 'marked';
2
+ import type { WalineTexRenderer } from '../typings';
3
+
4
+ const inlineMathStart = /\$.*?\$/;
5
+ const inlineMathReg = /^\$(.*?)\$/;
6
+ const blockMathReg = /^(?:\s{0,3})\$\$((?:[^\n]|\n[^\n])+?)\n{0,1}\$\$/;
7
+
8
+ export const markedTexExtensions = (
9
+ texRenderer: WalineTexRenderer
10
+ ): marked.TokenizerExtension[] => {
11
+ const blockMathExtension: marked.TokenizerExtension = {
12
+ name: 'blockMath',
13
+ level: 'block',
14
+ tokenizer(src: string) {
15
+ const cap = blockMathReg.exec(src);
16
+
17
+ if (cap !== null) {
18
+ return {
19
+ type: 'html',
20
+ raw: cap[0],
21
+ text: texRenderer(true, cap[1]),
22
+ };
23
+ }
24
+
25
+ return undefined;
26
+ },
27
+ };
28
+
29
+ const inlineMathExtension: marked.TokenizerExtension = {
30
+ name: 'inlineMath',
31
+ level: 'inline',
32
+ start(src: string) {
33
+ const idx = src.search(inlineMathStart);
34
+ return idx !== -1 ? idx : src.length;
35
+ },
36
+ tokenizer(src: string) {
37
+ const cap = inlineMathReg.exec(src);
38
+
39
+ if (cap !== null) {
40
+ return {
41
+ type: 'html',
42
+ raw: cap[0],
43
+ text: texRenderer(false, cap[1]),
44
+ };
45
+ }
46
+
47
+ return undefined;
48
+ },
49
+ };
50
+
51
+ return [blockMathExtension, inlineMathExtension];
52
+ };
@@ -0,0 +1,15 @@
1
+ export const decodePath = (path: string): string => {
2
+ try {
3
+ path = decodeURI(path);
4
+ } catch (err) {
5
+ // ignore error
6
+ }
7
+
8
+ return path;
9
+ };
10
+
11
+ export const removeEndingSplash = (content = ''): string =>
12
+ content.replace(/\/$/u, '');
13
+
14
+ export const isLinkHttp = (link: string): boolean =>
15
+ /^(https?:)?\/\//.test(link);
@@ -0,0 +1,2 @@
1
+ export const getQuery = (element: HTMLElement): string | null =>
2
+ element.dataset.path || element.getAttribute('id');
@@ -0,0 +1,75 @@
1
+ import type { WalineLocale } from '../typings';
2
+
3
+ const padWithZeros = (vNumber: number, width: number): string => {
4
+ let numAsString = vNumber.toString();
5
+
6
+ while (numAsString.length < width) {
7
+ numAsString = '0' + numAsString;
8
+ }
9
+
10
+ return numAsString;
11
+ };
12
+
13
+ const dateFormat = (date: Date): string => {
14
+ const vDay = padWithZeros(date.getDate(), 2);
15
+ const vMonth = padWithZeros(date.getMonth() + 1, 2);
16
+ const vYear = padWithZeros(date.getFullYear(), 2);
17
+
18
+ return `${vYear}-${vMonth}-${vDay}`;
19
+ };
20
+
21
+ export const timeAgo = (date: Date | string, locale: WalineLocale): string => {
22
+ if (date)
23
+ try {
24
+ if (typeof date === 'string') {
25
+ // compat with mysql output date
26
+ date = new Date(
27
+ date.indexOf(' ') !== -1 ? date.replace(/-/g, '/') : date
28
+ );
29
+ }
30
+ const oldTime = date.getTime();
31
+ const currTime = new Date().getTime();
32
+ const diffValue = currTime - oldTime;
33
+
34
+ const days = Math.floor(diffValue / (24 * 3600 * 1000));
35
+
36
+ // 计算相差小时数
37
+ if (days === 0) {
38
+ // 计算天数后剩余的毫秒数
39
+ const leave1 = diffValue % (24 * 3600 * 1000);
40
+ const hours = Math.floor(leave1 / (3600 * 1000));
41
+
42
+ //计算相差分钟数
43
+ if (hours === 0) {
44
+ // 计算小时数后剩余的毫秒数
45
+ const leave2 = leave1 % (3600 * 1000);
46
+ const minutes = Math.floor(leave2 / (60 * 1000));
47
+
48
+ // 计算相差秒数
49
+ if (minutes === 0) {
50
+ // 计算分钟数后剩余的毫秒数
51
+ const leave3 = leave2 % (60 * 1000);
52
+ const seconds = Math.round(leave3 / 1000);
53
+
54
+ return `${seconds} ${locale.seconds}`;
55
+ }
56
+ return `${minutes} ${locale.minutes}`;
57
+ }
58
+ return `${hours} ${locale.hours}`;
59
+ }
60
+
61
+ if (days < 0) {
62
+ return locale.now;
63
+ }
64
+
65
+ if (days < 8) {
66
+ return `${days} ${locale.days}`;
67
+ } else {
68
+ return dateFormat(date);
69
+ }
70
+ } catch (error) {
71
+ console.log(error);
72
+ }
73
+
74
+ return '';
75
+ };
@@ -0,0 +1,26 @@
1
+ export interface UserInfo {
2
+ // eslint-disable-next-line @typescript-eslint/naming-convention
3
+ display_name: string;
4
+ email: string;
5
+ url: string;
6
+ token: string;
7
+ avatar: string;
8
+ mailMd5: string;
9
+ }
10
+
11
+ const USER_KEY = 'WALINE_USER';
12
+
13
+ export const getUserInfo = (): UserInfo | null => {
14
+ try {
15
+ const localStorageData = localStorage.getItem(USER_KEY);
16
+ const sessionStorageData = sessionStorage.getItem(USER_KEY);
17
+
18
+ return localStorageData
19
+ ? (JSON.parse(localStorageData) as UserInfo)
20
+ : sessionStorageData
21
+ ? (JSON.parse(sessionStorageData) as UserInfo)
22
+ : null;
23
+ } catch (err) {
24
+ return null;
25
+ }
26
+ };
@@ -0,0 +1,20 @@
1
+ /**
2
+ * The wordCount module should be lightweight as it's packed into client.
3
+ *
4
+ * So We just make a simple implement here
5
+ *
6
+ * Forked from https://github.com/vuepress-theme-hope/vuepress-theme-hope/blob/main/packages/reading-time2/src/node/reading-time.ts
7
+ */
8
+
9
+ export const getWords = (content: string): string[] =>
10
+ content.match(/[\w\d\s\u00C0-\u024F]+/giu) || [];
11
+
12
+ export const getChinese = (content: string): string[] =>
13
+ content.match(/[\u4E00-\u9FA5]/gu) || [];
14
+
15
+ export const getWordNumber = (content: string): number =>
16
+ getWords(content).reduce(
17
+ (accumulator, word) =>
18
+ accumulator + (word.trim() === '' ? 0 : word.trim().split(/\s+/u).length),
19
+ 0
20
+ ) + getChinese(content).length;
package/src/version.ts ADDED
@@ -0,0 +1,3 @@
1
+ declare const VERSION: string;
2
+
3
+ export const version = VERSION;
@@ -0,0 +1 @@
1
+ export * from './recentComments';