dsh-calendar 0.14.0 → 0.14.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # 更新记录
2
2
 
3
+ ## 0.14.2 (2026-10-30)
4
+
5
+ - 原生插件列表与详情新增专门生成的鲸鱼娘图标:大头像、独立功能符号、透明留白,适合 30 / 36 px 显示;原封面保留。
6
+ - Add purpose-made whale-girl artwork with a distinct function emblem, transparent padding and small-size readability; preserve the original cover.
7
+
8
+
9
+ ## 0.14.1 (2026-10-30)
10
+
11
+ - 修复候选进入草稿后,夏令时回拨的重复钟点可能被换成另一个时间的问题;未修改的起止时间分别保留原 UTC 偏移。
12
+ - 表单显示实际时段及偏移,以绝对时间判断顺序;暂时清空日期或时间会停用保存,不再使编辑器崩溃。
13
+ - Preserve exact candidate instants through daylight-saving folds and show the resolved offsets before explicit saving.
14
+
3
15
  ## 0.14.0 (2026-10-30)
4
16
 
5
17
  - 新增「找空闲时段」与只读 `calendar_find_time`:设置工作日、时间、时长与缓冲,选择候选后进入原有新建草稿,保存仍由用户确认。
Binary file
@@ -5,6 +5,8 @@
5
5
  3. 点击「查找空闲」。页面明确显示浏览器时区、读取时间与占用日程数量。修改条件会清除旧候选;查询中可取消。
6
6
  4. 点击「用此时段新建」,只会填写原有日程草稿。补充标题、地点等内容并核对时间,再明确保存;关闭草稿不会创建日程。
7
7
 
8
+ 草稿会显示实际日期、钟点和 UTC 偏移。未修改的起止时间分别保留候选的绝对时间,夏令时回拨也不会把第二次出现的钟点换成第一次。修改日期或某一端钟点后,该端按浏览器普通日期规则重新换算,请核对显示的实际时段。
9
+
8
10
  查询最多 31 天,时长 5–480 分钟,缓冲 0–120 分钟。界面按浏览器时区解释工作时间、全天日期和未指定时区的浮动日程;已带 TZID / UTC 的原日程保留其时区。时长按实际经过分钟计算,夏令时变化可能使本地钟点差不同。每天只提供一个不跨日的工作窗口,候选也不跨本地日期。默认周一至周五 09:00–18:00,每 15 分钟一个候选,展示最早 20 个;候选可以相互重叠,数量达到上限会说明。
9
11
 
10
12
  只读取当前连接的一个日历集合,不查询参会者、其他日历或资源的忙闲。读取时间之后日程仍可能变化,候选不是预约;保存前请核对当前日程。透明(`TRANSP:TRANSPARENT`)及取消的日程不占用时间,全天结束日期不包含在事件内;未写结束日期的全天事件按一天计算。缓冲只应用于定时占用事件。
@@ -19,7 +21,7 @@
19
21
 
20
22
  ## 验证范围
21
23
 
22
- Windows 本地 221 项测试通过,包括夏令时、UTC 偏移、全天边界、透明 / 取消、重复改期、查询边界重叠、预算、取消及旧响应保护。真实回环 HTTP CalDAV 服务器验证了 calendar-query / calendar-multiget、无 `.ics` 后缀资源、损坏响应拒绝与取消。
24
+ Windows 本地 225 项测试通过,包括夏令时、UTC 偏移、全天边界、透明 / 取消、重复改期、查询边界重叠、预算、取消、旧响应保护及候选绝对时间保留。真实回环 HTTP CalDAV 服务器验证了 calendar-query / calendar-multiget、无 `.ics` 后缀资源、损坏响应拒绝与取消。
23
25
 
24
26
  官方 DSH 0.2.1-rc.5、0.2.2-alpha.2 中,每版覆盖中英文与桌面 / 390px 窄屏操作:查询、无结果、候选进入草稿、取消不写入、明确保存后本机 PUT,另检查不同浏览器时区。20 个插件均加载为 active。测试账号和日历均为模拟数据;没有真实日历服务账号、Electron 原生窗口或模型自主选择工具的验收。
25
27
 
@@ -27,10 +29,12 @@ Windows 本地 221 项测试通过,包括夏令时、UTC 偏移、全天边界
27
29
 
28
30
  Open **Calendar → Find available time**, set dates, duration, working hours/days and optional buffers, then choose **Find times**. The page shows the browser time zone and the time of the read. Changed filters clear old candidates; a pending search can be cancelled. **Create with this time** only fills the existing new-event draft. Review its title, dates and details before saving.
29
31
 
