koishi-plugin-monetary-bourse 3.0.0-alpha.20 → 3.0.1

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.
@@ -0,0 +1,8 @@
1
+ # Fonts
2
+
3
+ Place Roboto Mono font files here:
4
+
5
+ - RobotoMono-Regular.ttf
6
+ - RobotoMono-Bold.ttf
7
+
8
+ Source: https://fonts.google.com/specimen/Roboto+Mono (SIL OPEN FONT LICENSE Version 1.1)
Binary file
@@ -0,0 +1,10 @@
1
+ Place local SVG icon files for monetary-bourse templates here.
2
+
3
+ Current icons:
4
+
5
+ - stock.svg
6
+ - user.svg
7
+ - empty.svg
8
+ - clock.svg
9
+ - trend-up.svg
10
+ - trend-down.svg
@@ -0,0 +1,4 @@
1
+ <svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <circle cx="32" cy="32" r="22" fill="#1F242E" stroke="#30363D" stroke-width="4"/>
3
+ <path d="M32 20V33L40 38" stroke="#F0F3F5" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
4
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M19 28H27L31 34H33L37 28H45" stroke="#8B949E" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"/>
3
+ <path d="M19 21L32 32L45 21" stroke="#58A6FF" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"/>
4
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M18 40L28 30L34 36L46 22" stroke="white" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round"/>
3
+ <path d="M39 22H46V29" stroke="white" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round"/>
4
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M20 24L29 33L34 28L44 40" stroke="#089981" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round"/>
3
+ <path d="M38 40H44V34" stroke="#089981" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round"/>
4
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M20 40L29 31L34 36L44 24" stroke="#F23645" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round"/>
3
+ <path d="M38 24H44V30" stroke="#F23645" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round"/>
4
+ </svg>
@@ -0,0 +1,4 @@
1
+ <svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <circle cx="32" cy="24" r="8" fill="white"/>
3
+ <path d="M20 46C21.9587 39.9249 26.1789 37 32 37C37.8211 37 42.0413 39.9249 44 46" stroke="white" stroke-width="4.5" stroke-linecap="round"/>
4
+ </svg>
package/lib/index.js CHANGED
@@ -30,14 +30,90 @@ module.exports = __toCommonJS(src_exports);
30
30
  var import_koishi2 = require("koishi");
31
31
 
32
32
  // src/render.ts
33
+ var import_fs = require("fs");
33
34
  var import_koishi = require("koishi");
34
35
  var import_path = require("path");
35
- var import_fs = require("fs");
36
+ var import_url = require("url");
37
+ var import_fs2 = require("fs");
36
38
  var templatesDir = (0, import_path.resolve)(__dirname, "templates");
