@novacraft-engineering/mailbox 0.1.0 → 0.2.0
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/README.md +7 -2
- package/app/api/mail/accessors/route.ts +28 -24
- package/app/api/mail/emails/[id]/attachments/download/route.ts +3 -1
- package/app/api/mail/emails/[id]/attachments/forward/route.ts +3 -1
- package/app/api/mail/emails/[id]/route.ts +17 -9
- package/app/api/mail/emails/route.ts +35 -4
- package/app/api/mail/inbox/counts/route.ts +5 -4
- package/app/api/mail/inbox/route.ts +41 -262
- package/app/api/mail/maintenance/received/route.ts +64 -0
- package/app/api/mail/pixel/[id]/route.ts +8 -2
- package/app/api/mail/request-reset/route.ts +29 -25
- package/app/api/mail/signature-logo/route.ts +33 -8
- package/app/api/mail/threads/route.ts +1 -1
- package/app/brand/[file]/placeholder.ts +70 -0
- package/app/brand/[file]/route.ts +7 -1
- package/app/layout.tsx +10 -2
- package/app/mail/MailSelect.tsx +14 -3
- package/app/mail/RichEditor.tsx +168 -52
- package/app/mail/Ticker.tsx +40 -0
- package/app/mail/page.module.css +624 -70
- package/app/mail/page.tsx +1059 -244
- package/app/mail/search.ts +1 -1
- package/app/share/[id]/page.tsx +5 -3
- package/app/share/[id]/share.module.css +5 -5
- package/lib/email-html.ts +9 -1
- package/lib/emails/index.ts +26 -0
- package/lib/emails/templates/action.html +47 -0
- package/lib/fonts.ts +7 -1
- package/lib/mail-provider.ts +50 -5
- package/lib/mailbox.ts +303 -34
- package/lib/receive.ts +318 -0
- package/lib/sent-access.ts +22 -0
- package/lib/ses-send.ts +49 -0
- package/package.json +2 -2
- package/public/brand/README.md +5 -1
- package/scripts/sample-files.mjs +208 -28
- package/scripts/seed-dev.mjs +69 -26
- package/tools/README.md +1 -1
package/app/mail/page.module.css
CHANGED
|
@@ -515,6 +515,18 @@
|
|
|
515
515
|
transition: color var(--dur-fast) var(--nc-ease-out), border-color var(--dur-fast) var(--nc-ease-out);
|
|
516
516
|
}
|
|
517
517
|
.refreshBtn:hover { color: var(--nc-fg-1); border-color: var(--nc-line-2); }
|
|
518
|
+
.crossFolderHint {
|
|
519
|
+
align-self: flex-start;
|
|
520
|
+
margin: 0 16px 8px;
|
|
521
|
+
padding: 6px 11px;
|
|
522
|
+
border: 1px solid var(--nc-line-1);
|
|
523
|
+
border-radius: 999px;
|
|
524
|
+
background: var(--nc-ink-2);
|
|
525
|
+
color: var(--nc-violet-500);
|
|
526
|
+
font: 500 12px/1 var(--nc-font-sans);
|
|
527
|
+
cursor: pointer;
|
|
528
|
+
}
|
|
529
|
+
.crossFolderHint:hover { border-color: var(--nc-violet-500); }
|
|
518
530
|
.refreshBtn svg { width: 14px; height: 14px; }
|
|
519
531
|
.filterChip {
|
|
520
532
|
margin-left: auto;
|
|
@@ -652,12 +664,187 @@
|
|
|
652
664
|
cursor: progress;
|
|
653
665
|
}
|
|
654
666
|
|
|
667
|
+
/* Floats over the list rather than in it, so it does not move as rows load. The pane is
|
|
668
|
+
the positioning context; the list itself scrolls underneath. */
|
|
669
|
+
.toTop {
|
|
670
|
+
position: absolute;
|
|
671
|
+
right: 16px;
|
|
672
|
+
bottom: 18px;
|
|
673
|
+
z-index: 4;
|
|
674
|
+
width: 34px;
|
|
675
|
+
height: 34px;
|
|
676
|
+
display: grid;
|
|
677
|
+
place-items: center;
|
|
678
|
+
border: none;
|
|
679
|
+
border-radius: 999px;
|
|
680
|
+
background: var(--nc-violet-500);
|
|
681
|
+
color: #fff;
|
|
682
|
+
cursor: pointer;
|
|
683
|
+
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.22);
|
|
684
|
+
opacity: 0;
|
|
685
|
+
transform: translateY(8px);
|
|
686
|
+
pointer-events: none;
|
|
687
|
+
transition: opacity 140ms ease, transform 140ms ease;
|
|
688
|
+
}
|
|
689
|
+
.toTop svg {
|
|
690
|
+
width: 18px;
|
|
691
|
+
height: 18px;
|
|
692
|
+
/* The only chevron in the set points down. */
|
|
693
|
+
transform: rotate(180deg);
|
|
694
|
+
}
|
|
695
|
+
.toTopOn {
|
|
696
|
+
opacity: 1;
|
|
697
|
+
transform: translateY(0);
|
|
698
|
+
pointer-events: auto;
|
|
699
|
+
}
|
|
700
|
+
.toTop:hover {
|
|
701
|
+
background: var(--nc-violet-400);
|
|
702
|
+
}
|
|
703
|
+
.toTop:focus-visible {
|
|
704
|
+
outline: 2px solid var(--nc-violet-500);
|
|
705
|
+
outline-offset: 2px;
|
|
706
|
+
}
|
|
707
|
+
@media (prefers-reduced-motion: reduce) {
|
|
708
|
+
.toTop {
|
|
709
|
+
transition: none;
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
|
|
655
713
|
.list {
|
|
656
714
|
flex: 1;
|
|
657
715
|
min-height: 0;
|
|
658
716
|
overflow-y: auto;
|
|
659
717
|
overscroll-behavior: contain;
|
|
660
718
|
padding: 0 10px 16px;
|
|
719
|
+
/* Firefox, and Chrome from 121. Where these are honoured the WebKit rules below are
|
|
720
|
+
ignored outright, so the two have to describe the same scrollbar rather than
|
|
721
|
+
combine into one. */
|
|
722
|
+
scrollbar-width: thin;
|
|
723
|
+
scrollbar-color: color-mix(in srgb, var(--nc-violet-500) 55%, transparent) transparent;
|
|
724
|
+
}
|
|
725
|
+
.snoozeWrap {
|
|
726
|
+
position: relative;
|
|
727
|
+
display: inline-flex;
|
|
728
|
+
}
|
|
729
|
+
.snoozeMenu {
|
|
730
|
+
position: absolute;
|
|
731
|
+
top: calc(100% + 6px);
|
|
732
|
+
right: 0;
|
|
733
|
+
z-index: 60;
|
|
734
|
+
min-width: 232px;
|
|
735
|
+
padding: 6px;
|
|
736
|
+
display: flex;
|
|
737
|
+
flex-direction: column;
|
|
738
|
+
gap: 2px;
|
|
739
|
+
background: var(--nc-ink-1);
|
|
740
|
+
border: 1px solid var(--nc-line-2);
|
|
741
|
+
border-radius: var(--radius-card);
|
|
742
|
+
box-shadow: 0 18px 44px -18px rgba(0, 0, 0, .65);
|
|
743
|
+
}
|
|
744
|
+
.snoozeItem {
|
|
745
|
+
display: flex;
|
|
746
|
+
align-items: center;
|
|
747
|
+
justify-content: space-between;
|
|
748
|
+
gap: 16px;
|
|
749
|
+
min-height: 38px;
|
|
750
|
+
padding: 0 10px;
|
|
751
|
+
border: none;
|
|
752
|
+
border-radius: var(--radius-control);
|
|
753
|
+
background: transparent;
|
|
754
|
+
color: var(--nc-fg-1);
|
|
755
|
+
font: 500 13px/1.3 var(--nc-font-sans);
|
|
756
|
+
cursor: pointer;
|
|
757
|
+
text-align: left;
|
|
758
|
+
}
|
|
759
|
+
.snoozeItem:hover { background: var(--nc-ink-3); }
|
|
760
|
+
.snoozeWhen {
|
|
761
|
+
font: 500 12px/1.3 var(--nc-font-mono);
|
|
762
|
+
color: var(--nc-fg-3);
|
|
763
|
+
white-space: nowrap;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
/* One band, moved to whichever row is being dragged, rather than a wrapper per row: the
|
|
767
|
+
rows are staggered on entry by :nth-child and wrapping them would reset every one of
|
|
768
|
+
those to the first position. */
|
|
769
|
+
.swipeBand {
|
|
770
|
+
position: absolute;
|
|
771
|
+
left: 10px;
|
|
772
|
+
right: 10px;
|
|
773
|
+
display: flex;
|
|
774
|
+
align-items: center;
|
|
775
|
+
padding: 0 20px;
|
|
776
|
+
border-radius: 12px;
|
|
777
|
+
font: 600 13px/1 var(--nc-font-sans);
|
|
778
|
+
color: #fff;
|
|
779
|
+
pointer-events: none;
|
|
780
|
+
}
|
|
781
|
+
.swipeBandRight {
|
|
782
|
+
justify-content: flex-start;
|
|
783
|
+
background: color-mix(in srgb, var(--nc-violet-500) 55%, transparent);
|
|
784
|
+
}
|
|
785
|
+
.swipeBandLeft {
|
|
786
|
+
justify-content: flex-end;
|
|
787
|
+
background: color-mix(in srgb, var(--nc-danger) 55%, transparent);
|
|
788
|
+
}
|
|
789
|
+
.swipeBandArmed.swipeBandRight { background: var(--nc-violet-500); }
|
|
790
|
+
.swipeBandArmed.swipeBandLeft { background: var(--nc-danger); }
|
|
791
|
+
|
|
792
|
+
.list {
|
|
793
|
+
position: relative;
|
|
794
|
+
}
|
|
795
|
+
/* The row rides the finger, so it must not animate back on every frame. */
|
|
796
|
+
.item {
|
|
797
|
+
touch-action: pan-y;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
/* Sits between the header and the rows, growing out of the gap as the list is pulled
|
|
801
|
+
down, so the gesture has something to answer it. */
|
|
802
|
+
.pullHint {
|
|
803
|
+
display: flex;
|
|
804
|
+
align-items: center;
|
|
805
|
+
justify-content: center;
|
|
806
|
+
overflow: hidden;
|
|
807
|
+
transition: opacity 120ms ease;
|
|
808
|
+
}
|
|
809
|
+
.pullSpinner {
|
|
810
|
+
display: grid;
|
|
811
|
+
place-items: center;
|
|
812
|
+
width: 30px;
|
|
813
|
+
height: 30px;
|
|
814
|
+
border-radius: 999px;
|
|
815
|
+
background: var(--nc-ink-2);
|
|
816
|
+
border: 1px solid var(--nc-line-1);
|
|
817
|
+
color: var(--nc-fg-3);
|
|
818
|
+
}
|
|
819
|
+
.pullSpinner svg {
|
|
820
|
+
width: 16px;
|
|
821
|
+
height: 16px;
|
|
822
|
+
}
|
|
823
|
+
.pullSpinnerReady {
|
|
824
|
+
background: var(--nc-violet-500);
|
|
825
|
+
border-color: var(--nc-violet-500);
|
|
826
|
+
color: #fff;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
/* Safari, and older Chrome. */
|
|
830
|
+
.list::-webkit-scrollbar {
|
|
831
|
+
width: 10px;
|
|
832
|
+
}
|
|
833
|
+
.list::-webkit-scrollbar-track {
|
|
834
|
+
background: transparent;
|
|
835
|
+
}
|
|
836
|
+
.list::-webkit-scrollbar-thumb {
|
|
837
|
+
/* The transparent border plus padding-box clipping is what insets the thumb: there is
|
|
838
|
+
no padding property on a scrollbar, and a full-width bar against the rows reads as
|
|
839
|
+
part of the list rather than a control over it. */
|
|
840
|
+
background: color-mix(in srgb, var(--nc-violet-500) 45%, transparent);
|
|
841
|
+
background-clip: padding-box;
|
|
842
|
+
border: 3px solid transparent;
|
|
843
|
+
border-radius: 999px;
|
|
844
|
+
}
|
|
845
|
+
.list::-webkit-scrollbar-thumb:hover {
|
|
846
|
+
background: color-mix(in srgb, var(--nc-violet-500) 78%, transparent);
|
|
847
|
+
background-clip: padding-box;
|
|
661
848
|
}
|
|
662
849
|
.app[data-density='relaxed'] {
|
|
663
850
|
--row-pad: 18px 16px;
|
|
@@ -776,9 +963,9 @@
|
|
|
776
963
|
color: var(--nc-success);
|
|
777
964
|
border-color: color-mix(in srgb, var(--nc-success) 40%, transparent);
|
|
778
965
|
}
|
|
779
|
-
.chipScheduled { color: var(--nc-warn); border-color:
|
|
966
|
+
.chipScheduled { color: var(--nc-warn); border-color: color-mix(in srgb, var(--nc-warn) 30%, transparent); }
|
|
780
967
|
.chipDelivered { color: var(--nc-success); border-color: rgba(52,211,153,.3); }
|
|
781
|
-
.chipBounced { color: var(--nc-danger); border-color:
|
|
968
|
+
.chipBounced { color: var(--nc-danger); border-color: color-mix(in srgb, var(--nc-danger) 30%, transparent); }
|
|
782
969
|
.chipDraft { color: var(--nc-violet-200); border-color: rgba(165,120,255,.3); }
|
|
783
970
|
|
|
784
971
|
.emptyList {
|
|
@@ -918,8 +1105,8 @@
|
|
|
918
1105
|
}
|
|
919
1106
|
.actionBtn:hover { color: var(--nc-fg-1); border-color: var(--nc-line-2); background: var(--nc-ink-2); }
|
|
920
1107
|
.actionBtn svg { width: 13px; height: 13px; }
|
|
921
|
-
.actionDanger { color: var(--nc-danger); border-color:
|
|
922
|
-
.actionDanger:hover { color: var(--nc-danger-soft); border-color:
|
|
1108
|
+
.actionDanger { color: var(--nc-danger); border-color: color-mix(in srgb, var(--nc-danger) 30%, transparent); }
|
|
1109
|
+
.actionDanger:hover { color: var(--nc-danger-soft); border-color: color-mix(in srgb, var(--nc-danger) 55%, transparent); background: color-mix(in srgb, var(--nc-danger) 6%, transparent); }
|
|
923
1110
|
.countdownChip {
|
|
924
1111
|
display: inline-flex;
|
|
925
1112
|
align-items: center;
|
|
@@ -928,7 +1115,7 @@
|
|
|
928
1115
|
font-size: 12px;
|
|
929
1116
|
color: var(--nc-warn);
|
|
930
1117
|
padding: 8px 12px;
|
|
931
|
-
border: 1px solid
|
|
1118
|
+
border: 1px solid color-mix(in srgb, var(--nc-warn) 30%, transparent);
|
|
932
1119
|
border-radius: 9px;
|
|
933
1120
|
}
|
|
934
1121
|
.activity {
|
|
@@ -1096,12 +1283,11 @@
|
|
|
1096
1283
|
padding: 10px 0 2px;
|
|
1097
1284
|
}
|
|
1098
1285
|
.attachFloatBody .attachGrid { margin: 0 14px 10px; }
|
|
1099
|
-
/*
|
|
1100
|
-
|
|
1286
|
+
/* Tiles sit in the sheet's own bottom-left corner, flush with its border, the way they
|
|
1287
|
+
did before the sheet became a centred page. The page stays centred; only the tiles
|
|
1288
|
+
went back to the corner. */
|
|
1101
1289
|
.threadMsgBody .attachGrid {
|
|
1102
|
-
|
|
1103
|
-
margin: 0;
|
|
1104
|
-
padding: 0 34px 30px;
|
|
1290
|
+
margin: 12px 0 0;
|
|
1105
1291
|
}
|
|
1106
1292
|
.attachPillWrap {
|
|
1107
1293
|
position: absolute;
|
|
@@ -1131,23 +1317,20 @@
|
|
|
1131
1317
|
.readerBody {
|
|
1132
1318
|
flex: 1;
|
|
1133
1319
|
margin: 0 28px 24px;
|
|
1134
|
-
border-radius: var(--radius-card);
|
|
1135
|
-
border: 1px solid var(--nc-line-1);
|
|
1136
1320
|
overflow: hidden;
|
|
1137
|
-
background: var(--nc-ink-1);
|
|
1138
1321
|
min-height: 0;
|
|
1139
1322
|
}
|
|
1140
1323
|
.readerFrame {
|
|
1141
1324
|
width: 100%;
|
|
1142
1325
|
height: 100%;
|
|
1143
1326
|
border: none;
|
|
1144
|
-
background:
|
|
1327
|
+
background: transparent;
|
|
1145
1328
|
}
|
|
1146
1329
|
.readerText {
|
|
1147
|
-
max-width:
|
|
1148
|
-
margin: 0
|
|
1149
|
-
padding:
|
|
1150
|
-
font: 400 15px/1.
|
|
1330
|
+
max-width: 860px;
|
|
1331
|
+
margin: 0;
|
|
1332
|
+
padding: 8px 0 24px;
|
|
1333
|
+
font: 400 15px/var(--mail-lead, 1.3) var(--nc-font-sans);
|
|
1151
1334
|
color: var(--nc-fg-1);
|
|
1152
1335
|
white-space: pre-wrap;
|
|
1153
1336
|
word-break: break-word;
|
|
@@ -1156,7 +1339,6 @@
|
|
|
1156
1339
|
overflow-y: auto;
|
|
1157
1340
|
/* A plain-text part carries no styling of its own, so it follows the app rather
|
|
1158
1341
|
than sitting on the white paper an HTML email brings with it. */
|
|
1159
|
-
background: var(--nc-ink-2);
|
|
1160
1342
|
-webkit-font-smoothing: antialiased;
|
|
1161
1343
|
}
|
|
1162
1344
|
.readerSource {
|
|
@@ -1186,7 +1368,73 @@
|
|
|
1186
1368
|
padding: 10px 16px calc(12px + env(safe-area-inset-bottom, 0px));
|
|
1187
1369
|
background: var(--nc-ink-0);
|
|
1188
1370
|
border-top: 1px solid var(--nc-line-1);
|
|
1371
|
+
animation: ncReplyIn .24s cubic-bezier(.2, .7, .3, 1) both;
|
|
1372
|
+
}
|
|
1373
|
+
@keyframes ncReplyIn {
|
|
1374
|
+
from { opacity: 0; transform: translateY(14px); }
|
|
1375
|
+
to { opacity: 1; transform: none; }
|
|
1376
|
+
}
|
|
1377
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1378
|
+
.quickReplyBar { animation: none; }
|
|
1379
|
+
}
|
|
1380
|
+
/* The phone's stand-in for the docked reply: one line that opens the composer as a page.
|
|
1381
|
+
It keeps the dock's place at the foot of the reader so the layout does not shift. */
|
|
1382
|
+
.replyLaunch {
|
|
1383
|
+
flex: 0 0 auto;
|
|
1384
|
+
margin-top: auto;
|
|
1385
|
+
position: sticky;
|
|
1386
|
+
bottom: 0;
|
|
1387
|
+
z-index: 6;
|
|
1388
|
+
display: flex;
|
|
1389
|
+
gap: 8px;
|
|
1390
|
+
padding: 10px 16px calc(12px + env(safe-area-inset-bottom, 0px));
|
|
1391
|
+
background: var(--nc-ink-0);
|
|
1392
|
+
border-top: 1px solid var(--nc-line-1);
|
|
1393
|
+
}
|
|
1394
|
+
.replyLaunchBtn {
|
|
1395
|
+
flex: 1;
|
|
1396
|
+
min-width: 0;
|
|
1397
|
+
display: flex;
|
|
1398
|
+
align-items: center;
|
|
1399
|
+
gap: 10px;
|
|
1400
|
+
min-height: 44px;
|
|
1401
|
+
padding: 0 16px;
|
|
1402
|
+
border-radius: 12px;
|
|
1403
|
+
border: 1px solid var(--nc-line-1);
|
|
1404
|
+
background: var(--nc-ink-2);
|
|
1405
|
+
color: var(--nc-fg-2);
|
|
1406
|
+
font: 400 14px/1.4 var(--nc-font-sans);
|
|
1407
|
+
text-align: left;
|
|
1408
|
+
cursor: pointer;
|
|
1409
|
+
}
|
|
1410
|
+
.replyLaunchBtn span {
|
|
1411
|
+
min-width: 0;
|
|
1412
|
+
overflow: hidden;
|
|
1413
|
+
white-space: nowrap;
|
|
1414
|
+
text-overflow: ellipsis;
|
|
1189
1415
|
}
|
|
1416
|
+
.replyLaunchBtn svg {
|
|
1417
|
+
flex: 0 0 auto;
|
|
1418
|
+
width: 16px;
|
|
1419
|
+
height: 16px;
|
|
1420
|
+
color: var(--nc-fg-3);
|
|
1421
|
+
}
|
|
1422
|
+
.replyLaunchAll {
|
|
1423
|
+
flex: 0 0 44px;
|
|
1424
|
+
height: 44px;
|
|
1425
|
+
display: grid;
|
|
1426
|
+
place-items: center;
|
|
1427
|
+
border-radius: 12px;
|
|
1428
|
+
border: 1px solid var(--nc-line-1);
|
|
1429
|
+
background: transparent;
|
|
1430
|
+
color: var(--nc-fg-2);
|
|
1431
|
+
cursor: pointer;
|
|
1432
|
+
}
|
|
1433
|
+
.replyLaunchAll svg {
|
|
1434
|
+
width: 17px;
|
|
1435
|
+
height: 17px;
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1190
1438
|
.replyRecips {
|
|
1191
1439
|
display: flex;
|
|
1192
1440
|
flex-direction: column;
|
|
@@ -1445,6 +1693,58 @@
|
|
|
1445
1693
|
padding: 0;
|
|
1446
1694
|
}
|
|
1447
1695
|
.rteSwatchHi { border-radius: 50%; }
|
|
1696
|
+
.tickerHost {
|
|
1697
|
+
display: inline-block;
|
|
1698
|
+
max-width: 100%;
|
|
1699
|
+
min-width: 0;
|
|
1700
|
+
overflow: hidden;
|
|
1701
|
+
white-space: nowrap;
|
|
1702
|
+
text-overflow: ellipsis;
|
|
1703
|
+
vertical-align: bottom;
|
|
1704
|
+
}
|
|
1705
|
+
.tickerBlock { display: block; }
|
|
1706
|
+
.tickerHost[data-ticker='on'] { text-overflow: clip; }
|
|
1707
|
+
.tickerText { display: inline-block; white-space: nowrap; }
|
|
1708
|
+
.tickerHost[data-ticker='on'] .tickerText { animation: ncTicker var(--ticker-duration, 8s) ease-in-out var(--ticker-delay, 0s) infinite alternate; }
|
|
1709
|
+
@keyframes ncTicker {
|
|
1710
|
+
0%, 14% { transform: translateX(0); }
|
|
1711
|
+
86%, 100% { transform: translateX(calc(-1 * var(--ticker-shift, 0px))); }
|
|
1712
|
+
}
|
|
1713
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1714
|
+
.tickerHost[data-ticker='on'] .tickerText { animation: none; }
|
|
1715
|
+
.tickerHost[data-ticker='on'] { text-overflow: ellipsis; }
|
|
1716
|
+
}
|
|
1717
|
+
.itemSubject { display: flex; align-items: center; gap: 4px; }
|
|
1718
|
+
.itemSubjectText { flex: 1; }
|
|
1719
|
+
.threadMsgFrom { display: inline-flex; align-items: center; gap: 6px; max-width: 100%; }
|
|
1720
|
+
.threadMsgFromText { flex: 1; }
|
|
1721
|
+
.rteColourDot {
|
|
1722
|
+
display: inline-block;
|
|
1723
|
+
width: 16px;
|
|
1724
|
+
height: 16px;
|
|
1725
|
+
border-radius: 50%;
|
|
1726
|
+
background: conic-gradient(#b91c1c, #b45309, #047857, #1d4ed8, #6d28d9, #b91c1c);
|
|
1727
|
+
}
|
|
1728
|
+
.rtePalette {
|
|
1729
|
+
display: flex;
|
|
1730
|
+
align-items: center;
|
|
1731
|
+
gap: 10px;
|
|
1732
|
+
padding: 0 12px;
|
|
1733
|
+
max-height: 0;
|
|
1734
|
+
opacity: 0;
|
|
1735
|
+
overflow: hidden;
|
|
1736
|
+
transform: translateY(-6px);
|
|
1737
|
+
background: var(--nc-ink-2);
|
|
1738
|
+
transition: max-height .28s var(--nc-ease-out), opacity .22s var(--nc-ease-out), transform .28s var(--nc-ease-out), padding .28s var(--nc-ease-out);
|
|
1739
|
+
}
|
|
1740
|
+
.rtePaletteOpen {
|
|
1741
|
+
max-height: 60px;
|
|
1742
|
+
opacity: 1;
|
|
1743
|
+
transform: none;
|
|
1744
|
+
padding: 8px 12px;
|
|
1745
|
+
border-bottom: 1px solid var(--nc-line-1);
|
|
1746
|
+
}
|
|
1747
|
+
.rtePaletteGroup { display: flex; align-items: center; gap: 6px; }
|
|
1448
1748
|
.rteContent {
|
|
1449
1749
|
flex: 1;
|
|
1450
1750
|
min-height: 0;
|
|
@@ -1457,23 +1757,24 @@
|
|
|
1457
1757
|
min-height: 260px;
|
|
1458
1758
|
padding: 20px 22px;
|
|
1459
1759
|
outline: none;
|
|
1460
|
-
font: 400 var(--rte-size, 15px)/1.
|
|
1760
|
+
font: 400 var(--rte-size, 15px)/var(--rte-lead, 1.3) var(--rte-font, Arial, Helvetica, sans-serif);
|
|
1461
1761
|
color: var(--nc-fg-1);
|
|
1462
1762
|
}
|
|
1463
|
-
.rteSurface p { margin: 0 0
|
|
1763
|
+
.rteSurface p { margin: 0 0 var(--rte-gap, 8px); }
|
|
1464
1764
|
.rteSurface h1 { font-size: 24px; margin: 22px 0 12px; }
|
|
1465
1765
|
.rteSurface h2 { font-size: 20px; margin: 20px 0 10px; }
|
|
1466
1766
|
.rteSurface h3 { font-size: 17px; margin: 18px 0 8px; }
|
|
1467
|
-
.rteSurface ul, .rteSurface ol { margin: 0 0
|
|
1767
|
+
.rteSurface ul, .rteSurface ol { margin: 0 0 var(--rte-gap, 8px); padding-left: 22px; }
|
|
1468
1768
|
.rteSurface li { margin: 0 0 6px; }
|
|
1469
1769
|
.rteSurface a { color: var(--nc-violet-500); text-decoration: underline; }
|
|
1470
1770
|
.rteSurface blockquote {
|
|
1471
|
-
margin: 0 0
|
|
1771
|
+
margin: 0 0 var(--rte-gap, 8px);
|
|
1472
1772
|
padding-left: 14px;
|
|
1473
1773
|
border-left: 3px solid var(--nc-line-2);
|
|
1474
1774
|
color: var(--nc-fg-3);
|
|
1475
1775
|
}
|
|
1476
1776
|
.rteSurface pre {
|
|
1777
|
+
margin: 0 0 var(--rte-gap, 8px);
|
|
1477
1778
|
background: var(--nc-ink-3);
|
|
1478
1779
|
padding: 12px 14px;
|
|
1479
1780
|
border-radius: 6px;
|
|
@@ -1481,7 +1782,7 @@
|
|
|
1481
1782
|
font: 400 13px/1.5 var(--nc-font-mono);
|
|
1482
1783
|
}
|
|
1483
1784
|
.rteSurface img { max-width: 100%; height: auto; }
|
|
1484
|
-
.rteImageWrap { position: relative; margin: 0 0
|
|
1785
|
+
.rteImageWrap { position: relative; margin: 0 0 var(--rte-gap, 8px); }
|
|
1485
1786
|
.rteImageWrap[data-align='center'] { text-align: center; }
|
|
1486
1787
|
.rteImageWrap[data-align='right'] { text-align: right; }
|
|
1487
1788
|
.rteImageBox {
|
|
@@ -1563,7 +1864,7 @@
|
|
|
1563
1864
|
font-size: 11px;
|
|
1564
1865
|
color: var(--nc-fg-2);
|
|
1565
1866
|
}
|
|
1566
|
-
.rteSurface table { border-collapse: collapse; margin: 0 0
|
|
1867
|
+
.rteSurface table { border-collapse: collapse; margin: 0 0 var(--rte-gap, 8px); width: 100%; }
|
|
1567
1868
|
.rteSurface td, .rteSurface th {
|
|
1568
1869
|
border: 1px solid var(--nc-line-2);
|
|
1569
1870
|
padding: 7px 10px;
|
|
@@ -1607,8 +1908,8 @@
|
|
|
1607
1908
|
max-height: 64px;
|
|
1608
1909
|
margin-bottom: 10px;
|
|
1609
1910
|
}
|
|
1610
|
-
.composeSignatureBody { font: 400 13px/1
|
|
1611
|
-
.composeSignatureBody p { margin: 0 0
|
|
1911
|
+
.composeSignatureBody { font: 400 13px/1 var(--nc-font-sans); color: var(--nc-fg-2); }
|
|
1912
|
+
.composeSignatureBody p { margin: 0 0 2px; }
|
|
1612
1913
|
|
|
1613
1914
|
/* Logo picker in settings. */
|
|
1614
1915
|
.sigLogoRow {
|
|
@@ -1857,7 +2158,7 @@
|
|
|
1857
2158
|
padding: 0 2px;
|
|
1858
2159
|
}
|
|
1859
2160
|
.addrChip button:hover { color: var(--nc-danger); }
|
|
1860
|
-
.addrChipBad { border-color:
|
|
2161
|
+
.addrChipBad { border-color: color-mix(in srgb, var(--nc-danger) 50%, transparent); color: var(--nc-danger-soft); }
|
|
1861
2162
|
|
|
1862
2163
|
.toolbar {
|
|
1863
2164
|
display: flex;
|
|
@@ -1918,6 +2219,15 @@
|
|
|
1918
2219
|
transition: border-color var(--dur-fast) var(--nc-ease-out), color var(--dur-fast) var(--nc-ease-out);
|
|
1919
2220
|
}
|
|
1920
2221
|
.selectBtn:hover { border-color: var(--nc-line-2); color: var(--nc-fg-1); }
|
|
2222
|
+
.selectGlyph {
|
|
2223
|
+
flex-shrink: 0;
|
|
2224
|
+
font: 600 11px/1 var(--nc-font-mono);
|
|
2225
|
+
letter-spacing: .02em;
|
|
2226
|
+
color: var(--nc-fg-4);
|
|
2227
|
+
}
|
|
2228
|
+
.selectGlyphSerif { font: 600 14px/1 Georgia, 'Times New Roman', serif; color: var(--nc-fg-3); }
|
|
2229
|
+
.selectEditable .selectGlyph { margin-left: 9px; }
|
|
2230
|
+
.selectEditable .selectGlyph + .selectInput { padding-left: 6px; }
|
|
1921
2231
|
.selectBtn > span {
|
|
1922
2232
|
overflow: hidden;
|
|
1923
2233
|
text-overflow: ellipsis;
|
|
@@ -2029,7 +2339,7 @@
|
|
|
2029
2339
|
border-radius: 7px;
|
|
2030
2340
|
transition: color var(--dur-fast) var(--nc-ease-out), background-color var(--dur-fast) var(--nc-ease-out);
|
|
2031
2341
|
}
|
|
2032
|
-
.tplDelete:hover { color: var(--nc-danger); background:
|
|
2342
|
+
.tplDelete:hover { color: var(--nc-danger); background: color-mix(in srgb, var(--nc-danger) 8%, transparent); }
|
|
2033
2343
|
.tplSaveRow {
|
|
2034
2344
|
display: flex;
|
|
2035
2345
|
gap: 6px;
|
|
@@ -2079,9 +2389,9 @@
|
|
|
2079
2389
|
border: none;
|
|
2080
2390
|
background: transparent;
|
|
2081
2391
|
color: var(--nc-fg-3);
|
|
2082
|
-
font: 600
|
|
2083
|
-
padding:
|
|
2084
|
-
border-radius:
|
|
2392
|
+
font: 600 11.5px/1 var(--nc-font-sans);
|
|
2393
|
+
padding: 4px 9px;
|
|
2394
|
+
border-radius: 5px;
|
|
2085
2395
|
cursor: pointer;
|
|
2086
2396
|
transition: background-color var(--dur-fast) var(--nc-ease-out), color var(--dur-fast) var(--nc-ease-out);
|
|
2087
2397
|
}
|
|
@@ -2093,16 +2403,16 @@
|
|
|
2093
2403
|
display: flex;
|
|
2094
2404
|
align-items: center;
|
|
2095
2405
|
justify-content: space-between;
|
|
2096
|
-
gap:
|
|
2406
|
+
gap: 8px;
|
|
2097
2407
|
flex-wrap: wrap;
|
|
2098
|
-
margin: 0 28px
|
|
2408
|
+
margin: 0 28px 8px;
|
|
2099
2409
|
}
|
|
2100
2410
|
.readerModeTabs {
|
|
2101
2411
|
display: inline-flex;
|
|
2102
2412
|
gap: 2px;
|
|
2103
2413
|
background: var(--nc-ink-2);
|
|
2104
2414
|
border: 1px solid var(--nc-line-1);
|
|
2105
|
-
border-radius:
|
|
2415
|
+
border-radius: 7px;
|
|
2106
2416
|
padding: 2px;
|
|
2107
2417
|
}
|
|
2108
2418
|
|
|
@@ -2235,18 +2545,21 @@
|
|
|
2235
2545
|
}
|
|
2236
2546
|
.threadMsg {
|
|
2237
2547
|
flex: 0 0 auto;
|
|
2238
|
-
border:
|
|
2239
|
-
border-radius:
|
|
2548
|
+
border: 0;
|
|
2549
|
+
border-radius: 0;
|
|
2240
2550
|
overflow: hidden;
|
|
2241
|
-
background:
|
|
2551
|
+
background: transparent;
|
|
2242
2552
|
}
|
|
2243
|
-
.
|
|
2553
|
+
.threadMsg + .threadMsg { border-top: 1px solid var(--nc-line-1); }
|
|
2554
|
+
.threadMsgOpen .threadMsgHead { border-bottom: 1px solid var(--nc-line-1); }
|
|
2244
2555
|
.threadMsgHead {
|
|
2245
2556
|
display: flex;
|
|
2246
2557
|
align-items: center;
|
|
2247
2558
|
gap: 12px;
|
|
2248
2559
|
width: 100%;
|
|
2249
2560
|
padding: var(--thread-pad);
|
|
2561
|
+
padding-left: 0;
|
|
2562
|
+
padding-right: 0;
|
|
2250
2563
|
background: transparent;
|
|
2251
2564
|
border: none;
|
|
2252
2565
|
cursor: pointer;
|
|
@@ -2287,12 +2600,32 @@
|
|
|
2287
2600
|
.threadClip svg { width: 13px; height: 13px; }
|
|
2288
2601
|
/* ── Attachment tiles ────────────────────────────────────── */
|
|
2289
2602
|
.attachGrid {
|
|
2290
|
-
display:
|
|
2291
|
-
grid-template-columns: repeat(auto-fill, minmax(278px, 1fr));
|
|
2603
|
+
display: flex;
|
|
2292
2604
|
gap: 8px;
|
|
2293
2605
|
margin: 0 28px 14px;
|
|
2606
|
+
overflow-x: auto;
|
|
2607
|
+
scroll-snap-type: x proximity;
|
|
2608
|
+
padding-bottom: 6px;
|
|
2609
|
+
scrollbar-width: thin;
|
|
2610
|
+
scrollbar-color: color-mix(in srgb, var(--nc-violet-500) 55%, transparent) transparent;
|
|
2611
|
+
}
|
|
2612
|
+
.attachGrid::-webkit-scrollbar { height: 8px; }
|
|
2613
|
+
.readerAttach .attachGrid { margin: 0; flex: 1 1 100%; }
|
|
2614
|
+
.attachGrid::-webkit-scrollbar-track { background: transparent; }
|
|
2615
|
+
.attachGrid::-webkit-scrollbar-thumb {
|
|
2616
|
+
background: color-mix(in srgb, var(--nc-violet-500) 45%, transparent);
|
|
2617
|
+
background-clip: padding-box;
|
|
2618
|
+
border: 2px solid transparent;
|
|
2619
|
+
border-radius: 999px;
|
|
2620
|
+
}
|
|
2621
|
+
.attachGrid::-webkit-scrollbar-thumb:hover {
|
|
2622
|
+
background: color-mix(in srgb, var(--nc-violet-500) 78%, transparent);
|
|
2623
|
+
background-clip: padding-box;
|
|
2294
2624
|
}
|
|
2295
2625
|
.attachTile {
|
|
2626
|
+
flex: 0 0 auto;
|
|
2627
|
+
width: min(300px, 78vw);
|
|
2628
|
+
scroll-snap-align: start;
|
|
2296
2629
|
display: flex;
|
|
2297
2630
|
align-items: center;
|
|
2298
2631
|
gap: 9px;
|
|
@@ -2566,8 +2899,8 @@
|
|
|
2566
2899
|
vertical-align: middle;
|
|
2567
2900
|
white-space: nowrap;
|
|
2568
2901
|
}
|
|
2569
|
-
.linkBadgeWarn { color: var(--nc-warn); border: 1px solid
|
|
2570
|
-
.linkBadgeBad { color: var(--nc-danger); border: 1px solid
|
|
2902
|
+
.linkBadgeWarn { color: var(--nc-warn); border: 1px solid color-mix(in srgb, var(--nc-warn) 40%, transparent); }
|
|
2903
|
+
.linkBadgeBad { color: var(--nc-danger); border: 1px solid color-mix(in srgb, var(--nc-danger) 45%, transparent); }
|
|
2571
2904
|
.orderDivider {
|
|
2572
2905
|
width: 1px;
|
|
2573
2906
|
height: 16px;
|
|
@@ -2576,12 +2909,12 @@
|
|
|
2576
2909
|
flex-shrink: 0;
|
|
2577
2910
|
}
|
|
2578
2911
|
.orderToggle {
|
|
2579
|
-
padding:
|
|
2580
|
-
border-radius:
|
|
2912
|
+
padding: 3px 8px;
|
|
2913
|
+
border-radius: 6px;
|
|
2581
2914
|
border: 1px solid var(--nc-line-1);
|
|
2582
2915
|
background: transparent;
|
|
2583
2916
|
color: var(--nc-fg-2);
|
|
2584
|
-
font: 600
|
|
2917
|
+
font: 600 11px/1 var(--nc-font-mono);
|
|
2585
2918
|
cursor: pointer;
|
|
2586
2919
|
white-space: nowrap;
|
|
2587
2920
|
transition: color var(--dur-fast) var(--nc-ease-out), border-color var(--dur-fast) var(--nc-ease-out), background-color var(--dur-fast) var(--nc-ease-out);
|
|
@@ -2611,18 +2944,8 @@
|
|
|
2611
2944
|
all four sides, so you can see where the mail begins and where it ends rather than a
|
|
2612
2945
|
band of white running into the furniture above it. */
|
|
2613
2946
|
.threadMsgBody {
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
/* Auto margins on a grid item shrink it to its content, which sized the page to whatever
|
|
2617
|
-
the longest line happened to be. The width is stated, and the margins only centre it. */
|
|
2618
|
-
width: min(868px, calc(100% - var(--thread-inset) * 2));
|
|
2619
|
-
margin: var(--thread-inset) auto;
|
|
2620
|
-
border: 1px solid var(--nc-line-1);
|
|
2621
|
-
border-radius: var(--radius-card);
|
|
2622
|
-
/* The sheet is the paper the mail is printed on, so it is the paper's colour. Left on the
|
|
2623
|
-
card's own tone it vanished into the card, and the only edge you could see was the one
|
|
2624
|
-
the message's own white background drew, square, wherever its markup happened to start. */
|
|
2625
|
-
background: var(--nc-ink-0);
|
|
2947
|
+
width: 100%;
|
|
2948
|
+
margin: 0 0 8px;
|
|
2626
2949
|
font-size: var(--row-subject);
|
|
2627
2950
|
line-height: var(--row-lead);
|
|
2628
2951
|
overflow: hidden;
|
|
@@ -2631,7 +2954,7 @@
|
|
|
2631
2954
|
width: 100%;
|
|
2632
2955
|
min-height: 120px;
|
|
2633
2956
|
border: none;
|
|
2634
|
-
background:
|
|
2957
|
+
background: transparent;
|
|
2635
2958
|
display: block;
|
|
2636
2959
|
}
|
|
2637
2960
|
.threadMsgBody .readerText,
|
|
@@ -2645,9 +2968,10 @@
|
|
|
2645
2968
|
.threadMsgBody .readerText,
|
|
2646
2969
|
.threadMsgBody .readerSource {
|
|
2647
2970
|
max-width: none;
|
|
2971
|
+
padding: 6px 0 12px;
|
|
2648
2972
|
margin: 0;
|
|
2649
2973
|
}
|
|
2650
|
-
.threadMsgBody .readerText { padding-bottom: 16px; }
|
|
2974
|
+
.threadMsgBody .readerText { padding-bottom: 16px; padding-left: 0; padding-right: 0; }
|
|
2651
2975
|
.threadMsgBody .readerAttach { padding-top: 14px; }
|
|
2652
2976
|
|
|
2653
2977
|
.campaign {
|
|
@@ -3014,6 +3338,11 @@
|
|
|
3014
3338
|
font: 500 14px/1 var(--nc-font-sans);
|
|
3015
3339
|
color: var(--nc-fg-1);
|
|
3016
3340
|
}
|
|
3341
|
+
.toastError {
|
|
3342
|
+
border-color: color-mix(in srgb, var(--nc-danger) 45%, transparent);
|
|
3343
|
+
color: var(--nc-danger);
|
|
3344
|
+
}
|
|
3345
|
+
|
|
3017
3346
|
.toastCount {
|
|
3018
3347
|
font-family: var(--nc-font-mono);
|
|
3019
3348
|
color: var(--nc-warn);
|
|
@@ -3097,9 +3426,9 @@
|
|
|
3097
3426
|
flex-direction: column;
|
|
3098
3427
|
gap: 7px;
|
|
3099
3428
|
padding: 11px 13px;
|
|
3100
|
-
border: 1px solid
|
|
3429
|
+
border: 1px solid color-mix(in srgb, var(--nc-danger) 32%, transparent);
|
|
3101
3430
|
border-radius: var(--radius-control);
|
|
3102
|
-
background:
|
|
3431
|
+
background: color-mix(in srgb, var(--nc-danger) 7%, transparent);
|
|
3103
3432
|
}
|
|
3104
3433
|
.loginError {
|
|
3105
3434
|
font: 500 13px/1.45 var(--nc-font-sans);
|
|
@@ -3368,8 +3697,8 @@
|
|
|
3368
3697
|
margin: 0 28px 14px;
|
|
3369
3698
|
padding: 10px 14px;
|
|
3370
3699
|
border-radius: 10px;
|
|
3371
|
-
border: 1px solid
|
|
3372
|
-
background:
|
|
3700
|
+
border: 1px solid color-mix(in srgb, var(--nc-warn) 30%, transparent);
|
|
3701
|
+
background: color-mix(in srgb, var(--nc-warn) 6%, transparent);
|
|
3373
3702
|
color: var(--nc-fg-2);
|
|
3374
3703
|
font: 500 13px/1.4 var(--nc-font-sans);
|
|
3375
3704
|
}
|
|
@@ -3471,7 +3800,8 @@
|
|
|
3471
3800
|
position: fixed;
|
|
3472
3801
|
inset: 0;
|
|
3473
3802
|
background: rgba(5, 3, 9, .55);
|
|
3474
|
-
|
|
3803
|
+
/* Above .topRight, which floats at 500 and was landing squarely on the close button. */
|
|
3804
|
+
z-index: 530;
|
|
3475
3805
|
display: flex;
|
|
3476
3806
|
align-items: center;
|
|
3477
3807
|
justify-content: center;
|
|
@@ -3767,6 +4097,41 @@
|
|
|
3767
4097
|
gap: 15px;
|
|
3768
4098
|
padding: var(--thread-pad);
|
|
3769
4099
|
}
|
|
4100
|
+
/* The whole reader, drawn as shapes, for the moment between clicking a conversation and
|
|
4101
|
+
its messages arriving. It stands in for the header as well as the body, because that is
|
|
4102
|
+
what is missing: the pane otherwise keeps showing "Nothing open" or the last message. */
|
|
4103
|
+
.readerSkeleton {
|
|
4104
|
+
display: flex;
|
|
4105
|
+
flex-direction: column;
|
|
4106
|
+
gap: 18px;
|
|
4107
|
+
/* The real header, actions and thread all sit 28px in; anything else makes the page
|
|
4108
|
+
jump sideways the moment it replaces the skeleton. */
|
|
4109
|
+
padding: 22px 28px 0;
|
|
4110
|
+
}
|
|
4111
|
+
/* Scoped under the skeleton so they outrank the plain .skelBar rule, which comes later in
|
|
4112
|
+
the file and would otherwise flatten the subject back to a 13px line. */
|
|
4113
|
+
.readerSkeleton .skelSubject {
|
|
4114
|
+
height: 22px;
|
|
4115
|
+
width: 62%;
|
|
4116
|
+
border-radius: 8px;
|
|
4117
|
+
}
|
|
4118
|
+
.skelActions {
|
|
4119
|
+
display: flex;
|
|
4120
|
+
gap: 10px;
|
|
4121
|
+
}
|
|
4122
|
+
.skelActions .skelBar {
|
|
4123
|
+
width: 96px;
|
|
4124
|
+
height: 40px;
|
|
4125
|
+
border-radius: var(--radius-control);
|
|
4126
|
+
}
|
|
4127
|
+
.readerSkeleton .bodySkeleton {
|
|
4128
|
+
padding: 0;
|
|
4129
|
+
}
|
|
4130
|
+
/* The block placeholder is ink-2, which is white in the light theme — the same white the
|
|
4131
|
+
reader is painted on, so it vanished. ink-3 is a visible step in all three themes. */
|
|
4132
|
+
.readerSkeleton .skelBlock {
|
|
4133
|
+
background: var(--nc-ink-3);
|
|
4134
|
+
}
|
|
3770
4135
|
.skelHeadRow {
|
|
3771
4136
|
display: flex;
|
|
3772
4137
|
align-items: center;
|
|
@@ -4463,7 +4828,7 @@
|
|
|
4463
4828
|
gap: 2px;
|
|
4464
4829
|
background: var(--nc-ink-2);
|
|
4465
4830
|
border: 1px solid var(--nc-line-1);
|
|
4466
|
-
border-radius:
|
|
4831
|
+
border-radius: 7px;
|
|
4467
4832
|
padding: 2px;
|
|
4468
4833
|
}
|
|
4469
4834
|
|
|
@@ -5016,6 +5381,27 @@
|
|
|
5016
5381
|
animation: ncNoticeIn .26s cubic-bezier(.2, .7, .3, 1) both;
|
|
5017
5382
|
will-change: transform, opacity;
|
|
5018
5383
|
}
|
|
5384
|
+
/* The card behind peeks out above the one in front, so the next thing to deal with is
|
|
5385
|
+
visible without being readable or clickable until it comes forward. */
|
|
5386
|
+
.pwNoticeBehind {
|
|
5387
|
+
z-index: calc(75 - var(--depth, 1));
|
|
5388
|
+
transform: translateY(calc(var(--depth, 1) * -12px)) scale(calc(1 - var(--depth, 1) * .04));
|
|
5389
|
+
opacity: .92;
|
|
5390
|
+
pointer-events: none;
|
|
5391
|
+
animation: none;
|
|
5392
|
+
transition: transform .26s cubic-bezier(.2, .7, .3, 1);
|
|
5393
|
+
}
|
|
5394
|
+
.pwNoticeMore {
|
|
5395
|
+
position: absolute;
|
|
5396
|
+
top: -9px;
|
|
5397
|
+
right: 14px;
|
|
5398
|
+
padding: 3px 8px;
|
|
5399
|
+
border-radius: 999px;
|
|
5400
|
+
background: var(--nc-violet-500);
|
|
5401
|
+
color: #fff;
|
|
5402
|
+
font: 600 11px/1 var(--nc-font-sans);
|
|
5403
|
+
letter-spacing: .01em;
|
|
5404
|
+
}
|
|
5019
5405
|
|
|
5020
5406
|
.pwNoticeTitle {
|
|
5021
5407
|
margin: 0;
|
|
@@ -5364,7 +5750,7 @@
|
|
|
5364
5750
|
}
|
|
5365
5751
|
.sigPreviewOpen .sigPreviewFold { max-height: 2000px; }
|
|
5366
5752
|
.sigPreviewBody {
|
|
5367
|
-
font: 400 13px/1
|
|
5753
|
+
font: 400 13px/1 var(--nc-font-sans);
|
|
5368
5754
|
color: var(--nc-fg-2);
|
|
5369
5755
|
}
|
|
5370
5756
|
.sigPreviewBody a { color: var(--nc-violet-500); }
|
|
@@ -5910,9 +6296,7 @@
|
|
|
5910
6296
|
background: linear-gradient(to bottom, transparent 0%, var(--nc-ink-1) 78%);
|
|
5911
6297
|
pointer-events: none;
|
|
5912
6298
|
}
|
|
5913
|
-
|
|
5914
|
-
content rather than running on against an arbitrary ceiling. */
|
|
5915
|
-
.composeSignatureOpen .composeSignatureBody { max-height: var(--sig-h, 1200px); cursor: default; }
|
|
6299
|
+
.composeSignatureOpen .composeSignatureBody { max-height: none; overflow: visible; cursor: default; }
|
|
5916
6300
|
.composeSignatureOpen { padding-bottom: 30px; }
|
|
5917
6301
|
|
|
5918
6302
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -6259,6 +6643,12 @@
|
|
|
6259
6643
|
right: calc(18px + env(safe-area-inset-right, 0px));
|
|
6260
6644
|
bottom: calc(18px + env(safe-area-inset-bottom, 0px));
|
|
6261
6645
|
}
|
|
6646
|
+
/* Above the compose button, which appears at this width in the same corner: 54px tall
|
|
6647
|
+
sitting 18px off the bottom, so anything below 84px is underneath it. */
|
|
6648
|
+
.toTop {
|
|
6649
|
+
right: calc(18px + env(safe-area-inset-right, 0px));
|
|
6650
|
+
bottom: calc(84px + env(safe-area-inset-bottom, 0px));
|
|
6651
|
+
}
|
|
6262
6652
|
.list {
|
|
6263
6653
|
padding-left: calc(10px + env(safe-area-inset-left, 0px));
|
|
6264
6654
|
padding-right: calc(10px + env(safe-area-inset-right, 0px));
|
|
@@ -6274,6 +6664,7 @@
|
|
|
6274
6664
|
width: auto;
|
|
6275
6665
|
z-index: 445;
|
|
6276
6666
|
}
|
|
6667
|
+
.pwNoticeBehind { z-index: calc(445 - var(--depth, 1)); }
|
|
6277
6668
|
/* Forty-four pixels is the smallest thing a thumb finds reliably. */
|
|
6278
6669
|
.itemStar,
|
|
6279
6670
|
.iconBtn,
|
|
@@ -6318,3 +6709,166 @@
|
|
|
6318
6709
|
max-width: none;
|
|
6319
6710
|
}
|
|
6320
6711
|
}
|
|
6712
|
+
|
|
6713
|
+
/* ---- Phone corrections ------------------------------------------------------------
|
|
6714
|
+
Everything here was found by hit-testing the rendered page at 375x812 rather than by
|
|
6715
|
+
reading the stylesheet, because each fault only appears once the list is full width. */
|
|
6716
|
+
@media (max-width: 720px) {
|
|
6717
|
+
/* The notification and theme buttons float over the top-right corner. With a reading
|
|
6718
|
+
pane beside it that corner is empty; without one it is the list header, and these two
|
|
6719
|
+
sat on top of Refresh so it could not be tapped at all. Drop them behind anything that
|
|
6720
|
+
opens over the mailbox, and give the header room to put Refresh beside them. */
|
|
6721
|
+
.topRight {
|
|
6722
|
+
top: 18px;
|
|
6723
|
+
right: 12px;
|
|
6724
|
+
gap: 6px;
|
|
6725
|
+
z-index: 405;
|
|
6726
|
+
}
|
|
6727
|
+
.listHead .refreshBtn { display: none; }
|
|
6728
|
+
.actions { row-gap: 8px; gap: 6px; padding: 12px 15px; }
|
|
6729
|
+
.app { --thread-inset: 15px; }
|
|
6730
|
+
.readerHead { padding: 18px 15px 0; }
|
|
6731
|
+
.readerBody { margin: 0 15px 16px; }
|
|
6732
|
+
.readerModeBar { margin: 0 15px 12px; }
|
|
6733
|
+
.thread { padding-left: 15px; padding-right: 15px; }
|
|
6734
|
+
.attachGrid { margin: 0 15px 12px; }
|
|
6735
|
+
.activity { margin: 0 15px 14px; padding: 10px 12px; }
|
|
6736
|
+
.readerAttach { margin: 0 15px 14px; }
|
|
6737
|
+
.attachFloat {
|
|
6738
|
+
left: 0;
|
|
6739
|
+
right: 0;
|
|
6740
|
+
bottom: 0;
|
|
6741
|
+
border-left: 0;
|
|
6742
|
+
border-right: 0;
|
|
6743
|
+
border-bottom: 0;
|
|
6744
|
+
border-radius: 14px 14px 0 0;
|
|
6745
|
+
}
|
|
6746
|
+
.attachFloatBody .attachGrid { margin: 0 15px 10px; }
|
|
6747
|
+
.readerText { padding: 16px 15px; }
|
|
6748
|
+
.composeSignature { margin: 0 15px 14px; }
|
|
6749
|
+
.quickReplyBar { padding-left: 15px; padding-right: 15px; }
|
|
6750
|
+
.actions .iconBtn { min-width: 40px; width: 40px; padding: 0; }
|
|
6751
|
+
.actionSpacer { flex: 1 0 100%; min-width: 0; height: 0; }
|
|
6752
|
+
.rteToolbar {
|
|
6753
|
+
flex-wrap: nowrap;
|
|
6754
|
+
overflow-x: auto;
|
|
6755
|
+
gap: 6px;
|
|
6756
|
+
padding: 8px 12px;
|
|
6757
|
+
scroll-snap-type: x proximity;
|
|
6758
|
+
scrollbar-width: none;
|
|
6759
|
+
-webkit-overflow-scrolling: touch;
|
|
6760
|
+
}
|
|
6761
|
+
.rteToolbar::-webkit-scrollbar { display: none; }
|
|
6762
|
+
.rteGroup { flex: 0 0 auto; scroll-snap-align: start; }
|
|
6763
|
+
.rtePalette .rteSwatch { width: 24px; height: 24px; }
|
|
6764
|
+
.compose,
|
|
6765
|
+
.composeExpanded {
|
|
6766
|
+
top: auto;
|
|
6767
|
+
bottom: 0;
|
|
6768
|
+
height: 80dvh;
|
|
6769
|
+
max-height: 80dvh;
|
|
6770
|
+
border-radius: 18px 18px 0 0;
|
|
6771
|
+
}
|
|
6772
|
+
.listHead {
|
|
6773
|
+
padding-right: 106px;
|
|
6774
|
+
}
|
|
6775
|
+
.listTitle {
|
|
6776
|
+
flex: 0 0 auto;
|
|
6777
|
+
}
|
|
6778
|
+
.listMeta {
|
|
6779
|
+
min-width: 0;
|
|
6780
|
+
overflow: hidden;
|
|
6781
|
+
white-space: nowrap;
|
|
6782
|
+
text-overflow: ellipsis;
|
|
6783
|
+
}
|
|
6784
|
+
/* "60 convers…" reads as a fault rather than as a count. Below this width the number
|
|
6785
|
+
carries it alone; the unit is still in the header's tooltip and spelled out in full
|
|
6786
|
+
at the end of the list. */
|
|
6787
|
+
.listMetaUnit {
|
|
6788
|
+
display: none;
|
|
6789
|
+
}
|
|
6790
|
+
/* An earlier rule lifted this clear of the compose button; a later one pinned it back
|
|
6791
|
+
over the top, so its own buttons were half covered. */
|
|
6792
|
+
.pwNotice {
|
|
6793
|
+
bottom: calc(84px + env(safe-area-inset-bottom, 0px));
|
|
6794
|
+
}
|
|
6795
|
+
/* Nine formatting buttons at a size a finger can hit wrap onto three rows and take 134px
|
|
6796
|
+
of a reply that has not been written yet. One row that scrolls keeps every control and
|
|
6797
|
+
costs a third of the height. */
|
|
6798
|
+
.replyToolbar,
|
|
6799
|
+
.readerModeTabs,
|
|
6800
|
+
.readerLayoutTabs {
|
|
6801
|
+
flex-wrap: nowrap;
|
|
6802
|
+
overflow-x: auto;
|
|
6803
|
+
scrollbar-width: none;
|
|
6804
|
+
}
|
|
6805
|
+
.replyToolbar::-webkit-scrollbar,
|
|
6806
|
+
.readerModeTabs::-webkit-scrollbar,
|
|
6807
|
+
.readerLayoutTabs::-webkit-scrollbar {
|
|
6808
|
+
display: none;
|
|
6809
|
+
}
|
|
6810
|
+
.replyToolbar > *,
|
|
6811
|
+
.readerModeTabs > *,
|
|
6812
|
+
.readerLayoutTabs > * {
|
|
6813
|
+
flex: 0 0 auto;
|
|
6814
|
+
}
|
|
6815
|
+
|
|
6816
|
+
/* The card scrolls, and the footer scrolled away with it, so Save was reachable only
|
|
6817
|
+
after finding the bottom of a long form. */
|
|
6818
|
+
.settingsFoot {
|
|
6819
|
+
position: sticky;
|
|
6820
|
+
bottom: 0;
|
|
6821
|
+
z-index: 1;
|
|
6822
|
+
margin-top: 8px;
|
|
6823
|
+
padding: 10px 0 2px;
|
|
6824
|
+
background: var(--nc-ink-1);
|
|
6825
|
+
box-shadow: 0 -10px 16px -12px rgba(0, 0, 0, .5);
|
|
6826
|
+
}
|
|
6827
|
+
}
|
|
6828
|
+
|
|
6829
|
+
/* A finger is not a mouse pointer: anything it has to hit needs to be a target, not a
|
|
6830
|
+
label. These were 15px to 32px tall, which is a miss more often than a hit. */
|
|
6831
|
+
@media (pointer: coarse) {
|
|
6832
|
+
.modeTab,
|
|
6833
|
+
.orderToggle {
|
|
6834
|
+
min-height: 24px;
|
|
6835
|
+
}
|
|
6836
|
+
.settingsNavItem {
|
|
6837
|
+
min-height: 40px;
|
|
6838
|
+
}
|
|
6839
|
+
.toolBtn {
|
|
6840
|
+
min-width: 40px;
|
|
6841
|
+
min-height: 40px;
|
|
6842
|
+
}
|
|
6843
|
+
.headerToggle,
|
|
6844
|
+
.replyRecipsToggle,
|
|
6845
|
+
.composeSignatureLabel,
|
|
6846
|
+
.composeSignatureEdit,
|
|
6847
|
+
.composeSignatureMore {
|
|
6848
|
+
min-height: 36px;
|
|
6849
|
+
padding-top: 2px;
|
|
6850
|
+
padding-bottom: 2px;
|
|
6851
|
+
}
|
|
6852
|
+
.headerToggle {
|
|
6853
|
+
padding-left: 8px;
|
|
6854
|
+
padding-right: 8px;
|
|
6855
|
+
}
|
|
6856
|
+
}
|
|
6857
|
+
|
|
6858
|
+
@media (max-width: 720px) {
|
|
6859
|
+
/* Compose is already the whole screen here, so a control that makes it full width has
|
|
6860
|
+
nothing to do, and a phone has no Esc key to press. Between them they wrapped the
|
|
6861
|
+
compose header onto four lines. */
|
|
6862
|
+
.composeExpandBtn,
|
|
6863
|
+
.composeHintKeys {
|
|
6864
|
+
display: none;
|
|
6865
|
+
}
|
|
6866
|
+
/* The expanded composer takes its left edge from --list-w, and the chosen list width
|
|
6867
|
+
is written onto .app as an inline variable — which outranks this breakpoint's zero.
|
|
6868
|
+
So it opened 400px to the right of a 375px screen, wholly out of view. */
|
|
6869
|
+
.composeExpanded {
|
|
6870
|
+
left: 0;
|
|
6871
|
+
right: 0;
|
|
6872
|
+
width: auto;
|
|
6873
|
+
}
|
|
6874
|
+
}
|