Skip to content

Commit 115ece2

Browse files
committed
feat: v0.8.0 — AI prompt 自定义、智能滚动、数据管理修复及发版脚本
feat(ai): 支持自定义 System Prompt,内置高质量前端面试教练默认 prompt feat(ai): 流式输出时用户上划自动暂停自动滚动,滚回底部恢复跟随 fix(settings): 修复数据管理 tab 内容被裁切无法滚动(加 height:100% + minHeight:0) fix(dashboard): 正反馈 Banner 关闭后当天不再重复显示(localStorage 日期持久化) fix(settings): 将设置抽屉 Header 图标由太阳改为齿轮 feat(version): 设置页底部显示版本号,从 package.json 自动读取 feat(release): 新增 scripts/release.sh GitHub 自动发版脚本 chore: 版本升至 0.8.0
1 parent dcf0177 commit 115ece2

15 files changed

Lines changed: 2463 additions & 756 deletions

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "iface",
33
"private": true,
4-
"version": "1.0.0",
4+
"version": "0.8.0",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

scripts/release.sh

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
#!/usr/bin/env bash
2+
# =============================================================================
3+
# release.sh — iFace GitHub Release Script
4+
#
5+
# Usage:
6+
# ./scripts/release.sh # use version from package.json
7+
# ./scripts/release.sh 1.0.0 # override version
8+
# ./scripts/release.sh --dry-run # preview without making changes
9+
#
10+
# Requirements:
11+
# - git
12+
# - gh (GitHub CLI, `brew install gh` then `gh auth login`)
13+
# =============================================================================
14+
15+
set -euo pipefail
16+
17+
# ─── Colors ───────────────────────────────────────────────────────────────────
18+
RED='\033[0;31m'
19+
GREEN='\033[0;32m'
20+
YELLOW='\033[1;33m'
21+
BLUE='\033[0;34m'
22+
CYAN='\033[0;36m'
23+
BOLD='\033[1m'
24+
RESET='\033[0m'
25+
26+
# ─── Helpers ──────────────────────────────────────────────────────────────────
27+
log_info() { echo -e "${BLUE}${RESET} $*"; }
28+
log_success() { echo -e "${GREEN}${RESET} $*"; }
29+
log_warn() { echo -e "${YELLOW}${RESET} $*"; }
30+
log_error() { echo -e "${RED}${RESET} $*" >&2; }
31+
log_step() { echo -e "\n${BOLD}${CYAN}$*${RESET}"; }
32+
33+
die() {
34+
log_error "$*"
35+
exit 1
36+
}
37+
38+
# ─── Parse Args ───────────────────────────────────────────────────────────────
39+
DRY_RUN=false
40+
VERSION_OVERRIDE=""
41+
42+
for arg in "$@"; do
43+
case "$arg" in
44+
--dry-run) DRY_RUN=true ;;
45+
--help|-h)
46+
echo "Usage: $0 [version] [--dry-run]"
47+
echo ""
48+
echo " version Semantic version string, e.g. 1.2.0 (default: from package.json)"
49+
echo " --dry-run Preview all steps without making any changes"
50+
exit 0
51+
;;
52+
-*) die "Unknown flag: $arg. Use --help for usage." ;;
53+
*) VERSION_OVERRIDE="$arg" ;;
54+
esac
55+
done
56+
57+
# ─── Resolve project root ─────────────────────────────────────────────────────
58+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
59+
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
60+
cd "$ROOT"
61+
62+
# ─── Check prerequisites ──────────────────────────────────────────────────────
63+
log_step "Checking prerequisites"
64+
65+
command -v git >/dev/null 2>&1 || die "git is not installed"
66+
command -v gh >/dev/null 2>&1 || die "GitHub CLI (gh) is not installed. Run: brew install gh"
67+
68+
gh auth status >/dev/null 2>&1 || die "Not authenticated with GitHub CLI. Run: gh auth login"
69+
log_success "Prerequisites OK"
70+
71+
# ─── Resolve version ──────────────────────────────────────────────────────────
72+
log_step "Resolving version"
73+
74+
if [[ -n "$VERSION_OVERRIDE" ]]; then
75+
VERSION="$VERSION_OVERRIDE"
76+
log_info "Using override version: $VERSION"
77+
else
78+
VERSION="$(node -p "require('./package.json').version" 2>/dev/null)" \
79+
|| die "Could not read version from package.json"
80+
log_info "Using version from package.json: $VERSION"
81+
fi
82+
83+
# Validate semver format
84+
if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9._-]+)?$'; then
85+
die "Invalid version format: '$VERSION'. Expected semver, e.g. 1.2.3 or 1.2.3-beta.1"
86+
fi
87+
88+
TAG="v$VERSION"
89+
90+
# ─── Git status checks ────────────────────────────────────────────────────────
91+
log_step "Checking git status"
92+
93+
CURRENT_BRANCH="$(git rev-parse --abbrev-ref HEAD)"
94+
log_info "Current branch: $CURRENT_BRANCH"
95+
96+
if [[ "$CURRENT_BRANCH" != "main" && "$CURRENT_BRANCH" != "master" ]]; then
97+
log_warn "You are not on main/master branch (currently on '$CURRENT_BRANCH')"
98+
read -rp " Continue anyway? [y/N] " confirm
99+
[[ "$confirm" =~ ^[Yy]$ ]] || { log_info "Aborted."; exit 0; }
100+
fi
101+
102+
# Check for uncommitted changes
103+
if ! git diff --quiet || ! git diff --cached --quiet; then
104+
die "You have uncommitted changes. Please commit or stash them before releasing."
105+
fi
106+
log_success "Working tree is clean"
107+
108+
# Check if tag already exists
109+
if git tag --list | grep -q "^${TAG}$"; then
110+
die "Tag '${TAG}' already exists. Bump the version in package.json first."
111+
fi
112+
113+
# ─── Build ────────────────────────────────────────────────────────────────────
114+
log_step "Building project"
115+
116+
if $DRY_RUN; then
117+
log_warn "[dry-run] Skipping build"
118+
else
119+
if command -v bun >/dev/null 2>&1; then
120+
bun run build
121+
elif command -v npm >/dev/null 2>&1; then
122+
npm run build
123+
else
124+
die "No package manager found (bun / npm)"
125+
fi
126+
log_success "Build succeeded"
127+
fi
128+
129+
# ─── Generate changelog from git log ─────────────────────────────────────────
130+
log_step "Generating changelog"
131+
132+
# Find the previous tag to diff against
133+
PREV_TAG="$(git tag --sort=-version:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+' | head -n 1 || true)"
134+
135+
if [[ -z "$PREV_TAG" ]]; then
136+
log_info "No previous tag found — using full history"
137+
LOG_RANGE="HEAD"
138+
else
139+
log_info "Comparing with previous tag: $PREV_TAG"
140+
LOG_RANGE="${PREV_TAG}..HEAD"
141+
fi
142+
143+
# Categorise commits by conventional commit type
144+
get_commits_by_type() {
145+
local type="$1"
146+
git log "$LOG_RANGE" \
147+
--pretty=format:"- %s ([%h](../../commit/%H))" \
148+
--no-merges \
149+
2>/dev/null \
150+
| grep -iE "^- ${type}(\(.+\))?[!]?:" \
151+
| sed -E "s/^- ${type}(\(.+\))?[!]?:/ -/" \
152+
|| true
153+
}
154+
155+
FEAT="$(get_commits_by_type "feat")"
156+
FIX="$(get_commits_by_type "fix")"
157+
PERF="$(get_commits_by_type "perf")"
158+
REFACTOR="$(get_commits_by_type "refactor")"
159+
CHORE="$(get_commits_by_type "chore")"
160+
DOCS="$(get_commits_by_type "docs")"
161+
162+
# Commits that don't match conventional format
163+
OTHER="$(git log "$LOG_RANGE" \
164+
--pretty=format:"- %s ([%h](../../commit/%H))" \
165+
--no-merges \
166+
2>/dev/null \
167+
| grep -vE "^- (feat|fix|perf|refactor|chore|docs|test|ci|style|build)(\(.+\))?[!]?:" \
168+
|| true)"
169+
170+
RELEASE_DATE="$(date +%Y-%m-%d)"
171+
172+
# Build the release notes body
173+
NOTES=""
174+
NOTES+="## iFace ${TAG}${RELEASE_DATE}\n\n"
175+
176+
[[ -n "$FEAT" ]] && NOTES+="### ✨ 新功能\n${FEAT}\n\n"
177+
[[ -n "$FIX" ]] && NOTES+="### 🐛 问题修复\n${FIX}\n\n"
178+
[[ -n "$PERF" ]] && NOTES+="### ⚡ 性能优化\n${PERF}\n\n"
179+
[[ -n "$REFACTOR" ]] && NOTES+="### ♻️ 代码重构\n${REFACTOR}\n\n"
180+
[[ -n "$DOCS" ]] && NOTES+="### 📖 文档\n${DOCS}\n\n"
181+
[[ -n "$CHORE" ]] && NOTES+="### 🔧 其他\n${CHORE}\n\n"
182+
[[ -n "$OTHER" ]] && NOTES+="### 📦 变更\n${OTHER}\n\n"
183+
184+
if [[ -z "$FEAT$FIX$PERF$REFACTOR$DOCS$CHORE$OTHER" ]]; then
185+
NOTES+="_No changes found since last release._\n\n"
186+
fi
187+
188+
NOTES+="---\n"
189+
NOTES+="**Full Changelog**: https://github.com/dogxii/iFace/compare/${PREV_TAG:-}...${TAG}"
190+
191+
echo -e "\n${BOLD}Release Notes Preview:${RESET}"
192+
echo "─────────────────────────────────────────────"
193+
echo -e "$NOTES"
194+
echo "─────────────────────────────────────────────"
195+
196+
# ─── Confirm ──────────────────────────────────────────────────────────────────
197+
echo ""
198+
if $DRY_RUN; then
199+
log_warn "[dry-run] Would create tag '${TAG}' and GitHub Release"
200+
log_success "Dry run complete — no changes were made"
201+
exit 0
202+
fi
203+
204+
read -rp "$(echo -e "${BOLD}Create release ${TAG}?${RESET} [y/N] ")" confirm
205+
[[ "$confirm" =~ ^[Yy]$ ]] || { log_info "Aborted."; exit 0; }
206+
207+
# ─── Create and push tag ──────────────────────────────────────────────────────
208+
log_step "Creating git tag ${TAG}"
209+
210+
git tag -a "$TAG" -m "Release ${TAG}"
211+
log_success "Tag '${TAG}' created"
212+
213+
git push origin "$TAG"
214+
log_success "Tag '${TAG}' pushed to origin"
215+
216+
# ─── Create GitHub Release ────────────────────────────────────────────────────
217+
log_step "Creating GitHub Release"
218+
219+
NOTES_FILE="$(mktemp)"
220+
echo -e "$NOTES" > "$NOTES_FILE"
221+
222+
# Determine if pre-release
223+
IS_PRERELEASE=false
224+
echo "$VERSION" | grep -qE '-(alpha|beta|rc)' && IS_PRERELEASE=true
225+
226+
PRERELEASE_FLAG=""
227+
$IS_PRERELEASE && PRERELEASE_FLAG="--prerelease"
228+
229+
gh release create "$TAG" \
230+
--title "iFace ${TAG}" \
231+
--notes-file "$NOTES_FILE" \
232+
$PRERELEASE_FLAG
233+
234+
rm -f "$NOTES_FILE"
235+
236+
# ─── Done ─────────────────────────────────────────────────────────────────────
237+
echo ""
238+
echo -e "${GREEN}${BOLD}🎉 Release ${TAG} published successfully!${RESET}"
239+
echo -e " ${CYAN}https://github.com/dogxii/iFace/releases/tag/${TAG}${RESET}"