32
+ The draft shows resolved clock times and UTC offsets. Unchanged endpoints independently preserve their exact candidate instants, including repeated clock times during a DST fold. Editing the date or one clock endpoint resolves that endpoint using the browser's ordinary date rules; review the displayed resolved interval.
33
+
30
34
  Only the connected calendar collection is read. Candidates are not reservations and do not establish availability for other calendars, attendees or resources. Transparent and cancelled events do not block time. All-day end dates are exclusive; an all-day event without an end occupies one local day. Floating times use the query zone, while explicit source zones are preserved. Duration uses elapsed minutes across DST changes. The UI uses the browser zone and one daily working window; candidates do not cross a local date. Defaults: weekdays 09:00–18:00, 60-minute duration, 15-minute candidate spacing, earliest 20 candidates. Alternatives may overlap.
31
35
 
32
36
  The read-only `calendar_find_time` tool uses the same service. Provide offset-aware `start`/`end` and IANA `timeZone`; optional parameters set duration, hours, weekdays, timed-event buffers, spacing and result limit. It performs no calendar mutation or invitation delivery.
33
37
 
34
38
  Limits: 31 days; 5–480-minute duration; 0–120-minute buffer; 1000 resources / 8 MiB total; 512 KiB per resource; 2000 recurrence instances per resource and 10000 total; 100000 iterations per series; 20-second deadline. Incomplete/invalid data or exhausted budgets fail explicitly. Ranged recurrence changes, PERIOD RDATE and floating recurrence end times require correction in the source calendar. The feature does not infer free time from a truncated list.
35
39
 
36
- Validation: 221 Windows tests, a real loopback HTTP CalDAV fixture, and official DSH RC.5 / alpha.2 Web profiles in Chinese/English at desktop and 390px widths. Query, no-result, draft-only choice, cancellation and explicit PUT were exercised, including a different browser time zone. All 20 plugins loaded as active. Real provider accounts, the Electron window and autonomous model tool selection were not tested.
40
+ Validation: 225 Windows tests, a real loopback HTTP CalDAV fixture, and official DSH RC.5 / alpha.2 Web profiles in Chinese/English at desktop and 390px widths. Query, no-result, draft-only choice, cancellation and explicit PUT were exercised, including a different browser time zone. All 20 plugins loaded as active. Real provider accounts, the Electron window and autonomous model tool selection were not tested.
package/lib/client.js CHANGED
@@ -1941,7 +1941,7 @@ let calendarHostContext;
1941
1941
  const rawEnd = endDate(event);
1942
1942
  // 全天事件的 DTEND 是「次日零点」:编辑时退一天,否则用户会看到莫名多一天。
1943
1943
  const e = allDay ? addDays(rawEnd, -1) : rawEnd;
1944
- return {
1944
+ const draft = {
1945
1945
  uid: event.uid,
1946
1946
  isSeries: isRecurring(event),
1947
1947
  summary: event.summary || "",
@@ -1954,6 +1954,8 @@ let calendarHostContext;
1954
1954
  repeat: repeatKeyOf(event.rrule),
1955
1955
  rrule: event.rrule || ""
1956
1956
  };
1957
+ if (!allDay) draft.sourceRange = { date: draft.date, startTime: draft.startTime, endTime: draft.endTime, start: isoOf(s), end: isoOf(rawEnd) };
1958
+ return draft;
1957
1959
  }
1958
1960
  /** 新建表单:默认落在被点的那一天,上午 10:00–11:00(日历产品的惯例)。 */
1959
1961
  function draftAt(date, hour) {
@@ -2012,10 +2014,12 @@ let calendarHostContext;
2012
2014
  fields.allDay = true;
2013
2015
  }
2014
2016
  else {
2017
+ if (!/^\d{2}:\d{2}$/.test(draft.startTime || '') || !/^\d{2}:\d{2}$/.test(draft.endTime || '')) throw new Error(calendarText('请补全起止时间','Complete the start and end times'));
2015
2018
  const start = new Date(draft.date + "T" + (draft.startTime || "00:00") + ":00");
2016
2019
  const end = new Date(draft.date + "T" + (draft.endTime || "00:00") + ":00");
2017
- fields.start = isoOf(start);
2018
- fields.end = isoOf(end);
2020
+ const source = draft.sourceRange, sameDate = source && source.date === draft.date;
2021
+ fields.start = sameDate && source.startTime === draft.startTime ? source.start : isoOf(start);
2022
+ fields.end = sameDate && source.endTime === draft.endTime ? source.end : isoOf(end);
2019
2023
  fields.allDay = false;
2020
2024
  }
2021
2025
  const rrule = draft.repeat === "custom" ? (draft.rrule || "").trim() : rruleForRepeat(draft.repeat);
@@ -2027,8 +2031,10 @@ let calendarHostContext;
2027
2031
  }
2028
2032
  /** 草稿算出来的临时事件,用来做冲突检测(不落盘)。 */
2029
2033
  function asPseudoEvent(draft) {
2030
- const fields = fieldsOfDraft(draft);
2031
- return { uid: "__draft", summary: fields.summary || "(未命名)", start: fields.start, end: fields.end, allDay: draft.allDay === true };
2034
+ try {
2035
+ const fields = fieldsOfDraft(draft);
2036
+ return { uid: "__draft", summary: fields.summary || "(未命名)", start: fields.start, end: fields.end, allDay: draft.allDay === true };
2037
+ } catch { return null; }
2032
2038
  }
2033
2039
  function EventForm(props) {
2034
2040
  const { draft, events, onCancel, onSubmit, busy, error } = props;
@@ -2041,9 +2047,10 @@ __stardustUpdatesGuard(React, "dsh-calendar.event", { dirty: JSON.stringify(form
2041
2047
  const patch = (fields) => setForm((prev) => Object.assign({}, prev, fields));
2042
2048
  const isEdit = draft.uid !== undefined;
2043
2049
  const pseudo = asPseudoEvent(form);
2044
- const conflicts = form.summary.trim() === "" ? [] : events.filter((event) => overlaps(event, pseudo) && event.uid !== form.uid);
2045
- const canSave = form.summary.trim() !== "" && (form.allDay === true || form.startTime <= form.endTime);
2050
+ const conflicts = form.summary.trim() === "" || !pseudo ? [] : events.filter((event) => overlaps(event, pseudo) && event.uid !== form.uid);
2051
+ const canSave = form.summary.trim() !== "" && !!pseudo && Date.parse(pseudo.end) >= Date.parse(pseudo.start);
2046
2052
  const body = h("div", null, [
2053
+ form.sourceRange && !form.allDay && pseudo ? h('p',{className:'dshe-hint',role:'note'},calendarText('实际时段:','Resolved time: ')+[pseudo.start,pseudo.end].map(iso=>new Intl.DateTimeFormat(UI_LANG==='en'?'en-US':'zh-CN',{year:'numeric',month:'short',day:'numeric',hour:'2-digit',minute:'2-digit',timeZoneName:'shortOffset'}).format(new Date(iso))).join(' — ')+calendarText('。未修改的钟点保留原 UTC 偏移。',' · Unchanged times preserve their original UTC offsets.')) : null,
2047
2054
  draft.isSeries ? h("div", { className: "dshc-alert" }, t("form.series")) : null,
2048
2055
  error ? h("div", { className: "dshc-alert error" }, error) : null,
2049
2056
  h("div", { className: "dshc-field" }, [
@@ -3118,7 +3125,7 @@ calendarHostContext=ctx;W.register(ctx,{id:'dsh-calendar.workbench',label:()=>t(
3118
3125
  exports.apply = apply;
3119
3126
  exports.inject = inject;
3120
3127
  return module.exports;
3121
- } },{"name":"dsh-calendar","version":"0.1.0","hasWorkbench":true,"packageVersion":"0.14.0","label":"日历","labelEn":"Calendar","suiteComponent":true}));
3128
+ } },{"name":"dsh-calendar","version":"0.1.0","hasWorkbench":true,"packageVersion":"0.14.2","label":"日历","labelEn":"Calendar","suiteComponent":true}));
3122
3129
 
3123
3130
  })();
3124
3131
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-calendar",
3
- "version": "0.14.0",
3
+ "version": "0.14.2",
4
4
  "description": "DSH 日历工作台:CalDAV 日程、月 / 周 / 议程视图、空闲时段建议与 ICS 预览导入。欢迎提交 issues 和 PR。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -21,7 +21,8 @@
21
21
  "README.md",
22
22
  "README.en.md",
23
23
  "CHANGELOG.md",
24
- "docs"
24
+ "docs",
25
+ "assets/plugin-icon.webp"
25
26
  ],
26
27
  "scripts": {
27
28
  "build": "tsc -p tsconfig.json && node scripts/build-client.mjs && node scripts/build-updates.mjs",
@@ -83,5 +84,6 @@
83
84
  },
84
85
  "homepage": "https://github.com/STARDUSTLC666/dsh-calendar#readme",
85
86
  "author": "stardustlc",
86
- "packageManager": "pnpm@11.7.0"
87
+ "packageManager": "pnpm@11.7.0",
88
+ "icon": "./assets/plugin-icon.webp"
87
89
  }