diff --git a/src/apps/desktop/src/lib.rs b/src/apps/desktop/src/lib.rs index 81534d63bf..809c9e6e33 100644 --- a/src/apps/desktop/src/lib.rs +++ b/src/apps/desktop/src/lib.rs @@ -396,7 +396,7 @@ pub async fn _run() { { let candidates = ["mobile-web/dist", "mobile-web", "dist"]; let mut found = false; - let path = PathBuf::from("/data/storage/el2/base/files/dist"); + let path = PathBuf::from("/data/storage/el1/bundle/entry/resources/resfile/dist"); if path.join("index.html").exists() { log::info!("Found bundled mobile-web at: {}", path.display()); api::remote_connect_api::set_mobile_web_resource_path(path); diff --git a/src/apps/vcoder/AppScope/app.json5 b/src/apps/vcoder/AppScope/app.json5 index 7b14e5ef5e..e70e697036 100644 --- a/src/apps/vcoder/AppScope/app.json5 +++ b/src/apps/vcoder/AppScope/app.json5 @@ -5,7 +5,7 @@ "versionCode": 1000000, "versionName": "1.0.0", "buildVersion": "1", - "icon": "$media:layered_image", + "icon": "$media:bitfun_icon", "label": "$string:app_name" } } diff --git a/src/apps/vcoder/entry/src/main/ets/entryability/EntryAbility.ets b/src/apps/vcoder/entry/src/main/ets/entryability/EntryAbility.ets index 15acc7539c..a0ab8497ed 100644 --- a/src/apps/vcoder/entry/src/main/ets/entryability/EntryAbility.ets +++ b/src/apps/vcoder/entry/src/main/ets/entryability/EntryAbility.ets @@ -207,7 +207,7 @@ export default class EntryAbility extends RustAbility { let shareData: systemShare.SharedData = new systemShare.SharedData({ utd: uniformTypeDescriptor.UniformDataType.HYPERLINK, content, - title: "Bitfun", + title: "BitFun", description: "Phone", }); sharableTable.share(shareData) diff --git a/src/apps/vcoder/entry/src/main/module.json5 b/src/apps/vcoder/entry/src/main/module.json5 index d0827d935e..5d753164ec 100644 --- a/src/apps/vcoder/entry/src/main/module.json5 +++ b/src/apps/vcoder/entry/src/main/module.json5 @@ -16,7 +16,7 @@ "name": "EntryAbility", "srcEntry": "./ets/entryability/EntryAbility.ets", "description": "$string:EntryAbility_desc", - "icon": "$media:layered_image", + "icon": "$media:bitfun_icon", "label": "$string:EntryAbility_label", "startWindowIcon": "$media:startIcon", "startWindowBackground": "$color:start_window_background", diff --git a/src/apps/vcoder/entry/src/main/resources/base/element/string.json b/src/apps/vcoder/entry/src/main/resources/base/element/string.json index fafc54ff3a..fc8fbb64e7 100644 --- a/src/apps/vcoder/entry/src/main/resources/base/element/string.json +++ b/src/apps/vcoder/entry/src/main/resources/base/element/string.json @@ -9,7 +9,7 @@ }, { "name": "EntryAbility_label", - "value": "Bitfun" + "value": "BitFun" } ] } \ No newline at end of file diff --git a/src/apps/vcoder/entry/src/main/resources/base/media/bitfun_icon.png b/src/apps/vcoder/entry/src/main/resources/base/media/bitfun_icon.png new file mode 100644 index 0000000000..29f7221d00 Binary files /dev/null and b/src/apps/vcoder/entry/src/main/resources/base/media/bitfun_icon.png differ diff --git a/src/web-ui/src/app/components/AboutDialog/AboutDialog.scss b/src/web-ui/src/app/components/AboutDialog/AboutDialog.scss index e5ba8e5ceb..622b15f16e 100644 --- a/src/web-ui/src/app/components/AboutDialog/AboutDialog.scss +++ b/src/web-ui/src/app/components/AboutDialog/AboutDialog.scss @@ -640,50 +640,42 @@ } } -// ==================== User agreement card sections ==================== +// ==================== Privacy document layout ==================== -.bitfun-about-dialog__sub-card { - display: flex; - flex-direction: column; - gap: 8px; - padding: 14px 16px; - background: linear-gradient(135deg, - rgba(255, 255, 255, 0.03) 0%, - rgba(255, 255, 255, 0.01) 100% - ); - border: 1px solid var(--border-subtle); - border-left: 2px solid var(--color-accent-400); - border-radius: 8px; - transition: all 0.2s ease; +.bitfun-about-dialog__privacy-doc { + padding: 24px 28px 28px; + max-height: min(520px, calc(100vh - 200px)); + overflow-y: auto; + line-height: 1.8; +} - &:hover { - background: linear-gradient(135deg, - rgba(59, 130, 246, 0.06) 0%, - rgba(139, 92, 246, 0.03) 100% - ); - border-color: rgba(59, 130, 246, 0.2); - border-left-color: var(--color-accent-500); - box-shadow: - 0 4px 12px rgba(96, 165, 250, 0.08), - 0 2px 4px rgba(0, 0, 0, 0.08); - } +.bitfun-about-dialog__privacy-title { + font-size: 16px; + font-weight: 700; + color: var(--color-text-primary); + margin: 0 0 16px 0; + line-height: 1.4; + text-align: center; } -.bitfun-about-dialog__sub-card-heading { +.bitfun-about-dialog__privacy-section { font-size: 13px; font-weight: 600; color: var(--color-text-primary); - margin: 0; - display: flex; - align-items: center; - gap: 8px; + margin: 16px 0 6px 0; + line-height: 1.5; + + &:first-of-type { + margin-top: 0; + } } -.bitfun-about-dialog__sub-card-text { +.bitfun-about-dialog__privacy-text { font-size: 12px; color: var(--color-text-secondary); - line-height: 1.7; - margin: 0; + margin: 0 0 6px 0; + line-height: 1.8; + text-align: justify; } .bitfun-about-dialog__sub-footnote { diff --git a/src/web-ui/src/app/components/AboutDialog/AboutDialog.tsx b/src/web-ui/src/app/components/AboutDialog/AboutDialog.tsx index d4b74f63ac..c820a324d2 100644 --- a/src/web-ui/src/app/components/AboutDialog/AboutDialog.tsx +++ b/src/web-ui/src/app/components/AboutDialog/AboutDialog.tsx @@ -405,7 +405,7 @@ export const AboutDialog: React.FC = ({
-

