@brftech/filex-core 0.31.0 → 0.32.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 +26 -2
- package/dist/filex-core.js +9289 -8103
- package/dist/filex-core.js.map +1 -1
- package/dist/filex-core.umd.cjs +49 -49
- package/dist/filex-core.umd.cjs.map +1 -1
- package/dist/index.d.ts +186 -8
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/FileExplorer.vue +510 -12
- package/src/components/CommandPalette.vue +18 -2
- package/src/components/E2eRecoveryUnlockModal.vue +29 -4
- package/src/components/FilterBar.vue +244 -0
- package/src/components/GalleryView.vue +11 -0
- package/src/components/GridView.vue +46 -3
- package/src/components/InspectorPanel.vue +231 -8
- package/src/components/ListView.vue +10 -1
- package/src/components/SecondaryPane.vue +15 -1
- package/src/components/SideNav.vue +161 -1
- package/src/components/Toolbar.vue +235 -49
- package/src/components/ViewSwitcher.vue +81 -0
- package/src/composables/useFileApi.ts +45 -0
- package/src/index.ts +6 -0
- package/src/lib/e2ecrypto.ts +191 -3
- package/src/lib/fileFilters.ts +143 -0
- package/src/lib/listing.ts +47 -0
- package/src/locales/en.ts +83 -0
- package/src/locales/tr.ts +83 -0
- package/src/modals/PermissionsModal.vue +18 -2
- package/src/styles/base.css +491 -0
- package/src/types/ExplorerConfig.ts +17 -1
|
@@ -19,6 +19,20 @@ const props = defineProps<{
|
|
|
19
19
|
/** Server ceiling on a new link's life in days (capabilities.share_max_ttl_days).
|
|
20
20
|
* undefined/0 = no ceiling. The expiry choices are derived from it. */
|
|
21
21
|
shareMaxTtlDays?: number;
|
|
22
|
+
/**
|
|
23
|
+
* surucu:d1 — which tab this modal opens on. Absent = 'perms', the behaviour
|
|
24
|
+
* every existing caller has.
|
|
25
|
+
*
|
|
26
|
+
* The drive shell's "Request files" needs 'drop': the file-drop link already
|
|
27
|
+
* lives in this modal, and a menu entry that opens it on the permissions tab
|
|
28
|
+
* and leaves the user to find the third tab is a menu entry that lied about
|
|
29
|
+
* what it does.
|
|
30
|
+
*
|
|
31
|
+
* ⚠ A hint, not a lock: `load()` still falls back to 'share' when the caller
|
|
32
|
+
* turns out not to be an owner, because the permissions tab is not theirs to
|
|
33
|
+
* see.
|
|
34
|
+
*/
|
|
35
|
+
initialTab?: 'perms' | 'share' | 'drop';
|
|
22
36
|
}>();
|
|
23
37
|
const emit = defineEmits<{ (e: 'close'): void }>();
|
|
24
38
|
|
|
@@ -37,7 +51,7 @@ const pathParts = computed(() => {
|
|
|
37
51
|
});
|
|
38
52
|
|
|
39
53
|
type Tab = 'perms' | 'share' | 'drop';
|
|
40
|
-
const tab = ref<Tab>('perms');
|
|
54
|
+
const tab = ref<Tab>(props.initialTab ?? 'perms');
|
|
41
55
|
const canManage = ref(false); // owner/admin → can see the permissions tab
|
|
42
56
|
|
|
43
57
|
// ── permissions state ──
|
|
@@ -185,7 +199,9 @@ async function reload() {
|
|
|
185
199
|
if (st === 403) {
|
|
186
200
|
canManage.value = false;
|
|
187
201
|
// Editor (not owner): no permissions tab → fall back to the link tab.
|
|
188
|
-
|
|
202
|
+
// ⚠ Unless the caller asked for a specific one: "Request files" opens
|
|
203
|
+
// 'drop', which an editor may perfectly well use.
|
|
204
|
+
if (!props.initialTab || props.initialTab === 'perms') tab.value = 'share';
|
|
189
205
|
} else {
|
|
190
206
|
err.value = e instanceof Error ? e.message : String(e);
|
|
191
207
|
}
|
package/src/styles/base.css
CHANGED
|
@@ -3914,6 +3914,20 @@ filex-explorer {
|
|
|
3914
3914
|
font-size: 12.5px;
|
|
3915
3915
|
color: var(--fe-text-muted);
|
|
3916
3916
|
}
|
|
3917
|
+
/* Why the escrow key does not apply to THIS folder. Deliberately not muted
|
|
3918
|
+
to the point of invisibility: it is the answer to a question the reader
|
|
3919
|
+
is already asking, and the pre-adoption case has no other surface. */
|
|
3920
|
+
.fe-e2e-recover__note {
|
|
3921
|
+
margin: 0;
|
|
3922
|
+
padding: 8px 10px;
|
|
3923
|
+
font-size: 12.5px;
|
|
3924
|
+
line-height: 1.45;
|
|
3925
|
+
color: var(--fe-text);
|
|
3926
|
+
border: 1px solid var(--fe-border);
|
|
3927
|
+
border-left: 3px solid var(--fe-warning, #f59e0b);
|
|
3928
|
+
border-radius: var(--fe-radius);
|
|
3929
|
+
background: var(--fe-bg-elev);
|
|
3930
|
+
}
|
|
3917
3931
|
.fe-e2e-recover__tabs {
|
|
3918
3932
|
display: flex;
|
|
3919
3933
|
gap: 4px;
|
|
@@ -3988,6 +4002,36 @@ filex-explorer {
|
|
|
3988
4002
|
gap: 8px;
|
|
3989
4003
|
flex: none;
|
|
3990
4004
|
}
|
|
4005
|
+
/* wiring:e2 escrow-offer — the same strip, asking a heavier question:
|
|
4006
|
+
accepting hands the operator a permanent second key. Amber rather than
|
|
4007
|
+
the recovery offer's primary tint, so it does not read as routine. */
|
|
4008
|
+
.fe-e2e-upgrade--escrow {
|
|
4009
|
+
border-left-color: var(--fe-warning, #f59e0b);
|
|
4010
|
+
background: color-mix(in srgb, var(--fe-warning, #f59e0b) 7%, transparent);
|
|
4011
|
+
}
|
|
4012
|
+
/* The link to the honest long version has to LOOK like a link: it inherits
|
|
4013
|
+
the warning colour, and without this it read as ordinary sentence text —
|
|
4014
|
+
somebody being asked to hand over a key should be able to see the way to
|
|
4015
|
+
the page that explains what they are handing over. */
|
|
4016
|
+
.fe-e2e-upgrade__escrow a {
|
|
4017
|
+
color: inherit;
|
|
4018
|
+
text-decoration: underline;
|
|
4019
|
+
text-underline-offset: 2px;
|
|
4020
|
+
font-weight: 600;
|
|
4021
|
+
}
|
|
4022
|
+
.fe-e2e-upgrade__escrow a:hover,
|
|
4023
|
+
.fe-e2e-upgrade__escrow a:focus-visible {
|
|
4024
|
+
text-decoration-thickness: 2px;
|
|
4025
|
+
}
|
|
4026
|
+
.fe-e2e-upgrade__pw {
|
|
4027
|
+
display: flex;
|
|
4028
|
+
flex-direction: column;
|
|
4029
|
+
gap: 4px;
|
|
4030
|
+
margin-top: 8px;
|
|
4031
|
+
max-width: 320px;
|
|
4032
|
+
font-size: 12.5px;
|
|
4033
|
+
color: var(--fe-text-muted);
|
|
4034
|
+
}
|
|
3991
4035
|
/* === /wiring:e2 === */
|
|
3992
4036
|
|
|
3993
4037
|
/* === ui-fix — toolbar tek satır + katlama + ölçüm şeridi ============= */
|
|
@@ -4622,3 +4666,450 @@ filex-explorer {
|
|
|
4622
4666
|
margin-left: auto;
|
|
4623
4667
|
}
|
|
4624
4668
|
/* --- /gezinti:g1 --- */
|
|
4669
|
+
|
|
4670
|
+
/* === surucu:d1 — the Drive shell (uiProfile: 'drive', GitHub #14) ======
|
|
4671
|
+
Everything here is additive and gated on a class the other two profiles
|
|
4672
|
+
never carry, so `standard` and `simple` render exactly what they rendered
|
|
4673
|
+
before this block existed.
|
|
4674
|
+
|
|
4675
|
+
⚠ Unscoped and prefixed, like the rest of this file. A `<style scoped>` in
|
|
4676
|
+
an SFC compiles to `.cls[data-v-HASH]`, and the hash does not match in the
|
|
4677
|
+
web-component build — the rules silently stop applying in every embed, which
|
|
4678
|
+
is how the share dialog once shipped as raw unstyled HTML.
|
|
4679
|
+
---------------------------------------------------------------------- */
|
|
4680
|
+
|
|
4681
|
+
/* ── the header ───────────────────────────────────────────────────────── */
|
|
4682
|
+
.fe-toolbar--drive {
|
|
4683
|
+
gap: 10px;
|
|
4684
|
+
padding: 8px 12px;
|
|
4685
|
+
}
|
|
4686
|
+
/* The field takes the row. `min-width: 0` because a flex item's default `auto`
|
|
4687
|
+
minimum is its content width and an <input> reports a wide one — without it
|
|
4688
|
+
the header refuses to shrink and the whole explorer grows a horizontal
|
|
4689
|
+
scrollbar at 390px. */
|
|
4690
|
+
.fe-toolbar__search-group--drive {
|
|
4691
|
+
flex: 1 1 auto;
|
|
4692
|
+
min-width: 0;
|
|
4693
|
+
gap: 8px;
|
|
4694
|
+
}
|
|
4695
|
+
.fe-drivesearch {
|
|
4696
|
+
position: relative;
|
|
4697
|
+
display: flex;
|
|
4698
|
+
align-items: center;
|
|
4699
|
+
gap: 8px;
|
|
4700
|
+
flex: 1 1 auto;
|
|
4701
|
+
min-width: 0;
|
|
4702
|
+
max-width: 720px;
|
|
4703
|
+
padding: 0 10px 0 12px;
|
|
4704
|
+
height: 40px;
|
|
4705
|
+
border-radius: 999px;
|
|
4706
|
+
border: 1px solid transparent;
|
|
4707
|
+
background: var(--fe-bg-elev);
|
|
4708
|
+
color: var(--fe-text);
|
|
4709
|
+
}
|
|
4710
|
+
.fe-drivesearch:focus-within {
|
|
4711
|
+
background: var(--fe-bg);
|
|
4712
|
+
border-color: var(--fe-border-strong);
|
|
4713
|
+
box-shadow: var(--fe-shadow-sm);
|
|
4714
|
+
}
|
|
4715
|
+
.fe-drivesearch__glass {
|
|
4716
|
+
flex: 0 0 auto;
|
|
4717
|
+
width: 18px;
|
|
4718
|
+
height: 18px;
|
|
4719
|
+
color: var(--fe-text-muted);
|
|
4720
|
+
}
|
|
4721
|
+
.fe-drivesearch__input {
|
|
4722
|
+
flex: 1 1 auto;
|
|
4723
|
+
min-width: 0;
|
|
4724
|
+
border: 0;
|
|
4725
|
+
background: transparent;
|
|
4726
|
+
color: inherit;
|
|
4727
|
+
font: inherit;
|
|
4728
|
+
font-size: 14px;
|
|
4729
|
+
outline: none;
|
|
4730
|
+
}
|
|
4731
|
+
.fe-drivesearch__input::placeholder {
|
|
4732
|
+
color: var(--fe-text-muted);
|
|
4733
|
+
}
|
|
4734
|
+
/* The ⌘K chip. A real button, not decoration: it is the one visible door to
|
|
4735
|
+
the palette in this shell, so it has to be clickable and tab-reachable —
|
|
4736
|
+
printing a shortcut nobody can press with a mouse is a hint, not a control. */
|
|
4737
|
+
.fe-drivesearch__kbd {
|
|
4738
|
+
flex: 0 0 auto;
|
|
4739
|
+
padding: 3px 8px;
|
|
4740
|
+
border: 1px solid var(--fe-border);
|
|
4741
|
+
border-radius: var(--fe-radius-sm);
|
|
4742
|
+
background: var(--fe-bg);
|
|
4743
|
+
color: var(--fe-text-muted);
|
|
4744
|
+
font: inherit;
|
|
4745
|
+
font-size: 11.5px;
|
|
4746
|
+
letter-spacing: 0.02em;
|
|
4747
|
+
cursor: pointer;
|
|
4748
|
+
white-space: nowrap;
|
|
4749
|
+
}
|
|
4750
|
+
.fe-drivesearch__kbd:hover {
|
|
4751
|
+
color: var(--fe-text);
|
|
4752
|
+
border-color: var(--fe-border-strong);
|
|
4753
|
+
}
|
|
4754
|
+
.fe-drivesearch__kbd:focus-visible {
|
|
4755
|
+
outline: 2px solid var(--fe-primary);
|
|
4756
|
+
outline-offset: 1px;
|
|
4757
|
+
}
|
|
4758
|
+
|
|
4759
|
+
/* ── the "+ New" button ───────────────────────────────────────────────── */
|
|
4760
|
+
.fe-sidenav__new {
|
|
4761
|
+
display: inline-flex;
|
|
4762
|
+
align-items: center;
|
|
4763
|
+
gap: 10px;
|
|
4764
|
+
width: 100%;
|
|
4765
|
+
padding: 11px 18px;
|
|
4766
|
+
border-radius: 999px;
|
|
4767
|
+
border: 1px solid var(--fe-primary);
|
|
4768
|
+
background: var(--fe-primary);
|
|
4769
|
+
color: var(--fe-text-on-primary);
|
|
4770
|
+
font: inherit;
|
|
4771
|
+
font-size: 14px;
|
|
4772
|
+
font-weight: 600;
|
|
4773
|
+
text-align: left;
|
|
4774
|
+
white-space: nowrap;
|
|
4775
|
+
overflow: hidden;
|
|
4776
|
+
cursor: pointer;
|
|
4777
|
+
box-shadow: var(--fe-shadow-sm);
|
|
4778
|
+
}
|
|
4779
|
+
.fe-sidenav__new:hover {
|
|
4780
|
+
background: var(--fe-primary-hover);
|
|
4781
|
+
border-color: var(--fe-primary-hover);
|
|
4782
|
+
}
|
|
4783
|
+
.fe-sidenav__new:focus-visible {
|
|
4784
|
+
outline: 2px solid var(--fe-primary);
|
|
4785
|
+
outline-offset: 2px;
|
|
4786
|
+
}
|
|
4787
|
+
.fe-sidenav__new .fe-ficon {
|
|
4788
|
+
width: 20px;
|
|
4789
|
+
height: 20px;
|
|
4790
|
+
flex: 0 0 auto;
|
|
4791
|
+
}
|
|
4792
|
+
/* On the rail it becomes a circle: a pill with no label is a stretched blob. */
|
|
4793
|
+
.fe-sidenav--rail .fe-sidenav__new {
|
|
4794
|
+
width: 40px;
|
|
4795
|
+
height: 40px;
|
|
4796
|
+
padding: 0;
|
|
4797
|
+
justify-content: center;
|
|
4798
|
+
border-radius: 50%;
|
|
4799
|
+
}
|
|
4800
|
+
|
|
4801
|
+
/* ── the storage line ─────────────────────────────────────────────────── */
|
|
4802
|
+
.fe-sidenav__quota {
|
|
4803
|
+
flex: 0 0 auto;
|
|
4804
|
+
padding: 10px 12px 12px;
|
|
4805
|
+
border-top: 1px solid var(--fe-border);
|
|
4806
|
+
}
|
|
4807
|
+
.fe-sidenav--rail .fe-sidenav__quota {
|
|
4808
|
+
padding: 10px 8px;
|
|
4809
|
+
}
|
|
4810
|
+
.fe-sidenav__quota-label {
|
|
4811
|
+
margin: 0 0 6px;
|
|
4812
|
+
font-size: 11px;
|
|
4813
|
+
font-weight: 600;
|
|
4814
|
+
letter-spacing: 0.04em;
|
|
4815
|
+
text-transform: uppercase;
|
|
4816
|
+
color: var(--fe-text-muted);
|
|
4817
|
+
}
|
|
4818
|
+
.fe-sidenav__quota-bar {
|
|
4819
|
+
height: 5px;
|
|
4820
|
+
border-radius: 999px;
|
|
4821
|
+
background: var(--fe-bg-hover);
|
|
4822
|
+
overflow: hidden;
|
|
4823
|
+
}
|
|
4824
|
+
.fe-sidenav__quota-fill {
|
|
4825
|
+
display: block;
|
|
4826
|
+
height: 100%;
|
|
4827
|
+
border-radius: 999px;
|
|
4828
|
+
background: var(--fe-primary);
|
|
4829
|
+
transition: width 0.2s ease;
|
|
4830
|
+
}
|
|
4831
|
+
.fe-sidenav__quota-text {
|
|
4832
|
+
margin: 6px 0 0;
|
|
4833
|
+
font-size: 12px;
|
|
4834
|
+
color: var(--fe-text-muted);
|
|
4835
|
+
}
|
|
4836
|
+
|
|
4837
|
+
/* ── the breadcrumb row ───────────────────────────────────────────────── */
|
|
4838
|
+
/* Not a drive shell: the wrapper leaves layout entirely, so the breadcrumb is
|
|
4839
|
+
the same flex child of .fe__primary it has always been. */
|
|
4840
|
+
.fe-subhead--plain {
|
|
4841
|
+
display: contents;
|
|
4842
|
+
}
|
|
4843
|
+
.fe-subhead {
|
|
4844
|
+
display: flex;
|
|
4845
|
+
align-items: center;
|
|
4846
|
+
gap: 8px;
|
|
4847
|
+
min-width: 0;
|
|
4848
|
+
}
|
|
4849
|
+
.fe-subhead > .fe-breadcrumb {
|
|
4850
|
+
flex: 1 1 auto;
|
|
4851
|
+
min-width: 0;
|
|
4852
|
+
}
|
|
4853
|
+
.fe-subhead__actions {
|
|
4854
|
+
flex: 0 0 auto;
|
|
4855
|
+
display: flex;
|
|
4856
|
+
align-items: center;
|
|
4857
|
+
gap: 6px;
|
|
4858
|
+
padding-right: 8px;
|
|
4859
|
+
}
|
|
4860
|
+
|
|
4861
|
+
/* ── the filter row ───────────────────────────────────────────────────── */
|
|
4862
|
+
.fe-filterbar {
|
|
4863
|
+
flex: 0 0 auto;
|
|
4864
|
+
display: flex;
|
|
4865
|
+
align-items: center;
|
|
4866
|
+
gap: 8px;
|
|
4867
|
+
padding: 8px 10px;
|
|
4868
|
+
/* Scrolls sideways ON ITSELF at 390px rather than widening the explorer:
|
|
4869
|
+
three chips and a clear button do not fit a phone, and the alternative is
|
|
4870
|
+
a horizontal scrollbar on everything. */
|
|
4871
|
+
overflow-x: auto;
|
|
4872
|
+
overflow-y: hidden;
|
|
4873
|
+
scrollbar-width: none;
|
|
4874
|
+
}
|
|
4875
|
+
.fe-filterbar::-webkit-scrollbar {
|
|
4876
|
+
display: none;
|
|
4877
|
+
}
|
|
4878
|
+
.fe-filterbar__chip {
|
|
4879
|
+
flex: 0 0 auto;
|
|
4880
|
+
display: inline-flex;
|
|
4881
|
+
align-items: center;
|
|
4882
|
+
gap: 4px;
|
|
4883
|
+
height: 32px;
|
|
4884
|
+
padding: 0 10px 0 12px;
|
|
4885
|
+
border: 1px solid var(--fe-border-strong);
|
|
4886
|
+
border-radius: 999px;
|
|
4887
|
+
background: transparent;
|
|
4888
|
+
color: var(--fe-text);
|
|
4889
|
+
font: inherit;
|
|
4890
|
+
font-size: 13px;
|
|
4891
|
+
cursor: pointer;
|
|
4892
|
+
white-space: nowrap;
|
|
4893
|
+
}
|
|
4894
|
+
.fe-filterbar__chip:hover {
|
|
4895
|
+
background: var(--fe-bg-hover);
|
|
4896
|
+
}
|
|
4897
|
+
.fe-filterbar__chip.is-set {
|
|
4898
|
+
background: var(--fe-bg-selected);
|
|
4899
|
+
border-color: var(--fe-primary);
|
|
4900
|
+
font-weight: 600;
|
|
4901
|
+
}
|
|
4902
|
+
.fe-filterbar__chip.is-open {
|
|
4903
|
+
border-color: var(--fe-primary);
|
|
4904
|
+
}
|
|
4905
|
+
.fe-filterbar__chip:focus-visible {
|
|
4906
|
+
outline: 2px solid var(--fe-primary);
|
|
4907
|
+
outline-offset: 1px;
|
|
4908
|
+
}
|
|
4909
|
+
.fe-filterbar__caret {
|
|
4910
|
+
width: 15px;
|
|
4911
|
+
height: 15px;
|
|
4912
|
+
}
|
|
4913
|
+
.fe-filterbar__clear {
|
|
4914
|
+
flex: 0 0 auto;
|
|
4915
|
+
border: 0;
|
|
4916
|
+
background: transparent;
|
|
4917
|
+
color: var(--fe-primary);
|
|
4918
|
+
font: inherit;
|
|
4919
|
+
font-size: 12.5px;
|
|
4920
|
+
cursor: pointer;
|
|
4921
|
+
padding: 4px 6px;
|
|
4922
|
+
border-radius: var(--fe-radius-sm);
|
|
4923
|
+
white-space: nowrap;
|
|
4924
|
+
}
|
|
4925
|
+
.fe-filterbar__clear:hover {
|
|
4926
|
+
background: var(--fe-bg-hover);
|
|
4927
|
+
}
|
|
4928
|
+
.fe-filterbar__count {
|
|
4929
|
+
flex: 0 0 auto;
|
|
4930
|
+
margin-left: auto;
|
|
4931
|
+
padding-left: 8px;
|
|
4932
|
+
font-size: 12px;
|
|
4933
|
+
color: var(--fe-text-muted);
|
|
4934
|
+
white-space: nowrap;
|
|
4935
|
+
}
|
|
4936
|
+
/* Teleported to <body>: z-index 90, the layer the context menus already use.
|
|
4937
|
+
The onboarding tour sits above at 96 on purpose — a tour step must never end
|
|
4938
|
+
up behind a filter popover. */
|
|
4939
|
+
.fe-filterpop {
|
|
4940
|
+
position: fixed;
|
|
4941
|
+
z-index: 90;
|
|
4942
|
+
min-width: 190px;
|
|
4943
|
+
max-height: 60vh;
|
|
4944
|
+
overflow-y: auto;
|
|
4945
|
+
padding: 6px;
|
|
4946
|
+
border: 1px solid var(--fe-border);
|
|
4947
|
+
border-radius: var(--fe-radius-md);
|
|
4948
|
+
background: var(--fe-bg);
|
|
4949
|
+
color: var(--fe-text);
|
|
4950
|
+
box-shadow: var(--fe-shadow);
|
|
4951
|
+
font-family: var(--fe-font);
|
|
4952
|
+
}
|
|
4953
|
+
.fe-filterpop__item {
|
|
4954
|
+
display: flex;
|
|
4955
|
+
align-items: center;
|
|
4956
|
+
gap: 8px;
|
|
4957
|
+
width: 100%;
|
|
4958
|
+
padding: 7px 10px;
|
|
4959
|
+
border: 0;
|
|
4960
|
+
border-radius: var(--fe-radius-sm);
|
|
4961
|
+
background: transparent;
|
|
4962
|
+
color: inherit;
|
|
4963
|
+
font: inherit;
|
|
4964
|
+
font-size: 13px;
|
|
4965
|
+
text-align: left;
|
|
4966
|
+
cursor: pointer;
|
|
4967
|
+
}
|
|
4968
|
+
.fe-filterpop__item:hover {
|
|
4969
|
+
background: var(--fe-bg-hover);
|
|
4970
|
+
}
|
|
4971
|
+
.fe-filterpop__item:focus-visible {
|
|
4972
|
+
outline: 2px solid var(--fe-primary);
|
|
4973
|
+
outline-offset: -2px;
|
|
4974
|
+
}
|
|
4975
|
+
.fe-filterpop__tick {
|
|
4976
|
+
flex: 0 0 14px;
|
|
4977
|
+
color: var(--fe-primary);
|
|
4978
|
+
font-size: 12px;
|
|
4979
|
+
}
|
|
4980
|
+
|
|
4981
|
+
/* ── Folders / Files sections in the grid ─────────────────────────────── */
|
|
4982
|
+
/* A grid item spanning every column, so the cards keep flowing under it. */
|
|
4983
|
+
.fe-grid__heading {
|
|
4984
|
+
grid-column: 1 / -1;
|
|
4985
|
+
margin: 6px 2px 2px;
|
|
4986
|
+
font-size: 13px;
|
|
4987
|
+
font-weight: 600;
|
|
4988
|
+
color: var(--fe-text);
|
|
4989
|
+
}
|
|
4990
|
+
.fe-grid__heading:first-child {
|
|
4991
|
+
margin-top: 0;
|
|
4992
|
+
}
|
|
4993
|
+
|
|
4994
|
+
/* ── the details panel: tabs, people, link row ────────────────────────── */
|
|
4995
|
+
.fe-inspector__tabs {
|
|
4996
|
+
flex: 0 0 auto;
|
|
4997
|
+
display: flex;
|
|
4998
|
+
gap: 2px;
|
|
4999
|
+
padding: 0 10px;
|
|
5000
|
+
border-bottom: 1px solid var(--fe-border);
|
|
5001
|
+
}
|
|
5002
|
+
.fe-inspector__tab {
|
|
5003
|
+
padding: 9px 12px;
|
|
5004
|
+
border: 0;
|
|
5005
|
+
border-bottom: 2px solid transparent;
|
|
5006
|
+
background: transparent;
|
|
5007
|
+
color: var(--fe-text-muted);
|
|
5008
|
+
font: inherit;
|
|
5009
|
+
font-size: 13px;
|
|
5010
|
+
cursor: pointer;
|
|
5011
|
+
}
|
|
5012
|
+
.fe-inspector__tab:hover {
|
|
5013
|
+
color: var(--fe-text);
|
|
5014
|
+
}
|
|
5015
|
+
.fe-inspector__tab.is-active {
|
|
5016
|
+
color: var(--fe-primary);
|
|
5017
|
+
border-bottom-color: var(--fe-primary);
|
|
5018
|
+
font-weight: 600;
|
|
5019
|
+
}
|
|
5020
|
+
.fe-inspector__tab:focus-visible {
|
|
5021
|
+
outline: 2px solid var(--fe-primary);
|
|
5022
|
+
outline-offset: -2px;
|
|
5023
|
+
}
|
|
5024
|
+
.fe-inspector__people {
|
|
5025
|
+
list-style: none;
|
|
5026
|
+
margin: 0 0 8px;
|
|
5027
|
+
padding: 0;
|
|
5028
|
+
display: flex;
|
|
5029
|
+
flex-direction: column;
|
|
5030
|
+
gap: 8px;
|
|
5031
|
+
}
|
|
5032
|
+
.fe-inspector__person {
|
|
5033
|
+
display: flex;
|
|
5034
|
+
align-items: center;
|
|
5035
|
+
gap: 10px;
|
|
5036
|
+
min-width: 0;
|
|
5037
|
+
}
|
|
5038
|
+
.fe-inspector__avatar {
|
|
5039
|
+
flex: 0 0 28px;
|
|
5040
|
+
width: 28px;
|
|
5041
|
+
height: 28px;
|
|
5042
|
+
border-radius: 50%;
|
|
5043
|
+
display: inline-flex;
|
|
5044
|
+
align-items: center;
|
|
5045
|
+
justify-content: center;
|
|
5046
|
+
background: var(--fe-primary);
|
|
5047
|
+
color: var(--fe-text-on-primary);
|
|
5048
|
+
font-size: 12px;
|
|
5049
|
+
font-weight: 600;
|
|
5050
|
+
}
|
|
5051
|
+
.fe-inspector__person-main {
|
|
5052
|
+
display: flex;
|
|
5053
|
+
flex-direction: column;
|
|
5054
|
+
min-width: 0;
|
|
5055
|
+
}
|
|
5056
|
+
.fe-inspector__person-name {
|
|
5057
|
+
font-size: 13px;
|
|
5058
|
+
overflow: hidden;
|
|
5059
|
+
text-overflow: ellipsis;
|
|
5060
|
+
white-space: nowrap;
|
|
5061
|
+
}
|
|
5062
|
+
.fe-inspector__person-sub {
|
|
5063
|
+
font-size: 11.5px;
|
|
5064
|
+
color: var(--fe-text-muted);
|
|
5065
|
+
}
|
|
5066
|
+
.fe-inspector__linkrow {
|
|
5067
|
+
display: flex;
|
|
5068
|
+
align-items: center;
|
|
5069
|
+
gap: 8px;
|
|
5070
|
+
}
|
|
5071
|
+
.fe-inspector__linkicon {
|
|
5072
|
+
flex: 0 0 auto;
|
|
5073
|
+
font-size: 14px;
|
|
5074
|
+
}
|
|
5075
|
+
.fe-inspector__linknone {
|
|
5076
|
+
flex: 1 1 auto;
|
|
5077
|
+
min-width: 0;
|
|
5078
|
+
font-size: 13px;
|
|
5079
|
+
color: var(--fe-text-muted);
|
|
5080
|
+
}
|
|
5081
|
+
.fe-inspector__hint {
|
|
5082
|
+
margin: 6px 0 0;
|
|
5083
|
+
font-size: 12px;
|
|
5084
|
+
color: var(--fe-text-muted);
|
|
5085
|
+
}
|
|
5086
|
+
|
|
5087
|
+
/* ── narrow ───────────────────────────────────────────────────────────── */
|
|
5088
|
+
/* At 390px the chip is the only thing that has to go: the shortcut it prints
|
|
5089
|
+
cannot be typed on a phone anyway, and the palette stays reachable from the
|
|
5090
|
+
"⋯" menu. The field keeps the whole row. */
|
|
5091
|
+
.fe--narrow .fe-drivesearch__kbd {
|
|
5092
|
+
display: none;
|
|
5093
|
+
}
|
|
5094
|
+
.fe--narrow .fe-drivesearch {
|
|
5095
|
+
height: 36px;
|
|
5096
|
+
max-width: none;
|
|
5097
|
+
}
|
|
5098
|
+
.fe--narrow .fe-subhead__actions {
|
|
5099
|
+
padding-right: 4px;
|
|
5100
|
+
}
|
|
5101
|
+
.fe--narrow .fe-filterbar {
|
|
5102
|
+
padding: 6px 8px;
|
|
5103
|
+
}
|
|
5104
|
+
/* The narrow DRIVE header is three items: toggle · field · "⋯". The field is
|
|
5105
|
+
the only one that grows, and `min-width: 0` is what lets it shrink past its
|
|
5106
|
+
own content width — without it the row is wider than the phone. */
|
|
5107
|
+
.fe-toolbar--narrow.fe-toolbar--drive {
|
|
5108
|
+
gap: 6px;
|
|
5109
|
+
padding: 6px 8px;
|
|
5110
|
+
}
|
|
5111
|
+
.fe-toolbar--narrow .fe-drivesearch {
|
|
5112
|
+
flex: 1 1 auto;
|
|
5113
|
+
min-width: 0;
|
|
5114
|
+
}
|
|
5115
|
+
/* === /surucu:d1 === */
|
|
@@ -277,6 +277,22 @@ export interface ExplorerConfig {
|
|
|
277
277
|
* navigation panel starts expanded, the tab strip
|
|
278
278
|
* and split pane are off, the gallery view mode and
|
|
279
279
|
* the host's "How to connect" surface are hidden.
|
|
280
|
+
* 'drive' — everything `simple` does, plus the shell an end
|
|
281
|
+
* user already knows: one primary "New" menu, one
|
|
282
|
+
* search field in the header (with its ⌘K/Ctrl+K
|
|
283
|
+
* escalation into the command palette), a filter row
|
|
284
|
+
* under the breadcrumb, Folders and Files as
|
|
285
|
+
* labelled sections in grid view, the details panel
|
|
286
|
+
* split into Details / Activity, and a storage line
|
|
287
|
+
* under the navigation.
|
|
288
|
+
*
|
|
289
|
+
* ⚠ `drive` is a SUPERSET of `simple`, not a sibling: everything `simple`
|
|
290
|
+
* turns off stays off, and the code asks `simpleUi` for those questions so a
|
|
291
|
+
* later change to `simple` cannot silently miss `drive`. It is a third value
|
|
292
|
+
* rather than a second boolean because "which chrome" is ONE question with
|
|
293
|
+
* three answers — a `driveShell: true` next to `uiProfile: 'standard'` would
|
|
294
|
+
* be a combination nobody can describe, and keeping that question single is
|
|
295
|
+
* why `uiProfile` was a preset to begin with.
|
|
280
296
|
*
|
|
281
297
|
* Why it exists (GitHub #14): the reporter's users are not in IT and read
|
|
282
298
|
* split panes, tabs and mount instructions as a file manager they would have
|
|
@@ -288,7 +304,7 @@ export interface ExplorerConfig {
|
|
|
288
304
|
* rest of the chrome differ. A viewer's own collapse choice, once made,
|
|
289
305
|
* outranks the profile — it is a per-viewer preference, not a policy.
|
|
290
306
|
*/
|
|
291
|
-
uiProfile?: 'standard' | 'simple';
|
|
307
|
+
uiProfile?: 'standard' | 'simple' | 'drive';
|
|
292
308
|
|
|
293
309
|
/**
|
|
294
310
|
* Render the navigation panel (Upload · Recent / Starred / Shared with me /
|