39
+ var assetsDir = (0, import_path.resolve)(__dirname, "..", "assets");
40
+ var assetsBaseUrl = (0, import_url.pathToFileURL)(assetsDir).toString().replace(/\/$/, "");
41
+ var iconMap = {
42
+ stock: "icons/stock.svg",
43
+ user: "icons/user.svg",
44
+ empty: "icons/empty.svg",
45
+ clock: "icons/clock.svg",
46
+ trendUp: "icons/trend-up.svg",
47
+ trendDown: "icons/trend-down.svg"
48
+ };
49
+ var iconDataUrls = {};
50
+ function getAssetUrl(relativePath) {
51
+ return `${assetsBaseUrl}/${relativePath}`;
52
+ }
53
+ __name(getAssetUrl, "getAssetUrl");
54
+ for (const [key, relativePath] of Object.entries(iconMap)) {
55
+ try {
56
+ const absolutePath = (0, import_path.resolve)(assetsDir, relativePath);
57
+ const buffer = (0, import_fs.readFileSync)(absolutePath);
58
+ iconDataUrls[key] = `data:image/svg+xml;base64,${buffer.toString("base64")}`;
59
+ } catch {
60
+ iconDataUrls[key] = getAssetUrl(relativePath);
61
+ }
62
+ }
63
+ function getFontFaceCss() {
64
+ const fontRegularUrl = getAssetUrl("fonts/RobotoMono-Regular.ttf");
65
+ const fontBoldUrl = getAssetUrl("fonts/RobotoMono-Bold.ttf");
66
+ return `
67
+ @font-face {
68
+ font-family: 'Roboto Mono';
69
+ src: url('${fontRegularUrl}') format('truetype');
70
+ font-weight: 400;
71
+ font-style: normal;
72
+ font-display: swap;
73
+ }
74
+
75
+ @font-face {
76
+ font-family: 'Roboto Mono';
77
+ src: url('${fontBoldUrl}') format('truetype');
78
+ font-weight: 700;
79
+ font-style: normal;
80
+ font-display: swap;
81
+ }
82
+ `;
83
+ }
84
+ __name(getFontFaceCss, "getFontFaceCss");
85
+ function injectFontFace(template) {
86
+ return template.replace("{{FONT_FACE}}", getFontFaceCss());
87
+ }
88
+ __name(injectFontFace, "injectFontFace");
89
+ function getIconUrl(iconName) {
90
+ return iconDataUrls[iconName] || "";
91
+ }
92
+ __name(getIconUrl, "getIconUrl");
93
+ function replaceTemplateTokens(template, replacements) {
94
+ let result = template;
95
+ for (const [key, value] of Object.entries(replacements)) {
96
+ result = result.replace(new RegExp(key, "g"), () => value);
97
+ }
98
+ return result;
99
+ }
100
+ __name(replaceTemplateTokens, "replaceTemplateTokens");
101
+ function injectStaticAssets(template) {
102
+ return replaceTemplateTokens(injectFontFace(template), {
103
+ "{{ICON_STOCK}}": getIconUrl("stock"),
104
+ "{{ICON_USER}}": getIconUrl("user"),
105
+ "{{ICON_EMPTY}}": getIconUrl("empty"),
106
+ "{{ICON_CLOCK}}": getIconUrl("clock"),
107
+ "{{ICON_TREND_UP}}": getIconUrl("trendUp"),
108
+ "{{ICON_TREND_DOWN}}": getIconUrl("trendDown")
109
+ });
110
+ }
111
+ __name(injectStaticAssets, "injectStaticAssets");
37
112
  async function renderHoldingImage(ctx, logger2, username, holding, pending, currency) {
38
113
  try {
39
114
  const templatePath = (0, import_path.resolve)(templatesDir, "holding-card.html");
40
- let template = await import_fs.promises.readFile(templatePath, "utf-8");
115
+ let template = await import_fs2.promises.readFile(templatePath, "utf-8");
116
+ template = injectStaticAssets(template);
41
117
  const data = {
42
118
  username,
43
119
  holding,
@@ -65,7 +141,8 @@ __name(renderHoldingImage, "renderHoldingImage");
65
141
  async function renderTradeResultImage(ctx, logger2, tradeType, stockName, amount, tradePrice, totalCost, currency, priceHistory, sellInfo, newHolding, tradeMeta) {
66
142
  try {
67
143
  const templatePath = (0, import_path.resolve)(templatesDir, "trade-result.html");
68
- let template = await import_fs.promises.readFile(templatePath, "utf-8");
144
+ let template = await import_fs2.promises.readFile(templatePath, "utf-8");
145
+ template = injectStaticAssets(template);
69
146
  const tradeIndex = priceHistory.length - 1;
70
147
  const status = tradeMeta?.status ?? "settled";
71
148
  const pendingMinutes = tradeMeta?.pendingMinutes ?? 0;
@@ -126,7 +203,8 @@ async function renderStockImage(ctx, logger2, data, name2, viewLabel, current, h
126
203
  includeEmpty: klineOptions?.includeEmpty
127
204
  }) : [];
128
205
  const templatePath = (0, import_path.resolve)(templatesDir, "stock-chart.html");
129
- let html = await import_fs.promises.readFile(templatePath, "utf-8");
206
+ let html = await import_fs2.promises.readFile(templatePath, "utf-8");
207
+ html = injectStaticAssets(html);
130
208
  const colorScheme = {
131
209
  mainColor: isUp ? "#f23645" : "#089981",
132
210
  gradientStart: isUp ? "rgba(242, 54, 69, 0.25)" : "rgba(8, 153, 129, 0.25)",
@@ -154,7 +232,7 @@ async function renderStockImage(ctx, logger2, data, name2, viewLabel, current, h
154
232
  }),
155
233
  "{{CURRENT_PRICE}}": current.toFixed(2),
156
234
  "{{CHANGE_VALUE}}": `${change >= 0 ? "+" : ""}${change.toFixed(2)}`,
157
- "{{CHANGE_ICON}}": change >= 0 ? "" : "",
235
+ "{{CHANGE_ICON}}": change >= 0 ? getIconUrl("trendUp") : getIconUrl("trendDown"),
158
236
  "{{CHANGE_PERCENT}}": Math.abs(changePercent).toFixed(2),
159
237
  "{{CHART_TYPE}}": chartType,
160
238
  "{{HIGH_PRICE}}": high.toFixed(2),
@@ -168,9 +246,7 @@ async function renderStockImage(ctx, logger2, data, name2, viewLabel, current, h
168
246
  "{{KLINE_DATA}}": JSON.stringify(klineData),
169
247
  "{{G2_SCRIPT}}": ""
170
248
  };
171
- for (const [key, value] of Object.entries(replacements)) {
172
- html = html.replace(new RegExp(key, "g"), () => value);
173
- }
249
+ html = replaceTemplateTokens(html, replacements);
174
250
  const page = await ctx.puppeteer.page();
175
251
  try {
176
252
  await page.setContent(html);
package/lib/render.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Context, h, Logger } from "koishi";
2
+ export declare function injectStaticAssets(template: string): string;
2
3
  export type PricePoint = {
3
4
  time: string;
4
5
  price: number;
@@ -2,6 +2,8 @@
2
2
 
3
3
  <head>
4
4
  <style>
5
+ {{FONT_FACE}}
6
+
5
7
  :root {
6
8
  /* 统一配色 (From trade-result) */
7
9
  --bg-color: #0c0f15;
@@ -25,7 +27,7 @@
25
27
  position: relative;
26
28
  margin: 0;
27
29
  padding: 32px;
28
- font-family: 'Roboto Mono', 'Trebuchet MS', 'Inter', -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
30
+ font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
29
31
  background-color: var(--bg-color);
30
32
  width: 480px;
31
33
  box-sizing: border-box;
@@ -58,12 +60,15 @@
58
60
  display: flex;
59
61
  align-items: center;
60
62
  justify-content: center;
61
- color: white;
62
- font-size: 24px;
63
- font-weight: 700;
64
63
  box-shadow: 0 8px 16px rgba(99, 102, 241, 0.3);
65
64
  }
66
65
 
66
+ .avatar-icon {
67
+ width: 28px;
68
+ height: 28px;
69
+ display: block;
70
+ }
71
+
67
72
  .user-info {
68
73
  flex: 1;
69
74
  }
@@ -157,7 +162,7 @@
157
162
  color: var(--text-primary);
158
163
  padding: 4px 10px;
159
164
  border-radius: 6px;
160
- font-family: 'Roboto Mono', 'Trebuchet MS', monospace;
165
+ font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
161
166
  }
162
167
 
163
168
  .stock-body {
@@ -185,7 +190,7 @@
185
190
  font-size: 18px;
186
191
  font-weight: 450;
187
192
  color: var(--text-primary);
188
- font-family: 'Roboto Mono', 'Trebuchet MS', monospace;
193
+ font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
189
194
  letter-spacing: -0.3px;
190
195
  }
191
196
 
@@ -247,7 +252,10 @@
247
252
  }
248
253
 
249
254
  .empty-icon {
250
- font-size: 32px;
255
+ width: 36px;
256
+ height: 36px;
257
+ display: block;
258
+ margin: 0 auto 12px;
251
259
  margin-bottom: 12px;
252
260
  opacity: 0.5;
253
261
  }
@@ -359,10 +367,26 @@
359
367
  font-size: 18px;
360
368
  font-weight: 600;
361
369
  color: var(--text-primary);
362
- font-family: 'Roboto Mono', monospace;
370
+ font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
363
371
  letter-spacing: -0.5px;
364
372
  }
365
373
 
374
+ .inline-icon {
375
+ width: 16px;
376
+ height: 16px;
377
+ display: inline-block;
378
+ vertical-align: middle;
379
+ flex-shrink: 0;
380
+ }
381
+
382
+ .stock-name-icon {
383
+ width: 18px;
384
+ height: 18px;
385
+ display: inline-block;
386
+ vertical-align: middle;
387
+ flex-shrink: 0;
388
+ }
389
+
366
390
  /* Bottom Row */
367
391
  .pi-footer {
368
392
  display: flex;
@@ -401,9 +425,9 @@
401
425
  </head>
402
426
 
403
427
  <body>
404
- <div class="card">
405
- <div class="header">
406
- <div class="avatar" id="avatar"></div>
428
+ <div class="card">
429
+ <div class="header">
430
+ <div class="avatar"><img class="avatar-icon" src="{{ICON_USER}}" alt="" /></div>
407
431
  <div class="user-info">
408
432
  <div class="username" id="username"></div>
409
433
  <div class="account-label">股票账户</div>
@@ -422,7 +446,6 @@
422
446
  const DATA = JSON.parse(document.getElementById('data-source').textContent);
423
447
 
424
448
  // 渲染用户信息
425
- document.getElementById('avatar').textContent = DATA.username.charAt(0).toUpperCase();
426
449
  document.getElementById('username').textContent = DATA.username;
427
450
  document.getElementById('footer').textContent = `数据更新于 ${DATA.updateTime}`;
428
451
 
@@ -452,7 +475,7 @@
452
475
  <div class="stock-card">
453
476
  <div class="stock-header">
454
477
  <div class="stock-name">
455
- <span>⚡</span>
478
+ <img class="stock-name-icon" src="{{ICON_STOCK}}" alt="" />
456
479
  ${h.stockName}
457
480
  </div>
458
481
  <div class="stock-amount">${h.amount} 股</div>
@@ -486,7 +509,7 @@
486
509
  <div class="section">
487
510
  <div class="section-title">持仓详情 Holding</div>
488
511
  <div class="empty-state">
489
- <div class="empty-icon">📭</div>
512
+ <img class="empty-icon" src="{{ICON_EMPTY}}" alt="" />
490
513
  <div class="empty-text">暂无持仓</div>
491
514
  </div>
492
515
  </div>
@@ -500,7 +523,7 @@
500
523
  <div class="pending-item">
501
524
  <div class="pi-header">
502
525
  <span class="pi-badge ${p.typeClass}">${p.type}</span>
503
- <span class="pi-time">⏱ ${p.timeLeft}</span>
526
+ <span class="pi-time"><img class="inline-icon" src="{{ICON_CLOCK}}" alt="" />${p.timeLeft}</span>
504
527
  </div>
505
528
  <div class="pi-amount">${p.amount} 股</div>
506
529
  <div class="pi-footer">
@@ -544,4 +567,4 @@
544
567
  </script>
545
568
  </body>
546
569
 
547
- </html>
570
+ </html>
@@ -1,6 +1,8 @@
1
1
  <html>
2
2
  <head>
3
3
  <style>
4
+ {{FONT_FACE}}
5
+
4
6
  :root {
5
7
  --main-color: {{MAIN_COLOR}};
6
8
  --glow-color: {{GLOW_COLOR}};
@@ -23,7 +25,7 @@
23
25
 
24
26
  body {
25
27
  padding: 64px;
26
- font-family: 'Roboto Mono', 'Trebuchet MS', 'Inter', -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
28
+ font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
27
29
  background-color: var(--bg-color);
28
30
  width: 1024px;
29
31
  height: 768px;
@@ -77,10 +79,15 @@
77
79
  display: flex;
78
80
  align-items: center;
79
81
  justify-content: center;
80
- font-size: 24px;
81
82
  box-shadow: 0 8px 20px var(--icon-shadow);
82
83
  }
83
84
 
85
+ .stock-icon img {
86
+ width: 24px;
87
+ height: 24px;
88
+ display: block;
89
+ }
90
+
84
91
  .meta-info {
85
92
  display: flex;
86
93
  align-items: center;
@@ -97,7 +104,7 @@
97
104
  .price-group { text-align: right; }
98
105
 
99
106
  .current-price {
100
- font-family: 'Roboto Mono', 'Trebuchet MS', monospace;
107
+ font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
101
108
  font-size: 64px;
102
109
  font-weight: normal;
103
110
  color: var(--main-color);
@@ -125,6 +132,12 @@
125
132
  gap: 4px;
126
133
  }
127
134
 
135
+ .change-icon {
136
+ width: 14px;
137
+ height: 14px;
138
+ display: block;
139
+ }
140
+
128
141
  .chart-wrapper {
129
142
  position: relative;
130
143
  flex: 1;
@@ -182,7 +195,7 @@
182
195
  font-size: 20px;
183
196
  font-weight: 600;
184
197
  color: var(--text-primary);
185
- font-family: 'Roboto Mono', monospace;
198
+ font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
186
199
  letter-spacing: 0;
187
200
  }
188
201
 
@@ -202,7 +215,7 @@
202
215
  <div class="header">
203
216
  <div class="title-group">
204
217
  <div class="stock-name">
205
- <div class="stock-icon">¥</div>
218
+ <div class="stock-icon"><img src="{{ICON_STOCK}}" alt="" /></div>
206
219
  {{STOCK_NAME}}
207
220
  </div>
208
221
  <div class="meta-info">
@@ -226,9 +239,10 @@
226
239
  "
227
240
  >{{CHANGE_VALUE}}</span
228
241
  >
229
- <span class="change-badge"
230
- >{{CHANGE_ICON}} {{CHANGE_PERCENT}}%</span
231
- >
242
+ <span class="change-badge">
243
+ <img class="change-icon" src="{{CHANGE_ICON}}" alt="" />
244
+ {{CHANGE_PERCENT}}%
245
+ </span>
232
246
  </div>
233
247
  </div>
234
248
  </div>
@@ -329,7 +343,7 @@
329
343
 
330
344
  const val = yMin + (i / 4) * yRange;
331
345
  ctx.fillStyle = "#64748b";
332
- ctx.font = '12px "Trebuchet MS", monospace';
346
+ ctx.font = '12px "Roboto Mono"';
333
347
  ctx.textAlign = "left";
334
348
  ctx.textBaseline = "middle";
335
349
  ctx.fillText(val.toFixed(2), W - padding.right + 10, y);
@@ -393,7 +407,7 @@
393
407
  ctx.setLineDash([]);
394
408
 
395
409
  const currentPriceStr = prices[prices.length - 1].toFixed(2);
396
- ctx.font = 'bold 13px "Trebuchet MS", monospace';
410
+ ctx.font = 'bold 13px "Roboto Mono"';
397
411
  const labelWidth = ctx.measureText(currentPriceStr).width + 18;
398
412
  const labelHeight = 24;
399
413
  const labelX = W - padding.right;
@@ -421,8 +435,7 @@
421
435
  ctx.textAlign = "center";
422
436
  ctx.textBaseline = "top";
423
437
  ctx.fillStyle = "#64748b";
424
- ctx.font =
425
- '12px -apple-system, BlinkMacSystemFont, "Inter", sans-serif';
438
+ ctx.font = '12px "Roboto Mono"';
426
439
  const labelIndexes = [
427
440
  0,
428
441
  Math.floor((times.length - 1) / 2),
@@ -4,6 +4,8 @@
4
4
  <head>
5
5
  <meta charset="utf-8">
6
6
  <style>
7
+ {{FONT_FACE}}
8
+
7
9
  :root {
8
10
  --bg-color: #0c0f15;
9
11
  --card-bg: #161b22;
@@ -37,7 +39,7 @@
37
39
  /* 修改 3: 增大页面外间距 (32px -> 64px) */
38
40
  padding: 64px;
39
41
  /* 统一字体组合 */
40
- font-family: 'Roboto Mono', 'Trebuchet MS', 'Inter', -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
42
+ font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
41
43
  background-color: var(--bg-color);
42
44
  width: 960px;
43
45
  height: 720px;
@@ -85,8 +87,12 @@
85
87
  display: flex;
86
88
  align-items: center;
87
89
  justify-content: center;
88
- font-size: 32px;
89
- color: var(--text-primary);
90
+ }
91
+
92
+ .stock-icon img {
93
+ width: 32px;
94
+ height: 32px;
95
+ display: block;
90
96
  }
91
97
 
92
98
  .stock-meta {
@@ -209,7 +215,7 @@
209
215
  color: var(--text-primary);
210
216
  font-weight: 600;
211
217
  /* 修改 2: 强制使用数字字体 */
212
- font-family: 'Roboto Mono', monospace;
218
+ font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
213
219
  }
214
220
 
215
221
  /* --- 图表区域 --- */
@@ -304,7 +310,7 @@
304
310
  opacity: 0.8;
305
311
  margin-top: 6px;
306
312
  /* 百分比也属于数字,用一下 Roboto Mono 更好看 */
307
- font-family: 'Roboto Mono', sans-serif;
313
+ font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
308
314
  }
309
315
 
310
316
  /* 底部时间戳 */
@@ -322,7 +328,7 @@
322
328
  color: var(--text-secondary);
323
329
  padding: 6px 8px;
324
330
  border-radius: 6px;
325
- font-family: 'Roboto Mono', sans-serif;
331
+ font-family: 'Roboto Mono', 'PingFang SC', 'Microsoft YaHei', sans-serif;
326
332
  /* 包含时间数字 */
327
333
  }
328
334
  </style>
@@ -333,7 +339,7 @@
333
339
  <!-- 顶部信息栏 -->
334
340
  <div class="top-section">
335
341
  <div class="stock-group">
336
- <div class="stock-icon">⚡</div>
342
+ <div class="stock-icon"><img src="{{ICON_STOCK}}" alt="" /></div>
337
343
  <div class="stock-meta">
338
344
  <div class="stock-name" id="stock-name">--</div>
339
345
  <!-- 这里会动态插入徽标 -->
@@ -604,4 +610,4 @@
604
610
  </script>
605
611
  </body>
606
612
 
607
- </html>
613
+ </html>
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "koishi-plugin-monetary-bourse",
3
- "version": "3.0.0-alpha.20",
3
+ "version": "3.0.1",
4
4
  "main": "lib/index.js",
5
5
  "typings": "lib/index.d.ts",
6
6
  "files": [
7
+ "assets",
7
8
  "lib",
8
9
  "dist"
9
10
  ],
package/readme.md CHANGED
@@ -1,12 +1,28 @@
1
- # koishi-plugin-monetary-bourse
1
+ <h1 align="center">koishi-plugin-monetary-bourse</h1>
2
2
 
3
- [![npm](https://img.shields.io/npm/v/koishi-plugin-monetary-bourse?style=flat-square)](https://www.npmjs.com/package/koishi-plugin-monetary-bourse) [![GitHub stars](https://img.shields.io/github/stars/BYWled/koishi-plugin-monetary-bourse?style=flat-square&logo=github)](https://github.com/BYWled/koishi-plugin-monetary-bourse) [![Gitee](https://img.shields.io/badge/Gitee-Project-c71d23?style=flat-square&logo=gitee&logoColor=white)](https://gitee.com/BYWled/koishi-plugin-monetary-bourse)
3
+ <p align="center">
4
+ 为 Koishi 提供基于 <code>monetary</code> 通用货币系统的股票交易所功能。
5
+ </p>
4
6
 
5
- Koishi 提供基于 `monetary` 通用货币系统的股票交易所功能。
7
+ <p align="center">
8
+ <a href="https://www.npmjs.com/package/koishi-plugin-monetary-bourse"><img src="https://img.shields.io/npm/v/koishi-plugin-monetary-bourse?style=for-the-badge&logo=npm" alt="npm" /></a>
9
+ <a href="https://github.com/BYWled/koishi-plugin-monetary-bourse"><img src="https://img.shields.io/github/stars/BYWled/koishi-plugin-monetary-bourse?style=for-the-badge&logo=github" alt="GitHub Stars" /></a>
10
+ </p>
11
+
12
+ <p align="center">
13
+ <a href="https://gitee.com/BYWled/koishi-plugin-monetary-bourse"><img src="https://img.shields.io/badge/Gitee-代码镜像-C71D23?style=for-the-badge&logo=gitee&logoColor=white" alt="Gitee Mirror" /></a>
14
+ <a href="https://gitcode.com/BYWled/koishi-plugin-monetary-bourse"><img src="https://img.shields.io/badge/GitCode-代码镜像-2962FF?style=for-the-badge" alt="GitCode Mirror" /></a>
15
+ </p>
16
+
17
+ <p align="center">
18
+ <a href="https://github.com/BYWled/koishi-plugin-monetary-bourse">GitHub</a> ·
19
+ <a href="https://gitee.com/BYWled/koishi-plugin-monetary-bourse">Gitee</a> ·
20
+ <a href="https://gitcode.com/BYWled/koishi-plugin-monetary-bourse">GitCode</a>
21
+ </p>
6
22
 
7
23
  本插件模拟了一个具备自动宏观调控、25种经典K线形态、智能概率博弈和可视化交割单的深度拟真股票市场。用户可以使用机器人通用的货币(如信用点)进行股票买卖、炒股理财。
8
24
 
9
- > 版本:**3.0.0-alpha.20**
25
+ > 版本:**3.0.1**
10
26
 
11
27
  ## ✨ 特性
12
28
 
@@ -17,11 +33,16 @@
17
33
  - **🖼️ 全可视化交互**:
18
34
  - **专业走势图**:复刻 TradingView 风格的深色玻璃拟态 K 线图,包含动态呼吸灯、渐变填充与详细指标。
19
35
  - **持仓资产卡片**:精美渲染个人持仓、成本分析、浮动盈亏比及排队中的挂单详情。
20
- - **交易交割单**:**(New)** 买卖成交瞬间生成**交易回单图片**,在 K 线图上精确标记买卖点位,直观展示单笔盈亏与买入成本线。
36
+ - **交易交割单**:买卖成交瞬间生成**交易回单图片**,在 K 线图上精确标记买卖点位,直观展示单笔盈亏与买入成本线。
37
+ - **内置字体**:使用 Roboto Mono 字体,确保图表中的数字和文本清晰易读。
38
+ - **内置图标**:使用内置的箭头和标记图标,增强视觉效果,无需外部资源。
21
39
  - **❄️ 资金冻结与挂单排队**:
22
40
  - 交易采用 T+0 机制,但大额资金/股票会根据金额计算**动态冻结时间**。
23
41
  - 挂单采用**串行排队模式**,同一用户的多个挂单需依次读秒,防止通过拆单绕过冻结机制,增加博弈深度。
24
42
  - **💸 手续费与精度控制**:支持卖出手续费配置,回单展示净到账金额;可开启整数精度模式,适配不支持小数的货币体系。
43
+ - **📊 分红与新闻系统**:
44
+ - 定期分红:根据持仓占比和预设利润率进行分红,支持分红播报。
45
+ - 新闻触发:当价格偏离宏观目标超过设定阈值时,自动从利好/利空新闻库中随机选取并播报,增加市场氛围。
25
46
  - **🧭 宏观周期固定刷新**:支持固定时刻刷新宏观目标周期,便于在活动时段塑造更清晰的趋势节奏。
26
47
  - **🏦 银行联动**:支持与[ `koishi-plugin-monetary-bank`](https://github.com/BYWled/koishi-plugin-monetary-bank)[![npm](https://img.shields.io/npm/v/koishi-plugin-monetary-bank?style=flat-square)](https://www.npmjs.com/package/koishi-plugin-monetary-bank) 联动,现金不足时自动扣除银行活期存款。
27
48
 
@@ -197,6 +218,9 @@ negativeNews:
197
218
 
198
219
  详细的更新日志请查看 [CHANGELOG.md](./CHANGELOG.md)。
199
220
 
221
+ ## 🙏 第三方资源/致谢
222
+ - Roboto Mono 字体(SIL OPEN FONT LICENSE Version 1.1):[字体链接](https://fonts.google.com/specimen/Roboto+Mono)
223
+
200
224
  ---
201
225
 
202
226
  **开发者**: BYWled