@minibar-app/calendar 0.0.0-stage → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/main.js ADDED
@@ -0,0 +1,95 @@
1
+ // Calendar — next event countdown via ctx.calendar.upcoming().
2
+ const LABELS = {
3
+ en: {
4
+ tomorrow: 'Tomorrow ', now: 'now', inMinutes: (m) => 'in ' + m + 'm', inHours: (h, m) => 'in ' + h + 'h' + (m ? ' ' + m + 'm' : ''), inDays: (d) => 'in ' + d + 'd',
5
+ nothing: 'Nothing scheduled', noEvents: (hours) => 'No events in the next ' + hours + ' hours.', allDay: 'All day', untitled: '(untitled)',
6
+ join: 'Join / open link', event: 'Event',
7
+ },
8
+ zh: {
9
+ tomorrow: '明天 ', now: '现在', inMinutes: (m) => m + ' 分钟后', inHours: (h, m) => h + ' 小时' + (m ? ' ' + m + ' 分' : '') + '后', inDays: (d) => d + ' 天后',
10
+ nothing: '暂无日程', noEvents: (hours) => '接下来 ' + hours + ' 小时没有日程。', allDay: '全天', untitled: '(无标题)',
11
+ join: '加入 / 打开链接', event: '日程',
12
+ },
13
+ };
14
+
15
+ function timeLabel(ms, locale) {
16
+ return new Date(ms).toLocaleTimeString(locale, { hour: 'numeric', minute: '2-digit' });
17
+ }
18
+
19
+ function dayLabel(ms, now, locale, t) {
20
+ const d = new Date(ms), n = new Date(now);
21
+ if (d.toDateString() === n.toDateString()) return '';
22
+ const tomorrow = new Date(n.getTime() + 86400 * 1000);
23
+ if (d.toDateString() === tomorrow.toDateString()) return t.tomorrow;
24
+ return d.toLocaleDateString(locale, { weekday: 'short' }) + ' ';
25
+ }
26
+
27
+ function countdown(ms, t) {
28
+ const minutes = Math.round(ms / 60000);
29
+ if (minutes <= 0) return t.now;
30
+ if (minutes < 60) return t.inMinutes(minutes);
31
+ const hours = Math.floor(minutes / 60);
32
+ const rest = minutes % 60;
33
+ if (hours < 24) return t.inHours(hours, rest);
34
+ return t.inDays(Math.round(hours / 24));
35
+ }
36
+
37
+ defineMiniApp({
38
+ id: 'com.minibar.calendar',
39
+
40
+ render(ctx) {
41
+ const ui = ctx.ui;
42
+ const t = ctx.i18n(LABELS);
43
+ const now = Date.now();
44
+ const events = ctx.calendar.upcoming({
45
+ hours: Math.round(ctx.settings.get('hours', 12)),
46
+ limit: Math.round(ctx.settings.get('maxEvents', 5)),
47
+ includeAllDay: ctx.settings.get('includeAllDay', false),
48
+ });
49
+ const alertMinutes = ctx.settings.get('alertMinutes', 5);
50
+
51
+ if (events.length === 0) {
52
+ return {
53
+ title: null,
54
+ body: [ui.row([ui.icon('calendar', { size: 20, color: 'secondary' }),
55
+ ui.column([ui.text(t.nothing, { style: 'headline' }),
56
+ ui.text(t.noEvents(Math.round(ctx.settings.get('hours', 12))), { style: 'caption' })], { spacing: 2 })], { spacing: 10 })],
57
+ };
58
+ }
59
+
60
+ const next = events.find((e) => e.start > now) || events[0];
61
+ const inProgress = next.start <= now && next.end > now;
62
+ const untilStart = next.start - now;
63
+ const soon = !inProgress && untilStart <= alertMinutes * 60000 && untilStart > 0;
64
+
65
+ const rows = events.map((e) => {
66
+ const isNext = e.id === next.id;
67
+ const when = e.allDay ? t.allDay : dayLabel(e.start, now, ctx.locale, t) + timeLabel(e.start, ctx.locale) + '–' + timeLabel(e.end, ctx.locale);
68
+ return ui.row([
69
+ ui.icon('circle.fill', { size: 7, color: isNext ? 'accent' : 'muted' }),
70
+ ui.column([
71
+ ui.text(e.title || t.untitled, { style: isNext ? 'headline' : 'body', maxLines: 1 }),
72
+ ui.text(when + (e.location ? ' · ' + e.location : '') + (e.calendar && e.calendar.title ? ' · ' + e.calendar.title : ''), { style: 'caption', maxLines: 1 }),
73
+ ], { spacing: 1 }),
74
+ ui.spacer(),
75
+ ...(isNext ? [ui.badge(inProgress ? t.now : countdown(e.start - now, t), { color: soon || inProgress ? 'warning' : 'accent' })] : []),
76
+ ], { spacing: 8, align: 'top' });
77
+ });
78
+
79
+ const body = [ui.column(rows, { spacing: 8 })];
80
+ if (next.url) body.push(ui.button(t.join, { url: next.url }, { icon: 'video' }));
81
+
82
+ const shortTitle = (next.title || t.event).slice(0, 18);
83
+ const title = inProgress
84
+ ? { text: shortTitle + ' · ' + t.now, icon: 'calendar', color: 'warning' }
85
+ : { text: shortTitle + ' · ' + countdown(untilStart, t), icon: 'calendar', color: soon ? 'warning' : undefined };
86
+
87
+ return {
88
+ title,
89
+ body,
90
+ urgency: soon ? 0.9 : 0,
91
+ urgencyTTL: Math.max(60, Math.min(3600, Math.round(untilStart / 1000) + 60)),
92
+ refreshIn: untilStart > 0 && untilStart < 90 * 60000 ? 30 : undefined,
93
+ };
94
+ },
95
+ });
package/manifest.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "id": "com.minibar.calendar",
3
+ "name": "Calendar",
4
+ "version": "1.0.3",
5
+ "minHostVersion": "0.5.0",
6
+ "description": "Your next meeting and how long until it starts, from the macOS Calendar.",
7
+ "author": "Minibar",
8
+ "homepage": "https://minibar-registry.vercel.app/plugins.html#com.minibar.calendar",
9
+ "icon": "calendar",
10
+ "category": "productivity",
11
+ "refreshInterval": 60,
12
+ "permissions": ["calendar"],
13
+ "settings": [
14
+ { "key": "hours", "type": "number", "label": "Look ahead (hours)", "default": 12, "min": 1, "max": 72, "step": 1 },
15
+ { "key": "includeAllDay", "type": "toggle", "label": "Include all-day events", "default": false },
16
+ { "key": "alertMinutes", "type": "number", "label": "Take over the menu bar (minutes before)", "default": 5, "min": 0, "max": 60, "step": 1 },
17
+ { "key": "maxEvents", "type": "number", "label": "Events to list", "default": 5, "min": 1, "max": 15, "step": 1 }
18
+ ],
19
+ "localizations": {
20
+ "zh-Hans": {
21
+ "name": "日历",
22
+ "description": "来自 macOS 日历:下一个日程以及距开始还有多久。",
23
+ "settings": {
24
+ "hours": {
25
+ "label": "向前查看(小时)"
26
+ },
27
+ "includeAllDay": {
28
+ "label": "包含全天日程"
29
+ },
30
+ "alertMinutes": {
31
+ "label": "提前多少分钟抢占菜单栏"
32
+ },
33
+ "maxEvents": {
34
+ "label": "列出的日程数"
35
+ }
36
+ }
37
+ }
38
+ }
39
+ }
package/package.json CHANGED
@@ -1,6 +1,13 @@
1
1
  {
2
2
  "name": "@minibar-app/calendar",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.3",
4
+ "description": "Your next meeting and how long until it starts, from the macOS Calendar.",
5
+ "keywords": [
6
+ "minibar-plugin"
7
+ ],
8
+ "license": "UNLICENSED",
9
+ "minibar": {
10
+ "id": "com.minibar.calendar",
11
+ "minHostVersion": "0.5.0"
12
+ }
13
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.