@brftech/filex-core 0.30.1 → 0.31.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.
@@ -542,6 +542,11 @@ filex-explorer {
542
542
  opacity: 0.7;
543
543
  }
544
544
  .fe-grid__thumb {
545
+ /* yildiz:s1 — positioning context for the star chip in the corner. The
546
+ gallery's thumb already had one; this one did not, so an absolutely
547
+ placed chip would have resolved against whatever ancestor happened to be
548
+ positioned (the grid, or the explorer root) and landed in the wrong tile. */
549
+ position: relative;
545
550
  display: flex;
546
551
  align-items: center;
547
552
  justify-content: center;
@@ -3836,6 +3841,153 @@ filex-explorer {
3836
3841
  .fe-e2e-optlink:hover {
3837
3842
  text-decoration: underline;
3838
3843
  }
3844
+
3845
+ /* --- wiring:e2 recovery ---------------------------------------------
3846
+ Unscoped and fe-prefixed on purpose: a scoped <style> in an SFC does not
3847
+ survive the web-component build, so every class a core component uses has
3848
+ to live here. */
3849
+
3850
+ /* The shown-once key. Monospace, big, wrapping — it exists to be read off
3851
+ the screen and typed somewhere else, so legibility beats compactness. */
3852
+ .fe-e2e-rk {
3853
+ display: flex;
3854
+ flex-direction: column;
3855
+ gap: 12px;
3856
+ }
3857
+ .fe-e2e-rk__lead {
3858
+ margin: 0;
3859
+ font-size: 13px;
3860
+ color: var(--fe-text-muted);
3861
+ }
3862
+ .fe-e2e-rk__key {
3863
+ display: block;
3864
+ padding: 12px 14px;
3865
+ border: 1px solid var(--fe-border);
3866
+ border-radius: var(--fe-radius-sm);
3867
+ background: var(--fe-bg-elev);
3868
+ color: var(--fe-text);
3869
+ font-family: var(--fe-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
3870
+ font-size: 15px;
3871
+ letter-spacing: 0.04em;
3872
+ line-height: 1.7;
3873
+ word-break: break-all;
3874
+ user-select: all;
3875
+ text-align: center;
3876
+ }
3877
+ .fe-e2e-rk__actions {
3878
+ display: flex;
3879
+ gap: 8px;
3880
+ }
3881
+ .fe-e2e-rk__escrow {
3882
+ border: 1px solid var(--fe-border);
3883
+ border-left: 3px solid var(--fe-warning, var(--fe-primary));
3884
+ border-radius: var(--fe-radius-sm);
3885
+ padding: 8px 10px;
3886
+ font-size: 12.5px;
3887
+ }
3888
+ .fe-e2e-rk__escrow strong {
3889
+ display: block;
3890
+ margin-bottom: 2px;
3891
+ color: var(--fe-text);
3892
+ }
3893
+ .fe-e2e-rk__escrow p {
3894
+ margin: 0;
3895
+ color: var(--fe-text-muted);
3896
+ }
3897
+ .fe-e2e-rk__kid code {
3898
+ font-size: 11.5px;
3899
+ }
3900
+
3901
+ /* Unlock-without-the-password dialog. */
3902
+ .fe-e2e-recover {
3903
+ display: flex;
3904
+ flex-direction: column;
3905
+ gap: 12px;
3906
+ }
3907
+ .fe-e2e-recover__none {
3908
+ margin: 0;
3909
+ font-size: 13px;
3910
+ color: var(--fe-text-muted);
3911
+ }
3912
+ .fe-e2e-recover__hint {
3913
+ margin: 0;
3914
+ font-size: 12.5px;
3915
+ color: var(--fe-text-muted);
3916
+ }
3917
+ .fe-e2e-recover__tabs {
3918
+ display: flex;
3919
+ gap: 4px;
3920
+ padding: 3px;
3921
+ border: 1px solid var(--fe-border);
3922
+ border-radius: var(--fe-radius-lg);
3923
+ background: var(--fe-bg-elev);
3924
+ }
3925
+ .fe-e2e-recover__tab {
3926
+ flex: 1;
3927
+ padding: 6px 10px;
3928
+ border: 0;
3929
+ border-radius: calc(var(--fe-radius-lg) - 4px);
3930
+ background: transparent;
3931
+ color: var(--fe-text-muted);
3932
+ font: inherit;
3933
+ font-size: 12.5px;
3934
+ cursor: pointer;
3935
+ }
3936
+ .fe-e2e-recover__tab--on {
3937
+ background: var(--fe-bg);
3938
+ color: var(--fe-text);
3939
+ font-weight: 600;
3940
+ }
3941
+ .fe-e2e-recover__key {
3942
+ font-family: var(--fe-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
3943
+ letter-spacing: 0.04em;
3944
+ text-align: center;
3945
+ }
3946
+ .fe-e2e-recover__escrow {
3947
+ font-family: var(--fe-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
3948
+ font-size: 11.5px;
3949
+ resize: vertical;
3950
+ min-height: 90px;
3951
+ }
3952
+
3953
+ /* The offer to give a pre-recovery folder a recovery key. A strip, not a
3954
+ modal: it must be visible and dismissible, never something that blocks
3955
+ the folder the user came here to open. */
3956
+ .fe-e2e-upgrade {
3957
+ display: flex;
3958
+ align-items: flex-start;
3959
+ justify-content: space-between;
3960
+ gap: 14px;
3961
+ flex-wrap: wrap;
3962
+ margin: 0 0 10px;
3963
+ padding: 10px 12px;
3964
+ border: 1px solid var(--fe-border);
3965
+ border-left: 3px solid var(--fe-primary);
3966
+ border-radius: var(--fe-radius-sm);
3967
+ background: color-mix(in srgb, var(--fe-primary) 6%, transparent);
3968
+ }
3969
+ .fe-e2e-upgrade__text {
3970
+ flex: 1 1 320px;
3971
+ min-width: 0;
3972
+ }
3973
+ .fe-e2e-upgrade__text strong {
3974
+ display: block;
3975
+ font-size: 13px;
3976
+ color: var(--fe-text);
3977
+ }
3978
+ .fe-e2e-upgrade__text p {
3979
+ margin: 4px 0 0;
3980
+ font-size: 12.5px;
3981
+ color: var(--fe-text-muted);
3982
+ }
3983
+ .fe-e2e-upgrade__escrow {
3984
+ color: var(--fe-danger) !important;
3985
+ }
3986
+ .fe-e2e-upgrade__actions {
3987
+ display: flex;
3988
+ gap: 8px;
3989
+ flex: none;
3990
+ }
3839
3991
  /* === /wiring:e2 === */
3840
3992
 
3841
3993
  /* === ui-fix — toolbar tek satır + katlama + ölçüm şeridi ============= */
@@ -4220,6 +4372,106 @@ filex-explorer {
4220
4372
  letter-spacing: 0.02em;
4221
4373
  }
4222
4374
 
4375
+ /* === etiket:t1 — the Tags section ==================================== */
4376
+ /* A tag row is an ordinary panel item; only its label wraps differently,
4377
+ because a tag is user text and can be long. */
4378
+ .fe-sidenav__item--tag .fe-sidenav__text {
4379
+ text-transform: none;
4380
+ }
4381
+ /* "Show all (12)" / "Show fewer" — a link, not a button: it changes what this
4382
+ section shows, it is not a destination. */
4383
+ .fe-sidenav__more {
4384
+ display: block;
4385
+ width: 100%;
4386
+ margin: 2px 0 0;
4387
+ padding: 5px 10px;
4388
+ border: 0;
4389
+ border-radius: 999px;
4390
+ background: transparent;
4391
+ color: var(--fe-primary);
4392
+ font: inherit;
4393
+ font-size: 12.5px;
4394
+ text-align: left;
4395
+ cursor: pointer;
4396
+ }
4397
+ .fe-sidenav__more:hover {
4398
+ background: var(--fe-bg-hover);
4399
+ }
4400
+ .fe-sidenav__more:focus-visible {
4401
+ outline: 2px solid var(--fe-primary);
4402
+ outline-offset: 1px;
4403
+ }
4404
+ /* "No tags yet — add them from a file's menu." */
4405
+ .fe-sidenav__hint {
4406
+ margin: 2px 0 4px;
4407
+ padding: 0 10px;
4408
+ font-size: 11.5px;
4409
+ line-height: 1.45;
4410
+ color: var(--fe-text-muted);
4411
+ }
4412
+
4413
+ /* === yildiz:s1 — the star chip on a grid / gallery tile =============== */
4414
+ /* Positioned by the card, painted by StarButton (.filex-star-btn.is-card).
4415
+ ⚠ The rules live HERE and not in the SFC: the panel's styles do too, and
4416
+ these are about the CARD's geometry, which is this file's business. */
4417
+ .fe-grid__star,
4418
+ .fe-gal__star {
4419
+ position: absolute;
4420
+ top: 6px;
4421
+ right: 6px;
4422
+ z-index: 2;
4423
+ /* Hidden until the tile is hovered or something inside it is focused —
4424
+ EXCEPT when the file is starred, which is the whole point: the mark has
4425
+ to be visible without hovering every tile to find it (see .is-starred
4426
+ below). */
4427
+ opacity: 0;
4428
+ transition: opacity 120ms ease;
4429
+ }
4430
+ .fe-grid__card:hover .fe-grid__star,
4431
+ .fe-grid__card:focus-within .fe-grid__star,
4432
+ .fe-gal__card:hover .fe-gal__star,
4433
+ .fe-gal__card:focus-within .fe-gal__star,
4434
+ .fe-grid__star:focus-within,
4435
+ .fe-gal__star:focus-within,
4436
+ .fe-grid__star:has(.is-starred),
4437
+ .fe-gal__star:has(.is-starred) {
4438
+ opacity: 1;
4439
+ }
4440
+ /* `:has()` is the modern half of the rule above; this is the same thing for
4441
+ engines without it — a starred chip paints itself. Both are needed: the
4442
+ parent wrapper carries the opacity, the child cannot raise it on its own. */
4443
+ .filex-star-btn.is-card.is-starred {
4444
+ opacity: 1;
4445
+ }
4446
+ /* The chip itself: a round translucent puck, legible over a photo thumbnail
4447
+ as well as over a flat icon tile. */
4448
+ .filex-star-btn.is-card {
4449
+ width: 26px;
4450
+ height: 26px;
4451
+ padding: 0;
4452
+ justify-content: center;
4453
+ border-radius: 999px;
4454
+ background: rgba(17, 24, 39, 0.55);
4455
+ color: #fff;
4456
+ backdrop-filter: blur(2px);
4457
+ }
4458
+ .filex-star-btn.is-card:hover {
4459
+ background: rgba(17, 24, 39, 0.78);
4460
+ color: var(--filex-accent-amber, #f59e0b);
4461
+ }
4462
+ .filex-star-btn.is-card.is-starred {
4463
+ color: var(--filex-accent-amber, #f59e0b);
4464
+ }
4465
+ .filex-star-btn.is-card:focus-visible {
4466
+ outline: 2px solid var(--fe-primary);
4467
+ outline-offset: 1px;
4468
+ }
4469
+ /* The gallery's meta strip covers the tile's foot on hover and is
4470
+ aria-hidden; the star sits above it and stays clickable. */
4471
+ .fe-gal__star {
4472
+ z-index: 3;
4473
+ }
4474
+
4223
4475
  /* Narrow (<560px): a drawer over the listing, never a column. At 390px a
4224
4476
  232px column leaves 158px for the files, and a 56px rail still takes an
4225
4477
  eighth of the screen from a listing that is already tight. */
@@ -77,6 +77,9 @@ export interface EndpointMap {
77
77
  restore: string | null;
78
78
  trashList: string | null;
79
79
  trashRestore: string | null;
80
+ /* wiring:e2 */
81
+ e2eEscrowChallenge: string | null;
82
+ e2eEscrowUsed: string | null;
80
83
  }
81
84
 
82
85
  export interface ExplorerConfig {
@@ -157,6 +160,12 @@ export interface ExplorerConfig {
157
160
  /** filex trash restore endpoint — `POST { node_id }`. */
158
161
  trashRestore?: string;
159
162
 
163
+ /* wiring:e2 */
164
+ /** E2E escrow proof-of-possession — `POST { path } → { id, challenge }`. */
165
+ e2eEscrowChallenge?: string;
166
+ /** E2E escrow use report — `POST { path, id, nonce }`. */
167
+ e2eEscrowUsed?: string;
168
+
160
169
  /** Public share base URL — `${shareBase}/${uuid}` */
161
170
  shareBase?: string;
162
171
 
@@ -318,12 +327,39 @@ export interface ExplorerConfig {
318
327
  * the bug this became: for a year the sole place to mint the token the FTPS
319
328
  * guide told you to use was the admin panel.
320
329
  *
330
+ * ⚠ Role is not the same question as `callerKind`. "API keys" IS hidden for
331
+ * an app token — not because of what that caller may do, but because there is
332
+ * no single person behind it whose keys they would be. See `callerKind`.
333
+ *
321
334
  * ⚠ The entries live in the panel, so `sideNav: false` takes them with it.
322
335
  * Hosts that want the surface without the panel mount `<filex-connections>`
323
336
  * (or `ConnectionsPanel`) on a page of their own.
324
337
  */
325
338
  connections?: boolean;
326
339
 
340
+ /**
341
+ * Who is behind this explorer — a person, or an integration?
342
+ *
343
+ * `'user'` (a signed-in human, or their own API token) draws everything.
344
+ * `'app'` suppresses the surfaces that only mean something for ONE person:
345
+ * **API keys**, **Recent**, **Starred** and **Shared with me**. Upload, the
346
+ * storage list, Trash and "How to connect" stay — an embed's users still
347
+ * upload files and still need mount instructions.
348
+ *
349
+ * Why it exists: a filex API token authenticates AS its owner, and the embeds
350
+ * we run authenticate every visitor with ONE shared token injected by the
351
+ * host's proxy. v0.30.0 put "API keys" in the panel, so under that token an
352
+ * embed visitor could list and revoke the credential the embed itself runs
353
+ * on — and "your Recent" meant the token owner's history shown to a stranger.
354
+ *
355
+ * Default: read from `GET /api/files/capabilities` (`caller_kind`), which is
356
+ * authoritative because only the server knows the token's kind (migration
357
+ * 00030). Set this only to answer BEFORE that request lands — a host that
358
+ * already knows it proxies with an app token spares its users the flash of a
359
+ * Starred row that then disappears. A value here wins over the server's.
360
+ */
361
+ callerKind?: 'user' | 'app';
362
+
327
363
 
328
364
  /** Default view. */
329
365
  viewMode?: 'list' | 'grid';
@@ -88,11 +88,34 @@ export interface Capabilities {
88
88
  /** Longest life a new share link may be given, in days (0 = no ceiling).
89
89
  * Read by the share dialogs so they offer only expiries the server keeps. */
90
90
  share_max_ttl_days?: number;
91
+ /** Is the caller a person (`'user'` — a session OR their own API token) or an
92
+ * integration (`'app'` — a host proxy, a bot, an MCP client)? The explorer
93
+ * reads it to decide whether to draw the identity-bearing surfaces; see
94
+ * ExplorerConfig.callerKind. Absent on a server older than the app/user
95
+ * token split, which is why every reader treats "missing" as a person. */
96
+ caller_kind?: 'user' | 'app';
91
97
  external?: {
92
98
  onlyoffice?: ExternalServiceStatus;
93
99
  drawio?: ExternalServiceStatus;
94
100
  mermaid?: ExternalServiceStatus;
95
101
  };
102
+ /* wiring:e2 */
103
+ /** Whether this installation holds an escrow key for E2E-encrypted folders,
104
+ * and the public half the browser wraps new folders' master keys to.
105
+ *
106
+ * Published on purpose. Escrow means the operator can open the folders you
107
+ * create here without your password, and someone about to create one is
108
+ * entitled to know that BEFORE they create it. Fixed at install time
109
+ * (FILEX_INSTALLATION_E2E_ESCROW_KEY), so this answer never changes for a
110
+ * running installation. */
111
+ e2e_escrow?: {
112
+ enabled: boolean;
113
+ /** Short id of the escrow key (SHA-256(SPKI)[:8], hex). */
114
+ kid?: string;
115
+ alg?: string;
116
+ /** Base64 SPKI. Public material — it can only seal, never open. */
117
+ public_key?: string;
118
+ };
96
119
  }
97
120
 
98
121
  /** Single source of truth for "is the IdP/editor/diagram service ready?".