diff --git a/ROADMAP.md b/ROADMAP.md
index 859a1e1a..6f6f187a 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -208,6 +208,7 @@ Order = user-visible value first; each ships with backend + UI + tests.
- [x] `desloppify` pass — 73 review items dispositioned (46 fixed, 27 honestly skipped), strict score 77.1 (was 18.9); scorecard lives in README. `security-reviewer` pass done incrementally per batch (OAuth secrets, SSRF, process exec, path safety)
- [x] Tag management page (upstream #2962) — owner-scoped `tag:` metadata stats/rename/recolor/delete in `inventory.FileClient`, `GET/PATCH/DELETE /file/tag` routes, Settings → Tags tab with merge-on-rename semantics
- [x] Private space / vault (upstream #3447) — opt-in root folder flagged `sys:vault`; ancestry-based membership (zero flag maintenance; chain-less search results resolved lazily via `file_children`); `vaultNavigator` decorator gating `To`/`Children`/`Walk`/`ExecuteHook`; separate vault password (`salt:sha256`, sensitive) + 30-min cache-backed unlock session, unlock rate-limited 10/h; vault content never shareable and never direct-linkable; search filtered while locked; `vault_enabled`/`vault_unlocked` in user settings; unlock prompt in `ExplorerError`, Private space section in security settings, lock badge on vault folder (#195)
+- [x] Share `hide_readme` option (upstream #2729 item 6) — `ShareProps.HideReadMe` (only meaningful with `ShowReadMe`); share navigator filters `README.md`/`README.txt` (case-insensitive) from listings while direct-path resolution stays open for the readme viewer; `detectReadMe` URI fallback now probes unconditionally; owner-only `hide_readme` on share responses; Share dialog nested checkbox
## 6. Phase D — desktop, all platforms
diff --git a/frontend/public/locales/en-US/application.json b/frontend/public/locales/en-US/application.json
index f7c897e5..e779c6e3 100644
--- a/frontend/public/locales/en-US/application.json
+++ b/frontend/public/locales/en-US/application.json
@@ -688,7 +688,9 @@
"deleteViewSetting": "Delete view setting",
"shareTargets": "Share targets",
"shareTargetsCount_one": "{{count}} file selected",
- "shareTargetsCount_other": "{{count}} files selected"
+ "shareTargetsCount_other": "{{count}} files selected",
+ "hideReadme": "Hide readme file",
+ "hideReadmeDes": "Keep the README file itself out of the share listing. Visitors still see the rendered readme."
},
"uploader": {
"fileCopyName": "Copy of ",
diff --git a/frontend/public/locales/zh-CN/application.json b/frontend/public/locales/zh-CN/application.json
index 254f8424..95bef40f 100644
--- a/frontend/public/locales/zh-CN/application.json
+++ b/frontend/public/locales/zh-CN/application.json
@@ -688,7 +688,9 @@
"deleteViewSetting": "删除视图设置",
"shareTargets": "分享对象",
"shareTargetsCount_one": "已选择 {{count}} 个项目",
- "shareTargetsCount_other": "已选择 {{count}} 个项目"
+ "shareTargetsCount_other": "已选择 {{count}} 个项目",
+ "hideReadme": "隐藏 README 文件",
+ "hideReadmeDes": "在分享文件列表中隐藏 README 文件本身,访客仍可看到渲染后的说明内容。"
},
"uploader": {
"fileCopyName": "副本_",
diff --git a/frontend/src/api/explorer.ts b/frontend/src/api/explorer.ts
index 19bfa0a6..07dedd6c 100644
--- a/frontend/src/api/explorer.ts
+++ b/frontend/src/api/explorer.ts
@@ -90,6 +90,7 @@ export interface Share {
source_uri?: string;
password?: string;
show_readme?: boolean;
+ hide_readme?: boolean;
allow_upload?: boolean;
allow_edit?: boolean;
preview_only?: boolean;
@@ -360,6 +361,7 @@ export interface ShareCreateService {
expire?: number;
share_view?: boolean;
show_readme?: boolean;
+ hide_readme?: boolean;
allow_upload?: boolean;
allow_edit?: boolean;
preview_only?: boolean;
diff --git a/frontend/src/component/FileManager/Dialogs/Share/ShareDialog.tsx b/frontend/src/component/FileManager/Dialogs/Share/ShareDialog.tsx
index cc48642a..d20fcded 100644
--- a/frontend/src/component/FileManager/Dialogs/Share/ShareDialog.tsx
+++ b/frontend/src/component/FileManager/Dialogs/Share/ShareDialog.tsx
@@ -61,6 +61,7 @@ const shareToSetting = (share: ShareModel, t: TFunction): ShareSetting => {
use_custom_password: true,
share_view: share.share_view,
show_readme: share.show_readme,
+ hide_readme: share.hide_readme,
allow_upload: share.allow_upload,
allow_edit: share.allow_edit,
preview_only: share.preview_only,
diff --git a/frontend/src/component/FileManager/Dialogs/Share/ShareSetting.tsx b/frontend/src/component/FileManager/Dialogs/Share/ShareSetting.tsx
index 3dba2788..2b0ed860 100644
--- a/frontend/src/component/FileManager/Dialogs/Share/ShareSetting.tsx
+++ b/frontend/src/component/FileManager/Dialogs/Share/ShareSetting.tsx
@@ -85,6 +85,7 @@ export interface ShareSetting {
password?: string;
share_view?: boolean;
show_readme?: boolean;
+ hide_readme?: boolean;
allow_upload?: boolean;
allow_edit?: boolean;
preview_only?: boolean;
@@ -399,6 +400,16 @@ const ShareSettingContent = ({ setting, file, editing, onSettingChange }: ShareS
]} />
+
+
+
+ onSettingChange({ ...setting, hide_readme: !setting.hide_readme })}
+ />
+
+
>
diff --git a/frontend/src/redux/thunks/share.ts b/frontend/src/redux/thunks/share.ts
index 65a28106..281bb465 100644
--- a/frontend/src/redux/thunks/share.ts
+++ b/frontend/src/redux/thunks/share.ts
@@ -1,10 +1,9 @@
import i18next from "i18next";
import { closeSnackbar, enqueueSnackbar, SnackbarKey } from "notistack";
-import { getFileInfo, getFileList, getShareInfo, sendCreateShare, sendUpdateShare } from "../../api/api.ts";
+import { getFileInfo, getShareInfo, sendCreateShare, sendUpdateShare } from "../../api/api.ts";
import { FileResponse, Share, ShareCreateService } from "../../api/explorer.ts";
import { DefaultCloseAction, OpenReadMeAction } from "../../component/Common/Snackbar/snackbar.tsx";
import { ShareSetting } from "../../component/FileManager/Dialogs/Share/ShareSetting.tsx";
-import { getPaginationState } from "../../component/FileManager/Pagination/PaginationFooter.tsx";
import CrUri from "../../util/uri.ts";
import { fileUpdated } from "../fileManagerSlice.ts";
import {
@@ -32,6 +31,7 @@ export function createOrUpdateShareLink(
password: setting.password,
share_view: setting.share_view,
show_readme: setting.show_readme,
+ hide_readme: setting.show_readme ? setting.hide_readme : false,
allow_upload: setting.allow_upload || setting.allow_edit,
allow_edit: setting.allow_edit,
preview_only: setting.preview_only,
@@ -147,7 +147,7 @@ const supportedReadMeFiles = ["README.md", "README.txt"];
export function detectReadMe(index: number, isTablet: boolean): AppThunk> {
return async (dispatch, getState) => {
- const { files: list, pagination } = getState().fileManager[index]?.list ?? {};
+ const { files: list } = getState().fileManager[index]?.list ?? {};
if (list) {
// Find readme file from highest to lowest priority
for (const readmeFile of supportedReadMeFiles) {
@@ -159,10 +159,11 @@ export function detectReadMe(index: number, isTablet: boolean): AppThunk