Frontend

+

{t('about.openSourceFrontend')}

{dependencies.filter(d => d.category === 'frontend').length} @@ -429,7 +429,7 @@ export const AboutDialog: React.FC = ({
- FE + {t('about.openSourceTagFE')}
))} @@ -440,7 +440,7 @@ export const AboutDialog: React.FC = ({
-

Backend

+

{t('about.openSourceBackend')}

{dependencies.filter(d => d.category === 'backend').length} @@ -464,7 +464,7 @@ export const AboutDialog: React.FC = ({
- BE + {t('about.openSourceTagBE')}
))} @@ -478,53 +478,46 @@ export const AboutDialog: React.FC = ({
- {/* User Agreement dialog */} - setSubDialog(null)} - title={t('about.userAgreement')} - showCloseButton={true} - size="medium" - > -
-
-

- 1. 服务使用 -

-

- 用户在使用 BitFun 服务时应遵守相关法律法规。本软件仅供合法用途使用,不得用于任何非法或未经授权的活动。 -

-
-
-

- 2. 免责声明 -

-

- 本软件按"现状"提供,不提供任何明示或暗示的保证。在适用法律允许的最大范围内,开发者不承担任何损害赔偿的责任。使用风险由用户自行承担。 -

-
-
-

- 3. 隐私政策 -

-

- 我们重视你的隐私。本软件可能会收集必要的使用数据以改善服务质量。详细的隐私政策请参阅官方网站。 -

-
-

- 完整协议内容将在后续版本中完善。 -

-
-
+ {/* Privacy Agreement dialog */} + setSubDialog(null)} + title={t('about.userAgreement')} + showCloseButton={true} + size="medium" + > +
+

{t('about.privacyTitle')}

+

{t('about.privacyIntro')}

+

{t('about.privacyCommitment')}

+ +

{t('about.privacyS1Title')}

+

{t('about.privacyS1P1')}

+

{t('about.privacyS1P2')}

+

{t('about.privacyS1P3')}

+ +

{t('about.privacyS2Title')}

+

{t('about.privacyS2P1')}

+ +

{t('about.privacyS3Title')}

+

{t('about.privacyS3P1')}

+

{t('about.privacyS3P2')}

+ +

{t('about.privacyS4Title')}

+

{t('about.privacyS4P1')}

+

{t('about.privacyS4P2')}

+

{t('about.privacyS4P3')}

+
+
- - + + ); }; diff --git a/src/web-ui/src/app/scenes/skills/SkillsScene.scss b/src/web-ui/src/app/scenes/skills/SkillsScene.scss index 47d3a42626..ab9574706a 100644 --- a/src/web-ui/src/app/scenes/skills/SkillsScene.scss +++ b/src/web-ui/src/app/scenes/skills/SkillsScene.scss @@ -1064,6 +1064,12 @@ &__detail-link { text-decoration: underline; text-underline-offset: 2px; + background: none; + border: none; + padding: 0; + cursor: pointer; + font: inherit; + text-align: left; } &__path-input { diff --git a/src/web-ui/src/app/scenes/skills/SkillsScene.tsx b/src/web-ui/src/app/scenes/skills/SkillsScene.tsx index 21de8d8945..e8121e619b 100644 --- a/src/web-ui/src/app/scenes/skills/SkillsScene.tsx +++ b/src/web-ui/src/app/scenes/skills/SkillsScene.tsx @@ -23,6 +23,7 @@ import { Badge, Button, ConfirmDialog, Input, Modal, Search, Select } from '@/co import { GalleryDetailModal } from '@/app/components'; import type { SkillInfo, SkillLevel, SkillMarketItem } from '@/infrastructure/config/types'; import { workspaceAPI } from '@/infrastructure/api'; +import { systemAPI } from '@/infrastructure/api'; import { workspaceManager } from '@/infrastructure/services/business/workspaceManager'; import { useNotification } from '@/shared/notification-system'; import { isRemoteWorkspace } from '@/shared/types'; @@ -38,6 +39,12 @@ import { useGallerySceneAutoRefresh } from '@/app/hooks/useGallerySceneAutoRefre const log = createLogger('SkillsScene'); +function formatDisplayPath(path: string): string { + return path.replace( + '/data/storage/el2/base/files/bitfun', + '/storage/Users/currentUser/appdata/el2/base/com.huawei.BitFun/files/bitfun' + ); +} type SkillTab = 'installed' | 'discover'; const INSTALLED_PAGE_SIZE = 12; @@ -667,12 +674,12 @@ const SkillsScene: React.FC = () => { type="button" className="bitfun-skills-scene__detail-path-btn" title={t('list.item.openPathInExplorer')} - onClick={() => void handleRevealSkillPath(selectedInstalledSkill.path)} + onClick={() => void handleRevealSkillPath(formatDisplayPath(selectedInstalledSkill.path))} > - {selectedInstalledSkill.path} + {formatDisplayPath(selectedInstalledSkill.path)} ) : ( - {selectedInstalledSkill.path} + {formatDisplayPath(selectedInstalledSkill.path)} )}
@@ -692,19 +699,18 @@ const SkillsScene: React.FC = () => { ) : null} - {selectedMarketSkill?.url ? ( -
- {t('market.detail.linkLabel')} - - {selectedMarketSkill.url} - -
- ) : null} + {selectedMarketSkill?.url ? ( +
+ {t('market.detail.linkLabel')} + +
+ ) : null} = ({ } }, [getDialogTurn]); - return ( - - - - ); + // Currently unused — kept for re-enable. + void (handleExport as unknown as React.MouseEventHandler); + void (isExporting as boolean); + void (isExportingRef as React.MutableRefObject); + void (className as string); + + return null; }; ExportImageButton.displayName = 'ExportImageButton'; diff --git a/src/web-ui/src/infrastructure/config/components/SkillsConfig.tsx b/src/web-ui/src/infrastructure/config/components/SkillsConfig.tsx index ef2d76f057..1d697269c3 100644 --- a/src/web-ui/src/infrastructure/config/components/SkillsConfig.tsx +++ b/src/web-ui/src/infrastructure/config/components/SkillsConfig.tsx @@ -15,6 +15,13 @@ import {workspaceAPI} from "@/infrastructure"; const log = createLogger('SkillsConfig'); +function formatDisplayPath(path: string): string { + return path.replace( + '/data/storage/el2/base/files/bitfun', + '/storage/Users/currentUser/appdata/el2/base/com.huawei.BitFun/files/bitfun' + ); +} + const SkillsConfig: React.FC = () => { const { t } = useTranslation('settings/skills'); const [showAddForm, setShowAddForm] = useState(false); @@ -299,7 +306,7 @@ const SkillsConfig: React.FC = () => {
{skill.description}
{t('list.item.pathLabel')} - {skill.path} + {formatDisplayPath(skill.path)}
); diff --git a/src/web-ui/src/locales/en-US/common.json b/src/web-ui/src/locales/en-US/common.json index 3c585d1f71..2c8431ba38 100644 --- a/src/web-ui/src/locales/en-US/common.json +++ b/src/web-ui/src/locales/en-US/common.json @@ -486,6 +486,32 @@ "commit": "Commit", "branch": "Branch", "copyright": "© 2025 BitFun. All rights reserved.", + "openSource": "Open Source Software", + "openSourceDesc": "BitFun uses the following open source components:", + "openSourceFootnote": "Click a component name to open its official page for more information.", + "userAgreement": "Privacy Agreement", + "openSourceFrontend": "Frontend", + "openSourceBackend": "Backend", + "openSourceTagFE": "FE", + "openSourceTagBE": "BE", + "privacyTitle": "Privacy Statement Regarding BitFun", + "privacyUpdated": "Updated: 2026.5.15", + "privacyIntro": "BitFun is provided by Hangzhou Huawei Enterprise Communication Technology Co., Ltd. (hereinafter referred to as \"we\") as an AI-assisted coding and development productivity application. This privacy statement is established by us to govern the processing of your personal information.", + "privacyCommitment": "We value your personal information and privacy protection, and will provide appropriate security measures for your personal information in accordance with legal requirements and industry-standard security practices.", + "privacyS1Title": "1. How We Collect and Use Your Personal Information", + "privacyS1P1": "We only use your personal information when there is a legal basis to do so. Under applicable law, we may process your personal information based on your consent, as necessary for performing or entering into a contract with you, or as required to comply with legal obligations.", + "privacyS1P2": "1.1 Based on legal obligations or other circumstances prescribed by laws and regulations, we may process the following personal information:", + "privacyS1P3": "To implement application functions, we need to collect your commonly used instructions after obtaining your consent.", + "privacyS2Title": "2. Managing Your Personal Information", + "privacyS2P1": "If you have further requirements regarding your data subject rights, or have any questions, comments, or suggestions, please contact us through the methods described in the \"How to Contact Us\" section of this statement to exercise your relevant rights.", + "privacyS3Title": "3. Storage Location and Duration", + "privacyS3P1": "3.1 We commit that, unless otherwise required by laws and regulations, the retention period for your information shall be the minimum period necessary to achieve the processing purpose.", + "privacyS3P2": "3.2 The above information will be transmitted and stored on the local server.", + "privacyS4Title": "4. How to Contact Us", + "privacyS4P1": "You may contact us through the following methods to exercise your relevant rights, and we will respond as soon as possible.", + "privacyS4P2": "Address: Building 1, No. 410 Jianghong Road, Changhe Street, Binjiang District, Hangzhou, Zhejiang Province, China", + "privacyS4P3": "If you are not satisfied with our response, especially if the processing of personal information infringes upon your legitimate rights and interests, you may also seek resolution through external channels such as filing a lawsuit with a competent court or filing a complaint with industry self-regulatory organizations or relevant government authorities. You may also inquire with us about potentially applicable complaint channels.", + "privacyEffective": "Effective: 2026.5.15", "updateSectionTitle": "App updates", "updateSectionHint": "Keep BitFun up to date for the latest fixes and features." }, diff --git a/src/web-ui/src/locales/zh-CN/common.json b/src/web-ui/src/locales/zh-CN/common.json index f7750cd390..cc84b80027 100644 --- a/src/web-ui/src/locales/zh-CN/common.json +++ b/src/web-ui/src/locales/zh-CN/common.json @@ -486,6 +486,32 @@ "commit": "提交", "branch": "分支", "copyright": "© 2025 BitFun. All rights reserved.", + "openSource": "开源软件", + "openSourceDesc": "BitFun 使用了以下开源组件:", + "openSourceFootnote": "点击组件名称可打开其官方页面获取更多信息。", + "userAgreement": "隐私协议", + "openSourceFrontend": "前端", + "openSourceBackend": "后端", + "openSourceTagFE": "前端", + "openSourceTagBE": "后端", + "privacyTitle": "关于 BitFun 与隐私的声明", + "privacyUpdated": "更新日期:2026.5.15", + "privacyIntro": "BitFun 是由 杭州华为企业通信技术有限公司 (以下简称\u201C我们\u201D)为您提供的,用于提供 AI 辅助编程与开发效率提升服务的应用。本隐私声明由我们为处理您的个人信息而制定。", + "privacyCommitment": "我们非常重视您的个人信息和隐私保护,将会按照法律要求和业界成熟的安全标准,为您的个人信息提供相应的安全保护措施。", + "privacyS1Title": "1. 我们如何收集和使用您的个人信息", + "privacyS1P1": "我们仅在有合法性基础的情形下才会使用您的个人信息。根据适用的法律,我们可能会基于您的同意、为履行/订立您与我们的合同所必需、履行法定义务所必需等合法性基础,使用您的个人信息。", + "privacyS1P2": "1.1 基于履行法定义务或其他法律法规规定的情形,我们可能会处理您的以下个人信息:", + "privacyS1P3": "为了实现应用功能,在获取您的同意后我们需要收集您的常用指令。", + "privacyS2Title": "2. 管理您的个人信息", + "privacyS2P1": "如您对您的数据主体权利有进一步要求或存在任何疑问、意见或建议,可通过本声明中\u201C如何联系我们\u201D章节中所述方式与我们取得联系,并行使您的相关权利。", + "privacyS3Title": "3. 信息存储地点及期限", + "privacyS3P1": "3.1 我们承诺,除法律法规另有规定外,我们对您的信息的保存期限应当为实现处理目的所必要的最短时间。", + "privacyS3P2": "3.2 上述信息将会传输并保存至本地的服务器。", + "privacyS4Title": "4. 如何联系我们", + "privacyS4P1": "您可通过以下方式联系我们,并行使您的相关权利,我们会尽快回复。", + "privacyS4P2": "公司地址:浙江省杭州市滨江区长河街道江虹路410号1号楼", + "privacyS4P3": "如果您对我们的回复不满意,特别是当个人信息处理行为损害了您的合法权益时,您还可以通过向有管辖权的人民法院提起诉讼、向行业自律协会或政府相关管理机构投诉等外部途径进行解决。您也可以向我们了解可能适用的相关投诉途径的信息。", + "privacyEffective": "生效日期:2026.5.15", "updateSectionTitle": "应用更新", "updateSectionHint": "保持 BitFun 为最新版本,以获取修复与新功能。" }, diff --git a/src/web-ui/src/locales/zh-TW/common.json b/src/web-ui/src/locales/zh-TW/common.json index 78b7a74447..d62f5ac649 100644 --- a/src/web-ui/src/locales/zh-TW/common.json +++ b/src/web-ui/src/locales/zh-TW/common.json @@ -486,6 +486,32 @@ "commit": "提交", "branch": "分支", "copyright": "© 2025 BitFun. All rights reserved.", + "openSource": "開源軟件", + "openSourceDesc": "BitFun 使用了以下開源組件:", + "openSourceFootnote": "點擊組件名稱可打開其官方頁面獲取更多資訊。", + "userAgreement": "隱私協議", + "openSourceFrontend": "前端", + "openSourceBackend": "後端", + "openSourceTagFE": "前端", + "openSourceTagBE": "後端", + "privacyTitle": "關於 BitFun 與隱私的聲明", + "privacyUpdated": "更新日期:2026.5.15", + "privacyIntro": "BitFun 是由 杭州華為企業通信技術有限公司 (以下簡稱\u201C我們\u201D)為您提供的,用於提供 AI 輔助程式設計與開發效率提升服務的應用。本隱私聲明由我們為處理您的個人資訊而制定。", + "privacyCommitment": "我們非常重視您的個人資訊和隱私保護,將會按照法律要求和業界成熟的安全標準,為您的個人資訊提供相應的安全保護措施。", + "privacyS1Title": "1. 我們如何收集和使用您的個人資訊", + "privacyS1P1": "我們僅在有合法性基礎的情形下才會使用您的個人資訊。根據適用的法律,我們可能會基於您的同意、為履行/訂立您與我們的合約所必需、履行法定義務所必需等合法性基礎,使用您的個人資訊。", + "privacyS1P2": "1.1 基於履行法定義務或其他法律法規規定的情形,我們可能會處理您的以下個人資訊:", + "privacyS1P3": "為了實現應用功能,在獲取您的同意後我們需要收集您的常用指令。", + "privacyS2Title": "2. 管理您的個人資訊", + "privacyS2P1": "如您對您的數據主體權利有進一步要求或存在任何疑問、意見或建議,可通過本聲明中\u201C如何聯繫我們\u201D章節中所述方式與我們取得聯繫,並行駛您的相關權利。", + "privacyS3Title": "3. 資訊存儲地點及期限", + "privacyS3P1": "3.1 我們承諾,除法律法規另有規定外,我們對您的資訊的保存期限應當為實現處理目的所必要的最短時間。", + "privacyS3P2": "3.2 上述資訊將會傳輸並保存至本地的伺服器。", + "privacyS4Title": "4. 如何聯繫我們", + "privacyS4P1": "您可通過以下方式聯繫我們,並行駛您的相關權利,我們會盡快回覆。", + "privacyS4P2": "公司地址:浙江省杭州市濱江區長河街道江虹路410號1號樓", + "privacyS4P3": "如果您對我們旳回覆不滿意,特別是當個人資訊處理行為損害了您的合法權益時,您還可以通過向有管轄權的人民法院提起訴訟、向行業自律協會或政府相關管理機構投訴等外部途徑進行解決。您也可以向我們了解可能適用的相關投訴途徑的資訊。", + "privacyEffective": "生效日期:2026.5.15", "updateSectionTitle": "應用更新", "updateSectionHint": "保持 BitFun 為最新版本,以取得修復與新功能。" }, diff --git a/src/web-ui/src/tools/editor/components/EditorBreadcrumb.tsx b/src/web-ui/src/tools/editor/components/EditorBreadcrumb.tsx index 37a89d8dd7..6cc37a0bcb 100644 --- a/src/web-ui/src/tools/editor/components/EditorBreadcrumb.tsx +++ b/src/web-ui/src/tools/editor/components/EditorBreadcrumb.tsx @@ -263,35 +263,64 @@ export const EditorBreadcrumb: React.FC = ({ const parts = relativePath.split('/').filter(Boolean); if (parts.length === 0) return []; + const isUnderWorkspace = relativePath !== normalizedPath; + + log.debug('Building breadcrumb segments', { + filePath, + workspacePath, + normalizedPath, + normalizedWorkspace, + relativePath, + isUnderWorkspace, + }); + const result: PathSegment[] = []; - - // Add root directory as first level - if (normalizedWorkspace) { - const rootName = normalizedWorkspace.split('/').filter(Boolean).pop() || 'root'; - result.push({ - name: rootName, - fullPath: normalizedWorkspace, - isFile: false, - }); - } - let currentPath = normalizedWorkspace; + // File under workspace: show workspace root then relative parts + if (isUnderWorkspace) { + if (normalizedWorkspace) { + const rootName = normalizedWorkspace.split('/').filter(Boolean).pop() || 'root'; + result.push({ + name: rootName, + fullPath: normalizedWorkspace, + isFile: false, + }); + } - for (let i = 0; i < parts.length; i++) { - const part = parts[i]; - currentPath = currentPath ? `${currentPath}/${part}` : part; - result.push({ - name: part, - fullPath: currentPath, - isFile: i === parts.length - 1, - }); + let currentPath = normalizedWorkspace || ''; + for (let i = 0; i < parts.length; i++) { + currentPath = currentPath ? `${currentPath}/${parts[i]}` : parts[i]; + result.push({ + name: parts[i], + fullPath: currentPath, + isFile: i === parts.length - 1, + }); + } + } else { + // Absolute path outside workspace: rebuild fullPath from normalizedPath + // preserving leading root (e.g. '/' for Unix, '' for Windows drive letters) + const hasLeadingSlash = normalizedPath.startsWith('/'); + let currentPath = hasLeadingSlash ? '/' : ''; + for (let i = 0; i < parts.length; i++) { + currentPath = currentPath === '/' ? `/${parts[i]}` : currentPath ? `${currentPath}/${parts[i]}` : parts[i]; + result.push({ + name: parts[i], + fullPath: currentPath, + isFile: i === parts.length - 1, + }); + } } + log.debug('Breadcrumb segments result', { + result: result.map(s => ({ name: s.name, fullPath: s.fullPath, isFile: s.isFile })), + }); + return result; }, [filePath, workspacePath]); // Load directory contents const loadDirectoryContents = useCallback(async (dirPath: string) => { + log.debug('Loading directory contents', { dirPath }); setDropdownLoading(true); setCurrentDirPath(dirPath); try { @@ -311,9 +340,10 @@ export const EditorBreadcrumb: React.FC = ({ isDirectory: entry.isDirectory || false, })); + log.debug('Directory contents loaded', { dirPath, itemCount: items.length }); setDropdownItems(items); } catch (error) { - log.error('Failed to load directory', error); + log.error('Failed to load directory', { dirPath, error: String(error) }); setDropdownItems([]); } finally { setDropdownLoading(false); @@ -338,6 +368,13 @@ export const EditorBreadcrumb: React.FC = ({ ? segment.fullPath.substring(0, segment.fullPath.lastIndexOf('/')) : segment.fullPath; + log.debug('Breadcrumb segment clicked', { + segmentName: segment.name, + segmentFullPath: segment.fullPath, + isFile: segment.isFile, + resolvedDirPath: dirPath, + }); + setInitialDirPath(dirPath); loadDirectoryContents(dirPath); } @@ -345,6 +382,12 @@ export const EditorBreadcrumb: React.FC = ({ // Handle dropdown item selection const handleDropdownSelect = useCallback(async (item: FileItem) => { + log.debug('Breadcrumb dropdown item selected', { + name: item.name, + path: item.path, + isDirectory: item.isDirectory, + }); + if (item.isDirectory) { loadDirectoryContents(item.path); } else {