Prune stale docs and update .hermes content

Delete a large set of outdated documentation (many files under docs/ and .hermes/plans/, including audits, design, prd, technical, planning, assets, and todos). Update and consolidate .hermes content: refresh shared-memory pages (decision-log, development-workflow, document-map, pitfalls, project-overview, team-conventions) and several skills/references under .hermes/skills. Also modify AGENTS.md, README.md, UI_CODING_STANDARD.md, docs/README.md and .encoding-check-ignore. Purpose: clean up stale planning/audit material and keep current hermes documentation and related top-level docs in sync.
This commit is contained in:
2026-05-15 06:24:07 +08:00
parent 2eded08bc7
commit 3cb3efb4d0
708 changed files with 4033 additions and 142328 deletions

View File

@@ -929,7 +929,7 @@ test('拼图运行态主体使用主题语义类承接明暗主题', () => {
expect(container.querySelector('.puzzle-runtime-pill')).toBeTruthy();
});
test('合并块按实际拼块外轮廓描边', () => {
test('合并块不叠加可见轮廓和单块阴影', () => {
const mergedRun: PuzzleRunSnapshot = {
...clearedRun,
currentLevel: {
@@ -976,22 +976,16 @@ test('合并块按实际拼块外轮廓描边', () => {
expect(container.querySelector('.ring-2.ring-emerald-100\\/58')).toBeNull();
expect(
container.querySelector('[data-merged-group-outline="true"]'),
).toBeTruthy();
).toBeNull();
const outlineStroke = container.querySelector(
'[data-merged-group-outline-stroke="true"]',
);
expect(outlineStroke).toBeTruthy();
expect(outlineStroke?.getAttribute('d')).toContain('Q 2 1 1.84 1');
expect(outlineStroke?.getAttribute('d')).toContain('Q 1 1 1 1.16');
expect(
container
.querySelector('[data-merged-group-outline="true"]')
?.getAttribute('fill'),
).toBe('transparent');
expect(outlineStroke).toBeNull();
expect((outlinedPieces[0] as HTMLElement).style.clipPath).toBe('');
for (const outlinedPiece of outlinedPieces) {
const outlinedPieceElement = outlinedPiece as HTMLElement;
expect(outlinedPieceElement.className).not.toContain('bg-emerald-300/10');
expect(outlinedPieceElement.className).not.toContain('shadow-[');
expect(
outlinedPieceElement.querySelector('.absolute.inset-0.bg-black\\/8'),
).toBeNull();
@@ -999,7 +993,7 @@ test('合并块按实际拼块外轮廓描边', () => {
const clippedLayer = container.querySelector(
'[style*="clip-path"]',
) as HTMLElement | null;
expect(clippedLayer?.style.clipPath).toContain('url(#');
expect(clippedLayer).toBeNull();
});
test('合并块轮廓路径为内凹角生成圆角曲线', () => {
@@ -1026,7 +1020,7 @@ test('合并块轮廓路径为内凹角生成圆角曲线', () => {
expect(outlinePath).toContain('Q 1 1 1 1.16');
});
test('基础单块使用圆角裁剪图片', () => {
test('基础单块不叠加边框圆角或图片蒙版', () => {
const playingRun: PuzzleRunSnapshot = {
...clearedRun,
currentLevel: {
@@ -1055,7 +1049,9 @@ test('基础单块使用圆角裁剪图片', () => {
'[data-piece-id="piece-0"]',
) as HTMLElement | null;
expect(basePiece?.className).toContain('overflow-hidden');
expect(basePiece?.className).toContain('rounded-[0.85rem]');
expect(basePiece?.className).toContain('border-0');
expect(basePiece?.className).not.toContain('rounded-[0.85rem]');
expect(basePiece?.className).not.toContain('border-2');
expect(basePiece?.querySelector('.puzzle-runtime-piece-overlay')).toBeNull();
});

View File

@@ -9,7 +9,7 @@ import {
Sparkles,
Trophy,
} from 'lucide-react';
import { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import type {
DragPuzzlePieceRequest,
@@ -23,7 +23,6 @@ import type {
SwapPuzzlePiecesRequest,
} from '../../../packages/shared/src/contracts/puzzleRuntimeSession';
import { useResolvedAssetReadUrl } from '../../hooks/useResolvedAssetReadUrl';
import { resolvePuzzleUiBackgroundSource } from '../../services/puzzle-runtime/puzzleUiBackgroundSource';
import {
createRuntimeDragInputController,
createRuntimeInputPointFromClient,
@@ -32,6 +31,7 @@ import {
type RuntimeDragInputSession,
type RuntimeInputPoint,
} from '../../services/input-devices';
import { resolvePuzzleUiBackgroundSource } from '../../services/puzzle-runtime/puzzleUiBackgroundSource';
import {
DEFAULT_RUNTIME_LEVEL_AUDIO_CONFIG,
playRuntimeClickSound,
@@ -44,12 +44,9 @@ import { useAuthUi } from '../auth/AuthUiContext';
import { PixelIcon } from '../PixelIcon';
import { ResolvedAssetImage } from '../ResolvedAssetImage';
import {
buildMergedGroupClipPath,
buildMergedGroupOutlinePath,
resolveDraggedMergedGroupLayer,
resolveDraggedPieceCellLayer,
resolveDraggedPieceLayer,
sanitizeSvgId,
} from './puzzleRuntimeShape';
type PuzzleRuntimeShellProps = {
@@ -344,7 +341,6 @@ export function PuzzleRuntimeShell({
onUseProp,
onTimeExpired,
}: PuzzleRuntimeShellProps) {
const mergedGroupSvgIdPrefix = sanitizeSvgId(useId());
const authUi = useAuthUi();
const [selectedPieceId, setSelectedPieceId] = useState<string | null>(null);
const selectedPieceIdRef = useRef<string | null>(null);
@@ -1376,7 +1372,7 @@ export function PuzzleRuntimeShell({
pieceElementRefMap.current.delete(piece.pieceId);
}}
data-piece-id={piece?.pieceId ?? undefined}
className={`puzzle-runtime-piece relative flex h-full items-center justify-center overflow-hidden rounded-[0.85rem] border-2 text-sm font-black transition ${
className={`puzzle-runtime-piece relative flex h-full items-center justify-center overflow-hidden border-0 text-sm font-black transition ${
occupied
? isSelected
? 'puzzle-runtime-piece--selected'
@@ -1386,8 +1382,8 @@ export function PuzzleRuntimeShell({
: 'puzzle-runtime-piece--empty'
} ${
isMerged
? 'transition-colors'
: 'transition-[background-color,border-color,box-shadow,opacity]'
? 'transition-opacity'
: 'transition-[opacity,transform]'
}`}
style={{
zIndex: resolveDraggedPieceLayer(
@@ -1451,9 +1447,6 @@ export function PuzzleRuntimeShell({
);
})}
{mergedGroups.map((group) => {
const outlinePath = buildMergedGroupOutlinePath(group);
const clipPath = buildMergedGroupClipPath(group);
return (
<div
key={group.groupId}
@@ -1487,57 +1480,9 @@ export function PuzzleRuntimeShell({
height: `${(group.rowSpan / board.rows) * 100}%`,
}}
>
{outlinePath ? (
<svg
aria-hidden="true"
className="pointer-events-none absolute inset-0 z-20 h-full w-full overflow-visible"
preserveAspectRatio="none"
viewBox={`0 0 ${group.colSpan} ${group.rowSpan}`}
>
<defs>
<clipPath
clipPathUnits="objectBoundingBox"
id={`${mergedGroupSvgIdPrefix}-${sanitizeSvgId(
group.groupId,
)}`}
>
<path
clipRule="evenodd"
d={clipPath}
fillRule="evenodd"
/>
</clipPath>
</defs>
<path
d={outlinePath}
data-merged-group-outline="true"
fill="transparent"
fillRule="evenodd"
/>
<path
d={outlinePath}
data-merged-group-outline-stroke="true"
fill="none"
stroke="rgba(255, 255, 255, 0.22)"
strokeLinejoin="round"
strokeWidth="2"
vectorEffect="non-scaling-stroke"
/>
</svg>
) : null}
<div
className="pointer-events-none relative z-10 grid h-full w-full touch-none overflow-hidden active:scale-[0.992]"
style={{
WebkitClipPath: outlinePath
? `url(#${mergedGroupSvgIdPrefix}-${sanitizeSvgId(
group.groupId,
)})`
: undefined,
clipPath: outlinePath
? `url(#${mergedGroupSvgIdPrefix}-${sanitizeSvgId(
group.groupId,
)})`
: undefined,
gridTemplateColumns: `repeat(${group.colSpan}, minmax(0, 1fr))`,
gridTemplateRows: `repeat(${group.rowSpan}, minmax(0, 1fr))`,
}}
@@ -1545,7 +1490,7 @@ export function PuzzleRuntimeShell({
{group.pieces.map((piece) => (
<div
key={piece.pieceId}
className="pointer-events-auto relative touch-none overflow-hidden shadow-[0_12px_30px_rgba(15,23,42,0.16)]"
className="pointer-events-auto relative touch-none overflow-hidden"
data-merged-piece-outline="true"
style={{
gridColumn: piece.localCol + 1,