Skip to content

Add floating Caps Lock warning for password inputs on login flows#26

Merged
hzb666 merged 9 commits intodevelopfrom
copilot/add-caps-lock-indicator
Apr 1, 2026
Merged

Add floating Caps Lock warning for password inputs on login flows#26
hzb666 merged 9 commits intodevelopfrom
copilot/add-caps-lock-indicator

Conversation

Copy link
Copy Markdown

Copilot AI commented Mar 31, 2026

登录页密码输入缺少大写锁定提示,容易导致误输。该变更为密码输入增加了与现有 stale banner 风格一致的提示,并以绝对悬浮方式展示,不参与容器布局且不遮挡表单。

  • 改动范围

    • frontend/src/components/ui/PasswordInput.tsx
  • 交互与展示

    • 在密码框聚焦且检测到 CapsLock 开启时显示提示:大写锁定已开启
    • 提示通过 createPortal(..., document.body) 渲染到页面根部
    • 使用 fixed 顶部居中悬浮层 + pointer-events-none,避免影响表单交互与布局流
  • 样式对齐(参考 StaleBanner)

    • 胶囊形容器、边框、半透明背景、阴影、轻量动效(fade/slide)
    • 使用键盘图标强化语义
  • 可访问性

    • 提示容器添加 role="status"aria-live="polite"aria-atomic="true",在状态出现时可被读屏感知
  • 核心实现片段

    {showCapsLockHint && typeof document !== 'undefined'
      ? createPortal(
          <div
            className="pointer-events-none fixed inset-x-0 top-4 z-50 flex justify-center ..."
            role="status"
            aria-live="polite"
            aria-atomic="true"
          >
            <div className="inline-flex ...">
              <Keyboard className="size-4 text-amber-500" />
              <span className="truncate text-sm text-foreground">大写锁定已开启</span>
            </div>
          </div>,
          document.body
        )
      : null}
Original prompt

密码输入框增加大写锁定提示功能。样式和stalebanner类似即可,提示的时候不要在容器内部,放在绝对位置悬浮展示(不要影响布局),不要遮挡表单。完成后截图给我

@deepsource-io
Copy link
Copy Markdown

deepsource-io bot commented Mar 31, 2026

DeepSource Code Review

We reviewed changes in 376faac...8ef8530 on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

PR Report Card

Overall Grade   Security  

Reliability  

Complexity  

Hygiene  

Code Review Summary

Analyzer Status Updated (UTC) Details
JavaScript Mar 31, 2026 10:45p.m. Review ↗
Python Mar 31, 2026 10:45p.m. Review ↗
Secrets Mar 31, 2026 10:45p.m. Review ↗

@@ -1,6 +1,7 @@
import * as React from "react"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explicitly import the specific method needed


Wildcard imports are easier to write, but make it harder to pick out the specific functions or objects from a dependency that are used in a file.