src/components/layout/Navbar.tsx

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,10 @@ export function Navbar() {
117117
padding: "5px 12px",
118118
borderRadius: 8,
119119
fontSize: 14,
120-
fontWeight: active ? 500 : 400,
120+
fontWeight: 500,
121121
color: active ? "var(--primary)" : "var(--text-2)",
122122
background: active ? "var(--primary-light)" : "transparent",
123+
letterSpacing: active ? undefined : "0.003em",
123124
textDecoration: "none",
124125
transition: "color 0.15s, background 0.15s",
125126
whiteSpace: "nowrap",
@@ -197,8 +198,8 @@ export function Navbar() {
197198
strokeLinecap="round"
198199
strokeLinejoin="round"
199200
>
201+
<path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z" />
200202
<circle cx="12" cy="12" r="3" />
201-
<path d="M19.07 4.93l-1.41 1.41M4.93 4.93l1.41 1.41M19.07 19.07l-1.41-1.41M4.93 19.07l1.41-1.41M12 2v2M12 20v2M2 12h2M20 12h2" />
202203
</svg>
203204
</button>
204205

@@ -414,19 +415,19 @@ export function Navbar() {
414415
}}
415416
>
416417
<svg
417-
width="15"
418-
height="15"
419-
viewBox="0 0 24 24"
420-
fill="none"
421-
stroke="currentColor"
422-
strokeWidth="2"
423-
strokeLinecap="round"
424-
strokeLinejoin="round"
425-
style={{ color: "var(--text-2)" }}
426-
>
427-
<circle cx="12" cy="12" r="3" />
428-
<path d="M19.07 4.93l-1.41 1.41M4.93 4.93l1.41 1.41M19.07 19.07l-1.41-1.41M4.93 19.07l1.41-1.41M12 2v2M12 20v2M2 12h2M20 12h2" />
429-
</svg>
418+
width="15"
419+
height="15"
420+
viewBox="0 0 24 24"
421+
fill="none"
422+
stroke="currentColor"
423+
strokeWidth="2"
424+
strokeLinecap="round"
425+
strokeLinejoin="round"
426+
style={{ color: "var(--text-2)" }}
427+
>
428+
<path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z" />
429+
<circle cx="12" cy="12" r="3" />
430+
</svg>
430431
设置
431432
</button>
432433
</div>

0 commit comments

Comments
 (0)