@chance722/dsh-inbox 0.2.3 → 0.2.4
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/LICENSE +21 -21
- package/README.md +141 -134
- package/README.zh.md +141 -134
- package/cordis.patch.yml +11 -11
- package/lib/client.js +156 -55
- package/lib/index.js +106 -19
- package/lib/types/client/messages.d.ts +36 -18
- package/lib/types/host/remote/merge.d.ts +17 -1
- package/lib/types/host/remote/pull.d.ts +25 -3
- package/lib/types/host/s3/client.d.ts +25 -1
- package/lib/types/host/webdav/config.d.ts +4 -0
- package/lib/types/shared/panel-wire.d.ts +34 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -809,6 +809,11 @@ window.__ModuleLoader__.load({
|
|
|
809
809
|
"settings.region": "\u533A\u57DF",
|
|
810
810
|
"settings.bucketAddress": "\u5730\u5740",
|
|
811
811
|
"settings.bucketDir": "\u76EE\u5F55",
|
|
812
|
+
"settings.adoptForeign": "\u540C\u65F6\u5408\u5E76\u522B\u7684\u540C\u6B65\u76EE\u5F55",
|
|
813
|
+
"settings.adoptForeignHint": "\uFF08\u6362\u8FC7\u76EE\u5F55\u7684\u673A\u5668\u7559\u5728\u4E91\u7AEF\u7684\u8BB0\u5F55\u4E5F\u4F1A\u62C9\u56DE\u6765\uFF1B\u9ED8\u8BA4\u5173\uFF09",
|
|
814
|
+
// The default is the thing people get wrong: blank and `/` are the same as
|
|
815
|
+
// `inbox`, and the sync tree is `<directory>/sync`.
|
|
816
|
+
"settings.directoryPlaceholder": "inbox\uFF08\u9ED8\u8BA4\uFF1B\u7559\u7A7A\u6216 / \u90FD\u7B49\u4E8E\u5B83\uFF09",
|
|
812
817
|
"settings.dirS3.lead": "\u76EE\u5F55\u90A3\u4E00\u680F\u540C\u65F6\u662F S3 \u7684 key \u524D\u7F00\uFF08\u9ED8\u8BA4 ",
|
|
813
818
|
"settings.dirS3.mid": "\uFF0C\u4F1A\u8F6C\u6210 ",
|
|
814
819
|
"settings.dirS3.tail": "\uFF09\u3002",
|
|
@@ -858,17 +863,23 @@ window.__ModuleLoader__.load({
|
|
|
858
863
|
"sync.pushUnconfigured": "\u63A8\u9001\uFF1A\u8FD8\u6CA1\u914D\u7F6E\u8FDC\u7AEF",
|
|
859
864
|
"sync.pushFailed": "\u63A8\u9001\u5931\u8D25\uFF1A{reason}",
|
|
860
865
|
"sync.pushUnknown": "\u672A\u77E5\u539F\u56E0",
|
|
861
|
-
"sync.pushUpToDate": "\u63A8\u9001\uFF1A\u6CA1\u6709\u65B0\u5185\u5BB9\uFF08{count} \u6761\u5DF2\u662F\u6700\u65B0\uFF09",
|
|
862
|
-
"sync.pushDone": "\u63A8\u9001\uFF1A{records} \u6761\u8BB0\u5F55 / {attachments} \u4E2A\u9644\u4EF6",
|
|
863
866
|
"sync.pushPartial": "{head} \xB7 \u90E8\u5206\u5931\u8D25\uFF1A{reason}",
|
|
864
867
|
"sync.pullNoAddress": "\u8FD8\u6CA1\u914D\u7F6E\u5730\u5740",
|
|
865
868
|
"sync.pullFailed": "\u62C9\u53D6\u5931\u8D25\uFF1A{reason}",
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
"sync.
|
|
869
|
-
"sync.
|
|
870
|
-
"sync.
|
|
871
|
-
"sync.
|
|
869
|
+
// The refresh button's tooltip: counts first, reasons only when something was
|
|
870
|
+
// skipped. The toast carries the two numbers alone.
|
|
871
|
+
"sync.detailPushedIdle": "\u63A8\u9001 0 \u6761\uFF08{count} \u6761\u5DF2\u662F\u6700\u65B0\uFF09",
|
|
872
|
+
"sync.detailPushed": "\u63A8\u9001 {records} \u6761 / {attachments} \u4E2A\u9644\u4EF6",
|
|
873
|
+
"sync.detailPulled": "\u62C9\u53D6 {count} \u6761 \xB7 \u4E91\u7AEF {records} \u6761\u8BB0\u5F55 / {files} \u4E2A\u9644\u4EF6",
|
|
874
|
+
"sync.detailAdded": "\uFF08\u5176\u4E2D {count} \u6761\u662F\u65B0\u8BB0\u5F55\uFF09",
|
|
875
|
+
"sync.detailSkip": "\uFF08\u8DF3\u8FC7 {skipped}\uFF1A\u81EA\u5DF1\u7684 {sync}\u3001\u66F4\u65E7 {older}\uFF09",
|
|
876
|
+
"sync.pullForeignSync": " \xB7 \u26A0\uFE0F \u53E6\u6709 {records} \u6761\u8BB0\u5F55\u5728\u522B\u7684\u540C\u6B65\u76EE\u5F55\uFF1A{roots}",
|
|
877
|
+
"sync.detailForeign": " \xB7 \u26A0\uFE0F \u522B\u7684\u540C\u6B65\u76EE\u5F55\uFF1A{roots}\uFF08{records} \u6761\u8BB0\u5F55\uFF1B\u672C\u673A {ours}\uFF09",
|
|
878
|
+
// The toast: two numbers, nothing else. Everything else about a sync is
|
|
879
|
+
// detail, and detail belongs in a line that does not vanish.
|
|
880
|
+
"sync.shortPushed": "\u63A8\u9001 {count} \u6761",
|
|
881
|
+
"sync.shortPulled": "\u62C9\u53D6 {count} \u6761",
|
|
882
|
+
"sync.shortIdle": "\u5DF2\u662F\u6700\u65B0",
|
|
872
883
|
"sync.pullFailures": " \xB7 \u5931\u8D25 {count}\uFF1A{reason}",
|
|
873
884
|
"sync.thisPage": " \xB7 \u672C\u9875 {count} \u6761",
|
|
874
885
|
"settings.status": "{settings} \xB7 {webdav} \xB7 {s3}",
|
|
@@ -938,10 +949,15 @@ window.__ModuleLoader__.load({
|
|
|
938
949
|
"manual.5.auto.body": "\u5165\u5E93/\u6539\u52A8\u540E\u51E0\u79D2\u81EA\u52A8\u63A8\u9001\u4E00\u6B21\uFF08\u9632\u6296\uFF0C\u8FDE\u7740\u5B58\u4E94\u6761\u53EA\u4F1A\u63A8\u4E00\u6B21\uFF09\u3002",
|
|
939
950
|
"manual.5.manual.label": "\u624B\u52A8",
|
|
940
951
|
"manual.5.manual.body": "\u53F3\u4E0A\u89D2\u300C\u5237\u65B0\u300D= \u4E00\u6B21\u5B8C\u6574\u540C\u6B65\uFF1A\u5148\u63A8\u672C\u673A\u6539\u52A8\uFF0C\u518D\u62C9\u522B\u4EBA\u7684\uFF0C\u7136\u540E\u91CD\u8BFB\u5217\u8868\u3002",
|
|
952
|
+
// The one rule that decides whether two machines see each other at all.
|
|
953
|
+
"manual.5.twoMachines.label": "\u4E24\u53F0\u673A\u5668",
|
|
954
|
+
"manual.5.twoMachines.body": "\u4E24\u8FB9\u7684\u300C\u76EE\u5F55\u300D\u8981\u4E00\u6837\uFF08\u7559\u7A7A\u3001/\u3001inbox \u662F\u540C\u4E00\u4E2A\u610F\u601D\uFF09\u3002\u4E0D\u4E00\u6837\u65F6\u9ED8\u8BA4\u4E0D\u4E92\u901A\uFF1A\u5237\u65B0\u4F1A\u63D0\u793A\u300C\u53E6\u6709 N \u6761\u8BB0\u5F55\u5728\u522B\u7684\u540C\u6B65\u76EE\u5F55\u300D\u2014\u2014\u628A\u5B83\u4EEC\u6539\u6210\u540C\u4E00\u4E2A\uFF0C\u6216\u8005\u6253\u5F00\u8BBE\u7F6E\u91CC\u7684\u300C\u540C\u65F6\u5408\u5E76\u522B\u7684\u540C\u6B65\u76EE\u5F55\u300D\uFF0C\u90FD\u80FD\u5408\u8FC7\u6765\u3002",
|
|
941
955
|
"manual.5.delete.label": "\u5220\u9664",
|
|
942
956
|
"manual.5.delete.body": "\u9762\u677F\u91CC\u7684\u300C\u5220\u9664\u300D\u53EA\u662F\u628A\u5B83\u653E\u8FDB\u56DE\u6536\u7AD9\uFF08\u522B\u7684\u8BBE\u5907\u4E5F\u4F1A\u77E5\u9053\u5B83\u88AB\u5220\u4E86\uFF0C\u4E0D\u4F1A\u53C8\u88AB\u62C9\u56DE\u6765\uFF09\uFF1B\u300C\u6E05\u7A7A\u56DE\u6536\u7AD9\u300D\u624D\u662F\u771F\u5220\uFF0C\u4E91\u7AEF\u90A3\u4EFD\u4E5F\u4F1A\u4E00\u8D77\u5220\u3002",
|
|
943
|
-
"manual.5.
|
|
944
|
-
"manual.5.
|
|
957
|
+
"manual.5.merge.label": "\u5408\u5E76",
|
|
958
|
+
"manual.5.merge.body": "\u62C9\u56DE\u6765\u6309\u8BB0\u5F55\u7684 id + \u66F4\u65B0\u65F6\u95F4\u5408\u5E76\uFF1A\u8C01\u65B0\u8C01\u8D62\uFF0C\u4E0D\u7559\u51B2\u7A81\u526F\u672C\u3002",
|
|
959
|
+
"manual.5.cloud.label": "\u4E0A\u4E91\u4EC0\u4E48",
|
|
960
|
+
"manual.5.cloud.body": "\u8BB0\u5F55\u4E0E\u9644\u4EF6\u4E00\u8D77\u8D70\uFF1B\u53EA\u6709\u8D26\u5BC6\u6B63\u6587\u662F\u5BC6\u6587\uFF0C\u5176\u4F59\uFF08\u6587\u672C\u3001\u94FE\u63A5\u3001\u5907\u6CE8\u3001\u9644\u4EF6\uFF09\u662F\u660E\u6587\u2014\u2014\u6876\u52A1\u5FC5\u8BBE\u8BBF\u95EE\u63A7\u5236\u3002",
|
|
945
961
|
"manual.6.title": "6. \u5728\u5BF9\u8BDD\u91CC\u53D6\u56DE\u6765",
|
|
946
962
|
"manual.6.query.label": "\u67E5",
|
|
947
963
|
"manual.6.query.body": "\u5728\u5BF9\u8BDD\u91CC\u76F4\u63A5\u95EE\uFF1A\u300C\u6211\u7684\u6536\u4EF6\u7BB1\u91CC\u6709\u54EA\u4E9B\u8FD8\u6CA1\u770B\u7684\u94FE\u63A5\uFF1F\u300D\u52A9\u624B\u4F1A\u53BB\u4ED3\u5E93\u91CC\u627E\uFF0C \u6700\u591A\u5217 10 \u6761\uFF0C\u5E76\u544A\u8BC9\u4F60\u8FD8\u5269\u51E0\u6761\u3002\u53EB\u5B83\u300C\u6536\u4EF6\u7BB1\u300D\u300C\u4ED3\u5E93\u300D\u300C\u4E2A\u4EBA\u4ED3\u5E93\u300D\u8FD8\u662F\u300Cinbox\u300D\u90FD\u8BA4\u3002",
|
|
@@ -1068,6 +1084,9 @@ window.__ModuleLoader__.load({
|
|
|
1068
1084
|
"settings.region": "Region",
|
|
1069
1085
|
"settings.bucketAddress": "Address",
|
|
1070
1086
|
"settings.bucketDir": "Directory",
|
|
1087
|
+
"settings.adoptForeign": "Merge other sync directories too",
|
|
1088
|
+
"settings.adoptForeignHint": "(brings back records a machine left under an older directory; off by default)",
|
|
1089
|
+
"settings.directoryPlaceholder": "inbox (the default \u2014 blank or / means the same)",
|
|
1071
1090
|
"settings.dirS3.lead": "the directory is also the key prefix for S3 (default ",
|
|
1072
1091
|
"settings.dirS3.mid": ", which becomes ",
|
|
1073
1092
|
"settings.dirS3.tail": ").",
|
|
@@ -1115,17 +1134,19 @@ window.__ModuleLoader__.load({
|
|
|
1115
1134
|
"sync.pushUnconfigured": "Push: no remote configured yet",
|
|
1116
1135
|
"sync.pushFailed": "Push failed: {reason}",
|
|
1117
1136
|
"sync.pushUnknown": "unknown reason",
|
|
1118
|
-
"sync.pushUpToDate": "Push: nothing new ({count} already current)",
|
|
1119
|
-
"sync.pushDone": "Push: {records} records / {attachments} attachment(s)",
|
|
1120
1137
|
"sync.pushPartial": "{head} \xB7 partly failed: {reason}",
|
|
1121
1138
|
"sync.pullNoAddress": "No address configured yet",
|
|
1122
1139
|
"sync.pullFailed": "Pull failed: {reason}",
|
|
1123
|
-
"sync.
|
|
1124
|
-
"sync.
|
|
1125
|
-
"sync.
|
|
1126
|
-
"sync.
|
|
1127
|
-
"sync.
|
|
1128
|
-
"sync.pullForeignSync": " \xB7 \u26A0\uFE0F
|
|
1140
|
+
"sync.detailPushedIdle": "pushed 0 ({count} already up to date)",
|
|
1141
|
+
"sync.detailPushed": "pushed {records} / {attachments} attachments",
|
|
1142
|
+
"sync.detailPulled": "pulled {count} \xB7 the cloud holds {records} records / {files} attachments",
|
|
1143
|
+
"sync.detailAdded": " ({count} of them new)",
|
|
1144
|
+
"sync.detailSkip": " ({skipped} skipped: {sync} ours, {older} older)",
|
|
1145
|
+
"sync.pullForeignSync": " \xB7 \u26A0\uFE0F {records} records live in another sync directory: {roots}",
|
|
1146
|
+
"sync.detailForeign": " \xB7 \u26A0\uFE0F another sync directory: {roots} ({records} records; ours is {ours})",
|
|
1147
|
+
"sync.shortPushed": "pushed {count}",
|
|
1148
|
+
"sync.shortPulled": "pulled {count}",
|
|
1149
|
+
"sync.shortIdle": "up to date",
|
|
1129
1150
|
"sync.pullFailures": " \xB7 {count} failed: {reason}",
|
|
1130
1151
|
"sync.thisPage": " \xB7 {count} on this page",
|
|
1131
1152
|
"settings.status": "{settings} \xB7 {webdav} \xB7 {s3}",
|
|
@@ -1192,10 +1213,14 @@ window.__ModuleLoader__.load({
|
|
|
1192
1213
|
"manual.5.auto.body": "a few seconds after a capture or an edit a push goes out (debounced \u2014 five quick saves push once).",
|
|
1193
1214
|
"manual.5.manual.label": "Manual",
|
|
1194
1215
|
"manual.5.manual.body": "\u201CRefresh\u201D in the top right is a full sync: push local changes, pull the others, then re-read the list.",
|
|
1216
|
+
"manual.5.twoMachines.label": "Two machines",
|
|
1217
|
+
"manual.5.twoMachines.body": "Both machines need the same \u300C\u76EE\u5F55\u300D (blank, / and inbox all mean the same directory). While they differ nothing crosses over by default: the refresh says \u201CN records live in another sync directory\u201D, and either matching them or ticking \u201CMerge other sync directories too\u201D brings them in.",
|
|
1195
1218
|
"manual.5.delete.label": "Deleting",
|
|
1196
1219
|
"manual.5.delete.body": "\u201CDelete\u201D in the panel only moves a record to the bin (other devices learn it is gone instead of pushing it back); emptying the bin is the real delete, and the cloud copy goes with it.",
|
|
1197
|
-
"manual.5.
|
|
1198
|
-
"manual.5.
|
|
1220
|
+
"manual.5.merge.label": "Merging",
|
|
1221
|
+
"manual.5.merge.body": "Incoming records merge by id + timestamp: the newer write wins, no conflict copies.",
|
|
1222
|
+
"manual.5.cloud.label": "What goes up",
|
|
1223
|
+
"manual.5.cloud.body": "Records and attachments both travel; only credential bodies are ciphertext \u2014 text, links, notes and attachments are plain text, so the bucket needs access control.",
|
|
1199
1224
|
"manual.6.title": "6. Getting things back in the conversation",
|
|
1200
1225
|
"manual.6.query.label": "Ask",
|
|
1201
1226
|
"manual.6.query.body": "just ask: \u201Cwhich links in my inbox haven\u2019t I read yet?\u201D The assistant searches the vault, lists at most 10, and tells you how many are left. \u6536\u4EF6\u7BB1 / \u4ED3\u5E93 / inbox all mean the same shelf.",
|
|
@@ -1758,7 +1783,12 @@ window.__ModuleLoader__.load({
|
|
|
1758
1783
|
manual ran off the bottom of the screen with no visible frame.
|
|
1759
1784
|
*/
|
|
1760
1785
|
width: "min(560px, 100%)",
|
|
1761
|
-
|
|
1786
|
+
/*
|
|
1787
|
+
80vh rather than 92vh: at 92 the card still felt like a full screen
|
|
1788
|
+
and it pushed the panel out of sight, while the point of the manual
|
|
1789
|
+
is to be readable *beside* what it explains (asked 2026-09-21).
|
|
1790
|
+
*/
|
|
1791
|
+
maxHeight: "80vh",
|
|
1762
1792
|
// Border inside the width and the cap: without it the card is 2px
|
|
1763
1793
|
// wider than the number and 2px taller than the overlay's room.
|
|
1764
1794
|
boxSizing: "border-box",
|
|
@@ -1809,6 +1839,7 @@ window.__ModuleLoader__.load({
|
|
|
1809
1839
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1810
1840
|
"div",
|
|
1811
1841
|
{
|
|
1842
|
+
className: "dsh-inbox-scroll",
|
|
1812
1843
|
style: {
|
|
1813
1844
|
padding: 18,
|
|
1814
1845
|
display: "flex",
|
|
@@ -1852,8 +1883,10 @@ window.__ModuleLoader__.load({
|
|
|
1852
1883
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Section, { title: t("manual.5.title"), children: [
|
|
1853
1884
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: t("manual.5.auto.label"), children: t("manual.5.auto.body") }),
|
|
1854
1885
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: t("manual.5.manual.label"), children: t("manual.5.manual.body") }),
|
|
1855
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: t("manual.5.
|
|
1856
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: t("manual.5.
|
|
1886
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: t("manual.5.merge.label"), children: t("manual.5.merge.body") }),
|
|
1887
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: t("manual.5.twoMachines.label"), children: t("manual.5.twoMachines.body") }),
|
|
1888
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: t("manual.5.cloud.label"), children: t("manual.5.cloud.body") }),
|
|
1889
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: t("manual.5.delete.label"), children: t("manual.5.delete.body") })
|
|
1857
1890
|
] }),
|
|
1858
1891
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Section, { title: t("manual.6.title"), children: [
|
|
1859
1892
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: t("manual.6.query.label"), children: t("manual.6.query.body") }),
|
|
@@ -2098,6 +2131,7 @@ window.__ModuleLoader__.load({
|
|
|
2098
2131
|
const [text, setText] = import_react8.default.useState("");
|
|
2099
2132
|
const [staged, setStaged] = import_react8.default.useState([]);
|
|
2100
2133
|
const [notice, setNotice] = import_react8.default.useState();
|
|
2134
|
+
const [syncDetail, setSyncDetail] = import_react8.default.useState();
|
|
2101
2135
|
const [busy, setBusy] = import_react8.default.useState(false);
|
|
2102
2136
|
const [dragging, setDragging] = import_react8.default.useState(false);
|
|
2103
2137
|
const picker = import_react8.default.useRef(null);
|
|
@@ -2250,19 +2284,27 @@ window.__ModuleLoader__.load({
|
|
|
2250
2284
|
setNotice(t("notice.syncing"));
|
|
2251
2285
|
try {
|
|
2252
2286
|
const pushed = await call(INBOX_ENDPOINT_PUSH, {});
|
|
2253
|
-
const pushLine = pushed.ok ? describePush(pushed.value) : t("notice.pushFailed", { reason: pushed.error.message });
|
|
2254
2287
|
const pulled = await call(INBOX_ENDPOINT_PULL, {});
|
|
2255
|
-
let suffix = "";
|
|
2256
|
-
if (pulled.ok) {
|
|
2257
|
-
const result = pulled.value;
|
|
2258
|
-
suffix = ` \xB7 ${describePull(result)}`;
|
|
2259
|
-
} else {
|
|
2260
|
-
suffix = ` \xB7 ${t("notice.pullFailed", { reason: pulled.error.message })}`;
|
|
2261
|
-
}
|
|
2262
2288
|
await refresh();
|
|
2289
|
+
const pushFail = pushed.ok ? void 0 : t("notice.pushFailed", { reason: pushed.error.message });
|
|
2290
|
+
const pullFail = pulled.ok ? void 0 : t("notice.pullFailed", { reason: pulled.error.message });
|
|
2291
|
+
const pushCount = pushed.ok ? writtenBy(pushed.value) : 0;
|
|
2292
|
+
const pullCount = pulled.ok ? arrivedFrom(pulled.value) : 0;
|
|
2293
|
+
const warnings = pulled.ok ? syncWarnings(pulled.value) : "";
|
|
2263
2294
|
const total = list?.matched;
|
|
2295
|
+
const idle = pushFail === void 0 && pullFail === void 0 && pushCount === 0 && pullCount === 0;
|
|
2296
|
+
const short = idle ? t("sync.shortIdle") : [
|
|
2297
|
+
pushFail ?? t("sync.shortPushed", { count: pushCount }),
|
|
2298
|
+
pullFail ?? t("sync.shortPulled", { count: pullCount })
|
|
2299
|
+
].join(" \xB7 ");
|
|
2264
2300
|
setNotice(
|
|
2265
|
-
`${
|
|
2301
|
+
`${short}${warnings}${total === void 0 ? "" : t("sync.thisPage", { count: total })}`
|
|
2302
|
+
);
|
|
2303
|
+
setSyncDetail(
|
|
2304
|
+
[
|
|
2305
|
+
pushed.ok ? describePush(pushed.value) : t("notice.pushFailed", { reason: pushed.error.message }),
|
|
2306
|
+
pulled.ok ? describePull(pulled.value) : t("notice.pullFailed", { reason: pulled.error.message })
|
|
2307
|
+
].join(" \xB7 ")
|
|
2266
2308
|
);
|
|
2267
2309
|
} finally {
|
|
2268
2310
|
setBusy(false);
|
|
@@ -2463,6 +2505,28 @@ window.__ModuleLoader__.load({
|
|
|
2463
2505
|
if (entry.url !== void 0) setZoom({ href: entry.url, label: headingOf(entry) });
|
|
2464
2506
|
}, []);
|
|
2465
2507
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { ref: panelRef, style: { ...panelStyle, colorScheme: scheme }, children: [
|
|
2508
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: `
|
|
2509
|
+
/*
|
|
2510
|
+
Chromium ignores ::-webkit-scrollbar the moment the standard
|
|
2511
|
+
scrollbar-width is set, so the two engines get the rule they can use:
|
|
2512
|
+
engines that know ::-webkit-scrollbar get the 8px bar, and Firefox \u2014
|
|
2513
|
+
which does not \u2014 falls back to its own thin one. Measured: with both
|
|
2514
|
+
set, Chromium kept its 10px thin bar and the 8px rule did nothing.
|
|
2515
|
+
*/
|
|
2516
|
+
@supports not selector(::-webkit-scrollbar) {
|
|
2517
|
+
.dsh-inbox-scroll { scrollbar-width: thin; }
|
|
2518
|
+
}
|
|
2519
|
+
.dsh-inbox-scroll::-webkit-scrollbar { width: 8px; height: 8px; }
|
|
2520
|
+
.dsh-inbox-scroll::-webkit-scrollbar-track { background: transparent; }
|
|
2521
|
+
.dsh-inbox-scroll::-webkit-scrollbar-thumb {
|
|
2522
|
+
background: color-mix(in srgb, currentColor 22%, transparent);
|
|
2523
|
+
border-radius: 8px;
|
|
2524
|
+
}
|
|
2525
|
+
.dsh-inbox-scroll::-webkit-scrollbar-thumb:hover {
|
|
2526
|
+
background: color-mix(in srgb, currentColor 34%, transparent);
|
|
2527
|
+
}
|
|
2528
|
+
.dsh-inbox-scroll::-webkit-scrollbar-corner { background: transparent; }
|
|
2529
|
+
` }),
|
|
2466
2530
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("header", { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", alignItems: "flex-start", gap: 10 }, children: [
|
|
2467
2531
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
|
|
2468
2532
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { style: { margin: "0 0 4px" }, children: "dsh-inbox" }),
|
|
@@ -2867,6 +2931,7 @@ window.__ModuleLoader__.load({
|
|
|
2867
2931
|
"button",
|
|
2868
2932
|
{
|
|
2869
2933
|
type: "button",
|
|
2934
|
+
title: syncDetail,
|
|
2870
2935
|
style: { ...buttonStyle, height: CONTROL_HEIGHT, boxSizing: "border-box" },
|
|
2871
2936
|
disabled: busy,
|
|
2872
2937
|
onClick: () => void refreshAll(),
|
|
@@ -3047,6 +3112,7 @@ window.__ModuleLoader__.load({
|
|
|
3047
3112
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3048
3113
|
"div",
|
|
3049
3114
|
{
|
|
3115
|
+
className: "dsh-inbox-scroll",
|
|
3050
3116
|
style: listMode === "compact" ? {
|
|
3051
3117
|
display: "flex",
|
|
3052
3118
|
flexDirection: "column",
|
|
@@ -3215,37 +3281,47 @@ window.__ModuleLoader__.load({
|
|
|
3215
3281
|
)
|
|
3216
3282
|
] });
|
|
3217
3283
|
}
|
|
3284
|
+
function writtenBy(result) {
|
|
3285
|
+
return result.status === "ok" || result.status === "partial" ? result.pushed : 0;
|
|
3286
|
+
}
|
|
3287
|
+
function arrivedFrom(result) {
|
|
3288
|
+
return result.status === "ok" ? result.pulled + (result.merged ?? 0) : 0;
|
|
3289
|
+
}
|
|
3290
|
+
function syncWarnings(result) {
|
|
3291
|
+
return warningsOf(result, false);
|
|
3292
|
+
}
|
|
3293
|
+
function warningsOf(result, verbose) {
|
|
3294
|
+
const roots = result.foreignSyncRoots ?? [];
|
|
3295
|
+
const foreign = roots.length === 0 ? "" : verbose ? t("sync.detailForeign", {
|
|
3296
|
+
roots: roots.join("\u3001"),
|
|
3297
|
+
ours: result.syncRoot ?? "",
|
|
3298
|
+
records: result.foreignRecords ?? 0
|
|
3299
|
+
}) : t("sync.pullForeignSync", { roots: roots.join("\u3001"), records: result.foreignRecords ?? 0 });
|
|
3300
|
+
const failed = result.failed > 0 ? t("sync.pullFailures", { count: result.failed, reason: result.reason ?? "" }) : "";
|
|
3301
|
+
return `${foreign}${failed}`;
|
|
3302
|
+
}
|
|
3218
3303
|
function describePush(result) {
|
|
3219
3304
|
if (result.status === "unconfigured") return t("sync.pushUnconfigured");
|
|
3220
3305
|
if (result.status === "failed") return t("sync.pushFailed", { reason: result.reason ?? t("sync.pushUnknown") });
|
|
3221
|
-
const head = result.pushed === 0 && result.attachments === 0 ? t("sync.
|
|
3306
|
+
const head = result.pushed === 0 && result.attachments === 0 ? t("sync.detailPushedIdle", { count: result.skipped }) : t("sync.detailPushed", { records: result.pushed, attachments: result.attachments });
|
|
3222
3307
|
return result.status === "partial" ? t("sync.pushPartial", { head, reason: result.reason ?? "" }) : head;
|
|
3223
3308
|
}
|
|
3224
3309
|
function describePull(result) {
|
|
3225
3310
|
if (result.status === "unconfigured") return result.reason ?? t("sync.pullNoAddress");
|
|
3226
3311
|
if (result.status === "failed") return t("sync.pullFailed", { reason: result.reason ?? t("sync.pushUnknown") });
|
|
3227
3312
|
const merged = result.merged ?? 0;
|
|
3228
|
-
const
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
});
|
|
3233
|
-
if (result.pulled === 0 && result.skipped === 0 && result.failed === 0) return syncPart;
|
|
3234
|
-
const skippedWhy = result.skippedSync === void 0 ? "" : t("sync.pullSkipWhy", { sync: result.skippedSync, older: result.skippedOlder ?? 0 });
|
|
3235
|
-
const foreignWhy = result.foreignSyncRoots === void 0 || result.foreignSyncRoots.length === 0 ? "" : t("sync.pullForeignSync", {
|
|
3236
|
-
roots: result.foreignSyncRoots.join("\u3001"),
|
|
3237
|
-
ours: result.syncRoot ?? ""
|
|
3313
|
+
const head = t("sync.detailPulled", {
|
|
3314
|
+
count: result.pulled + merged,
|
|
3315
|
+
records: result.remoteRecords ?? 0,
|
|
3316
|
+
files: result.remoteAttachments ?? 0
|
|
3238
3317
|
});
|
|
3239
|
-
const
|
|
3240
|
-
|
|
3241
|
-
reason: result.reason ?? t("sync.pushUnknown")
|
|
3242
|
-
}) : "";
|
|
3243
|
-
return t("sync.pullDone", {
|
|
3244
|
-
listed: result.listed,
|
|
3245
|
-
pulled: result.pulled,
|
|
3318
|
+
const gained = (result.added ?? 0) === 0 ? "" : t("sync.detailAdded", { count: result.added ?? 0 });
|
|
3319
|
+
const skipped = result.skipped === 0 ? "" : t("sync.detailSkip", {
|
|
3246
3320
|
skipped: result.skipped,
|
|
3247
|
-
|
|
3321
|
+
sync: result.skippedSync ?? 0,
|
|
3322
|
+
older: result.skippedOlder ?? 0
|
|
3248
3323
|
});
|
|
3324
|
+
return `${head}${gained}${skipped}${warningsOf(result, true)}`;
|
|
3249
3325
|
}
|
|
3250
3326
|
function EncryptionSettings({ call }) {
|
|
3251
3327
|
const [status, setStatus] = import_react8.default.useState();
|
|
@@ -3340,6 +3416,7 @@ window.__ModuleLoader__.load({
|
|
|
3340
3416
|
const [status, setStatus] = import_react8.default.useState();
|
|
3341
3417
|
const [baseUrl, setBaseUrl] = import_react8.default.useState("");
|
|
3342
3418
|
const [directory, setDirectory] = import_react8.default.useState("/inbox");
|
|
3419
|
+
const [adoptForeignRoots, setAdoptForeignRoots] = import_react8.default.useState(false);
|
|
3343
3420
|
const [username, setUsername] = import_react8.default.useState("");
|
|
3344
3421
|
const [password, setPassword] = import_react8.default.useState("");
|
|
3345
3422
|
const [protocol, setProtocol] = import_react8.default.useState("webdav");
|
|
@@ -3364,6 +3441,7 @@ window.__ModuleLoader__.load({
|
|
|
3364
3441
|
setStatus(next);
|
|
3365
3442
|
setBaseUrl(next.settings.baseUrl);
|
|
3366
3443
|
setDirectory(next.settings.directory);
|
|
3444
|
+
setAdoptForeignRoots(next.settings.adoptForeignRoots === true);
|
|
3367
3445
|
setUsername(next.settings.username);
|
|
3368
3446
|
setProtocol(next.settings.protocol);
|
|
3369
3447
|
setEndpoint(next.settings.endpoint);
|
|
@@ -3393,6 +3471,7 @@ window.__ModuleLoader__.load({
|
|
|
3393
3471
|
protocol,
|
|
3394
3472
|
baseUrl,
|
|
3395
3473
|
directory,
|
|
3474
|
+
adoptForeignRoots,
|
|
3396
3475
|
username,
|
|
3397
3476
|
// Sending nothing leaves the stored password alone; sending "" clears it.
|
|
3398
3477
|
...password.length === 0 ? {} : { password },
|
|
@@ -3494,11 +3573,15 @@ window.__ModuleLoader__.load({
|
|
|
3494
3573
|
value: directory,
|
|
3495
3574
|
disabled: busy,
|
|
3496
3575
|
onChange: (event) => setDirectory(event.target.value),
|
|
3497
|
-
placeholder: "
|
|
3576
|
+
placeholder: t("settings.directoryPlaceholder"),
|
|
3498
3577
|
style: { ...inputStyle, flex: 1 }
|
|
3499
3578
|
}
|
|
3500
3579
|
),
|
|
3501
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.
|
|
3580
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { style: { opacity: 0.6 }, children: [
|
|
3581
|
+
t("settings.bucketHint"),
|
|
3582
|
+
" \xB7 ",
|
|
3583
|
+
t("settings.syncRootNow", { root: syncRootFor(directory) })
|
|
3584
|
+
] })
|
|
3502
3585
|
] }),
|
|
3503
3586
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
|
|
3504
3587
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { opacity: 0.7, minWidth: 64 }, children: t("settings.username") }),
|
|
@@ -3612,14 +3695,31 @@ window.__ModuleLoader__.load({
|
|
|
3612
3695
|
value: directory,
|
|
3613
3696
|
disabled: busy,
|
|
3614
3697
|
onChange: (event) => setDirectory(event.target.value),
|
|
3615
|
-
placeholder: "
|
|
3698
|
+
placeholder: t("settings.directoryPlaceholder"),
|
|
3616
3699
|
style: { ...inputStyle, flex: 1 }
|
|
3617
3700
|
}
|
|
3618
3701
|
),
|
|
3619
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.
|
|
3702
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { style: { opacity: 0.6 }, children: [
|
|
3703
|
+
t("settings.bucketHint"),
|
|
3704
|
+
" \xB7 ",
|
|
3705
|
+
t("settings.syncRootNow", { root: syncRootFor(directory) })
|
|
3706
|
+
] })
|
|
3620
3707
|
] })
|
|
3621
3708
|
] }),
|
|
3622
3709
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" }, children: [
|
|
3710
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { style: { display: "flex", gap: 6, alignItems: "center", flex: "1 0 100%" }, children: [
|
|
3711
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3712
|
+
"input",
|
|
3713
|
+
{
|
|
3714
|
+
type: "checkbox",
|
|
3715
|
+
checked: adoptForeignRoots,
|
|
3716
|
+
disabled: busy,
|
|
3717
|
+
onChange: (event) => setAdoptForeignRoots(event.target.checked)
|
|
3718
|
+
}
|
|
3719
|
+
),
|
|
3720
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: t("settings.adoptForeign") }),
|
|
3721
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { opacity: 0.6 }, children: t("settings.adoptForeignHint") })
|
|
3722
|
+
] }),
|
|
3623
3723
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", style: buttonStyle, disabled: busy, onClick: () => void save(), children: t("settings.save") }),
|
|
3624
3724
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", style: buttonStyle, disabled: busy, onClick: () => void selfTest(), children: t("settings.probe") }),
|
|
3625
3725
|
detailNotice(notice)
|
|
@@ -4050,6 +4150,7 @@ window.__ModuleLoader__.load({
|
|
|
4050
4150
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
4051
4151
|
"div",
|
|
4052
4152
|
{
|
|
4153
|
+
className: "dsh-inbox-scroll",
|
|
4053
4154
|
style: {
|
|
4054
4155
|
flex: 1,
|
|
4055
4156
|
minHeight: 0,
|