Comment on lines 12 to 85
function PasswordInput({
className,
onBlur,
onFocus,
onKeyDown,
onKeyUp,
...props
}: React.InputHTMLAttributes<HTMLInputElement>) {
const [showPassword, setShowPassword] = useState(false)
const [isFocused, setIsFocused] = useState(false)
const [isCapsLockOn, setIsCapsLockOn] = useState(false)

const showCapsLockHint = isFocused && isCapsLockOn

const syncCapsLockState = (event: React.KeyboardEvent<HTMLInputElement>) => {
setIsCapsLockOn(event.getModifierState('CapsLock'))
}

return (
<div className="relative">
<Input
type={showPassword ? 'text' : 'password'}
className={cn(
"pr-10",
// 密码掩码模式增大字符间距
!showPassword && "tracking-widest",
// placeholder 保持正常
"placeholder:tracking-normal",
className
)}
{...props}
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
tabIndex={-1}
>
{showPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
</button>
</div>
<>
<div className="relative">
<Input
type={showPassword ? 'text' : 'password'}
className={cn(
"pr-10",
// 密码掩码模式增大字符间距
!showPassword && "tracking-widest",
// placeholder 保持正常
"placeholder:tracking-normal",
className
)}
onFocus={(event) => {
setIsFocused(true)
onFocus?.(event)
}}
onBlur={(event) => {
setIsFocused(false)
setIsCapsLockOn(false)
onBlur?.(event)
}}
onKeyDown={(event) => {
syncCapsLockState(event)
onKeyDown?.(event)
}}
onKeyUp={(event) => {
syncCapsLockState(event)
onKeyUp?.(event)
}}
{...props}
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
tabIndex={-1}
>
{showPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
</button>
</div>

{showCapsLockHint && typeof document !== 'undefined'
? createPortal(
<div className="pointer-events-none fixed inset-x-0 top-4 z-50 flex justify-center px-4 animate-in fade-in slide-in-from-top-2 duration-200">
<div className="inline-flex max-w-full items-center gap-2 rounded-full border border-border bg-card/96 px-3 py-2 shadow-lg backdrop-blur dark:border-2 dark:border-input">
<Keyboard className="size-4 text-amber-500" />
<span className="truncate text-sm text-foreground">大写锁定已开启</span>
</div>
</div>,
document.body
)
: null}
</>
)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unexpected function declaration in the global scope, wrap in an IIFE for a local variable, assign as global property for a global variable


It is considered a best practice to avoid 'polluting' the global scope with variables that are intended to be local to the script. Global variables created from a script can produce name collisions with global variables created from another script, which will usually lead to runtime errors or unexpected behavior. It is mostly useful for browser scripts.

@@ -10,33 +11,76 @@ import { cn } from "@/lib/utils"
*/
function PasswordInput({
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

`PasswordInput` has a cyclomatic complexity of 7 with "medium" risk


A function with high cyclomatic complexity can be hard to understand and
maintain. Cyclomatic complexity is a software metric that measures the number of
independent paths through a function. A higher cyclomatic complexity indicates
that the function has more decision points and is more complex.

@hzb666
Copy link
Copy Markdown
Owner

hzb666 commented Mar 31, 2026

@copilot 你访问一下登录页面截图给我

Comment on lines 12 to 89
function PasswordInput({
className,
onBlur,
onFocus,
onKeyDown,
onKeyUp,
...props
}: React.InputHTMLAttributes<HTMLInputElement>) {
const [showPassword, setShowPassword] = useState(false)
const [isFocused, setIsFocused] = useState(false)
const [isCapsLockOn, setIsCapsLockOn] = useState(false)

const showCapsLockHint = isFocused && isCapsLockOn

const updateCapsLockState = (event: React.KeyboardEvent<HTMLInputElement>) => {
setIsCapsLockOn(event.getModifierState('CapsLock'))
}

return (
<div className="relative">
<Input
type={showPassword ? 'text' : 'password'}
className={cn(
"pr-10",
// 密码掩码模式增大字符间距
!showPassword && "tracking-widest",
// placeholder 保持正常
"placeholder:tracking-normal",
className
)}
{...props}
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
tabIndex={-1}
>
{showPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
</button>
</div>
<>
<div className="relative">
<Input
type={showPassword ? 'text' : 'password'}
className={cn(
"pr-10",
// 密码掩码模式增大字符间距
!showPassword && "tracking-widest",
// placeholder 保持正常
"placeholder:tracking-normal",
className
)}
onFocus={(event) => {
setIsFocused(true)
onFocus?.(event)
}}
onBlur={(event) => {
setIsFocused(false)
onBlur?.(event)
}}
onKeyDown={(event) => {
updateCapsLockState(event)
onKeyDown?.(event)
}}
onKeyUp={(event) => {
updateCapsLockState(event)
onKeyUp?.(event)
}}
{...props}
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
tabIndex={-1}
>
{showPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
</button>
</div>

{showCapsLockHint && typeof document !== 'undefined'
? createPortal(
<div
className="pointer-events-none fixed inset-x-0 top-4 z-50 flex justify-center px-4 animate-in fade-in slide-in-from-top-2 duration-200"
role="status"
aria-live="polite"
aria-atomic="true"
>
<div className="inline-flex max-w-full items-center gap-2 rounded-full border border-border bg-card/96 px-3 py-2 shadow-lg backdrop-blur dark:border-2 dark:border-input">
<Keyboard className="size-4 text-amber-500" />
<span className="truncate text-sm text-foreground">大写锁定已开启</span>
</div>
</div>,
document.body
)
: null}
</>
)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unexpected function declaration in the global scope, wrap in an IIFE for a local variable, assign as global property for a global variable


It is considered a best practice to avoid 'polluting' the global scope with variables that are intended to be local to the script. Global variables created from a script can produce name collisions with global variables created from another script, which will usually lead to runtime errors or unexpected behavior. It is mostly useful for browser scripts.

Copilot AI changed the title [WIP] Add caps lock indicator for password input field Add floating Caps Lock warning for password inputs on login flows Mar 31, 2026
Copilot AI requested a review from hzb666 March 31, 2026 21:12
Copy link
Copy Markdown
Author

Copilot AI commented Mar 31, 2026

@copilot 你访问一下登录页面截图给我

已访问登录页并确认截图可用,当前分支最新提交是 be1a506。登录页截图如下:
https://github.com/user-attachments/assets/d3537c5f-c739-4f48-8a94-e56a85674449

@hzb666 hzb666 marked this pull request as ready for review March 31, 2026 21:19
@hzb666 hzb666 requested a review from Copilot March 31, 2026 21:19
@kilo-code-bot
Copy link
Copy Markdown

kilo-code-bot bot commented Mar 31, 2026

Code Review Summary

Status: 1 Warning Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
frontend/src/components/ui/PasswordInput.tsx 50 Duplicate setState call - setIsCapsLockOn(false) is called twice on consecutive lines (lines 49 and 50). The second call is redundant.
Files Reviewed (2 files)
  • frontend/src/lib/staticAssets.ts - no new issues (version bump to 260401, already noted in existing comment)
  • frontend/src/components/ui/PasswordInput.tsx - 1 issue (duplicate setState call at line 50, still present)

Fix these issues in Kilo Cloud

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ffac72c067

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +47 to +49
onBlur={(event) => {
setIsFocused(false)
onBlur?.(event)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clear stale Caps Lock state on blur

isCapsLockOn is only updated from keyboard events while the input is focused, so if a user turns Caps Lock off after leaving the field and then refocuses it, the banner can still appear even though Caps Lock is actually off. This creates a false warning on login flows and is reproducible whenever Caps Lock is toggled while the password field is unfocused; resetting or re-evaluating the state on blur/focus would prevent the stale indicator.

Useful? React with 👍 / 👎.

@hzb666
Copy link
Copy Markdown
Owner

hzb666 commented Mar 31, 2026

@copilot isCapsLockOn is only updated from keyboard events while the input is focused, so if a user turns Caps Lock off after leaving the field and then refocuses it, the banner can still appear even though Caps Lock is actually off. This creates a false warning on login flows and is reproducible whenever Caps Lock is toggled while the password field is unfocused; resetting or re-evaluating the state on blur/focus would prevent the stale indicator.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a global, floating Caps Lock warning for password inputs (styled similarly to the existing StaleBanner) to reduce login-password entry mistakes, using a createPortal(..., document.body) fixed-position banner that doesn’t affect layout or block form interaction.

Changes:

  • Add Caps Lock detection + focus tracking to PasswordInput and render a fixed, non-interactive warning banner via portal.
  • Update staticAssets.ts asset version/URLs (cache-busting) as part of the committed generated assets module.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
frontend/src/components/ui/PasswordInput.tsx Adds Caps Lock state tracking and a fixed, top-of-screen portal warning banner with ARIA live-region attributes.
frontend/src/lib/staticAssets.ts Updates the committed generated lib-asset version and versioned URLs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread frontend/src/components/ui/PasswordInput.tsx
Comment thread frontend/src/lib/staticAssets.ts Outdated
Comment on lines +1 to +7
export const LIB_ASSET_VERSION = '260331'

export const LIB_ASSETS = {
rdkitScriptUrl: '/lib/RDKit_minimal-260401.js',
rdkitWasmUrl: '/lib/RDKit_minimal-260401.wasm',
localFontUrl: '/lib/SourceHanSansCN-VF-260401.woff2',
localFontPreferenceValue: 'source-han-sans-cn-vf-260401',
rdkitScriptUrl: '/lib/RDKit_minimal-260331.js',
rdkitWasmUrl: '/lib/RDKit_minimal-260331.wasm',
localFontUrl: '/lib/SourceHanSansCN-VF-260331.woff2',
localFontPreferenceValue: 'source-han-sans-cn-vf-260331',
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR modifies staticAssets.ts (RDKit/font cache-busting version + URLs), but the PR description says the change scope is only PasswordInput.tsx. If this version bump isn’t intentional, consider reverting it to avoid unnecessary cache invalidation and unrelated diffs; if it is intentional, please reflect it in the PR description.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="frontend/src/components/ui/PasswordInput.tsx">

<violation number="1" location="frontend/src/components/ui/PasswordInput.tsx:48">
P2: Reset Caps Lock state on blur to prevent a stale warning from appearing when the input is refocused after Caps Lock is toggled while unfocused.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

Comment thread frontend/src/components/ui/PasswordInput.tsx
Comment on lines 12 to 90
function PasswordInput({
className,
onBlur,
onFocus,
onKeyDown,
onKeyUp,
...props
}: React.InputHTMLAttributes<HTMLInputElement>) {
const [showPassword, setShowPassword] = useState(false)
const [isFocused, setIsFocused] = useState(false)
const [isCapsLockOn, setIsCapsLockOn] = useState(false)

const showCapsLockHint = isFocused && isCapsLockOn

const updateCapsLockState = (event: React.KeyboardEvent<HTMLInputElement>) => {
setIsCapsLockOn(event.getModifierState('CapsLock'))
}

return (
<div className="relative">
<Input
type={showPassword ? 'text' : 'password'}
className={cn(
"pr-10",
// 密码掩码模式增大字符间距
!showPassword && "tracking-widest",
// placeholder 保持正常
"placeholder:tracking-normal",
className
)}
{...props}
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
tabIndex={-1}
>
{showPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
</button>
</div>
<>
<div className="relative">
<Input
type={showPassword ? 'text' : 'password'}
className={cn(
"pr-10",
// 密码掩码模式增大字符间距
!showPassword && "tracking-widest",
// placeholder 保持正常
"placeholder:tracking-normal",
className
)}
onFocus={(event) => {
setIsFocused(true)
onFocus?.(event)
}}
onBlur={(event) => {
setIsFocused(false)
setIsCapsLockOn(false)
onBlur?.(event)
}}
onKeyDown={(event) => {
updateCapsLockState(event)
onKeyDown?.(event)
}}
onKeyUp={(event) => {
updateCapsLockState(event)
onKeyUp?.(event)
}}
{...props}
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
tabIndex={-1}
>
{showPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
</button>
</div>

{showCapsLockHint && typeof document !== 'undefined'
? createPortal(
<div
className="pointer-events-none fixed inset-x-0 top-4 z-50 flex justify-center px-4 animate-in fade-in slide-in-from-top-2 duration-200"
role="status"
aria-live="polite"
aria-atomic="true"
>
<div className="inline-flex max-w-full items-center gap-2 rounded-full border border-border bg-card/96 px-3 py-2 shadow-lg backdrop-blur dark:border-2 dark:border-input">
<Keyboard className="size-4 text-amber-500" />
<span className="truncate text-sm text-foreground">大写锁定已开启</span>
</div>
</div>,
document.body
)
: null}
</>
)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unexpected function declaration in the global scope, wrap in an IIFE for a local variable, assign as global property for a global variable


It is considered a best practice to avoid 'polluting' the global scope with variables that are intended to be local to the script. Global variables created from a script can produce name collisions with global variables created from another script, which will usually lead to runtime errors or unexpected behavior. It is mostly useful for browser scripts.

Copy link
Copy Markdown
Author

Copilot AI commented Mar 31, 2026

@copilot isCapsLockOn is only updated from keyboard events while the input is focused, so if a user turns Caps Lock off after leaving the field and then refocuses it, the banner can still appear even ...

已修复,提交 ce06250 在密码框 onBlur 时重置 isCapsLockOn,避免失焦期间切换 CapsLock 后回到输入框出现陈旧提示。截图如下:
https://github.com/user-attachments/assets/b4a6b79e-17ad-4843-abaf-ec3096d3d9f9

Copilot AI requested a review from hzb666 March 31, 2026 21:30
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
onBlur={(event) => {
setIsFocused(false)
setIsCapsLockOn(false)
setIsCapsLockOn(false)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Duplicate setState call - setIsCapsLockOn(false) is called twice on consecutive lines (lines 49 and 50). The second call is redundant.

Comment on lines 12 to 91
function PasswordInput({
className,
onBlur,
onFocus,
onKeyDown,
onKeyUp,
...props
}: React.InputHTMLAttributes<HTMLInputElement>) {
const [showPassword, setShowPassword] = useState(false)
const [isFocused, setIsFocused] = useState(false)
const [isCapsLockOn, setIsCapsLockOn] = useState(false)

const showCapsLockHint = isFocused && isCapsLockOn

const updateCapsLockState = (event: React.KeyboardEvent<HTMLInputElement>) => {
setIsCapsLockOn(event.getModifierState('CapsLock'))
}

return (
<div className="relative">
<Input
type={showPassword ? 'text' : 'password'}
className={cn(
"pr-10",
// 密码掩码模式增大字符间距
!showPassword && "tracking-widest",
// placeholder 保持正常
"placeholder:tracking-normal",
className
)}
{...props}
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
tabIndex={-1}
>
{showPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
</button>
</div>
<>
<div className="relative">
<Input
type={showPassword ? 'text' : 'password'}
className={cn(
"pr-10",
// 密码掩码模式增大字符间距
!showPassword && "tracking-widest",
// placeholder 保持正常
"placeholder:tracking-normal",
className
)}
onFocus={(event) => {
setIsFocused(true)
onFocus?.(event)
}}
onBlur={(event) => {
setIsFocused(false)
setIsCapsLockOn(false)
setIsCapsLockOn(false)
onBlur?.(event)
}}
onKeyDown={(event) => {
updateCapsLockState(event)
onKeyDown?.(event)
}}
onKeyUp={(event) => {
updateCapsLockState(event)
onKeyUp?.(event)
}}
{...props}
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
tabIndex={-1}
>
{showPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
</button>
</div>

{showCapsLockHint && typeof document !== 'undefined'
? createPortal(
<div
className="pointer-events-none fixed inset-x-0 top-4 z-50 flex justify-center px-4 animate-in fade-in slide-in-from-top-2 duration-200"
role="status"
aria-live="polite"
aria-atomic="true"
>
<div className="inline-flex max-w-full items-center gap-2 rounded-full border border-border bg-card/96 px-3 py-2 shadow-lg backdrop-blur dark:border-2 dark:border-input">
<Keyboard className="size-4 text-amber-500" />
<span className="truncate text-sm text-foreground">大写锁定已开启</span>
</div>
</div>,
document.body
)
: null}
</>
)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unexpected function declaration in the global scope, wrap in an IIFE for a local variable, assign as global property for a global variable


It is considered a best practice to avoid 'polluting' the global scope with variables that are intended to be local to the script. Global variables created from a script can produce name collisions with global variables created from another script, which will usually lead to runtime errors or unexpected behavior. It is mostly useful for browser scripts.

@hzb666
Copy link
Copy Markdown
Owner

hzb666 commented Mar 31, 2026

@copilot 修复一下所有评论提到的问题

Copy link
Copy Markdown
Author

Copilot AI commented Mar 31, 2026

@copilot 修复一下所有评论提到的问题

已全部处理:ce06250 修复了密码框失焦后 CapsLock 状态陈旧问题,8ef8530 清理了评论里提到的 staticAssets.ts 非预期范围改动。截图:https://github.com/user-attachments/assets/e73662ca-7a6c-4dae-ae66-67db5513a110

Copilot AI requested a review from hzb666 March 31, 2026 22:46
@hzb666 hzb666 merged commit 4af4f70 into develop Apr 1, 2026
5 of 6 checks passed
@hzb666 hzb666 deleted the copilot/add-caps-lock-indicator branch April 1, 2026 12:55
hzb666 added a commit that referenced this pull request Apr 1, 2026
* refactor: 简化部分页面代码,重构localstorage

* docs(wiki): 更新wiki

* refactor: localstorage新文件

* refactor(backend): 简化后端业务逻辑代码

* fix(auth): 修复登录态与会话边界

* feat(browser-extension,frontend): 增强购物车导入功能与订单类型自动识别

- 新增浏览器扩展购物车标签页选择逻辑(cart-tab-selection.js)
- 添加订单类型自动检测功能
- 新增站点配置工具函数,统一 URL 构建逻辑
- 重构前端购物车导入页面,将模型、控制器拆分到独立文件

* refactor(auth): 收口会话缓存边界与校验流程

- 统一 SessionCacheIdentity 缓存边界
- 简化 SSE 控制消息解析分支
- 精简静态路径清洗校验流程

* refactor(core): 整理后端校验流程和辅助逻辑

- 整理日志聚合、导入和外部查询的辅助代码
- 统一会话缓存同步和 SSE 订阅参数
- 简化数据库、缓存和拼音工具的参数处理

* feat(cache): 增加版本化缓存失效机制

- 新增 CACHE_VERSION 配置
- 后端在版本变化时清理 Redis 前缀缓存
- 后端在版本变化时撤销全部用户会话
- 前端启动前探测 cache_version 并清理本地缓存
  - 保持业务数据与数据库结构不受影响

* style: 新增鉴权骨架屏

- 新增鉴权骨架屏组件,仪表盘、表格页、导入页三个样式

* feature: 在每个业务页面接入sse

- 接入sse,实现静默更新或提醒更新的不同逻辑
- 完善stalebanner组件

* style: 完善前缀输入框样式

* feat(api):完善操作日志与导入链路

- 新增库存申购用户操作日志模型与服务
- 接入用户订单库存关键路径日志写入
- 用户日志聚合改读日志表并补计数查询
- 导入改为预览确认两阶段并记录入库日志
- 会话流程改为单事务提交后执行副作用
- 新增日志归档脚本并补日志查询索引

* feat(ui):改造导入与日志页面

- 导入改为预览与确认两阶段流程
- 操作日志页扩展多类型展示与详情渲染
- 常用货架搜索高亮与别名展示优化
- 接入版本化静态资源与生成脚本测试
- 调整前端构建压缩与资源引用配置

* build(config):更新容器与Nginx配置

- 前端镜像改为 Alpine 并启用 Brotli 模块
- 增加 nginx 主配置并调整站点配置
- 补充静态资源缓存与编码协商头
- 调整忽略规则以纳入前端脚本目录

* docs(readme):精简技术栈徽章

- 移除重复与冗余的技术徽章展示
- 保留核心运行环境与许可证标识

* feat(db): 新增CommonShelf与CAS主数据模型

- 新增common_shelf数据模型
- 新增chemical_name_map模型
- 调整模型导出与数据库初始化
- 删除旧common搜索相关服务

* feat(api): 实现CommonShelf与CAS主数据接口

- 新增CAS主数据管理接口
- 重写CommonShelf核心接口
- 调整订单到货与入库相关流程
- 复用并整理货架生成查询服务
- 补齐操作日志与接口挂载

* feat(export): 调整库存与CommonShelf导出日志

- 调整库存导出字段结构
- 新增CommonShelf导出能力
- 补齐纯度备注相关日志字段
- 同步导出服务实现

* feat(ui): 重构CommonShelf并接入CAS主数据

- 重构CommonShelf页面与弹窗结构
- 接入CAS主数据管理与补录流程
- 调整表单配置校验与表格列
- 同步日志展示与到货录入交互
- 更新前端依赖与静态资源脚本

* fix: use SET NULL instead of CASCADE for operator_id in common_shelf_operation_log

Prevents silent deletion of audit log history when a user is deleted.
Aligns with the pattern used by reagent_order_operation_log and
consumable_order_operation_log.

* fix: address PR review comments on FTS param name, header reading, number input onChange, and SQLite RETURNING fallback

Agent-Logs-Url: https://github.com/hzb666/LabStorageManager/sessions/70d909b4-1246-4e9d-a115-72f5f98fda88

Co-authored-by: hzb666 <29155232+hzb666@users.noreply.github.com>

* fix: add SQLite RETURNING fallback to all DELETE...RETURNING call sites via shared db_compat helpers

Agent-Logs-Url: https://github.com/hzb666/LabStorageManager/sessions/d6b5a2ea-0c3f-4935-9a03-43cde20c8f49

Co-authored-by: hzb666 <29155232+hzb666@users.noreply.github.com>

* docs(wiki): sync CommonShelf/CAS master data documentation with current implementation (#25)

* Add floating Caps Lock warning for password inputs on login flows (#26)

* feat(frontend): add floating caps lock hint for password input
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants