@chance722/dsh-inbox 0.2.2 → 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 +179 -48
- package/lib/index.js +171 -54
- package/lib/types/client/messages.d.ts +42 -18
- package/lib/types/host/remote/merge.d.ts +17 -1
- package/lib/types/host/remote/pull.d.ts +27 -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 +79 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -624,6 +624,14 @@ window.__ModuleLoader__.load({
|
|
|
624
624
|
var INBOX_ENDPOINT_TAGS = "tags";
|
|
625
625
|
var INBOX_ENDPOINT_SECRET = "secret";
|
|
626
626
|
var INBOX_ENDPOINT_PUSH = "push";
|
|
627
|
+
var DEFAULT_SYNC_DIRECTORY = "inbox";
|
|
628
|
+
function syncDirectory(raw) {
|
|
629
|
+
const trimmed = (raw ?? "").trim().replace(/^\/+|\/+$/g, "");
|
|
630
|
+
return trimmed.length === 0 ? DEFAULT_SYNC_DIRECTORY : trimmed;
|
|
631
|
+
}
|
|
632
|
+
function syncRootFor(raw) {
|
|
633
|
+
return `${syncDirectory(raw)}/sync`;
|
|
634
|
+
}
|
|
627
635
|
function isSuccess(status) {
|
|
628
636
|
return status >= 200 && status < 300;
|
|
629
637
|
}
|
|
@@ -801,6 +809,11 @@ window.__ModuleLoader__.load({
|
|
|
801
809
|
"settings.region": "\u533A\u57DF",
|
|
802
810
|
"settings.bucketAddress": "\u5730\u5740",
|
|
803
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",
|
|
804
817
|
"settings.dirS3.lead": "\u76EE\u5F55\u90A3\u4E00\u680F\u540C\u65F6\u662F S3 \u7684 key \u524D\u7F00\uFF08\u9ED8\u8BA4 ",
|
|
805
818
|
"settings.dirS3.mid": "\uFF0C\u4F1A\u8F6C\u6210 ",
|
|
806
819
|
"settings.dirS3.tail": "\uFF09\u3002",
|
|
@@ -816,6 +829,7 @@ window.__ModuleLoader__.load({
|
|
|
816
829
|
"settings.save": "\u4FDD\u5B58",
|
|
817
830
|
"settings.probe": "\u81EA\u68C0",
|
|
818
831
|
"settings.details": "\u8BE6\u60C5\uFF08{count} \u6B21\u8BF7\u6C42\uFF09",
|
|
832
|
+
"settings.syncRootNow": "\u540C\u6B65\u6839\uFF1A{root}",
|
|
819
833
|
"settings.noServiceBody": "\u8FD9\u4E2A\u7EC4\u5408\u91CC\u6CA1\u6709\u8BBE\u7F6E\u670D\u52A1\uFF0C\u5730\u5740\u6539\u4E0D\u4E86\u2014\u2014\u4F60\u591A\u534A\u5728\u7528 headless \u5F62\u6001\u5F00\u53D1\u3002",
|
|
820
834
|
"settings.bucketHint": "\u522B\u7684\u8BBE\u5907\u5F80\u8FD9\u91CC\u6254\u4E1C\u897F",
|
|
821
835
|
// Notices (the one-line status under the toolbar).
|
|
@@ -849,16 +863,24 @@ window.__ModuleLoader__.load({
|
|
|
849
863
|
"sync.pushUnconfigured": "\u63A8\u9001\uFF1A\u8FD8\u6CA1\u914D\u7F6E\u8FDC\u7AEF",
|
|
850
864
|
"sync.pushFailed": "\u63A8\u9001\u5931\u8D25\uFF1A{reason}",
|
|
851
865
|
"sync.pushUnknown": "\u672A\u77E5\u539F\u56E0",
|
|
852
|
-
"sync.pushUpToDate": "\u63A8\u9001\uFF1A\u6CA1\u6709\u65B0\u5185\u5BB9\uFF08{count} \u6761\u5DF2\u662F\u6700\u65B0\uFF09",
|
|
853
|
-
"sync.pushDone": "\u63A8\u9001\uFF1A{records} \u6761\u8BB0\u5F55 / {attachments} \u4E2A\u9644\u4EF6",
|
|
854
866
|
"sync.pushPartial": "{head} \xB7 \u90E8\u5206\u5931\u8D25\uFF1A{reason}",
|
|
855
867
|
"sync.pullNoAddress": "\u8FD8\u6CA1\u914D\u7F6E\u5730\u5740",
|
|
856
868
|
"sync.pullFailed": "\u62C9\u53D6\u5931\u8D25\uFF1A{reason}",
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
"sync.
|
|
860
|
-
"sync.
|
|
861
|
-
"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",
|
|
883
|
+
"sync.pullFailures": " \xB7 \u5931\u8D25 {count}\uFF1A{reason}",
|
|
862
884
|
"sync.thisPage": " \xB7 \u672C\u9875 {count} \u6761",
|
|
863
885
|
"settings.status": "{settings} \xB7 {webdav} \xB7 {s3}",
|
|
864
886
|
"settings.statusOn": "\u8BBE\u7F6E\u670D\u52A1\u5728",
|
|
@@ -927,10 +949,15 @@ window.__ModuleLoader__.load({
|
|
|
927
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",
|
|
928
950
|
"manual.5.manual.label": "\u624B\u52A8",
|
|
929
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",
|
|
930
955
|
"manual.5.delete.label": "\u5220\u9664",
|
|
931
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",
|
|
932
|
-
"manual.5.
|
|
933
|
-
"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",
|
|
934
961
|
"manual.6.title": "6. \u5728\u5BF9\u8BDD\u91CC\u53D6\u56DE\u6765",
|
|
935
962
|
"manual.6.query.label": "\u67E5",
|
|
936
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",
|
|
@@ -1057,6 +1084,9 @@ window.__ModuleLoader__.load({
|
|
|
1057
1084
|
"settings.region": "Region",
|
|
1058
1085
|
"settings.bucketAddress": "Address",
|
|
1059
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)",
|
|
1060
1090
|
"settings.dirS3.lead": "the directory is also the key prefix for S3 (default ",
|
|
1061
1091
|
"settings.dirS3.mid": ", which becomes ",
|
|
1062
1092
|
"settings.dirS3.tail": ").",
|
|
@@ -1072,6 +1102,7 @@ window.__ModuleLoader__.load({
|
|
|
1072
1102
|
"settings.save": "Save",
|
|
1073
1103
|
"settings.probe": "Test",
|
|
1074
1104
|
"settings.details": "Details ({count} requests)",
|
|
1105
|
+
"settings.syncRootNow": "sync root: {root}",
|
|
1075
1106
|
"settings.noServiceBody": "this composition has no settings service, so the address cannot be changed \u2014 you are most likely running the headless shape.",
|
|
1076
1107
|
"settings.bucketHint": "other devices drop things here",
|
|
1077
1108
|
"notice.listFailed": "Could not read the list: {reason}",
|
|
@@ -1103,16 +1134,20 @@ window.__ModuleLoader__.load({
|
|
|
1103
1134
|
"sync.pushUnconfigured": "Push: no remote configured yet",
|
|
1104
1135
|
"sync.pushFailed": "Push failed: {reason}",
|
|
1105
1136
|
"sync.pushUnknown": "unknown reason",
|
|
1106
|
-
"sync.pushUpToDate": "Push: nothing new ({count} already current)",
|
|
1107
|
-
"sync.pushDone": "Push: {records} records / {attachments} attachment(s)",
|
|
1108
1137
|
"sync.pushPartial": "{head} \xB7 partly failed: {reason}",
|
|
1109
1138
|
"sync.pullNoAddress": "No address configured yet",
|
|
1110
1139
|
"sync.pullFailed": "Pull failed: {reason}",
|
|
1111
|
-
"sync.
|
|
1112
|
-
"sync.
|
|
1113
|
-
"sync.
|
|
1114
|
-
"sync.
|
|
1115
|
-
"sync.
|
|
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",
|
|
1150
|
+
"sync.pullFailures": " \xB7 {count} failed: {reason}",
|
|
1116
1151
|
"sync.thisPage": " \xB7 {count} on this page",
|
|
1117
1152
|
"settings.status": "{settings} \xB7 {webdav} \xB7 {s3}",
|
|
1118
1153
|
"settings.statusOn": "settings service present",
|
|
@@ -1178,10 +1213,14 @@ window.__ModuleLoader__.load({
|
|
|
1178
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).",
|
|
1179
1214
|
"manual.5.manual.label": "Manual",
|
|
1180
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.",
|
|
1181
1218
|
"manual.5.delete.label": "Deleting",
|
|
1182
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.",
|
|
1183
|
-
"manual.5.
|
|
1184
|
-
"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.",
|
|
1185
1224
|
"manual.6.title": "6. Getting things back in the conversation",
|
|
1186
1225
|
"manual.6.query.label": "Ask",
|
|
1187
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.",
|
|
@@ -1744,7 +1783,12 @@ window.__ModuleLoader__.load({
|
|
|
1744
1783
|
manual ran off the bottom of the screen with no visible frame.
|
|
1745
1784
|
*/
|
|
1746
1785
|
width: "min(560px, 100%)",
|
|
1747
|
-
|
|
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",
|
|
1748
1792
|
// Border inside the width and the cap: without it the card is 2px
|
|
1749
1793
|
// wider than the number and 2px taller than the overlay's room.
|
|
1750
1794
|
boxSizing: "border-box",
|
|
@@ -1795,6 +1839,7 @@ window.__ModuleLoader__.load({
|
|
|
1795
1839
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
1796
1840
|
"div",
|
|
1797
1841
|
{
|
|
1842
|
+
className: "dsh-inbox-scroll",
|
|
1798
1843
|
style: {
|
|
1799
1844
|
padding: 18,
|
|
1800
1845
|
display: "flex",
|
|
@@ -1838,8 +1883,10 @@ window.__ModuleLoader__.load({
|
|
|
1838
1883
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Section, { title: t("manual.5.title"), children: [
|
|
1839
1884
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: t("manual.5.auto.label"), children: t("manual.5.auto.body") }),
|
|
1840
1885
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: t("manual.5.manual.label"), children: t("manual.5.manual.body") }),
|
|
1841
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: t("manual.5.
|
|
1842
|
-
/* @__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") })
|
|
1843
1890
|
] }),
|
|
1844
1891
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Section, { title: t("manual.6.title"), children: [
|
|
1845
1892
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: t("manual.6.query.label"), children: t("manual.6.query.body") }),
|
|
@@ -2084,6 +2131,7 @@ window.__ModuleLoader__.load({
|
|
|
2084
2131
|
const [text, setText] = import_react8.default.useState("");
|
|
2085
2132
|
const [staged, setStaged] = import_react8.default.useState([]);
|
|
2086
2133
|
const [notice, setNotice] = import_react8.default.useState();
|
|
2134
|
+
const [syncDetail, setSyncDetail] = import_react8.default.useState();
|
|
2087
2135
|
const [busy, setBusy] = import_react8.default.useState(false);
|
|
2088
2136
|
const [dragging, setDragging] = import_react8.default.useState(false);
|
|
2089
2137
|
const picker = import_react8.default.useRef(null);
|
|
@@ -2236,19 +2284,27 @@ window.__ModuleLoader__.load({
|
|
|
2236
2284
|
setNotice(t("notice.syncing"));
|
|
2237
2285
|
try {
|
|
2238
2286
|
const pushed = await call(INBOX_ENDPOINT_PUSH, {});
|
|
2239
|
-
const pushLine = pushed.ok ? describePush(pushed.value) : t("notice.pushFailed", { reason: pushed.error.message });
|
|
2240
2287
|
const pulled = await call(INBOX_ENDPOINT_PULL, {});
|
|
2241
|
-
let suffix = "";
|
|
2242
|
-
if (pulled.ok) {
|
|
2243
|
-
const result = pulled.value;
|
|
2244
|
-
suffix = ` \xB7 ${describePull(result)}`;
|
|
2245
|
-
} else {
|
|
2246
|
-
suffix = ` \xB7 ${t("notice.pullFailed", { reason: pulled.error.message })}`;
|
|
2247
|
-
}
|
|
2248
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) : "";
|
|
2249
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 ");
|
|
2250
2300
|
setNotice(
|
|
2251
|
-
`${
|
|
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 ")
|
|
2252
2308
|
);
|
|
2253
2309
|
} finally {
|
|
2254
2310
|
setBusy(false);
|
|
@@ -2449,6 +2505,28 @@ window.__ModuleLoader__.load({
|
|
|
2449
2505
|
if (entry.url !== void 0) setZoom({ href: entry.url, label: headingOf(entry) });
|
|
2450
2506
|
}, []);
|
|
2451
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
|
+
` }),
|
|
2452
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: [
|
|
2453
2531
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
|
|
2454
2532
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { style: { margin: "0 0 4px" }, children: "dsh-inbox" }),
|
|
@@ -2853,6 +2931,7 @@ window.__ModuleLoader__.load({
|
|
|
2853
2931
|
"button",
|
|
2854
2932
|
{
|
|
2855
2933
|
type: "button",
|
|
2934
|
+
title: syncDetail,
|
|
2856
2935
|
style: { ...buttonStyle, height: CONTROL_HEIGHT, boxSizing: "border-box" },
|
|
2857
2936
|
disabled: busy,
|
|
2858
2937
|
onClick: () => void refreshAll(),
|
|
@@ -3033,6 +3112,7 @@ window.__ModuleLoader__.load({
|
|
|
3033
3112
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3034
3113
|
"div",
|
|
3035
3114
|
{
|
|
3115
|
+
className: "dsh-inbox-scroll",
|
|
3036
3116
|
style: listMode === "compact" ? {
|
|
3037
3117
|
display: "flex",
|
|
3038
3118
|
flexDirection: "column",
|
|
@@ -3201,28 +3281,47 @@ window.__ModuleLoader__.load({
|
|
|
3201
3281
|
)
|
|
3202
3282
|
] });
|
|
3203
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
|
+
}
|
|
3204
3303
|
function describePush(result) {
|
|
3205
3304
|
if (result.status === "unconfigured") return t("sync.pushUnconfigured");
|
|
3206
3305
|
if (result.status === "failed") return t("sync.pushFailed", { reason: result.reason ?? t("sync.pushUnknown") });
|
|
3207
|
-
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 });
|
|
3208
3307
|
return result.status === "partial" ? t("sync.pushPartial", { head, reason: result.reason ?? "" }) : head;
|
|
3209
3308
|
}
|
|
3210
3309
|
function describePull(result) {
|
|
3211
3310
|
if (result.status === "unconfigured") return result.reason ?? t("sync.pullNoAddress");
|
|
3212
3311
|
if (result.status === "failed") return t("sync.pullFailed", { reason: result.reason ?? t("sync.pushUnknown") });
|
|
3213
3312
|
const merged = result.merged ?? 0;
|
|
3214
|
-
const
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3313
|
+
const head = t("sync.detailPulled", {
|
|
3314
|
+
count: result.pulled + merged,
|
|
3315
|
+
records: result.remoteRecords ?? 0,
|
|
3316
|
+
files: result.remoteAttachments ?? 0
|
|
3218
3317
|
});
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
listed: result.listed,
|
|
3222
|
-
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", {
|
|
3223
3320
|
skipped: result.skipped,
|
|
3224
|
-
|
|
3321
|
+
sync: result.skippedSync ?? 0,
|
|
3322
|
+
older: result.skippedOlder ?? 0
|
|
3225
3323
|
});
|
|
3324
|
+
return `${head}${gained}${skipped}${warningsOf(result, true)}`;
|
|
3226
3325
|
}
|
|
3227
3326
|
function EncryptionSettings({ call }) {
|
|
3228
3327
|
const [status, setStatus] = import_react8.default.useState();
|
|
@@ -3317,6 +3416,7 @@ window.__ModuleLoader__.load({
|
|
|
3317
3416
|
const [status, setStatus] = import_react8.default.useState();
|
|
3318
3417
|
const [baseUrl, setBaseUrl] = import_react8.default.useState("");
|
|
3319
3418
|
const [directory, setDirectory] = import_react8.default.useState("/inbox");
|
|
3419
|
+
const [adoptForeignRoots, setAdoptForeignRoots] = import_react8.default.useState(false);
|
|
3320
3420
|
const [username, setUsername] = import_react8.default.useState("");
|
|
3321
3421
|
const [password, setPassword] = import_react8.default.useState("");
|
|
3322
3422
|
const [protocol, setProtocol] = import_react8.default.useState("webdav");
|
|
@@ -3341,6 +3441,7 @@ window.__ModuleLoader__.load({
|
|
|
3341
3441
|
setStatus(next);
|
|
3342
3442
|
setBaseUrl(next.settings.baseUrl);
|
|
3343
3443
|
setDirectory(next.settings.directory);
|
|
3444
|
+
setAdoptForeignRoots(next.settings.adoptForeignRoots === true);
|
|
3344
3445
|
setUsername(next.settings.username);
|
|
3345
3446
|
setProtocol(next.settings.protocol);
|
|
3346
3447
|
setEndpoint(next.settings.endpoint);
|
|
@@ -3370,6 +3471,7 @@ window.__ModuleLoader__.load({
|
|
|
3370
3471
|
protocol,
|
|
3371
3472
|
baseUrl,
|
|
3372
3473
|
directory,
|
|
3474
|
+
adoptForeignRoots,
|
|
3373
3475
|
username,
|
|
3374
3476
|
// Sending nothing leaves the stored password alone; sending "" clears it.
|
|
3375
3477
|
...password.length === 0 ? {} : { password },
|
|
@@ -3471,11 +3573,15 @@ window.__ModuleLoader__.load({
|
|
|
3471
3573
|
value: directory,
|
|
3472
3574
|
disabled: busy,
|
|
3473
3575
|
onChange: (event) => setDirectory(event.target.value),
|
|
3474
|
-
placeholder: "
|
|
3576
|
+
placeholder: t("settings.directoryPlaceholder"),
|
|
3475
3577
|
style: { ...inputStyle, flex: 1 }
|
|
3476
3578
|
}
|
|
3477
3579
|
),
|
|
3478
|
-
/* @__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
|
+
] })
|
|
3479
3585
|
] }),
|
|
3480
3586
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
|
|
3481
3587
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { opacity: 0.7, minWidth: 64 }, children: t("settings.username") }),
|
|
@@ -3581,15 +3687,39 @@ window.__ModuleLoader__.load({
|
|
|
3581
3687
|
}
|
|
3582
3688
|
)
|
|
3583
3689
|
] }),
|
|
3584
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("
|
|
3585
|
-
t("settings.
|
|
3586
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3690
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
|
|
3691
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { opacity: 0.7, minWidth: 64 }, children: t("settings.bucketDir") }),
|
|
3692
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
3693
|
+
"input",
|
|
3694
|
+
{
|
|
3695
|
+
value: directory,
|
|
3696
|
+
disabled: busy,
|
|
3697
|
+
onChange: (event) => setDirectory(event.target.value),
|
|
3698
|
+
placeholder: t("settings.directoryPlaceholder"),
|
|
3699
|
+
style: { ...inputStyle, flex: 1 }
|
|
3700
|
+
}
|
|
3701
|
+
),
|
|
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
|
+
] })
|
|
3590
3707
|
] })
|
|
3591
3708
|
] }),
|
|
3592
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
|
+
] }),
|
|
3593
3723
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", style: buttonStyle, disabled: busy, onClick: () => void save(), children: t("settings.save") }),
|
|
3594
3724
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", style: buttonStyle, disabled: busy, onClick: () => void selfTest(), children: t("settings.probe") }),
|
|
3595
3725
|
detailNotice(notice)
|
|
@@ -4020,6 +4150,7 @@ window.__ModuleLoader__.load({
|
|
|
4020
4150
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
4021
4151
|
"div",
|
|
4022
4152
|
{
|
|
4153
|
+
className: "dsh-inbox-scroll",
|
|
4023
4154
|
style: {
|
|
4024
4155
|
flex: 1,
|
|
4025
4156
|
minHeight: 0,
|