@novacraft-engineering/mailbox 0.1.0 → 0.2.1
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 +23 -4
- package/app/mail/RichEditor.tsx +167 -53
- package/app/mail/Ticker.tsx +40 -0
- package/app/mail/page.module.css +643 -76
- package/app/mail/page.tsx +1062 -247
- 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;
|
|
1189
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;
|
|
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;
|
|
@@ -1433,7 +1681,7 @@
|
|
|
1433
1681
|
border-radius: 6px;
|
|
1434
1682
|
background: var(--nc-ink-1);
|
|
1435
1683
|
color: var(--nc-fg-1);
|
|
1436
|
-
font: 500
|
|
1684
|
+
font: 500 11px/1 var(--nc-font-sans);
|
|
1437
1685
|
}
|
|
1438
1686
|
.rteSwatches { display: flex; gap: 3px; }
|
|
1439
1687
|
.rteSwatch {
|
|
@@ -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,24 @@
|
|
|
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
|
+
display: inline-flex;
|
|
2225
|
+
align-items: center;
|
|
2226
|
+
justify-content: center;
|
|
2227
|
+
width: 16px;
|
|
2228
|
+
height: 16px;
|
|
2229
|
+
font: 700 11px/1 var(--nc-font-sans);
|
|
2230
|
+
letter-spacing: .01em;
|
|
2231
|
+
color: var(--nc-fg-3);
|
|
2232
|
+
}
|
|
2233
|
+
.selectGlyph svg { width: 13px; height: 13px; }
|
|
2234
|
+
.selectGlyphSerif { font: 700 13px/1 Georgia, 'Times New Roman', serif; }
|
|
2235
|
+
.selectLabel {
|
|
2236
|
+
flex-shrink: 0;
|
|
2237
|
+
color: var(--nc-fg-3);
|
|
2238
|
+
white-space: nowrap;
|
|
2239
|
+
}
|
|
1921
2240
|
.selectBtn > span {
|
|
1922
2241
|
overflow: hidden;
|
|
1923
2242
|
text-overflow: ellipsis;
|
|
@@ -1960,6 +2279,7 @@
|
|
|
1960
2279
|
.selectOptionOn { background: var(--nc-ink-4); color: var(--nc-violet-200); }
|
|
1961
2280
|
|
|
1962
2281
|
.toolbar .selectBtn { padding: 7px 10px; font-size: 12px; }
|
|
2282
|
+
.selectBtn.rteSelect { font-size: 11.5px; }
|
|
1963
2283
|
.campaignSelectBtn {
|
|
1964
2284
|
width: 100%;
|
|
1965
2285
|
justify-content: space-between;
|
|
@@ -2029,7 +2349,7 @@
|
|
|
2029
2349
|
border-radius: 7px;
|
|
2030
2350
|
transition: color var(--dur-fast) var(--nc-ease-out), background-color var(--dur-fast) var(--nc-ease-out);
|
|
2031
2351
|
}
|
|
2032
|
-
.tplDelete:hover { color: var(--nc-danger); background:
|
|
2352
|
+
.tplDelete:hover { color: var(--nc-danger); background: color-mix(in srgb, var(--nc-danger) 8%, transparent); }
|
|
2033
2353
|
.tplSaveRow {
|
|
2034
2354
|
display: flex;
|
|
2035
2355
|
gap: 6px;
|
|
@@ -2079,9 +2399,9 @@
|
|
|
2079
2399
|
border: none;
|
|
2080
2400
|
background: transparent;
|
|
2081
2401
|
color: var(--nc-fg-3);
|
|
2082
|
-
font: 600
|
|
2083
|
-
padding:
|
|
2084
|
-
border-radius:
|
|
2402
|
+
font: 600 11.5px/1 var(--nc-font-sans);
|
|
2403
|
+
padding: 4px 9px;
|
|
2404
|
+
border-radius: 5px;
|
|
2085
2405
|
cursor: pointer;
|
|
2086
2406
|
transition: background-color var(--dur-fast) var(--nc-ease-out), color var(--dur-fast) var(--nc-ease-out);
|
|
2087
2407
|
}
|
|
@@ -2093,16 +2413,16 @@
|
|
|
2093
2413
|
display: flex;
|
|
2094
2414
|
align-items: center;
|
|
2095
2415
|
justify-content: space-between;
|
|
2096
|
-
gap:
|
|
2416
|
+
gap: 8px;
|
|
2097
2417
|
flex-wrap: wrap;
|
|
2098
|
-
margin: 0 28px
|
|
2418
|
+
margin: 0 28px 8px;
|
|
2099
2419
|
}
|
|
2100
2420
|
.readerModeTabs {
|
|
2101
2421
|
display: inline-flex;
|
|
2102
2422
|
gap: 2px;
|
|
2103
2423
|
background: var(--nc-ink-2);
|
|
2104
2424
|
border: 1px solid var(--nc-line-1);
|
|
2105
|
-
border-radius:
|
|
2425
|
+
border-radius: 7px;
|
|
2106
2426
|
padding: 2px;
|
|
2107
2427
|
}
|
|
2108
2428
|
|
|
@@ -2235,18 +2555,21 @@
|
|
|
2235
2555
|
}
|
|
2236
2556
|
.threadMsg {
|
|
2237
2557
|
flex: 0 0 auto;
|
|
2238
|
-
border:
|
|
2239
|
-
border-radius:
|
|
2558
|
+
border: 0;
|
|
2559
|
+
border-radius: 0;
|
|
2240
2560
|
overflow: hidden;
|
|
2241
|
-
background:
|
|
2561
|
+
background: transparent;
|
|
2242
2562
|
}
|
|
2243
|
-
.
|
|
2563
|
+
.threadMsg + .threadMsg { border-top: 1px solid var(--nc-line-1); }
|
|
2564
|
+
.threadMsgOpen .threadMsgHead { border-bottom: 1px solid var(--nc-line-1); }
|
|
2244
2565
|
.threadMsgHead {
|
|
2245
2566
|
display: flex;
|
|
2246
2567
|
align-items: center;
|
|
2247
2568
|
gap: 12px;
|
|
2248
2569
|
width: 100%;
|
|
2249
2570
|
padding: var(--thread-pad);
|
|
2571
|
+
padding-left: 0;
|
|
2572
|
+
padding-right: 0;
|
|
2250
2573
|
background: transparent;
|
|
2251
2574
|
border: none;
|
|
2252
2575
|
cursor: pointer;
|
|
@@ -2287,12 +2610,32 @@
|
|
|
2287
2610
|
.threadClip svg { width: 13px; height: 13px; }
|
|
2288
2611
|
/* ── Attachment tiles ────────────────────────────────────── */
|
|
2289
2612
|
.attachGrid {
|
|
2290
|
-
display:
|
|
2291
|
-
grid-template-columns: repeat(auto-fill, minmax(278px, 1fr));
|
|
2613
|
+
display: flex;
|
|
2292
2614
|
gap: 8px;
|
|
2293
2615
|
margin: 0 28px 14px;
|
|
2616
|
+
overflow-x: auto;
|
|
2617
|
+
scroll-snap-type: x proximity;
|
|
2618
|
+
padding-bottom: 6px;
|
|
2619
|
+
scrollbar-width: thin;
|
|
2620
|
+
scrollbar-color: color-mix(in srgb, var(--nc-violet-500) 55%, transparent) transparent;
|
|
2621
|
+
}
|
|
2622
|
+
.attachGrid::-webkit-scrollbar { height: 8px; }
|
|
2623
|
+
.readerAttach .attachGrid { margin: 0; flex: 1 1 100%; }
|
|
2624
|
+
.attachGrid::-webkit-scrollbar-track { background: transparent; }
|
|
2625
|
+
.attachGrid::-webkit-scrollbar-thumb {
|
|
2626
|
+
background: color-mix(in srgb, var(--nc-violet-500) 45%, transparent);
|
|
2627
|
+
background-clip: padding-box;
|
|
2628
|
+
border: 2px solid transparent;
|
|
2629
|
+
border-radius: 999px;
|
|
2630
|
+
}
|
|
2631
|
+
.attachGrid::-webkit-scrollbar-thumb:hover {
|
|
2632
|
+
background: color-mix(in srgb, var(--nc-violet-500) 78%, transparent);
|
|
2633
|
+
background-clip: padding-box;
|
|
2294
2634
|
}
|
|
2295
2635
|
.attachTile {
|
|
2636
|
+
flex: 0 0 auto;
|
|
2637
|
+
width: min(300px, 78vw);
|
|
2638
|
+
scroll-snap-align: start;
|
|
2296
2639
|
display: flex;
|
|
2297
2640
|
align-items: center;
|
|
2298
2641
|
gap: 9px;
|
|
@@ -2566,8 +2909,8 @@
|
|
|
2566
2909
|
vertical-align: middle;
|
|
2567
2910
|
white-space: nowrap;
|
|
2568
2911
|
}
|
|
2569
|
-
.linkBadgeWarn { color: var(--nc-warn); border: 1px solid
|
|
2570
|
-
.linkBadgeBad { color: var(--nc-danger); border: 1px solid
|
|
2912
|
+
.linkBadgeWarn { color: var(--nc-warn); border: 1px solid color-mix(in srgb, var(--nc-warn) 40%, transparent); }
|
|
2913
|
+
.linkBadgeBad { color: var(--nc-danger); border: 1px solid color-mix(in srgb, var(--nc-danger) 45%, transparent); }
|
|
2571
2914
|
.orderDivider {
|
|
2572
2915
|
width: 1px;
|
|
2573
2916
|
height: 16px;
|
|
@@ -2576,12 +2919,12 @@
|
|
|
2576
2919
|
flex-shrink: 0;
|
|
2577
2920
|
}
|
|
2578
2921
|
.orderToggle {
|
|
2579
|
-
padding:
|
|
2580
|
-
border-radius:
|
|
2922
|
+
padding: 3px 8px;
|
|
2923
|
+
border-radius: 6px;
|
|
2581
2924
|
border: 1px solid var(--nc-line-1);
|
|
2582
2925
|
background: transparent;
|
|
2583
2926
|
color: var(--nc-fg-2);
|
|
2584
|
-
font: 600
|
|
2927
|
+
font: 600 11px/1 var(--nc-font-mono);
|
|
2585
2928
|
cursor: pointer;
|
|
2586
2929
|
white-space: nowrap;
|
|
2587
2930
|
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 +2954,8 @@
|
|
|
2611
2954
|
all four sides, so you can see where the mail begins and where it ends rather than a
|
|
2612
2955
|
band of white running into the furniture above it. */
|
|
2613
2956
|
.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);
|
|
2957
|
+
width: 100%;
|
|
2958
|
+
margin: 0 0 8px;
|
|
2626
2959
|
font-size: var(--row-subject);
|
|
2627
2960
|
line-height: var(--row-lead);
|
|
2628
2961
|
overflow: hidden;
|
|
@@ -2631,7 +2964,7 @@
|
|
|
2631
2964
|
width: 100%;
|
|
2632
2965
|
min-height: 120px;
|
|
2633
2966
|
border: none;
|
|
2634
|
-
background:
|
|
2967
|
+
background: transparent;
|
|
2635
2968
|
display: block;
|
|
2636
2969
|
}
|
|
2637
2970
|
.threadMsgBody .readerText,
|
|
@@ -2645,9 +2978,10 @@
|
|
|
2645
2978
|
.threadMsgBody .readerText,
|
|
2646
2979
|
.threadMsgBody .readerSource {
|
|
2647
2980
|
max-width: none;
|
|
2981
|
+
padding: 6px 0 12px;
|
|
2648
2982
|
margin: 0;
|
|
2649
2983
|
}
|
|
2650
|
-
.threadMsgBody .readerText { padding-bottom: 16px; }
|
|
2984
|
+
.threadMsgBody .readerText { padding-bottom: 16px; padding-left: 0; padding-right: 0; }
|
|
2651
2985
|
.threadMsgBody .readerAttach { padding-top: 14px; }
|
|
2652
2986
|
|
|
2653
2987
|
.campaign {
|
|
@@ -3014,6 +3348,11 @@
|
|
|
3014
3348
|
font: 500 14px/1 var(--nc-font-sans);
|
|
3015
3349
|
color: var(--nc-fg-1);
|
|
3016
3350
|
}
|
|
3351
|
+
.toastError {
|
|
3352
|
+
border-color: color-mix(in srgb, var(--nc-danger) 45%, transparent);
|
|
3353
|
+
color: var(--nc-danger);
|
|
3354
|
+
}
|
|
3355
|
+
|
|
3017
3356
|
.toastCount {
|
|
3018
3357
|
font-family: var(--nc-font-mono);
|
|
3019
3358
|
color: var(--nc-warn);
|
|
@@ -3097,9 +3436,9 @@
|
|
|
3097
3436
|
flex-direction: column;
|
|
3098
3437
|
gap: 7px;
|
|
3099
3438
|
padding: 11px 13px;
|
|
3100
|
-
border: 1px solid
|
|
3439
|
+
border: 1px solid color-mix(in srgb, var(--nc-danger) 32%, transparent);
|
|
3101
3440
|
border-radius: var(--radius-control);
|
|
3102
|
-
background:
|
|
3441
|
+
background: color-mix(in srgb, var(--nc-danger) 7%, transparent);
|
|
3103
3442
|
}
|
|
3104
3443
|
.loginError {
|
|
3105
3444
|
font: 500 13px/1.45 var(--nc-font-sans);
|
|
@@ -3368,8 +3707,8 @@
|
|
|
3368
3707
|
margin: 0 28px 14px;
|
|
3369
3708
|
padding: 10px 14px;
|
|
3370
3709
|
border-radius: 10px;
|
|
3371
|
-
border: 1px solid
|
|
3372
|
-
background:
|
|
3710
|
+
border: 1px solid color-mix(in srgb, var(--nc-warn) 30%, transparent);
|
|
3711
|
+
background: color-mix(in srgb, var(--nc-warn) 6%, transparent);
|
|
3373
3712
|
color: var(--nc-fg-2);
|
|
3374
3713
|
font: 500 13px/1.4 var(--nc-font-sans);
|
|
3375
3714
|
}
|
|
@@ -3471,7 +3810,8 @@
|
|
|
3471
3810
|
position: fixed;
|
|
3472
3811
|
inset: 0;
|
|
3473
3812
|
background: rgba(5, 3, 9, .55);
|
|
3474
|
-
|
|
3813
|
+
/* Above .topRight, which floats at 500 and was landing squarely on the close button. */
|
|
3814
|
+
z-index: 530;
|
|
3475
3815
|
display: flex;
|
|
3476
3816
|
align-items: center;
|
|
3477
3817
|
justify-content: center;
|
|
@@ -3767,6 +4107,41 @@
|
|
|
3767
4107
|
gap: 15px;
|
|
3768
4108
|
padding: var(--thread-pad);
|
|
3769
4109
|
}
|
|
4110
|
+
/* The whole reader, drawn as shapes, for the moment between clicking a conversation and
|
|
4111
|
+
its messages arriving. It stands in for the header as well as the body, because that is
|
|
4112
|
+
what is missing: the pane otherwise keeps showing "Nothing open" or the last message. */
|
|
4113
|
+
.readerSkeleton {
|
|
4114
|
+
display: flex;
|
|
4115
|
+
flex-direction: column;
|
|
4116
|
+
gap: 18px;
|
|
4117
|
+
/* The real header, actions and thread all sit 28px in; anything else makes the page
|
|
4118
|
+
jump sideways the moment it replaces the skeleton. */
|
|
4119
|
+
padding: 22px 28px 0;
|
|
4120
|
+
}
|
|
4121
|
+
/* Scoped under the skeleton so they outrank the plain .skelBar rule, which comes later in
|
|
4122
|
+
the file and would otherwise flatten the subject back to a 13px line. */
|
|
4123
|
+
.readerSkeleton .skelSubject {
|
|
4124
|
+
height: 22px;
|
|
4125
|
+
width: 62%;
|
|
4126
|
+
border-radius: 8px;
|
|
4127
|
+
}
|
|
4128
|
+
.skelActions {
|
|
4129
|
+
display: flex;
|
|
4130
|
+
gap: 10px;
|
|
4131
|
+
}
|
|
4132
|
+
.skelActions .skelBar {
|
|
4133
|
+
width: 96px;
|
|
4134
|
+
height: 40px;
|
|
4135
|
+
border-radius: var(--radius-control);
|
|
4136
|
+
}
|
|
4137
|
+
.readerSkeleton .bodySkeleton {
|
|
4138
|
+
padding: 0;
|
|
4139
|
+
}
|
|
4140
|
+
/* The block placeholder is ink-2, which is white in the light theme — the same white the
|
|
4141
|
+
reader is painted on, so it vanished. ink-3 is a visible step in all three themes. */
|
|
4142
|
+
.readerSkeleton .skelBlock {
|
|
4143
|
+
background: var(--nc-ink-3);
|
|
4144
|
+
}
|
|
3770
4145
|
.skelHeadRow {
|
|
3771
4146
|
display: flex;
|
|
3772
4147
|
align-items: center;
|
|
@@ -4463,7 +4838,7 @@
|
|
|
4463
4838
|
gap: 2px;
|
|
4464
4839
|
background: var(--nc-ink-2);
|
|
4465
4840
|
border: 1px solid var(--nc-line-1);
|
|
4466
|
-
border-radius:
|
|
4841
|
+
border-radius: 7px;
|
|
4467
4842
|
padding: 2px;
|
|
4468
4843
|
}
|
|
4469
4844
|
|
|
@@ -5016,6 +5391,27 @@
|
|
|
5016
5391
|
animation: ncNoticeIn .26s cubic-bezier(.2, .7, .3, 1) both;
|
|
5017
5392
|
will-change: transform, opacity;
|
|
5018
5393
|
}
|
|
5394
|
+
/* The card behind peeks out above the one in front, so the next thing to deal with is
|
|
5395
|
+
visible without being readable or clickable until it comes forward. */
|
|
5396
|
+
.pwNoticeBehind {
|
|
5397
|
+
z-index: calc(75 - var(--depth, 1));
|
|
5398
|
+
transform: translateY(calc(var(--depth, 1) * -12px)) scale(calc(1 - var(--depth, 1) * .04));
|
|
5399
|
+
opacity: .92;
|
|
5400
|
+
pointer-events: none;
|
|
5401
|
+
animation: none;
|
|
5402
|
+
transition: transform .26s cubic-bezier(.2, .7, .3, 1);
|
|
5403
|
+
}
|
|
5404
|
+
.pwNoticeMore {
|
|
5405
|
+
position: absolute;
|
|
5406
|
+
top: -9px;
|
|
5407
|
+
right: 14px;
|
|
5408
|
+
padding: 3px 8px;
|
|
5409
|
+
border-radius: 999px;
|
|
5410
|
+
background: var(--nc-violet-500);
|
|
5411
|
+
color: #fff;
|
|
5412
|
+
font: 600 11px/1 var(--nc-font-sans);
|
|
5413
|
+
letter-spacing: .01em;
|
|
5414
|
+
}
|
|
5019
5415
|
|
|
5020
5416
|
.pwNoticeTitle {
|
|
5021
5417
|
margin: 0;
|
|
@@ -5364,7 +5760,7 @@
|
|
|
5364
5760
|
}
|
|
5365
5761
|
.sigPreviewOpen .sigPreviewFold { max-height: 2000px; }
|
|
5366
5762
|
.sigPreviewBody {
|
|
5367
|
-
font: 400 13px/1
|
|
5763
|
+
font: 400 13px/1 var(--nc-font-sans);
|
|
5368
5764
|
color: var(--nc-fg-2);
|
|
5369
5765
|
}
|
|
5370
5766
|
.sigPreviewBody a { color: var(--nc-violet-500); }
|
|
@@ -5910,9 +6306,7 @@
|
|
|
5910
6306
|
background: linear-gradient(to bottom, transparent 0%, var(--nc-ink-1) 78%);
|
|
5911
6307
|
pointer-events: none;
|
|
5912
6308
|
}
|
|
5913
|
-
|
|
5914
|
-
content rather than running on against an arbitrary ceiling. */
|
|
5915
|
-
.composeSignatureOpen .composeSignatureBody { max-height: var(--sig-h, 1200px); cursor: default; }
|
|
6309
|
+
.composeSignatureOpen .composeSignatureBody { max-height: none; overflow: visible; cursor: default; }
|
|
5916
6310
|
.composeSignatureOpen { padding-bottom: 30px; }
|
|
5917
6311
|
|
|
5918
6312
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -6208,20 +6602,23 @@
|
|
|
6208
6602
|
.fontInput { flex: 1 1 160px; height: 34px; padding: 0 10px; border: 1px solid var(--nc-line-1); border-radius: 8px; background: var(--nc-ink-1); color: var(--nc-fg-1); font: 500 13px/1 var(--nc-font-sans); }
|
|
6209
6603
|
.fontUpload { height: 34px; display: inline-flex; align-items: center; padding: 0 12px; border: 1px solid var(--nc-line-1); border-radius: 8px; font: 500 13px/1 var(--nc-font-sans); color: var(--nc-fg-1); cursor: pointer; }
|
|
6210
6604
|
|
|
6211
|
-
.selectEditable { padding: 0 6px 0
|
|
6212
|
-
.selectInput {
|
|
6605
|
+
.selectEditable { padding: 0 6px 0 8px; gap: 6px; }
|
|
6606
|
+
.selectBtn .selectInput {
|
|
6213
6607
|
width: 3.5em;
|
|
6214
6608
|
min-width: 0;
|
|
6215
6609
|
height: 100%;
|
|
6216
|
-
padding: 0
|
|
6610
|
+
padding: 0;
|
|
6217
6611
|
border: 0;
|
|
6612
|
+
border-radius: 0;
|
|
6218
6613
|
background: transparent;
|
|
6614
|
+
box-shadow: none;
|
|
6615
|
+
appearance: none;
|
|
6219
6616
|
color: inherit;
|
|
6220
6617
|
font: inherit;
|
|
6221
6618
|
outline: none;
|
|
6222
6619
|
}
|
|
6223
|
-
|
|
6224
|
-
.rteSizeSelect .selectInput {
|
|
6620
|
+
.selectBtn .selectInput:focus { border: 0; box-shadow: none; }
|
|
6621
|
+
.rteSizeSelect .selectInput { width: 3.2em; }
|
|
6225
6622
|
.selectChevronBtn { display: inline-flex; align-items: center; padding: 0 4px; border: 0; background: transparent; color: inherit; cursor: pointer; }
|
|
6226
6623
|
.mailboxDomainSelect.selectBtn { padding: 4px 6px; background: transparent; border: 0; }
|
|
6227
6624
|
|
|
@@ -6259,6 +6656,12 @@
|
|
|
6259
6656
|
right: calc(18px + env(safe-area-inset-right, 0px));
|
|
6260
6657
|
bottom: calc(18px + env(safe-area-inset-bottom, 0px));
|
|
6261
6658
|
}
|
|
6659
|
+
/* Above the compose button, which appears at this width in the same corner: 54px tall
|
|
6660
|
+
sitting 18px off the bottom, so anything below 84px is underneath it. */
|
|
6661
|
+
.toTop {
|
|
6662
|
+
right: calc(18px + env(safe-area-inset-right, 0px));
|
|
6663
|
+
bottom: calc(84px + env(safe-area-inset-bottom, 0px));
|
|
6664
|
+
}
|
|
6262
6665
|
.list {
|
|
6263
6666
|
padding-left: calc(10px + env(safe-area-inset-left, 0px));
|
|
6264
6667
|
padding-right: calc(10px + env(safe-area-inset-right, 0px));
|
|
@@ -6274,6 +6677,7 @@
|
|
|
6274
6677
|
width: auto;
|
|
6275
6678
|
z-index: 445;
|
|
6276
6679
|
}
|
|
6680
|
+
.pwNoticeBehind { z-index: calc(445 - var(--depth, 1)); }
|
|
6277
6681
|
/* Forty-four pixels is the smallest thing a thumb finds reliably. */
|
|
6278
6682
|
.itemStar,
|
|
6279
6683
|
.iconBtn,
|
|
@@ -6318,3 +6722,166 @@
|
|
|
6318
6722
|
max-width: none;
|
|
6319
6723
|
}
|
|
6320
6724
|
}
|
|
6725
|
+
|
|
6726
|
+
/* ---- Phone corrections ------------------------------------------------------------
|
|
6727
|
+
Everything here was found by hit-testing the rendered page at 375x812 rather than by
|
|
6728
|
+
reading the stylesheet, because each fault only appears once the list is full width. */
|
|
6729
|
+
@media (max-width: 720px) {
|
|
6730
|
+
/* The notification and theme buttons float over the top-right corner. With a reading
|
|
6731
|
+
pane beside it that corner is empty; without one it is the list header, and these two
|
|
6732
|
+
sat on top of Refresh so it could not be tapped at all. Drop them behind anything that
|
|
6733
|
+
opens over the mailbox, and give the header room to put Refresh beside them. */
|
|
6734
|
+
.topRight {
|
|
6735
|
+
top: 18px;
|
|
6736
|
+
right: 12px;
|
|
6737
|
+
gap: 6px;
|
|
6738
|
+
z-index: 405;
|
|
6739
|
+
}
|
|
6740
|
+
.listHead .refreshBtn { display: none; }
|
|
6741
|
+
.actions { row-gap: 8px; gap: 6px; padding: 12px 15px; }
|
|
6742
|
+
.app { --thread-inset: 15px; }
|
|
6743
|
+
.readerHead { padding: 18px 15px 0; }
|
|
6744
|
+
.readerBody { margin: 0 15px 16px; }
|
|
6745
|
+
.readerModeBar { margin: 0 15px 12px; }
|
|
6746
|
+
.thread { padding-left: 15px; padding-right: 15px; }
|
|
6747
|
+
.attachGrid { margin: 0 15px 12px; }
|
|
6748
|
+
.activity { margin: 0 15px 14px; padding: 10px 12px; }
|
|
6749
|
+
.readerAttach { margin: 0 15px 14px; }
|
|
6750
|
+
.attachFloat {
|
|
6751
|
+
left: 0;
|
|
6752
|
+
right: 0;
|
|
6753
|
+
bottom: 0;
|
|
6754
|
+
border-left: 0;
|
|
6755
|
+
border-right: 0;
|
|
6756
|
+
border-bottom: 0;
|
|
6757
|
+
border-radius: 14px 14px 0 0;
|
|
6758
|
+
}
|
|
6759
|
+
.attachFloatBody .attachGrid { margin: 0 15px 10px; }
|
|
6760
|
+
.readerText { padding: 16px 15px; }
|
|
6761
|
+
.composeSignature { margin: 0 15px 14px; }
|
|
6762
|
+
.quickReplyBar { padding-left: 15px; padding-right: 15px; }
|
|
6763
|
+
.actions .iconBtn { min-width: 40px; width: 40px; padding: 0; }
|
|
6764
|
+
.actionSpacer { flex: 1 0 100%; min-width: 0; height: 0; }
|
|
6765
|
+
.rteToolbar {
|
|
6766
|
+
flex-wrap: nowrap;
|
|
6767
|
+
overflow-x: auto;
|
|
6768
|
+
gap: 6px;
|
|
6769
|
+
padding: 8px 12px;
|
|
6770
|
+
scroll-snap-type: x proximity;
|
|
6771
|
+
scrollbar-width: none;
|
|
6772
|
+
-webkit-overflow-scrolling: touch;
|
|
6773
|
+
}
|
|
6774
|
+
.rteToolbar::-webkit-scrollbar { display: none; }
|
|
6775
|
+
.rteGroup { flex: 0 0 auto; scroll-snap-align: start; }
|
|
6776
|
+
.rtePalette .rteSwatch { width: 24px; height: 24px; }
|
|
6777
|
+
.compose,
|
|
6778
|
+
.composeExpanded {
|
|
6779
|
+
top: auto;
|
|
6780
|
+
bottom: 0;
|
|
6781
|
+
height: 80dvh;
|
|
6782
|
+
max-height: 80dvh;
|
|
6783
|
+
border-radius: 18px 18px 0 0;
|
|
6784
|
+
}
|
|
6785
|
+
.listHead {
|
|
6786
|
+
padding-right: 106px;
|
|
6787
|
+
}
|
|
6788
|
+
.listTitle {
|
|
6789
|
+
flex: 0 0 auto;
|
|
6790
|
+
}
|
|
6791
|
+
.listMeta {
|
|
6792
|
+
min-width: 0;
|
|
6793
|
+
overflow: hidden;
|
|
6794
|
+
white-space: nowrap;
|
|
6795
|
+
text-overflow: ellipsis;
|
|
6796
|
+
}
|
|
6797
|
+
/* "60 convers…" reads as a fault rather than as a count. Below this width the number
|
|
6798
|
+
carries it alone; the unit is still in the header's tooltip and spelled out in full
|
|
6799
|
+
at the end of the list. */
|
|
6800
|
+
.listMetaUnit {
|
|
6801
|
+
display: none;
|
|
6802
|
+
}
|
|
6803
|
+
/* An earlier rule lifted this clear of the compose button; a later one pinned it back
|
|
6804
|
+
over the top, so its own buttons were half covered. */
|
|
6805
|
+
.pwNotice {
|
|
6806
|
+
bottom: calc(84px + env(safe-area-inset-bottom, 0px));
|
|
6807
|
+
}
|
|
6808
|
+
/* Nine formatting buttons at a size a finger can hit wrap onto three rows and take 134px
|
|
6809
|
+
of a reply that has not been written yet. One row that scrolls keeps every control and
|
|
6810
|
+
costs a third of the height. */
|
|
6811
|
+
.replyToolbar,
|
|
6812
|
+
.readerModeTabs,
|
|
6813
|
+
.readerLayoutTabs {
|
|
6814
|
+
flex-wrap: nowrap;
|
|
6815
|
+
overflow-x: auto;
|
|
6816
|
+
scrollbar-width: none;
|
|
6817
|
+
}
|
|
6818
|
+
.replyToolbar::-webkit-scrollbar,
|
|
6819
|
+
.readerModeTabs::-webkit-scrollbar,
|
|
6820
|
+
.readerLayoutTabs::-webkit-scrollbar {
|
|
6821
|
+
display: none;
|
|
6822
|
+
}
|
|
6823
|
+
.replyToolbar > *,
|
|
6824
|
+
.readerModeTabs > *,
|
|
6825
|
+
.readerLayoutTabs > * {
|
|
6826
|
+
flex: 0 0 auto;
|
|
6827
|
+
}
|
|
6828
|
+
|
|
6829
|
+
/* The card scrolls, and the footer scrolled away with it, so Save was reachable only
|
|
6830
|
+
after finding the bottom of a long form. */
|
|
6831
|
+
.settingsFoot {
|
|
6832
|
+
position: sticky;
|
|
6833
|
+
bottom: 0;
|
|
6834
|
+
z-index: 1;
|
|
6835
|
+
margin-top: 8px;
|
|
6836
|
+
padding: 10px 0 2px;
|
|
6837
|
+
background: var(--nc-ink-1);
|
|
6838
|
+
box-shadow: 0 -10px 16px -12px rgba(0, 0, 0, .5);
|
|
6839
|
+
}
|
|
6840
|
+
}
|
|
6841
|
+
|
|
6842
|
+
/* A finger is not a mouse pointer: anything it has to hit needs to be a target, not a
|
|
6843
|
+
label. These were 15px to 32px tall, which is a miss more often than a hit. */
|
|
6844
|
+
@media (pointer: coarse) {
|
|
6845
|
+
.modeTab,
|
|
6846
|
+
.orderToggle {
|
|
6847
|
+
min-height: 24px;
|
|
6848
|
+
}
|
|
6849
|
+
.settingsNavItem {
|
|
6850
|
+
min-height: 40px;
|
|
6851
|
+
}
|
|
6852
|
+
.toolBtn {
|
|
6853
|
+
min-width: 40px;
|
|
6854
|
+
min-height: 40px;
|
|
6855
|
+
}
|
|
6856
|
+
.headerToggle,
|
|
6857
|
+
.replyRecipsToggle,
|
|
6858
|
+
.composeSignatureLabel,
|
|
6859
|
+
.composeSignatureEdit,
|
|
6860
|
+
.composeSignatureMore {
|
|
6861
|
+
min-height: 36px;
|
|
6862
|
+
padding-top: 2px;
|
|
6863
|
+
padding-bottom: 2px;
|
|
6864
|
+
}
|
|
6865
|
+
.headerToggle {
|
|
6866
|
+
padding-left: 8px;
|
|
6867
|
+
padding-right: 8px;
|
|
6868
|
+
}
|
|
6869
|
+
}
|
|
6870
|
+
|
|
6871
|
+
@media (max-width: 720px) {
|
|
6872
|
+
/* Compose is already the whole screen here, so a control that makes it full width has
|
|
6873
|
+
nothing to do, and a phone has no Esc key to press. Between them they wrapped the
|
|
6874
|
+
compose header onto four lines. */
|
|
6875
|
+
.composeExpandBtn,
|
|
6876
|
+
.composeHintKeys {
|
|
6877
|
+
display: none;
|
|
6878
|
+
}
|
|
6879
|
+
/* The expanded composer takes its left edge from --list-w, and the chosen list width
|
|
6880
|
+
is written onto .app as an inline variable — which outranks this breakpoint's zero.
|
|
6881
|
+
So it opened 400px to the right of a 375px screen, wholly out of view. */
|
|
6882
|
+
.composeExpanded {
|
|
6883
|
+
left: 0;
|
|
6884
|
+
right: 0;
|
|
6885
|
+
width: auto;
|
|
6886
|
+
}
|
|
6887
|
+
}
|