feat:移除了弹窗,服务器添加sls
This commit is contained in:
@ -0,0 +1,82 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import type { StorageNode } from './storageData'
|
||||
import { sortStorageChildren } from './storageData'
|
||||
import StorageTreeRow from './StorageTreeRow.vue'
|
||||
|
||||
defineOptions({ name: 'StorageTreeNode' })
|
||||
|
||||
const props = defineProps<{
|
||||
node: StorageNode
|
||||
depth: number
|
||||
parentTotal: number
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
action: [node: StorageNode]
|
||||
}>()
|
||||
|
||||
const hasChildren = computed(() => (props.node.children?.length ?? 0) > 0)
|
||||
const expanded = ref(false)
|
||||
const totalSize = computed(() => props.node.size.actual + props.node.size.symlink)
|
||||
const visibleChildren = computed(() => sortStorageChildren(props.node.children))
|
||||
|
||||
function onToggle(event: Event) {
|
||||
expanded.value = (event.target as HTMLDetailsElement).open
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 有子节点时使用原生 <details>/<summary> 管理展开收起 -->
|
||||
<details v-if="hasChildren" class="flex flex-col" @toggle="onToggle">
|
||||
<summary class="tree-row-reveal">
|
||||
<StorageTreeRow
|
||||
:node="node"
|
||||
:depth="depth"
|
||||
:parent-total="parentTotal"
|
||||
:expanded="expanded"
|
||||
@action="emit('action', $event)"
|
||||
/>
|
||||
</summary>
|
||||
|
||||
<div class="tree-children">
|
||||
<StorageTreeNode
|
||||
v-for="child in visibleChildren"
|
||||
:key="child.id"
|
||||
:node="child"
|
||||
:depth="depth + 1"
|
||||
:parent-total="totalSize"
|
||||
@action="emit('action', $event)"
|
||||
/>
|
||||
</div>
|
||||
</details>
|
||||
<div v-else class="flex flex-col">
|
||||
<!-- 叶子节点不需要展开,直接渲染行 -->
|
||||
<StorageTreeRow
|
||||
:node="node"
|
||||
:depth="depth"
|
||||
:parent-total="parentTotal"
|
||||
:expanded="false"
|
||||
@action="emit('action', $event)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 原生的 <summary> 需要去掉默认的展开三角形与缩进 */
|
||||
.tree-row-reveal {
|
||||
display: block;
|
||||
list-style: none;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tree-row-reveal::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tree-row-reveal::marker {
|
||||
content: none;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,304 @@
|
||||
<script setup lang="ts">
|
||||
import { ChevronRightIcon, FileIcon, FolderIcon, FolderOpenIcon } from '@modrinth/assets'
|
||||
import { useFormatBytes, useVIntl } from '@modrinth/ui'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import type { StorageNode, StorageNodeType } from './storageData'
|
||||
import { storageMessages } from './storageMessages'
|
||||
|
||||
type MessageDescriptor = (typeof storageMessages)['total']
|
||||
|
||||
defineOptions({ name: 'StorageTreeRow' })
|
||||
|
||||
const props = defineProps<{
|
||||
node: StorageNode
|
||||
depth: number
|
||||
parentTotal: number
|
||||
expanded: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
action: [node: StorageNode]
|
||||
}>()
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatBytes = useFormatBytes()
|
||||
|
||||
const typeLabels: Record<StorageNodeType, MessageDescriptor> = {
|
||||
instances: storageMessages.instanceData,
|
||||
cache: storageMessages.cacheData,
|
||||
meta: storageMessages.metaData,
|
||||
database: storageMessages.database,
|
||||
other: storageMessages.other,
|
||||
instance: storageMessages.instance,
|
||||
mods: storageMessages.mods,
|
||||
replay: storageMessages.replay,
|
||||
resourcepacks: storageMessages.resourcepacks,
|
||||
saves: storageMessages.saves,
|
||||
world: storageMessages.world,
|
||||
schematics: storageMessages.schematics,
|
||||
screenshots: storageMessages.screenshots,
|
||||
shaderpacks: storageMessages.shaderpacks,
|
||||
minimap: storageMessages.minimap,
|
||||
'distant-horizons': storageMessages.distantHorizons,
|
||||
'db-file': storageMessages.dbFile,
|
||||
'db-backup': storageMessages.dbBackup,
|
||||
}
|
||||
|
||||
const hasChildren = computed(() => (props.node.children?.length ?? 0) > 0)
|
||||
const isDirectory = computed(() => (props.node.paths[0]?.kind ?? 'directory') !== 'file')
|
||||
|
||||
const actualShare = computed(() =>
|
||||
props.parentTotal > 0 ? props.node.size.actual / props.parentTotal : 0,
|
||||
)
|
||||
|
||||
const symShare = computed(() =>
|
||||
props.parentTotal > 0 ? props.node.size.symlink / props.parentTotal : 0,
|
||||
)
|
||||
|
||||
const totalShare = computed(() => Math.min(1, actualShare.value + symShare.value))
|
||||
|
||||
const percent = computed(() => Math.round(totalShare.value * 100))
|
||||
|
||||
const displayLabel = computed(() => props.node.name ?? formatMessage(typeLabels[props.node.type]))
|
||||
|
||||
const tooltipText = computed(() => props.node.paths.map((path) => path.path).join('\n'))
|
||||
|
||||
const nameTooltip = computed(() => ({
|
||||
content: tooltipText.value,
|
||||
popperClass: 'storage-tooltip',
|
||||
}))
|
||||
|
||||
const actualSizeTooltip = computed(() =>
|
||||
formatMessage(storageMessages.actualSizeTooltip, {
|
||||
size: formatBytes(props.node.size.actual),
|
||||
}),
|
||||
)
|
||||
|
||||
const symlinkSizeTooltip = computed(() =>
|
||||
formatMessage(storageMessages.symlinkSizeTooltip, {
|
||||
size: formatBytes(props.node.size.symlink),
|
||||
}),
|
||||
)
|
||||
|
||||
const progressTitle = computed(() =>
|
||||
props.node.size.symlink > 0
|
||||
? `${actualSizeTooltip.value}\n${symlinkSizeTooltip.value}`
|
||||
: actualSizeTooltip.value,
|
||||
)
|
||||
|
||||
const progressTooltip = computed(() => ({
|
||||
content: progressTitle.value,
|
||||
popperClass: 'storage-tooltip',
|
||||
}))
|
||||
|
||||
const progressLabel = computed(() =>
|
||||
props.node.size.symlink > 0
|
||||
? `${actualSizeTooltip.value} ${symlinkSizeTooltip.value}`
|
||||
: actualSizeTooltip.value,
|
||||
)
|
||||
|
||||
const actualSizeText = computed(() => formatBytes(props.node.size.actual))
|
||||
const symlinkSizeText = computed(() => formatBytes(props.node.size.symlink))
|
||||
|
||||
const openActionLabel = computed(() => formatMessage(storageMessages.openAction))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="tree-row hover:bg-surface-2" :class="{ clickable: hasChildren, 'is-expanded': expanded }">
|
||||
<!-- 左侧树层级与名称部分 -->
|
||||
<div class="flex h-full min-w-0 flex-1 items-center">
|
||||
<!-- 根据 depth 生成层级缩进和导轨线 -->
|
||||
<div class="indent-guides" aria-hidden="true">
|
||||
<span v-for="i in depth" :key="i" class="guide-line" />
|
||||
</div>
|
||||
|
||||
<!-- 展开/折叠 箭头(装饰性,由原生的 <summary> 负责展开) -->
|
||||
<span
|
||||
v-if="hasChildren"
|
||||
class="chevron-icon inline-flex h-5 w-5 shrink-0 items-center justify-center p-0 text-secondary"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<ChevronRightIcon class="size-3.5" />
|
||||
</span>
|
||||
<span v-else class="chevron-placeholder" aria-hidden="true" />
|
||||
|
||||
<!-- 文件/文件夹按钮:打开位置(不展开树) -->
|
||||
<button
|
||||
v-tooltip="openActionLabel"
|
||||
type="button"
|
||||
class="node-type-btn mr-1.5 inline-flex cursor-pointer items-center justify-center rounded border-0 bg-transparent p-0 text-secondary hover:bg-surface-3 hover:text-contrast"
|
||||
:aria-label="`${displayLabel}: ${openActionLabel}`"
|
||||
@click.stop="emit('action', node)"
|
||||
>
|
||||
<FolderOpenIcon v-if="hasChildren && expanded" class="node-type-icon text-brand" />
|
||||
<FolderIcon v-else-if="isDirectory" class="node-type-icon" />
|
||||
<FileIcon v-else class="node-type-icon" />
|
||||
</button>
|
||||
|
||||
<!-- 节点名称 -->
|
||||
<span v-tooltip="nameTooltip" class="node-name">
|
||||
{{ displayLabel }}
|
||||
</span>
|
||||
|
||||
<span v-if="node.count !== undefined" class="count-badge">
|
||||
{{ node.count }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- 右侧数据与进度列 -->
|
||||
<div class="ml-4 flex shrink-0 items-center gap-4">
|
||||
<div class="storage-size">
|
||||
{{ actualSizeText }}
|
||||
<span v-if="node.size.symlink > 0" class="text-secondary"> + {{ symlinkSizeText }} </span>
|
||||
</div>
|
||||
|
||||
<div class="w-9 max-sm:hidden text-right text-xs tabular-nums text-secondary">{{ percent }}%</div>
|
||||
|
||||
<progress
|
||||
v-tooltip="progressTooltip"
|
||||
class="storage-progress h-1 w-24 shrink-0 appearance-none overflow-hidden rounded-full border-0 bg-surface-3"
|
||||
:value="totalShare"
|
||||
:max="1"
|
||||
:aria-label="progressLabel"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 整体行布局:去卡片化、去分割线 */
|
||||
.tree-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 2rem;
|
||||
padding: 0 0.5rem;
|
||||
border: none;
|
||||
background: transparent;
|
||||
user-select: none;
|
||||
transition: background-color 0.1s ease;
|
||||
}
|
||||
|
||||
.tree-row.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 竖向缩进线容器 */
|
||||
.indent-guides {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 垂直导轨线:跟随主题 surface 描边色,浅色/深色模式都清晰可见 */
|
||||
.guide-line {
|
||||
display: block;
|
||||
width: 1.25rem;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.guide-line::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0.5rem;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 1px;
|
||||
background-color: var(--surface-5);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* 展开状态下高亮当前父级的引导线,使用主题品牌色 */
|
||||
.tree-row.is-expanded .indent-guides .guide-line:last-child::before {
|
||||
background-color: var(--color-brand);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* 折叠/展开 箭头图标 */
|
||||
.chevron-icon {
|
||||
transition: transform 0.15s ease;
|
||||
}
|
||||
|
||||
.tree-row.is-expanded .chevron-icon {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.chevron-placeholder {
|
||||
width: 1.25rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 文件/文件夹打开按钮 */
|
||||
.node-type-icon {
|
||||
width: 1.125rem;
|
||||
height: 1.125rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.node-type-btn:focus-visible {
|
||||
outline: 2px solid var(--color-brand);
|
||||
outline-offset: -1px;
|
||||
}
|
||||
|
||||
.node-name {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 400;
|
||||
color: var(--color-contrast);
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.count-badge {
|
||||
margin-left: 0.375rem;
|
||||
padding: 0 0.35rem;
|
||||
height: 1rem;
|
||||
border-radius: 0.25rem;
|
||||
background: var(--surface-3);
|
||||
color: var(--color-secondary);
|
||||
font-size: 0.6875rem;
|
||||
line-height: 1rem;
|
||||
font-variant-numeric: tabular-nums;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.storage-size {
|
||||
font-size: 0.8125rem;
|
||||
font-variant-numeric: tabular-nums;
|
||||
color: var(--color-primary);
|
||||
white-space: nowrap;
|
||||
text-align: right;
|
||||
min-width: 5rem;
|
||||
}
|
||||
|
||||
/* 原生 <progress> 作为共享占用进度条 */
|
||||
.storage-progress::-webkit-progress-bar {
|
||||
background: var(--surface-3);
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
.storage-progress::-webkit-progress-value {
|
||||
background: var(--color-brand);
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
.storage-progress::-moz-progress-bar {
|
||||
background: var(--color-brand);
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.storage-progress {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* 存储页多行 tooltip:内容换行并限制宽度 */
|
||||
:global(.v-popper__popper.storage-tooltip .v-popper__inner) {
|
||||
white-space: pre-line;
|
||||
max-width: 22rem;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,522 @@
|
||||
export type StorageNodeType =
|
||||
| 'instances'
|
||||
| 'cache'
|
||||
| 'meta'
|
||||
| 'database'
|
||||
| 'other'
|
||||
| 'instance'
|
||||
| 'mods'
|
||||
| 'replay'
|
||||
| 'resourcepacks'
|
||||
| 'saves'
|
||||
| 'world'
|
||||
| 'schematics'
|
||||
| 'screenshots'
|
||||
| 'shaderpacks'
|
||||
| 'minimap'
|
||||
| 'distant-horizons'
|
||||
| 'db-file'
|
||||
| 'db-backup'
|
||||
|
||||
export interface StoragePath {
|
||||
path: string
|
||||
kind: 'file' | 'directory'
|
||||
}
|
||||
|
||||
export interface StorageSize {
|
||||
actual: number
|
||||
symlink: number
|
||||
}
|
||||
|
||||
export interface StorageNode {
|
||||
id: string
|
||||
type: StorageNodeType
|
||||
name?: string
|
||||
instance_id?: string
|
||||
size: StorageSize
|
||||
count?: number
|
||||
paths: StoragePath[]
|
||||
children?: StorageNode[]
|
||||
}
|
||||
|
||||
export interface StorageTree {
|
||||
version?: number
|
||||
scannedAt?: string
|
||||
total: StorageSize
|
||||
categories: StorageNode[]
|
||||
rootOther: StorageNode | null
|
||||
}
|
||||
|
||||
export function sortStorageChildren(children: StorageNode[] | undefined): StorageNode[] {
|
||||
if (!children) return []
|
||||
return [...children]
|
||||
.filter((child) => child.size.actual + child.size.symlink > 0)
|
||||
.sort((a, b) => {
|
||||
const aIsOther = a.type === 'other' ? 1 : 0
|
||||
const bIsOther = b.type === 'other' ? 1 : 0
|
||||
if (aIsOther !== bIsOther) return aIsOther - bIsOther
|
||||
const aTotal = a.size.actual + a.size.symlink
|
||||
const bTotal = b.size.actual + b.size.symlink
|
||||
return bTotal - aTotal
|
||||
})
|
||||
}
|
||||
|
||||
const gb = (value: number) => Math.round(value * 1024 ** 3)
|
||||
const mb = (value: number) => Math.round(value * 1024 ** 2)
|
||||
|
||||
const appData = 'C:/Users/You/AppData/Roaming/red.ghs.axolotl'
|
||||
|
||||
const instanceAPath = `${appData}/profiles/红石生电优化【Redstone Survival Optimization】`
|
||||
const instanceBPath = `${appData}/profiles/Fabric 1.21.11`
|
||||
const instanceCPath = `${appData}/profiles/Vanilla 26.2`
|
||||
const instanceDPath = `${appData}/profiles/CurseForge Pack`
|
||||
|
||||
const worldNode = (
|
||||
id: string,
|
||||
name: string,
|
||||
actual: number,
|
||||
symlink = 0,
|
||||
parentPath: string,
|
||||
): StorageNode => ({
|
||||
id,
|
||||
type: 'world',
|
||||
name,
|
||||
size: { actual, symlink },
|
||||
paths: [{ path: `${parentPath}/saves/${name}`, kind: 'directory' }],
|
||||
})
|
||||
|
||||
const instanceANode: StorageNode = {
|
||||
id: 'instance-a',
|
||||
type: 'instance',
|
||||
name: '红石生电优化【Redstone Survival Optimization】',
|
||||
size: { actual: gb(20), symlink: gb(1.5) },
|
||||
paths: [{ path: instanceAPath, kind: 'directory' }],
|
||||
children: [
|
||||
{
|
||||
id: 'instance-a-mods',
|
||||
type: 'mods',
|
||||
size: { actual: gb(1.6), symlink: gb(0.2) },
|
||||
count: 42,
|
||||
paths: [{ path: `${instanceAPath}/mods`, kind: 'directory' }],
|
||||
},
|
||||
{
|
||||
id: 'instance-a-replay',
|
||||
type: 'replay',
|
||||
size: { actual: gb(5.2), symlink: 0 },
|
||||
paths: [
|
||||
{ path: `${instanceAPath}/flashback`, kind: 'directory' },
|
||||
{ path: `${instanceAPath}/replay_recordings`, kind: 'directory' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'instance-a-resourcepacks',
|
||||
type: 'resourcepacks',
|
||||
size: { actual: gb(0.8), symlink: 0 },
|
||||
count: 4,
|
||||
paths: [{ path: `${instanceAPath}/resourcepacks`, kind: 'directory' }],
|
||||
},
|
||||
{
|
||||
id: 'instance-a-saves',
|
||||
type: 'saves',
|
||||
size: { actual: gb(9.6), symlink: 0 },
|
||||
count: 2,
|
||||
paths: [{ path: `${instanceAPath}/saves`, kind: 'directory' }],
|
||||
children: [
|
||||
worldNode('instance-a-world1', 'world1', gb(8.4), 0, instanceAPath),
|
||||
worldNode('instance-a-world2', 'world2', gb(1.2), 0, instanceAPath),
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'instance-a-schematics',
|
||||
type: 'schematics',
|
||||
size: { actual: gb(0.15), symlink: 0 },
|
||||
count: 86,
|
||||
paths: [{ path: `${instanceAPath}/schematics`, kind: 'directory' }],
|
||||
},
|
||||
{
|
||||
id: 'instance-a-screenshots',
|
||||
type: 'screenshots',
|
||||
size: { actual: gb(0.6), symlink: 0 },
|
||||
count: 214,
|
||||
paths: [{ path: `${instanceAPath}/screenshots`, kind: 'directory' }],
|
||||
},
|
||||
{
|
||||
id: 'instance-a-shaderpacks',
|
||||
type: 'shaderpacks',
|
||||
size: { actual: gb(0.9), symlink: gb(0.1) },
|
||||
count: 3,
|
||||
paths: [{ path: `${instanceAPath}/shaderpacks`, kind: 'directory' }],
|
||||
},
|
||||
{
|
||||
id: 'instance-a-minimap',
|
||||
type: 'minimap',
|
||||
size: { actual: gb(0.05), symlink: gb(0.8) },
|
||||
paths: [
|
||||
{ path: `${instanceAPath}/voxelmap`, kind: 'directory' },
|
||||
{ path: `${instanceAPath}/xaero`, kind: 'directory' },
|
||||
{ path: `${instanceAPath}/XaeroWaypoints_BACKUP`, kind: 'directory' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'instance-a-distant-horizons',
|
||||
type: 'distant-horizons',
|
||||
size: { actual: gb(0.3), symlink: gb(0.4) },
|
||||
paths: [
|
||||
{ path: `${instanceAPath}/.voxy`, kind: 'directory' },
|
||||
{ path: `${instanceAPath}/Distant_Horizons_server_data`, kind: 'directory' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'instance-a-other',
|
||||
type: 'other',
|
||||
size: { actual: gb(0.8), symlink: 0 },
|
||||
paths: [{ path: instanceAPath, kind: 'directory' }],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const instanceBNode: StorageNode = {
|
||||
id: 'instance-b',
|
||||
type: 'instance',
|
||||
name: 'Fabric 1.21.11',
|
||||
size: { actual: gb(9.8), symlink: 0 },
|
||||
paths: [{ path: instanceBPath, kind: 'directory' }],
|
||||
children: [
|
||||
{
|
||||
id: 'instance-b-mods',
|
||||
type: 'mods',
|
||||
size: { actual: gb(2.4), symlink: 0 },
|
||||
count: 57,
|
||||
paths: [{ path: `${instanceBPath}/mods`, kind: 'directory' }],
|
||||
},
|
||||
{
|
||||
id: 'instance-b-replay',
|
||||
type: 'replay',
|
||||
size: { actual: gb(0.1), symlink: 0 },
|
||||
paths: [
|
||||
{ path: `${instanceBPath}/flashback`, kind: 'directory' },
|
||||
{ path: `${instanceBPath}/replay_recordings`, kind: 'directory' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'instance-b-resourcepacks',
|
||||
type: 'resourcepacks',
|
||||
size: { actual: gb(0.2), symlink: 0 },
|
||||
count: 3,
|
||||
paths: [{ path: `${instanceBPath}/resourcepacks`, kind: 'directory' }],
|
||||
},
|
||||
{
|
||||
id: 'instance-b-saves',
|
||||
type: 'saves',
|
||||
size: { actual: gb(5.6), symlink: 0 },
|
||||
count: 2,
|
||||
paths: [{ path: `${instanceBPath}/saves`, kind: 'directory' }],
|
||||
children: [
|
||||
worldNode('instance-b-world1', 'world1', gb(3.2), 0, instanceBPath),
|
||||
worldNode('instance-b-world2', 'world2', gb(2.4), 0, instanceBPath),
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'instance-b-schematics',
|
||||
type: 'schematics',
|
||||
size: { actual: gb(0.05), symlink: 0 },
|
||||
count: 12,
|
||||
paths: [{ path: `${instanceBPath}/schematics`, kind: 'directory' }],
|
||||
},
|
||||
{
|
||||
id: 'instance-b-screenshots',
|
||||
type: 'screenshots',
|
||||
size: { actual: gb(0.3), symlink: 0 },
|
||||
count: 76,
|
||||
paths: [{ path: `${instanceBPath}/screenshots`, kind: 'directory' }],
|
||||
},
|
||||
{
|
||||
id: 'instance-b-shaderpacks',
|
||||
type: 'shaderpacks',
|
||||
size: { actual: gb(0.4), symlink: 0 },
|
||||
count: 2,
|
||||
paths: [{ path: `${instanceBPath}/shaderpacks`, kind: 'directory' }],
|
||||
},
|
||||
{
|
||||
id: 'instance-b-minimap',
|
||||
type: 'minimap',
|
||||
size: { actual: gb(0.02), symlink: 0 },
|
||||
paths: [
|
||||
{ path: `${instanceBPath}/voxelmap`, kind: 'directory' },
|
||||
{ path: `${instanceBPath}/xaero`, kind: 'directory' },
|
||||
{ path: `${instanceBPath}/XaeroWaypoints_BACKUP`, kind: 'directory' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'instance-b-distant-horizons',
|
||||
type: 'distant-horizons',
|
||||
size: { actual: gb(0.1), symlink: 0 },
|
||||
paths: [
|
||||
{ path: `${instanceBPath}/.voxy`, kind: 'directory' },
|
||||
{ path: `${instanceBPath}/Distant_Horizons_server_data`, kind: 'directory' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'instance-b-other',
|
||||
type: 'other',
|
||||
size: { actual: gb(0.63), symlink: 0 },
|
||||
paths: [{ path: instanceBPath, kind: 'directory' }],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const instanceCNode: StorageNode = {
|
||||
id: 'instance-c',
|
||||
type: 'instance',
|
||||
name: 'Vanilla 26.2',
|
||||
size: { actual: gb(0.5), symlink: gb(12) },
|
||||
paths: [
|
||||
{ path: `${appData}/profiles/Vanilla 26.2`, kind: 'directory' },
|
||||
{ path: `${appData}/.minecraft/versions/Vanilla 26.2`, kind: 'directory' },
|
||||
],
|
||||
children: [
|
||||
{
|
||||
id: 'instance-c-mods',
|
||||
type: 'mods',
|
||||
size: { actual: gb(0.05), symlink: gb(0.5) },
|
||||
count: 1,
|
||||
paths: [{ path: `${instanceCPath}/mods`, kind: 'directory' }],
|
||||
},
|
||||
{
|
||||
id: 'instance-c-replay',
|
||||
type: 'replay',
|
||||
size: { actual: 0, symlink: 0 },
|
||||
paths: [
|
||||
{ path: `${instanceCPath}/flashback`, kind: 'directory' },
|
||||
{ path: `${instanceCPath}/replay_recordings`, kind: 'directory' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'instance-c-resourcepacks',
|
||||
type: 'resourcepacks',
|
||||
size: { actual: gb(0.02), symlink: gb(0.3) },
|
||||
count: 1,
|
||||
paths: [{ path: `${instanceCPath}/resourcepacks`, kind: 'directory' }],
|
||||
},
|
||||
{
|
||||
id: 'instance-c-saves',
|
||||
type: 'saves',
|
||||
size: { actual: gb(0.4), symlink: gb(9) },
|
||||
count: 1,
|
||||
paths: [{ path: `${instanceCPath}/saves`, kind: 'directory' }],
|
||||
children: [worldNode('instance-c-world1', 'world1', gb(0.4), gb(9), instanceCPath)],
|
||||
},
|
||||
{
|
||||
id: 'instance-c-schematics',
|
||||
type: 'schematics',
|
||||
size: { actual: 0, symlink: 0 },
|
||||
count: 0,
|
||||
paths: [{ path: `${instanceCPath}/schematics`, kind: 'directory' }],
|
||||
},
|
||||
{
|
||||
id: 'instance-c-screenshots',
|
||||
type: 'screenshots',
|
||||
size: { actual: 0, symlink: 0 },
|
||||
count: 0,
|
||||
paths: [{ path: `${instanceCPath}/screenshots`, kind: 'directory' }],
|
||||
},
|
||||
{
|
||||
id: 'instance-c-shaderpacks',
|
||||
type: 'shaderpacks',
|
||||
size: { actual: gb(0.01), symlink: gb(2) },
|
||||
count: 1,
|
||||
paths: [{ path: `${instanceCPath}/shaderpacks`, kind: 'directory' }],
|
||||
},
|
||||
{
|
||||
id: 'instance-c-minimap',
|
||||
type: 'minimap',
|
||||
size: { actual: gb(0.01), symlink: gb(0.1) },
|
||||
paths: [
|
||||
{ path: `${instanceCPath}/voxelmap`, kind: 'directory' },
|
||||
{ path: `${instanceCPath}/xaero`, kind: 'directory' },
|
||||
{ path: `${instanceCPath}/XaeroWaypoints_BACKUP`, kind: 'directory' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'instance-c-distant-horizons',
|
||||
type: 'distant-horizons',
|
||||
size: { actual: gb(0.01), symlink: gb(0.1) },
|
||||
paths: [
|
||||
{ path: `${instanceCPath}/.voxy`, kind: 'directory' },
|
||||
{ path: `${instanceCPath}/Distant_Horizons_server_data`, kind: 'directory' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'instance-c-other',
|
||||
type: 'other',
|
||||
size: { actual: 0, symlink: 0 },
|
||||
paths: [{ path: instanceCPath, kind: 'directory' }],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const instanceDNode: StorageNode = {
|
||||
id: 'instance-d',
|
||||
type: 'instance',
|
||||
name: 'CurseForge Pack',
|
||||
size: { actual: gb(4), symlink: gb(0.2) },
|
||||
paths: [{ path: instanceDPath, kind: 'directory' }],
|
||||
children: [
|
||||
{
|
||||
id: 'instance-d-mods',
|
||||
type: 'mods',
|
||||
size: { actual: gb(2), symlink: gb(0.1) },
|
||||
count: 35,
|
||||
paths: [{ path: `${instanceDPath}/mods`, kind: 'directory' }],
|
||||
},
|
||||
{
|
||||
id: 'instance-d-replay',
|
||||
type: 'replay',
|
||||
size: { actual: gb(0.05), symlink: 0 },
|
||||
paths: [
|
||||
{ path: `${instanceDPath}/flashback`, kind: 'directory' },
|
||||
{ path: `${instanceDPath}/replay_recordings`, kind: 'directory' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'instance-d-resourcepacks',
|
||||
type: 'resourcepacks',
|
||||
size: { actual: gb(0.3), symlink: 0 },
|
||||
count: 5,
|
||||
paths: [{ path: `${instanceDPath}/resourcepacks`, kind: 'directory' }],
|
||||
},
|
||||
{
|
||||
id: 'instance-d-saves',
|
||||
type: 'saves',
|
||||
size: { actual: gb(1), symlink: 0 },
|
||||
count: 2,
|
||||
paths: [{ path: `${instanceDPath}/saves`, kind: 'directory' }],
|
||||
children: [
|
||||
worldNode('instance-d-world1', 'world1', gb(0.6), 0, instanceDPath),
|
||||
worldNode('instance-d-world2', 'world2', gb(0.4), 0, instanceDPath),
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'instance-d-schematics',
|
||||
type: 'schematics',
|
||||
size: { actual: gb(0.1), symlink: 0 },
|
||||
count: 24,
|
||||
paths: [{ path: `${instanceDPath}/schematics`, kind: 'directory' }],
|
||||
},
|
||||
{
|
||||
id: 'instance-d-screenshots',
|
||||
type: 'screenshots',
|
||||
size: { actual: gb(0.2), symlink: 0 },
|
||||
count: 88,
|
||||
paths: [{ path: `${instanceDPath}/screenshots`, kind: 'directory' }],
|
||||
},
|
||||
{
|
||||
id: 'instance-d-shaderpacks',
|
||||
type: 'shaderpacks',
|
||||
size: { actual: gb(0.2), symlink: 0 },
|
||||
count: 4,
|
||||
paths: [{ path: `${instanceDPath}/shaderpacks`, kind: 'directory' }],
|
||||
},
|
||||
{
|
||||
id: 'instance-d-minimap',
|
||||
type: 'minimap',
|
||||
size: { actual: gb(0.03), symlink: 0 },
|
||||
paths: [
|
||||
{ path: `${instanceDPath}/voxelmap`, kind: 'directory' },
|
||||
{ path: `${instanceDPath}/xaero`, kind: 'directory' },
|
||||
{ path: `${instanceDPath}/XaeroWaypoints_BACKUP`, kind: 'directory' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'instance-d-distant-horizons',
|
||||
type: 'distant-horizons',
|
||||
size: { actual: gb(0.02), symlink: 0 },
|
||||
paths: [
|
||||
{ path: `${instanceDPath}/.voxy`, kind: 'directory' },
|
||||
{ path: `${instanceDPath}/Distant_Horizons_server_data`, kind: 'directory' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'instance-d-other',
|
||||
type: 'other',
|
||||
size: { actual: gb(0.1), symlink: gb(0.1) },
|
||||
paths: [{ path: instanceDPath, kind: 'directory' }],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const instancesCategory: StorageNode = {
|
||||
id: 'category-instances',
|
||||
type: 'instances',
|
||||
size: { actual: gb(34.7), symlink: gb(13.7) },
|
||||
count: 4,
|
||||
paths: [{ path: `${appData}/profiles`, kind: 'directory' }],
|
||||
children: [
|
||||
{
|
||||
id: 'profiles-root-other',
|
||||
type: 'other',
|
||||
size: { actual: gb(0.4), symlink: 0 },
|
||||
paths: [{ path: `${appData}/profiles`, kind: 'directory' }],
|
||||
},
|
||||
instanceANode,
|
||||
instanceBNode,
|
||||
instanceCNode,
|
||||
instanceDNode,
|
||||
],
|
||||
}
|
||||
|
||||
const cacheCategory: StorageNode = {
|
||||
id: 'category-cache',
|
||||
type: 'cache',
|
||||
size: { actual: gb(2.1), symlink: 0 },
|
||||
count: 128,
|
||||
paths: [{ path: `${appData}/caches`, kind: 'directory' }],
|
||||
}
|
||||
|
||||
const metaCategory: StorageNode = {
|
||||
id: 'category-meta',
|
||||
type: 'meta',
|
||||
size: { actual: gb(6.3), symlink: 0 },
|
||||
count: 3421,
|
||||
paths: [{ path: `${appData}/meta`, kind: 'directory' }],
|
||||
}
|
||||
|
||||
const databaseCategory: StorageNode = {
|
||||
id: 'category-database',
|
||||
type: 'database',
|
||||
size: { actual: gb(0.5), symlink: 0 },
|
||||
count: 2,
|
||||
paths: [{ path: `${appData}`, kind: 'directory' }],
|
||||
children: [
|
||||
{
|
||||
id: 'database-app-db',
|
||||
type: 'db-file',
|
||||
size: { actual: mb(20), symlink: 0 },
|
||||
paths: [{ path: `${appData}/app.db`, kind: 'file' }],
|
||||
},
|
||||
{
|
||||
id: 'database-backups',
|
||||
type: 'db-backup',
|
||||
size: { actual: mb(480), symlink: 0 },
|
||||
paths: [{ path: `${appData}/Backups/app-db`, kind: 'directory' }],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const rootOther: StorageNode = {
|
||||
id: 'root-other',
|
||||
type: 'other',
|
||||
size: { actual: gb(0.3), symlink: 0 },
|
||||
paths: [
|
||||
{ path: `${appData}/launcher_logs`, kind: 'directory' },
|
||||
{ path: `${appData}/app-window-state.json`, kind: 'file' },
|
||||
{ path: `${appData}/download.log`, kind: 'file' },
|
||||
{ path: `${appData}/download-reputation.json`, kind: 'file' },
|
||||
],
|
||||
}
|
||||
|
||||
export const storageTree: StorageTree = {
|
||||
total: { actual: gb(43.9), symlink: gb(13.7) },
|
||||
categories: [instancesCategory, cacheCategory, metaCategory, databaseCategory, rootOther],
|
||||
rootOther,
|
||||
}
|
||||
@ -0,0 +1,137 @@
|
||||
import { defineMessages } from '@modrinth/ui'
|
||||
|
||||
export const storageMessages = defineMessages({
|
||||
total: {
|
||||
id: 'app.settings.storage.total',
|
||||
defaultMessage: 'Total size',
|
||||
},
|
||||
totalDescription: {
|
||||
id: 'app.settings.storage.total-description',
|
||||
defaultMessage: 'Total size including symbolic links and junctions',
|
||||
},
|
||||
instanceData: {
|
||||
id: 'app.settings.storage.instance-data',
|
||||
defaultMessage: 'Instance data',
|
||||
},
|
||||
cacheData: {
|
||||
id: 'app.settings.storage.cache-data',
|
||||
defaultMessage: 'Cache data',
|
||||
},
|
||||
metaData: {
|
||||
id: 'app.settings.storage.meta-data',
|
||||
defaultMessage: 'Meta data',
|
||||
},
|
||||
database: {
|
||||
id: 'app.settings.storage.database',
|
||||
defaultMessage: 'Database',
|
||||
},
|
||||
other: {
|
||||
id: 'app.settings.storage.other',
|
||||
defaultMessage: 'Other',
|
||||
},
|
||||
instance: {
|
||||
id: 'app.settings.storage.instance',
|
||||
defaultMessage: 'Instance',
|
||||
},
|
||||
mods: {
|
||||
id: 'app.settings.storage.mods',
|
||||
defaultMessage: 'Mods',
|
||||
},
|
||||
replay: {
|
||||
id: 'app.settings.storage.replay',
|
||||
defaultMessage: 'Replay recordings',
|
||||
},
|
||||
resourcepacks: {
|
||||
id: 'app.settings.storage.resourcepacks',
|
||||
defaultMessage: 'Resource packs',
|
||||
},
|
||||
saves: {
|
||||
id: 'app.settings.storage.saves',
|
||||
defaultMessage: 'Saves',
|
||||
},
|
||||
world: {
|
||||
id: 'app.settings.storage.world',
|
||||
defaultMessage: 'World',
|
||||
},
|
||||
schematics: {
|
||||
id: 'app.settings.storage.schematics',
|
||||
defaultMessage: 'Schematics',
|
||||
},
|
||||
screenshots: {
|
||||
id: 'app.settings.storage.screenshots',
|
||||
defaultMessage: 'Screenshots',
|
||||
},
|
||||
shaderpacks: {
|
||||
id: 'app.settings.storage.shaderpacks',
|
||||
defaultMessage: 'Shader packs',
|
||||
},
|
||||
minimap: {
|
||||
id: 'app.settings.storage.minimap',
|
||||
defaultMessage: 'Minimap data',
|
||||
},
|
||||
distantHorizons: {
|
||||
id: 'app.settings.storage.distant-horizons',
|
||||
defaultMessage: 'Distant Horizons cache',
|
||||
},
|
||||
dbFile: {
|
||||
id: 'app.settings.storage.db-file',
|
||||
defaultMessage: 'App database',
|
||||
},
|
||||
dbBackup: {
|
||||
id: 'app.settings.storage.db-backup',
|
||||
defaultMessage: 'Database backups',
|
||||
},
|
||||
itemCount: {
|
||||
id: 'app.settings.storage.item-count',
|
||||
defaultMessage: '{count} items',
|
||||
},
|
||||
instanceCount: {
|
||||
id: 'app.settings.storage.instance-count',
|
||||
defaultMessage: '{count} instances',
|
||||
},
|
||||
actualSizeTooltip: {
|
||||
id: 'app.settings.storage.actual-size-tooltip',
|
||||
defaultMessage: 'Actual size: {size}',
|
||||
},
|
||||
symlinkSizeTooltip: {
|
||||
id: 'app.settings.storage.symlink-size-tooltip',
|
||||
defaultMessage: 'Symbolic link or junction referenced size: {size}',
|
||||
},
|
||||
openAction: {
|
||||
id: 'app.settings.storage.open-action',
|
||||
defaultMessage: 'Open in launcher or open location',
|
||||
},
|
||||
symlinkLabel: {
|
||||
id: 'app.settings.storage.symlink-label',
|
||||
defaultMessage: 'symlink',
|
||||
},
|
||||
symlinkHelp: {
|
||||
id: 'app.settings.storage.symlink-help',
|
||||
defaultMessage: 'What is a symlink?',
|
||||
},
|
||||
symlinkHelpTooltip: {
|
||||
id: 'app.settings.storage.symlink-help-tooltip',
|
||||
defaultMessage:
|
||||
'Symbolic links reference Minecraft resources from other locations — these files may actually live in another launcher\u2019s directory.\nFor example, \u201c20MB + 1.2GB\u201d means the launcher folder contains 20MB of files and references an external 1.2GB of files.',
|
||||
},
|
||||
update: {
|
||||
id: 'app.settings.storage.update',
|
||||
defaultMessage: 'Update',
|
||||
},
|
||||
updating: {
|
||||
id: 'app.settings.storage.updating',
|
||||
defaultMessage: 'Updating…',
|
||||
},
|
||||
lastUpdatedLabel: {
|
||||
id: 'app.settings.storage.last-updated-label',
|
||||
defaultMessage: 'Last updated:',
|
||||
},
|
||||
scanning: {
|
||||
id: 'app.settings.storage.scanning',
|
||||
defaultMessage: 'Scanning storage…',
|
||||
},
|
||||
storageTab: {
|
||||
id: 'app.settings.tabs.storage',
|
||||
defaultMessage: 'Storage',
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user