style: format the repository with prettier

These files were already out of step with the project's Prettier config
before this branch of work; running `pnpm format` finally swept them up.
No semantic changes.

Kept separate from the functional commits so the reformatting does not
bury them in diff noise.
This commit is contained in:
zihluwang committed 2026-09-25 15:37:59 +08:00
1 parent 6800f7251d
commit 861852961f
50 files changed
+643 -678

No files matched your search

+20 -25
View File
@@ -49,8 +49,8 @@ Minimal setup:
```tsx ```tsx
// theme/index.tsx // theme/index.tsx
import './index.css'; // optional import "./index.css" // optional
export * from '@rspress/core/theme-original'; export * from "@rspress/core/theme-original"
``` ```
**Critical import rule**: Inside `theme/` files, always import from `@rspress/core/theme-original`. The path `@rspress/core/theme` resolves to your own `theme/index.tsx`, which causes circular imports. (In `docs/` MDX files, `@rspress/core/theme` is fine — it correctly points to your custom theme.) **Critical import rule**: Inside `theme/` files, always import from `@rspress/core/theme-original`. The path `@rspress/core/theme` resolves to your own `theme/index.tsx`, which causes circular imports. (In `docs/` MDX files, `@rspress/core/theme` is fine — it correctly points to your custom theme.)
@@ -80,8 +80,8 @@ Override CSS custom properties for brand colors, backgrounds, text, code blocks,
```ts ```ts
// rspress.config.ts // rspress.config.ts
export default defineConfig({ export default defineConfig({
globalStyles: path.join(__dirname, 'styles/custom.css'), globalStyles: path.join(__dirname, "styles/custom.css"),
}); })
``` ```
> **Full variable list**: Read `references/css-variables.md` for all available CSS variables with light/dark defaults. > **Full variable list**: Read `references/css-variables.md` for all available CSS variables with light/dark defaults.
@@ -104,13 +104,11 @@ Inject content at specific positions in the layout without replacing built-in co
```tsx ```tsx
// theme/index.tsx // theme/index.tsx
import { Layout as OriginalLayout } from '@rspress/core/theme-original'; import { Layout as OriginalLayout } from "@rspress/core/theme-original"
export * from '@rspress/core/theme-original'; export * from "@rspress/core/theme-original"
export function Layout() { export function Layout() {
return ( return <OriginalLayout beforeNavTitle={<MyLogo />} bottom={<CustomFooter />} />
<OriginalLayout beforeNavTitle={<MyLogo />} bottom={<CustomFooter />} />
);
} }
``` ```
@@ -132,8 +130,8 @@ rspress eject DocFooter # eject to theme/components/DocFooter/
Then re-export in `theme/index.tsx` (named export takes precedence over the wildcard): Then re-export in `theme/index.tsx` (named export takes precedence over the wildcard):
```tsx ```tsx
export * from '@rspress/core/theme-original'; export * from "@rspress/core/theme-original"
export { DocFooter } from './components/DocFooter'; export { DocFooter } from "./components/DocFooter"
``` ```
> **Component list & patterns**: Read `references/eject-components.md` for available components, workflow, and common patterns. > **Component list & patterns**: Read `references/eject-components.md` for available components, workflow, and common patterns.
@@ -147,38 +145,38 @@ Rspress has 27 built-in icons used across the UI. You can replace any of them by
**Icon type**: Each icon is a React component or a URL string: **Icon type**: Each icon is a React component or a URL string:
```ts ```ts
import type { FC, SVGProps } from 'react'; import type { FC, SVGProps } from "react"
type Icon = FC<SVGProps<SVGSVGElement>> | string; type Icon = FC<SVGProps<SVGSVGElement>> | string
``` ```
**Example 1** — Replace an icon with a custom SVG component: **Example 1** — Replace an icon with a custom SVG component:
```tsx ```tsx
// theme/index.tsx // theme/index.tsx
export * from '@rspress/core/theme-original'; export * from "@rspress/core/theme-original"
// Named export overrides the wildcard — replaces the GitHub icon site-wide // Named export overrides the wildcard — replaces the GitHub icon site-wide
export const IconGithub = (props: React.SVGProps<SVGSVGElement>) => ( export const IconGithub = (props: React.SVGProps<SVGSVGElement>) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" {...props}> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" {...props}>
<path d="M12 2C6.477 2 2 6.484 2 12.017c0 ..." fill="currentColor" /> <path d="M12 2C6.477 2 2 6.484 2 12.017c0 ..." fill="currentColor" />
</svg> </svg>
); )
``` ```
**Example 2** — Use an SVGR import: **Example 2** — Use an SVGR import:
```tsx ```tsx
// theme/index.tsx // theme/index.tsx
export * from '@rspress/core/theme-original'; export * from "@rspress/core/theme-original"
import CustomGithubIcon from './icons/github.svg?react'; import CustomGithubIcon from "./icons/github.svg?react"
export const IconGithub = CustomGithubIcon; export const IconGithub = CustomGithubIcon
``` ```
**Using `SvgWrapper` in MDX or custom components**: **Using `SvgWrapper` in MDX or custom components**:
```mdx ```mdx
import { SvgWrapper, IconGithub } from '@rspress/core/theme'; import { SvgWrapper, IconGithub } from "@rspress/core/theme"
<SvgWrapper icon={IconGithub} width={24} height={24} /> <SvgWrapper icon={IconGithub} width={24} height={24} />
``` ```
@@ -197,13 +195,10 @@ For components that should render on every page without theme overrides:
// rspress.config.ts // rspress.config.ts
export default defineConfig({ export default defineConfig({
globalUIComponents: [ globalUIComponents: [
path.join(__dirname, 'components', 'BackToTop.tsx'), path.join(__dirname, "components", "BackToTop.tsx"),
[ [path.join(__dirname, "components", "Analytics.tsx"), { trackingId: "..." }],
path.join(__dirname, 'components', 'Analytics.tsx'),
{ trackingId: '...' },
],
], ],
}); })
``` ```
--- ---
@@ -50,8 +50,8 @@ Ejected source is placed in `theme/components/<ComponentName>/`.
```tsx ```tsx
// theme/index.tsx // theme/index.tsx
export * from '@rspress/core/theme-original'; export * from "@rspress/core/theme-original"
export { DocFooter } from './components/DocFooter'; export { DocFooter } from "./components/DocFooter"
``` ```
The named export takes precedence over the wildcard re-export, so Rspress uses your custom version. The named export takes precedence over the wildcard re-export, so Rspress uses your custom version.
@@ -64,21 +64,21 @@ The most common eject use case is wrapping the entire app in a context provider
```tsx ```tsx
// theme/components/Root/index.tsx // theme/components/Root/index.tsx
import type { RootProps } from '@rspress/core/theme'; import type { RootProps } from "@rspress/core/theme"
export function Root({ children }: RootProps) { export function Root({ children }: RootProps) {
return ( return (
<ThemeProvider> <ThemeProvider>
<AnalyticsProvider>{children}</AnalyticsProvider> <AnalyticsProvider>{children}</AnalyticsProvider>
</ThemeProvider> </ThemeProvider>
); )
} }
``` ```
```tsx ```tsx
// theme/index.tsx // theme/index.tsx
export * from '@rspress/core/theme-original'; export * from "@rspress/core/theme-original"
export { Root } from './components/Root'; export { Root } from "./components/Root"
``` ```
## Common Pattern: Custom Home Page (HomeLayout) ## Common Pattern: Custom Home Page (HomeLayout)
@@ -87,12 +87,12 @@ When the default home page structure (Hero + Features) doesn't meet the design r
```tsx ```tsx
// theme/components/HomeLayout/index.tsx // theme/components/HomeLayout/index.tsx
import { useSite, useLang } from '@rspress/core/runtime'; import { useSite, useLang } from "@rspress/core/runtime"
export function HomeLayout() { export function HomeLayout() {
const site = useSite(); const site = useSite()
const lang = useLang(); const lang = useLang()
const { title, description } = site.siteData; const { title, description } = site.siteData
return ( return (
<div className="custom-home"> <div className="custom-home">
@@ -100,10 +100,7 @@ export function HomeLayout() {
<h1>{title}</h1> <h1>{title}</h1>
<p>{description}</p> <p>{description}</p>
<div className="hero-actions"> <div className="hero-actions">
<a <a href={lang === "zh" ? "/zh/guide/start" : "/guide/start"} className="primary-btn">
href={lang === 'zh' ? '/zh/guide/start' : '/guide/start'}
className="primary-btn"
>
Get Started Get Started
</a> </a>
<a href="https://github.com/..." className="secondary-btn"> <a href="https://github.com/..." className="secondary-btn">
@@ -116,14 +113,14 @@ export function HomeLayout() {
{/* Custom content: testimonials, stats, demos, etc. */} {/* Custom content: testimonials, stats, demos, etc. */}
</section> </section>
</div> </div>
); )
} }
``` ```
```tsx ```tsx
// theme/index.tsx // theme/index.tsx
export * from '@rspress/core/theme-original'; export * from "@rspress/core/theme-original"
export { HomeLayout } from './components/HomeLayout'; export { HomeLayout } from "./components/HomeLayout"
``` ```
The named export overrides the built-in `HomeLayout` from the wildcard re-export — no need to eject first. The named export overrides the built-in `HomeLayout` from the wildcard re-export — no need to eject first.
@@ -134,16 +131,16 @@ If you only need to add content before/after the Hero or Features sections (with
```tsx ```tsx
// theme/components/DocFooter/index.tsx // theme/components/DocFooter/index.tsx
import { useFrontmatter } from '@rspress/core/runtime'; import { useFrontmatter } from "@rspress/core/runtime"
export function DocFooter() { export function DocFooter() {
const frontmatter = useFrontmatter(); const frontmatter = useFrontmatter()
return ( return (
<footer className="custom-doc-footer"> <footer className="custom-doc-footer">
{frontmatter.author && <span>Author: {frontmatter.author}</span>} {frontmatter.author && <span>Author: {frontmatter.author}</span>}
<a href="https://github.com/...">Edit this page</a> <a href="https://github.com/...">Edit this page</a>
</footer> </footer>
); )
} }
``` ```
@@ -71,11 +71,11 @@ All examples below follow the same structure in `theme/index.tsx`. The key parts
```tsx ```tsx
// theme/index.tsx // theme/index.tsx
import { Layout as OriginalLayout } from '@rspress/core/theme-original'; import { Layout as OriginalLayout } from "@rspress/core/theme-original"
export * from '@rspress/core/theme-original'; export * from "@rspress/core/theme-original"
export function Layout() { export function Layout() {
return <OriginalLayout beforeNavTitle={<MyLogo />} />; return <OriginalLayout beforeNavTitle={<MyLogo />} />
} }
``` ```
@@ -83,8 +83,8 @@ export function Layout() {
```tsx ```tsx
// theme/index.tsx // theme/index.tsx
import { Layout as OriginalLayout } from '@rspress/core/theme-original'; import { Layout as OriginalLayout } from "@rspress/core/theme-original"
export * from '@rspress/core/theme-original'; export * from "@rspress/core/theme-original"
export function Layout() { export function Layout() {
return ( return (
@@ -93,7 +93,7 @@ export function Layout() {
bottom={<footer>© 2025 My Company</footer>} bottom={<footer>© 2025 My Company</footer>}
afterOutline={<div>Related resources</div>} afterOutline={<div>Related resources</div>}
/> />
); )
} }
``` ```
@@ -101,17 +101,17 @@ export function Layout() {
```tsx ```tsx
// theme/index.tsx // theme/index.tsx
import { Layout as OriginalLayout } from '@rspress/core/theme-original'; import { Layout as OriginalLayout } from "@rspress/core/theme-original"
import { useLang } from '@rspress/core/runtime'; import { useLang } from "@rspress/core/runtime"
export * from '@rspress/core/theme-original'; export * from "@rspress/core/theme-original"
function LocalizedBanner() { function LocalizedBanner() {
const lang = useLang(); const lang = useLang()
return <div>{lang === 'zh' ? '欢迎' : 'Welcome'}</div>; return <div>{lang === "zh" ? "欢迎" : "Welcome"}</div>
} }
export function Layout() { export function Layout() {
return <OriginalLayout top={<LocalizedBanner />} />; return <OriginalLayout top={<LocalizedBanner />} />
} }
``` ```
@@ -121,17 +121,15 @@ The `components` slot accepts a `Record<string, React.ComponentType>` to overrid
```tsx ```tsx
// theme/index.tsx // theme/index.tsx
import { Layout as OriginalLayout } from '@rspress/core/theme-original'; import { Layout as OriginalLayout } from "@rspress/core/theme-original"
export * from '@rspress/core/theme-original'; export * from "@rspress/core/theme-original"
function CustomH1({ children }: { children: React.ReactNode }) { function CustomH1({ children }: { children: React.ReactNode }) {
return ( return <h1 style={{ borderBottom: "2px solid var(--rp-c-brand)" }}>{children}</h1>
<h1 style={{ borderBottom: '2px solid var(--rp-c-brand)' }}>{children}</h1>
);
} }
export function Layout() { export function Layout() {
return <OriginalLayout components={{ h1: CustomH1 }} />; return <OriginalLayout components={{ h1: CustomH1 }} />
} }
``` ```
+1 -1
View File
@@ -5,7 +5,7 @@ This project incorporates the following third-party open source software.
## Direct Dependencies ## Direct Dependencies
| Package | License | | Package | License |
|-------------------|---------| | ----------------- | ------- |
| @rspress/core | MIT | | @rspress/core | MIT |
| react | MIT | | react | MIT |
| react-dom | MIT | | react-dom | MIT |
+2 -2
View File
@@ -11,7 +11,7 @@ author:
## When to Add Seasonings ## When to Add Seasonings
| Seasoning Type | Timing | Suitable Dishes | Effect | | Seasoning Type | Timing | Suitable Dishes | Effect |
|----------------------------------------|----------------------------------------|------------------------------------------------------|-------------------------------------------------------------------------------| | -------------------------------------- | -------------------------------------- | ---------------------------------------------------- | ----------------------------------------------------------------------------- |
| **Salt** | Last | Leafy greens | Prevents leaves from wilting and releasing water, keeps them crisp and tender | | **Salt** | Last | Leafy greens | Prevents leaves from wilting and releasing water, keeps them crisp and tender |
| | Mid-way | Shredded potatoes, green beans, garlic shoots | Better flavour absorption | | | Mid-way | Shredded potatoes, green beans, garlic shoots | Better flavour absorption |
| **MSG, Light Soy Sauce, Oyster Sauce** | Towards the end | Dishes needing umami and aroma enhancement | Adds umami and aroma; high heat destroys freshness and fragrance | | **MSG, Light Soy Sauce, Oyster Sauce** | Towards the end | Dishes needing umami and aroma enhancement | Adds umami and aroma; high heat destroys freshness and fragrance |
@@ -24,7 +24,7 @@ author:
## Quick Reference ## Quick Reference
| Seasoning | Timing | Effect | Example | | Seasoning | Timing | Effect | Example |
|-----------------------------------------------|------------------------------|-----------------------------------------------------------------------------|---------------------------------------------------------------------------| | --------------------------------------------- | ---------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| **Salt** | 10–15 seconds before serving | Prevents premature water loss and wilting of leafy greens; keeps them crisp | Sprinkle salt and toss before turning off the heat for lettuce or spinach | | **Salt** | 10–15 seconds before serving | Prevents premature water loss and wilting of leafy greens; keeps them crisp | Sprinkle salt and toss before turning off the heat for lettuce or spinach |
| **Minced Garlic / Ginger** | First, after oil is hot | Releases fragrance and enhances flavour | Sauté minced garlic in hot oil, then add greens | | **Minced Garlic / Ginger** | First, after oil is hot | Releases fragrance and enhances flavour | Sauté minced garlic in hot oil, then add greens |
| **Light Soy Sauce / Oyster Sauce** (optional) | After salt | Adds umami; use sparingly to avoid overpowering the vegetables | Add 1–2 drops of light soy sauce after salting Shanghai greens | | **Light Soy Sauce / Oyster Sauce** (optional) | After salt | Adds umami; use sparingly to avoid overpowering the vegetables | Add 1–2 drops of light soy sauce after salting Shanghai greens |
@@ -17,13 +17,13 @@ author:
```javascript ```javascript
function processResponse(response) { function processResponse(response) {
if (response.status === 200 && response.data) { if (response.status === 200 && response.data) {
return { success: true, data: response.data }; return { success: true, data: response.data }
} else if (response.status === 404) { } else if (response.status === 404) {
return { success: false, error: 'Not found' }; return { success: false, error: "Not found" }
} else if (response.status >= 500) { } else if (response.status >= 500) {
return { success: false, error: 'Server error' }; return { success: false, error: "Server error" }
} else { } else {
return { success: false, error: 'Unknown error' }; return { success: false, error: "Unknown error" }
} }
} }
``` ```
+3 -3
View File
@@ -19,11 +19,11 @@ sound more confident and professional.
## Scenario Cheatsheet ## Scenario Cheatsheet
| Scenario | ❌ Don't Use (sounds…) | ✅ Use Instead (sounds…) | Why It Works | | Scenario | ❌ Don't Use (sounds…) | ✅ Use Instead (sounds…) | Why It Works |
|----------------------------|----------------------------------------------|-----------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| | -------------------------- | -------------------------------------------- | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Late reply | **Sorry for the delay** | **Thanks for your patience** | Swaps an apology for gratitude — the focus shifts from "I was wrong" to "you were generous," acknowledging the delay while making the recipient feel respected | | Late reply | **Sorry for the delay** | **Thanks for your patience** | Swaps an apology for gratitude — the focus shifts from "I was wrong" to "you were generous," acknowledging the delay while making the recipient feel respected |
| Scheduling | **What works best for you?** | **Could you do …?** | The former throws the decision entirely back to the other person; the latter offers a concrete option, cutting down on back-and-forth | | Scheduling | **What works best for you?** | **Could you do …?** | The former throws the decision entirely back to the other person; the latter offers a concrete option, cutting down on back-and-forth |
| After helping someone | **No problem / No worries** | **Always happy to help** | The former implies the task *could* have been a problem; the latter signals you enjoyed it and would happily do it again | | After helping someone | **No problem / No worries** | **Always happy to help** | The former implies the task _could_ have been a problem; the latter signals you enjoyed it and would happily do it again |
| Making a suggestion | **I think maybe we should …** | **It'd be best if we …** | The former oozes hesitation and self-doubt; the latter delivers a clear judgment — like someone with experience making a decision | | Making a suggestion | **I think maybe we should …** | **It'd be best if we …** | The former oozes hesitation and self-doubt; the latter delivers a clear judgment — like someone with experience making a decision |
| Text isn't working | **_Spending 30 minutes rewriting an email_** | **It'd be easier to discuss in person** | Recognising the medium itself is the bottleneck and switching channels can be the most efficient move | | Text isn't working | **_Spending 30 minutes rewriting an email_** | **It'd be easier to discuss in person** | Recognising the medium itself is the bottleneck and switching channels can be the most efficient move |
| Checking for understanding | **Hopefully that makes sense?** | **Let me know if you have questions** | The former betrays doubt about your own clarity; the latter calmly shares responsibility — the reader now has an action item too | | Checking for understanding | **Hopefully that makes sense?** | **Let me know if you have questions** | The former betrays doubt about your own clarity; the latter calmly shares responsibility — the reader now has an action item too |
@@ -8,7 +8,7 @@ tags:
--- ---
> This article was originally written by **落格博客 > This article was originally written by **落格博客
**: [落格博客](https://www.logcg.com/) » [Frequent Wake-from-Sleep Issues After Upgrading to macOS Monterey](https://www.logcg.com/archives/3528.html) > **: [落格博客](https://www.logcg.com/) » [Frequent Wake-from-Sleep Issues After Upgrading to macOS Monterey](https://www.logcg.com/archives/3528.html)
After upgrading to macOS Monterey, my screen kept lighting up in the middle of the night for no apparent reason. It had After upgrading to macOS Monterey, my screen kept lighting up in the middle of the night for no apparent reason. It had
happened before, but only when notifications came in. Now the screen lights up on its own with no trigger — same happened before, but only when notifications came in. Now the screen lights up on its own with no trigger — same
@@ -33,8 +33,8 @@ So for Power Nap, we have to go through the command line. First, check the curre
Also check **`tcpkeepalive`** — it likely isn't 0 either and should also be turned off. It controls whether your Mac Also check **`tcpkeepalive`** — it likely isn't 0 either and should also be turned off. It controls whether your Mac
maintains TCP connections while sleeping. Run **`sudo pmset -a tcpkeepalive 0`** — you'll see a terminal warning: maintains TCP connections while sleeping. Run **`sudo pmset -a tcpkeepalive 0`** — you'll see a terminal warning:
***Warning: This option disables TCP Keep Alive mechanism when system is sleeping. This will result in some critical **_Warning: This option disables TCP Keep Alive mechanism when system is sleeping. This will result in some critical
features like 'Find My Mac' not to function properly.*** features like 'Find My Mac' not to function properly._**
Essentially, turning it off limits some features — the system simply won't connect to the network while asleep. I'm Essentially, turning it off limits some features — the system simply won't connect to the network while asleep. I'm
fairly confident that if someone actually steals your Mac, they won't be getting it online anyway. fairly confident that if someone actually steals your Mac, they won't be getting it online anyway.
@@ -10,8 +10,8 @@ author:
email: real@zihluwang.me email: real@zihluwang.me
--- ---
| Chinese Size Name | English Point Size (pt) | mm | px | | Chinese Size Name | English Point Size (pt) | mm | px |
|--------------------|-------------------------|-------|------| | -------------------- | ----------------------- | ----- | ---- |
| 初号 (Primary) | 42 | 14.82 | 56 | | 初号 (Primary) | 42 | 14.82 | 56 |
| 小初 (Small Primary) | 36 | 12.7 | 48 | | 小初 (Small Primary) | 36 | 12.7 | 48 |
| 一号 (No. 1) | 26 | 9.17 | 34.7 | | 一号 (No. 1) | 26 | 9.17 | 34.7 |
@@ -23,9 +23,9 @@ author:
- **Axios Instance**: Create a centralised Axios instance for API calls to apply common configuration (base URL, - **Axios Instance**: Create a centralised Axios instance for API calls to apply common configuration (base URL,
headers, interceptors). headers, interceptors).
- **Interceptors**: Use Axios interceptors to: - **Interceptors**: Use Axios interceptors to:
- Add authentication tokens to outgoing requests. - Add authentication tokens to outgoing requests.
- Handle global error responses (e.g., display a notification for `401 Unauthorized`). - Handle global error responses (e.g., display a notification for `401 Unauthorized`).
- Log requests/responses in development environments. - Log requests/responses in development environments.
- **Error Handling**: Centralise API error handling in Axios interceptors or custom utility functions to provide - **Error Handling**: Centralise API error handling in Axios interceptors or custom utility functions to provide
consistent user feedback. consistent user feedback.
@@ -34,14 +34,14 @@ author:
- **Function Components & Hooks**: Prefer function components with React Hooks over class components for new - **Function Components & Hooks**: Prefer function components with React Hooks over class components for new
development. development.
- **Props**: - **Props**:
- Define `interface` or `type` for component props to ensure type safety. - Define `interface` or `type` for component props to ensure type safety.
- Destructure props at the component entry point for clarity. - Destructure props at the component entry point for clarity.
- **State Management (Redux)**: - **State Management (Redux)**:
- Use Redux Toolkit for efficient, boilerplate-reduced Redux development. - Use Redux Toolkit for efficient, boilerplate-reduced Redux development.
- Use `createSlice` to organise Redux logic into "slices" (feature-specific reducers, actions, and selectors). - Use `createSlice` to organise Redux logic into "slices" (feature-specific reducers, actions, and selectors).
- Follow the "ducks" pattern or "slices" approach to co-locate Redux logic. - Follow the "ducks" pattern or "slices" approach to co-locate Redux logic.
- **Component Composition**: Break down complex UIs into smaller, reusable, single-responsibility components. - **Component Composition**: Break down complex UIs into smaller, reusable, single-responsibility components.
- **Ant Design**: - **Ant Design**:
- Leverage Ant Design components for consistent UI/UX. - Leverage Ant Design components for consistent UI/UX.
- Use CSS-in-JS solutions to consistently customise Ant Design themes and styles across the application if needed. - Use CSS-in-JS solutions to consistently customise Ant Design themes and styles across the application if needed.
- **Accessibility**: Design and implement components with web accessibility (a11y) in mind from the start. - **Accessibility**: Design and implement components with web accessibility (a11y) in mind from the start.
+1 -1
View File
@@ -16,7 +16,7 @@ variable in the code. Simplified Chinese sits at 84% and Traditional Chinese at
Navigate to `/path/to/gitlab/embedded/service/gitlab-rails/app/helpers/preferred_language_switcher_helper.rb` and modify Navigate to `/path/to/gitlab/embedded/service/gitlab-rails/app/helpers/preferred_language_switcher_helper.rb` and modify
the code as follows: the code as follows:
```diff title="/path/to/gitlab/embedded/service/gitlab-rails/app/helpers/preferred_language_switcher_helper.rb" ```diff title="/path/to/gitlab/embedded/service/gitlab-rails/app/helpers/preferred_language_switcher_helper.rb"
- SWITCHER_MINIMUM_TRANSLATION_LEVEL = 90 - SWITCHER_MINIMUM_TRANSLATION_LEVEL = 90
+ SWITCHER_MINIMUM_TRANSLATION_LEVEL = 80 + SWITCHER_MINIMUM_TRANSLATION_LEVEL = 80
``` ```
@@ -105,7 +105,7 @@ The N+1 problem occurs when JPA executes one query for the parent entity, then N
**Fixes:** **Fixes:**
| Approach | When to use | | Approach | When to use |
|--------------------------|---------------------------------------------------| | ------------------------ | ------------------------------------------------- |
| `@EntityGraph` | Declarative, good for entity-specific fetch plans | | `@EntityGraph` | Declarative, good for entity-specific fetch plans |
| `JOIN FETCH` in `@Query` | Fine-grained control per query | | `JOIN FETCH` in `@Query` | Fine-grained control per query |
| `@BatchSize` | Reduces N+1 to N/k+1 by batching | | `@BatchSize` | Reduces N+1 to N/k+1 by batching |
@@ -144,6 +144,7 @@ This happens when you access a lazily-loaded association outside the persistence
@Query("SELECT new com.example.UserDto(u.id, u.name) FROM User u WHERE u.id = :id") @Query("SELECT new com.example.UserDto(u.id, u.name) FROM User u WHERE u.id = :id")
UserDto findUserDtoById(@Param("id") long id); UserDto findUserDtoById(@Param("id") long id);
``` ```
3. **`@Transactional(readOnly = true)`** on the service method — keep the session open for the entire method scope. 3. **`@Transactional(readOnly = true)`** on the service method — keep the session open for the entire method scope.
### When should I use `@Transactional(readOnly = true)`? ### When should I use `@Transactional(readOnly = true)`?
@@ -275,7 +276,7 @@ Note that `@JsonAnyGetter` must be placed on the **getter method**, not the fiel
### How to choose ### How to choose
| Scenario | Annotation | | Scenario | Annotation |
|-------------------------------------------------------|--------------------| | --------------------------------------------------------------- | ---------------- |
| Flatten nested objects with fixed fields (e.g. address, config) | `@JsonUnwrapped` | | Flatten nested objects with fixed fields (e.g. address, config) | `@JsonUnwrapped` |
| Flatten dynamic key-value pairs with unknown keys | `@JsonAnyGetter` | | Flatten dynamic key-value pairs with unknown keys | `@JsonAnyGetter` |
@@ -98,7 +98,6 @@ author:
## Spring Boot Best Practices & Layered Architecture ## Spring Boot Best Practices & Layered Architecture
- **Layered Architecture (MVC with Manager Layer)**: Our backend applications follow a strict multi-layered architecture, ensuring clear separation of responsibilities and improving maintainability and testability. The layers and their responsibilities are: - **Layered Architecture (MVC with Manager Layer)**: Our backend applications follow a strict multi-layered architecture, ensuring clear separation of responsibilities and improving maintainability and testability. The layers and their responsibilities are:
- **Controller Layer**: Located in the `controller` package. Responsible for exposing RESTful APIs, handling HTTP requests, and mapping request parameters/bodies to service layer calls. Controllers should remain lightweight, focusing primarily on input validation (using DTOs) and coordinating calls to the `Service` layer. - **Controller Layer**: Located in the `controller` package. Responsible for exposing RESTful APIs, handling HTTP requests, and mapping request parameters/bodies to service layer calls. Controllers should remain lightweight, focusing primarily on input validation (using DTOs) and coordinating calls to the `Service` layer.
- **Service Layer**: Located in the `service` package. This layer encapsulates core business logic. Services expose a directly consumable API to the `Controller` layer, abstracting business processes and transaction management. Services coordinate calls to the `Manager` layer to execute business operations. - **Service Layer**: Located in the `service` package. This layer encapsulates core business logic. Services expose a directly consumable API to the `Controller` layer, abstracting business processes and transaction management. Services coordinate calls to the `Manager` layer to execute business operations.
- **Manager Layer**: Located in the `manager` package. This layer provides atomic business operations that can be composed by the `Service` layer. Managers typically handle more complex business logic and may involve interacting with multiple repositories or other external systems at a finer granularity. - **Manager Layer**: Located in the `manager` package. This layer provides atomic business operations that can be composed by the `Service` layer. Managers typically handle more complex business logic and may involve interacting with multiple repositories or other external systems at a finer granularity.
@@ -120,7 +119,7 @@ author:
- Data operation conventions: - Data operation conventions:
- Use JPA for simple database operations. - Use JPA for simple database operations.
- Use MyBatis for complex database operations. - Use MyBatis for complex database operations.
- When performing paginated queries, page numbers should start from 0 (*compatible with Spring Data JPA*). - When performing paginated queries, page numbers should start from 0 (_compatible with Spring Data JPA_).
- **Data Access Method Naming Conventions**: - **Data Access Method Naming Conventions**:
- For **querying data lists**: methods **must** start with `selectListBy`, followed by the filter criteria (e.g., `selectListByUserId`, `selectListByDepartmentIdAndStatus`). These methods must also include a `PageRequest` parameter for pagination. - For **querying data lists**: methods **must** start with `selectListBy`, followed by the filter criteria (e.g., `selectListByUserId`, `selectListByDepartmentIdAndStatus`). These methods must also include a `PageRequest` parameter for pagination.
- For querying **single data records**: methods **must** start with `selectOne` (e.g., `selectOneById`, `selectOneByUsername`). - For querying **single data records**: methods **must** start with `selectOne` (e.g., `selectOneById`, `selectOneByUsername`).
+2 -2
View File
@@ -5,8 +5,8 @@ tags:
- json - json
- database - database
author: author:
name: Zihlu Wang name: Zihlu Wang
email: real@zihluwang.me email: real@zihluwang.me
--- ---
MySQL (since version 5.7) **does not directly support a data type called `jsonb`**. `jsonb` is a data type specific to PostgreSQL, which stores JSON data in a binary format with pre-parsing for faster access and manipulation during queries. MySQL (since version 5.7) **does not directly support a data type called `jsonb`**. `jsonb` is a data type specific to PostgreSQL, which stores JSON data in a binary format with pre-parsing for faster access and manipulation during queries.
+6 -7
View File
@@ -26,11 +26,11 @@ https://gravatar.com/avatar/<hash>
### Node.js ### Node.js
```js ```js
import { createHash } from "node:crypto"; import { createHash } from "node:crypto"
const email = "user@example.com".trim().toLowerCase(); const email = "user@example.com".trim().toLowerCase()
const hash = createHash("sha256").update(email).digest("hex"); const hash = createHash("sha256").update(email).digest("hex")
const url = `https://gravatar.com/avatar/${hash}`; const url = `https://gravatar.com/avatar/${hash}`
``` ```
### Python ### Python
@@ -54,7 +54,7 @@ echo -n "user@example.com" | tr '[:upper:]' '[:lower:]' | sha256sum | cut -d ' '
The `/avatar/` endpoint accepts several query parameters to customise the result: The `/avatar/` endpoint accepts several query parameters to customise the result:
| Parameter | Description | Example | | Parameter | Description | Example |
|-----------|-----------------------------------------|----------------| | --------- | --------------------------------------- | -------------- |
| `s` | Size in pixels (default: 80) | `?s=200` | | `s` | Size in pixels (default: 80) | `?s=200` |
| `d` | Default image when no Gravatar is found | `?d=identicon` | | `d` | Default image when no Gravatar is found | `?d=identicon` |
| `r` | Content rating (`g`, `pg`, `r`, `x`) | `?r=g` | | `r` | Content rating (`g`, `pg`, `r`, `x`) | `?r=g` |
@@ -77,8 +77,7 @@ The `/avatar/` endpoint accepts several query parameters to customise the result
src="https://gravatar.com/avatar/a3b4c5d6e7f8?s=160&d=robohash&r=g" src="https://gravatar.com/avatar/a3b4c5d6e7f8?s=160&d=robohash&r=g"
alt="User avatar" alt="User avatar"
width="160" width="160"
height="160" height="160" />
/>
``` ```
Always pass the `d` parameter to avoid broken images for users who have not set up a Gravatar. `identicon` and Always pass the `d` parameter to avoid broken images for users who have not set up a Gravatar. `identicon` and
@@ -7,7 +7,7 @@ title: Calendar Toolbox API
The library is organised under the base package `com.onixbyte.calendar` with the following structure: The library is organised under the base package `com.onixbyte.calendar` with the following structure:
| Package | Description | | Package | Description |
|--------------------------------------------|--------------------------------------------| | ------------------------------------------ | ------------------------------------------ |
| `com.onixbyte.calendar` | Root calendar object | | `com.onixbyte.calendar` | Root calendar object |
| `com.onixbyte.calendar.component` | iCalendar components (VEVENT, VTODO, etc.) | | `com.onixbyte.calendar.component` | iCalendar components (VEVENT, VTODO, etc.) |
| `com.onixbyte.calendar.component.property` | Component-level properties | | `com.onixbyte.calendar.component.property` | Component-level properties |
@@ -47,7 +47,7 @@ String icsContent = calendar.formatted();
All calendar-level properties live in `com.onixbyte.calendar.property` and implement `CalendarProperty`. All calendar-level properties live in `com.onixbyte.calendar.property` and implement `CalendarProperty`.
| Class | iCalendar Property | Description | | Class | iCalendar Property | Description |
|--------------------------|----------------------|-------------------------------------------| | ------------------------ | -------------------- | ----------------------------------------- |
| `CalendarScale` | `CALSCALE` | Calendar system (e.g., GREGORIAN) | | `CalendarScale` | `CALSCALE` | Calendar system (e.g., GREGORIAN) |
| `Method` | `METHOD` | iCalendar method (e.g., PUBLISH, REQUEST) | | `Method` | `METHOD` | iCalendar method (e.g., PUBLISH, REQUEST) |
| `ProductIdentifier` | `PRODID` | Product identifier of the calendar | | `ProductIdentifier` | `PRODID` | Product identifier of the calendar |
@@ -277,7 +277,7 @@ Alarm emailAlarm = Alarm.builder()
Component properties live in `com.onixbyte.calendar.component.property` and implement `ComponentProperty`. Component properties live in `com.onixbyte.calendar.component.property` and implement `ComponentProperty`.
| Class | iCalendar Property | Description | | Class | iCalendar Property | Description |
|---------------------------|--------------------|----------------------------------------------------------| | ------------------------- | ------------------ | -------------------------------------------------------- |
| `Action` | `ACTION` | Alarm action type (AUDIO, DISPLAY, EMAIL) | | `Action` | `ACTION` | Alarm action type (AUDIO, DISPLAY, EMAIL) |
| `Attachment` | `ATTACH` | Document attachment | | `Attachment` | `ATTACH` | Document attachment |
| `Attendee` | `ATTENDEE` | Event/task attendee | | `Attendee` | `ATTENDEE` | Event/task attendee |
@@ -328,7 +328,7 @@ Component properties live in `com.onixbyte.calendar.component.property` and impl
Parameters in `com.onixbyte.calendar.parameter` provide additional qualifiers on component properties. Parameters in `com.onixbyte.calendar.parameter` provide additional qualifiers on component properties.
| Class | iCalendar Parameter | Description | | Class | iCalendar Parameter | Description |
|-------------------------------|---------------------|--------------------------------------------------------| | ----------------------------- | ------------------- | ------------------------------------------------------ |
| `AlarmTriggerRelationship` | `RELATED` | Trigger relationship (START, END) | | `AlarmTriggerRelationship` | `RELATED` | Trigger relationship (START, END) |
| `AlternateTextRepresentation` | `ALTREP` | Alternate text URI | | `AlternateTextRepresentation` | `ALTREP` | Alternate text URI |
| `CalendarUserType` | `CUTYPE` | Calendar user type (INDIVIDUAL, GROUP, RESOURCE, etc.) | | `CalendarUserType` | `CUTYPE` | Calendar user type (INDIVIDUAL, GROUP, RESOURCE, etc.) |
@@ -357,7 +357,7 @@ Parameters in `com.onixbyte.calendar.parameter` provide additional qualifiers on
Located in `com.onixbyte.calendar.recurrence`. Located in `com.onixbyte.calendar.recurrence`.
| Class | Description | | Class | Description |
|--------------|---------------------------------------------------------------------------| | ------------ | ------------------------------------------------------------------------- |
| `Frequency` | Frequency constants (DAILY, WEEKLY, MONTHLY, YEARLY) for recurrence rules | | `Frequency` | Frequency constants (DAILY, WEEKLY, MONTHLY, YEARLY) for recurrence rules |
| `WeekdayNum` | Weekday number for recurrence rules (e.g., 2nd Monday) | | `WeekdayNum` | Weekday number for recurrence rules (e.g., 2nd Monday) |
@@ -368,7 +368,7 @@ Located in `com.onixbyte.calendar.recurrence`.
Located in `com.onixbyte.calendar.value`. Located in `com.onixbyte.calendar.value`.
| Class | Description | | Class | Description |
|---------------------|------------------------------------------------| | ------------------- | ---------------------------------------------- |
| `FreeBusyTimeValue` | Represents free/busy time value | | `FreeBusyTimeValue` | Represents free/busy time value |
| `PeriodOfTime` | Represents a period of time with start and end | | `PeriodOfTime` | Represents a period of time with start and end |
| `PropertyValue` | Base value type for properties | | `PropertyValue` | Base value type for properties |
@@ -22,10 +22,8 @@ With Calendar Toolbox, you can effortlessly create, customise, and export calend
<Tabs> <Tabs>
<Tab label="Gradle with Kotlin DSL"> <Tab label="Gradle with Kotlin DSL">
```kotlin title="build.gradle.kts" ```kotlin title="build.gradle.kts" dependencies{" "}
dependencies { {implementation("com.onixbyte:calendar-toolbox:1.1.0")}
implementation("com.onixbyte:calendar-toolbox:1.1.0")
}
``` ```
</Tab> </Tab>
<Tab label="Maven"> <Tab label="Maven">
+51 -51
View File
@@ -6,19 +6,19 @@ title: Captcha API
The library is organised under the base package `com.onixbyte.captcha` with the following structure: The library is organised under the base package `com.onixbyte.captcha` with the following structure:
| Package | Description | | Package | Description |
|---------------------------------------------|---------------------------------------| | -------------------------------------- | ------------------------------------- |
| `com.onixbyte.captcha` | Core interfaces | | `com.onixbyte.captcha` | Core interfaces |
| `com.onixbyte.captcha.impl` | Default implementations | | `com.onixbyte.captcha.impl` | Default implementations |
| `com.onixbyte.captcha.text` | Text producer and renderer interfaces | | `com.onixbyte.captcha.text` | Text producer and renderer interfaces |
| `com.onixbyte.captcha.text.impl` | Text producer and renderer impls | | `com.onixbyte.captcha.text.impl` | Text producer and renderer impls |
| `com.onixbyte.captcha.text.enums` | Text-related enums | | `com.onixbyte.captcha.text.enums` | Text-related enums |
| `com.onixbyte.captcha.background` | Background producer interface | | `com.onixbyte.captcha.background` | Background producer interface |
| `com.onixbyte.captcha.background.impl` | Background producer implementations | | `com.onixbyte.captcha.background.impl` | Background producer implementations |
| `com.onixbyte.captcha.noise` | Noise producer interface | | `com.onixbyte.captcha.noise` | Noise producer interface |
| `com.onixbyte.captcha.noise.impl` | Noise producer implementations | | `com.onixbyte.captcha.noise.impl` | Noise producer implementations |
| `com.onixbyte.captcha.gimpy` | Distortion engine interface | | `com.onixbyte.captcha.gimpy` | Distortion engine interface |
| `com.onixbyte.captcha.gimpy.impl` | Distortion engine implementations | | `com.onixbyte.captcha.gimpy.impl` | Distortion engine implementations |
--- ---
@@ -45,17 +45,17 @@ BufferedImage image = captcha.createImage(text);
### DefaultCaptchaProducer Builder ### DefaultCaptchaProducer Builder
| Method | Default Value | Description | | Method | Default Value | Description |
|---------------------|---------------------------------------------|---------------------------------------| | -------------------- | --------------------------------------------- | ------------------------------ |
| `textProducer` | `DefaultTextProducer.builder().build()` | The text producer to use | | `textProducer` | `DefaultTextProducer.builder().build()` | The text producer to use |
| `wordRenderer` | `DefaultWordRenderer.builder().build()` | The word renderer to use | | `wordRenderer` | `DefaultWordRenderer.builder().build()` | The word renderer to use |
| `gimpyEngine` | `WaterRipple.builder().build()` | The distortion engine to use | | `gimpyEngine` | `WaterRipple.builder().build()` | The distortion engine to use |
| `backgroundProducer`| `DefaultBackgroundProducer.builder().build()`| The background producer to use | | `backgroundProducer` | `DefaultBackgroundProducer.builder().build()` | The background producer to use |
| `width` | `200` | Width of the CAPTCHA image | | `width` | `200` | Width of the CAPTCHA image |
| `height` | `50` | Height of the CAPTCHA image | | `height` | `50` | Height of the CAPTCHA image |
| `borderDrawn` | `true` | Whether a border is drawn | | `borderDrawn` | `true` | Whether a border is drawn |
| `borderColour` | `Color.BLACK` | The colour of the border | | `borderColour` | `Color.BLACK` | The colour of the border |
| `borderThickness` | `1` | The thickness of the border | | `borderThickness` | `1` | The thickness of the border |
--- ---
@@ -82,10 +82,10 @@ DefaultTextProducer textProducer = DefaultTextProducer.builder()
.build(); .build();
``` ```
| Method | Default Value | Description | | Method | Default Value | Description |
|----------|-----------------------------------------------------------|--------------------------------| | -------- | ----------------------------------- | ------------------------------ |
| `length` | `6` | Length of generated text | | `length` | `6` | Length of generated text |
| `chars` | `a-z`, `A-Z`, `0-9` (62 characters) | Characters used for generation | | `chars` | `a-z`, `A-Z`, `0-9` (62 characters) | Characters used for generation |
### WordRenderer ### WordRenderer
@@ -111,24 +111,24 @@ DefaultWordRenderer wordRenderer = DefaultWordRenderer.builder()
.build(); .build();
``` ```
| Method | Default Value | Description | | Method | Default Value | Description |
|-------------|-----------------------------------|-----------------------------------------| | ------------ | ---------------------- | -------------------------------------- |
| `fontSize` | `40` | Font size in pixels | | `fontSize` | `40` | Font size in pixels |
| `fonts` | `["Arial", "Courier"]` | Font families to use (randomly chosen) | | `fonts` | `["Arial", "Courier"]` | Font families to use (randomly chosen) |
| `fontColour`| `Color.BLACK` | Font colour | | `fontColour` | `Color.BLACK` | Font colour |
| `charSpace` | `2` | Space between characters in pixels | | `charSpace` | `2` | Space between characters in pixels |
| `fontStyle` | `FontStyle.BOLD` | Font style to apply | | `fontStyle` | `FontStyle.BOLD` | Font style to apply |
### FontStyle ### FontStyle
Defines the supported font styles for rendering CAPTCHA text. Defines the supported font styles for rendering CAPTCHA text.
| Value | Corresponding AWT Constant | Description | | Value | Corresponding AWT Constant | Description |
|---------------|-------------------------------------|------------------------------| | ------------- | -------------------------- | ------------ | ------------------------ |
| `PLAIN` | `java.awt.Font.PLAIN` | Plain style | | `PLAIN` | `java.awt.Font.PLAIN` | Plain style |
| `BOLD` | `java.awt.Font.BOLD` | Bold style | | `BOLD` | `java.awt.Font.BOLD` | Bold style |
| `ITALIC` | `java.awt.Font.ITALIC` | Italic style | | `ITALIC` | `java.awt.Font.ITALIC` | Italic style |
| `BOLD_ITALIC` | `java.awt.Font.BOLD | Font.ITALIC` | Bold and italic combined | | `BOLD_ITALIC` | `java.awt.Font.BOLD | Font.ITALIC` | Bold and italic combined |
--- ---
@@ -152,9 +152,9 @@ WaterRipple gimpy = WaterRipple.builder()
.build(); .build();
``` ```
| Method | Default Value | Description | | Method | Default Value | Description |
|-----------------|---------------------------------------------|------------------------------| | --------------- | ---------------------------------------- | ------------------------- |
| `noiseProducer` | `DefaultNoiseProducer.builder().build()` | The noise producer to use | | `noiseProducer` | `DefaultNoiseProducer.builder().build()` | The noise producer to use |
### FishEyeGimpy ### FishEyeGimpy
@@ -175,9 +175,9 @@ ShadowGimpy gimpy = ShadowGimpy.builder()
.build(); .build();
``` ```
| Method | Default Value | Description | | Method | Default Value | Description |
|-----------------|---------------------------------------------|------------------------------| | --------------- | ---------------------------------------- | ------------------------- |
| `noiseProducer` | `DefaultNoiseProducer.builder().build()` | The noise producer to use | | `noiseProducer` | `DefaultNoiseProducer.builder().build()` | The noise producer to use |
--- ---
@@ -204,9 +204,9 @@ DefaultNoiseProducer noise = DefaultNoiseProducer.builder()
.build(); .build();
``` ```
| Method | Default Value | Description | | Method | Default Value | Description |
|---------------|---------------|---------------------| | ------------- | ------------- | ---------------- |
| `noiseColour` | `Color.BLACK` | The noise colour | | `noiseColour` | `Color.BLACK` | The noise colour |
#### NoNoiseProducer #### NoNoiseProducer
@@ -243,6 +243,6 @@ DefaultBackgroundProducer background = DefaultBackgroundProducer.builder()
``` ```
| Method | Default Value | Description | | Method | Default Value | Description |
|--------------|--------------------|---------------------------------| | ------------ | ------------------ | ------------------------------- |
| `colourFrom` | `Color.LIGHT_GRAY` | The starting colour of gradient | | `colourFrom` | `Color.LIGHT_GRAY` | The starting colour of gradient |
| `colourTo` | `Color.WHITE` | The ending colour of gradient | | `colourTo` | `Color.WHITE` | The ending colour of gradient |
@@ -23,10 +23,7 @@ With Captcha, you can easily generate customisable CAPTCHA images with various d
<Tabs> <Tabs>
<Tab label="Gradle with Kotlin DSL"> <Tab label="Gradle with Kotlin DSL">
```kotlin title="build.gradle.kts" ```kotlin title="build.gradle.kts" dependencies {implementation("com.onixbyte:captcha:1.0.0")}
dependencies {
implementation("com.onixbyte:captcha:1.0.0")
}
``` ```
</Tab> </Tab>
<Tab label="Maven"> <Tab label="Maven">
@@ -23,6 +23,7 @@ Skills are deployed directly into Claude Code's own skills directory (`~/.claude
| Windows (amd64) | `ccsm-windows-amd64.exe` | | Windows (amd64) | `ccsm-windows-amd64.exe` |
Place the binary somewhere on your `PATH` and rename it to `ccsm` (or `ccsm.exe` on Windows). Place the binary somewhere on your `PATH` and rename it to `ccsm` (or `ccsm.exe` on Windows).
</Tab> </Tab>
<Tab label="Cargo install"> <Tab label="Cargo install">
```bash ```bash
@@ -109,10 +110,10 @@ ccsm marketplace remove my-marketplace
CCSM supports two deploy modes: CCSM supports two deploy modes:
| Mode | Behaviour | Use case | | Mode | Behaviour | Use case |
|------|-----------|----------| | ---------------- | --------------------------------------------------------- | --------------------------------------------------------------------- |
| `copy` (default) | Files are physically copied to `~/.claude/skills/<name>/` | Normal use — safe and self-contained | | `copy` (default) | Files are physically copied to `~/.claude/skills/<name>/` | Normal use — safe and self-contained |
| `link` | A symlink is created pointing to the local store | Skill development — changes to source files are picked up immediately | | `link` | A symlink is created pointing to the local store | Skill development — changes to source files are picked up immediately |
Set the default mode in your config, or override per install: Set the default mode in your config, or override per install:
@@ -158,21 +159,21 @@ your-marketplace-repo/
**Top-level fields:** **Top-level fields:**
| Field | Required | Description | | Field | Required | Description |
|-------|----------|-------------| | --------- | -------- | ---------------------------------------------- |
| `name` | Yes | Marketplace display name | | `name` | Yes | Marketplace display name |
| `version` | No | Arbitrary version string for your own tracking | | `version` | No | Arbitrary version string for your own tracking |
| `skills` | No | Array of skill entries (defaults to `[]`) | | `skills` | No | Array of skill entries (defaults to `[]`) |
**Skill entry fields:** **Skill entry fields:**
| Field | Required | Description | | Field | Required | Description |
|-------|----------|-------------| | ------------- | -------- | --------------------------------------------------------------------------------------------------------- |
| `name` | Yes | Unique skill identifier (used with `ccsm install <name>`) | | `name` | Yes | Unique skill identifier (used with `ccsm install <name>`) |
| `description` | Yes | One-line summary shown in `ccsm search` output | | `description` | Yes | One-line summary shown in `ccsm search` output |
| `repository` | Yes | Public Git repository URL for the skill | | `repository` | Yes | Public Git repository URL for the skill |
| `branch` | No | Git branch to install from (tries `main` first, falls back to `master`) | | `branch` | No | Git branch to install from (tries `main` first, falls back to `master`) |
| `path` | No | Subdirectory within the repository where the skill files live — enables monorepos hosting multiple skills | | `path` | No | Subdirectory within the repository where the skill files live — enables monorepos hosting multiple skills |
### How it works ### How it works
@@ -202,12 +203,12 @@ my-skill-repo/
## How CCSM Stores Data ## How CCSM Stores Data
| Path | Purpose | | Path | Purpose |
|------|---------| | ------------------------------ | ---------------------------------------------------------- |
| `~/.claude/skills/` | Claude Code's skills directory — where skills are deployed | | `~/.claude/skills/` | Claude Code's skills directory — where skills are deployed |
| `~/.config/ccsm/config.json` | CCSM configuration (registered marketplaces, default mode) | | `~/.config/ccsm/config.json` | CCSM configuration (registered marketplaces, default mode) |
| `~/.config/ccsm/marketplaces/` | Cached marketplace manifests | | `~/.config/ccsm/marketplaces/` | Cached marketplace manifests |
| `~/.config/ccsm/skills/` | Downloaded skill files (local store) | | `~/.config/ccsm/skills/` | Downloaded skill files (local store) |
On Windows, `~/.config/` resolves to `C:\Users\<user>\AppData\Roaming\`. On Windows, `~/.config/` resolves to `C:\Users\<user>\AppData\Roaming\`.
@@ -6,64 +6,64 @@ title: Delta Force Guide API
The backend is a Spring Boot 3 application organised under the base package `com.onixbyte.deltaforceguide`: The backend is a Spring Boot 3 application organised under the base package `com.onixbyte.deltaforceguide`:
| Package | Description | | Package | Description |
|---------------------------------------------|-----------------------------------| | ------------- | -------------------------------------------- |
| `controller` | REST API endpoint controllers | | `controller` | REST API endpoint controllers |
| `service` | Business logic layer | | `service` | Business logic layer |
| `repository` | MyBatis data access layer | | `repository` | MyBatis data access layer |
| `domain` | JPA entity and domain objects | | `domain` | JPA entity and domain objects |
| `config` | Spring configuration (security, cache, etc.) | | `config` | Spring configuration (security, cache, etc.) |
| `properties` | Configuration property classes | | `properties` | Configuration property classes |
| `enumeration` | Enumerated types | | `enumeration` | Enumerated types |
| `shared` | Shared utilities | | `shared` | Shared utilities |
## API Endpoints ## API Endpoints
### Authentication ### Authentication
| Method | Path | Description | Auth | | Method | Path | Description | Auth |
|--------|-----------------------|--------------------------|------| | ------ | ------------------- | ------------------------ | ---- |
| `POST` | `/api/auth/login` | Log in with credentials | No | | `POST` | `/api/auth/login` | Log in with credentials | No |
| `POST` | `/api/auth/refresh` | Refresh access token | No | | `POST` | `/api/auth/refresh` | Refresh access token | No |
| `GET` | `/api/auth/profile` | Get current user profile | Yes | | `GET` | `/api/auth/profile` | Get current user profile | Yes |
### Firearms ### Firearms
| Method | Path | Description | | Method | Path | Description |
|----------|-----------------------|--------------------------| | -------- | ------------------- | --------------------------------------------- |
| `GET` | `/api/firearms` | List firearms (paginated, filterable by type) | | `GET` | `/api/firearms` | List firearms (paginated, filterable by type) |
| `GET` | `/api/firearms/:id` | Get firearm by ID | | `GET` | `/api/firearms/:id` | Get firearm by ID |
| `POST` | `/api/firearms` | Create a new firearm | | `POST` | `/api/firearms` | Create a new firearm |
| `PUT` | `/api/firearms/:id` | Update an existing firearm | | `PUT` | `/api/firearms/:id` | Update an existing firearm |
| `DELETE` | `/api/firearms/:id` | Remove a firearm | | `DELETE` | `/api/firearms/:id` | Remove a firearm |
### Modifications ### Modifications
| Method | Path | Description | | Method | Path | Description |
|----------|-----------------------------------|--------------------------------| | -------- | --------------------------------- | -------------------------------------------------------------- |
| `GET` | `/api/modifications` | List modifications (paginated, filterable by firearm and tags) | | `GET` | `/api/modifications` | List modifications (paginated, filterable by firearm and tags) |
| `GET` | `/api/modifications/:id` | Get modification by ID | | `GET` | `/api/modifications/:id` | Get modification by ID |
| `POST` | `/api/modifications` | Create a modification | | `POST` | `/api/modifications` | Create a modification |
| `POST` | `/api/modifications/batch` | Batch create modifications | | `POST` | `/api/modifications/batch` | Batch create modifications |
| `PUT` | `/api/modifications/:id` | Update a modification | | `PUT` | `/api/modifications/:id` | Update a modification |
| `DELETE` | `/api/modifications/:id` | Remove a modification | | `DELETE` | `/api/modifications/:id` | Remove a modification |
| `DELETE` | `/api/modifications/batch-delete` | Batch remove modifications | | `DELETE` | `/api/modifications/batch-delete` | Batch remove modifications |
### Tags ### Tags
| Method | Path | Description | | Method | Path | Description |
|--------|-----------------------|-----------------------| | ------ | ----------- | ------------- |
| `GET` | `/api/tags` | List all tags | | `GET` | `/api/tags` | List all tags |
## Frontend Routes ## Frontend Routes
| Path | Layout | Page | Description | | Path | Layout | Page | Description |
|---------------|-------------|-------------------|---------------------------------| | ------------ | ------------ | ------------------ | ----------------------------- |
| `/` | Hero Layout | Firearms Browser | Home page — firearm list | | `/` | Hero Layout | Firearms Browser | Home page — firearm list |
| `/firearms` | Hero Layout | Firearms Browser | Firearm detail and search | | `/firearms` | Hero Layout | Firearms Browser | Firearm detail and search |
| `/mod-codes` | Hero Layout | Modification Codes| Modification code library | | `/mod-codes` | Hero Layout | Modification Codes | Modification code library |
| `/legal` | Hero Layout | Legal | Legal and privacy information | | `/legal` | Hero Layout | Legal | Legal and privacy information |
| `/login` | Empty Layout| Login | User authentication page | | `/login` | Empty Layout | Login | User authentication page |
## Frontend State Management ## Frontend State Management
@@ -82,10 +82,10 @@ The frontend uses a shared `WebClient` wrapper around Axios that handles:
## Infrastructure ## Infrastructure
| Component | Technology | | Component | Technology |
|---------------|-----------------------------| | ---------------- | ----------------------------------- |
| Database | PostgreSQL (with Flyway migrations) | | Database | PostgreSQL (with Flyway migrations) |
| Cache | Redis (session/token storage) | | Cache | Redis (session/token storage) |
| File Storage | AWS S3 (firearm images) | | File Storage | AWS S3 (firearm images) |
| Containerisation | Docker | | Containerisation | Docker |
| Deployment | Docker Compose behind Nginx | | Deployment | Docker Compose behind Nginx |
@@ -23,12 +23,12 @@ The live site is available at **[dfguide.onixbyte.cn](https://dfguide.onixbyte.c
## Architecture ## Architecture
| Component | Tech Stack | | Component | Tech Stack |
|---------------|---------------------------------------------------------------| | ------------ | --------------------------------------------------------------------------------------------------- |
| **Frontend** | React 19, TypeScript, Tailwind CSS 4, Ant Design 6, React Router 7, Redux Toolkit, TanStack Virtual | | **Frontend** | React 19, TypeScript, Tailwind CSS 4, Ant Design 6, React Router 7, Redux Toolkit, TanStack Virtual |
| **Backend** | Spring Boot 3, Java 21, PostgreSQL, MyBatis, Flyway, Redis | | **Backend** | Spring Boot 3, Java 21, PostgreSQL, MyBatis, Flyway, Redis |
| **Auth** | JWT (access + refresh tokens), CAPTCHA integration | | **Auth** | JWT (access + refresh tokens), CAPTCHA integration |
| **Storage** | AWS S3 for image uploads | | **Storage** | AWS S3 for image uploads |
The frontend is a static site served by Vite, communicating with the Spring Boot REST API backend. Authentication uses JWT access and refresh tokens with Redis-backed refresh token management. The frontend is a static site served by Vite, communicating with the Spring Boot REST API backend. Authentication uses JWT access and refresh tokens with Redis-backed refresh token management.
@@ -6,13 +6,13 @@ title: OnixByte Toolbox API
All modules are published under the `com.onixbyte` group ID. Each module has its own base package: All modules are published under the `com.onixbyte` group ID. Each module has its own base package:
| Module | Maven Artifact | Base Package | | Module | Maven Artifact | Base Package |
|----------------------|------------------------|---------------------------------------| | ------------------ | -------------------- | -------------------------------- |
| Common Toolbox | `common-toolbox` | `com.onixbyte.common` | | Common Toolbox | `common-toolbox` | `com.onixbyte.common` |
| Crypto Toolbox | `crypto-toolbox` | `com.onixbyte.crypto` | | Crypto Toolbox | `crypto-toolbox` | `com.onixbyte.crypto` |
| Math Toolbox | `math-toolbox` | `com.onixbyte.math` | | Math Toolbox | `math-toolbox` | `com.onixbyte.math` |
| Tuple | `tuple` | `com.onixbyte.tuple` | | Tuple | `tuple` | `com.onixbyte.tuple` |
| Identity Generator | `identity-generator` | `com.onixbyte.identitygenerator` | | Identity Generator | `identity-generator` | `com.onixbyte.identitygenerator` |
--- ---
@@ -28,16 +28,16 @@ import com.onixbyte.common.adapter.ObjectMapAdapter;
### Utility Classes ### Utility Classes
| Class | Description | | Class | Description |
|--------------------|----------------------------------------------------------| | ---------------- | ---------------------------------------------------------- |
| `AesUtil` | AES symmetric encryption/decryption with key generation | | `AesUtil` | AES symmetric encryption/decryption with key generation |
| `Base64Util` | Base64 encoding and decoding with multiple charset support| | `Base64Util` | Base64 encoding and decoding with multiple charset support |
| `BoolUtil` | Boolean parsing and conversion utilities | | `BoolUtil` | Boolean parsing and conversion utilities |
| `BranchUtil` | Conditional branching helpers with functional-style chains| | `BranchUtil` | Conditional branching helpers with functional-style chains |
| `CollectionUtil` | Collection manipulation — grouping, partitioning, merging | | `CollectionUtil` | Collection manipulation — grouping, partitioning, merging |
| `HashUtil` | Hashing utilities supporting MD5, SHA-1, SHA-256, SHA-512 | | `HashUtil` | Hashing utilities supporting MD5, SHA-1, SHA-256, SHA-512 |
| `MapUtil` | Map builder, merge, and transformation helpers | | `MapUtil` | Map builder, merge, and transformation helpers |
| `RangeUtil` | Numeric range operations with boundary handling | | `RangeUtil` | Numeric range operations with boundary handling |
**AesUtil Example:** **AesUtil Example:**
@@ -70,12 +70,12 @@ Provides a clean abstraction for loading cryptographic keys in PEM format. Suppo
### Key Loaders ### Key Loaders
| Class | Description | | Class | Description |
|---------------------------|--------------------------------------------| | --------------------- | ---------------------------------------- |
| `RSAPrivateKeyLoader` | Load RSA private keys from PEM strings | | `RSAPrivateKeyLoader` | Load RSA private keys from PEM strings |
| `RSAPublicKeyLoader` | Load RSA public keys from PEM strings | | `RSAPublicKeyLoader` | Load RSA public keys from PEM strings |
| `ECPrivateKeyLoader` | Load ECDSA private keys from PEM strings | | `ECPrivateKeyLoader` | Load ECDSA private keys from PEM strings |
| `ECPublicKeyLoader` | Load ECDSA public keys from PEM strings | | `ECPublicKeyLoader` | Load ECDSA public keys from PEM strings |
All key loaders implement either `PrivateKeyLoader` or `PublicKeyLoader` interfaces. All key loaders implement either `PrivateKeyLoader` or `PublicKeyLoader` interfaces.
@@ -99,9 +99,9 @@ var ecPublicKey = new ECPublicKeyLoader().load(pemString);
### Exceptions ### Exceptions
| Class | Description | | Class | Description |
|------------------------|---------------------------------------------------| | --------------------- | ---------------------------------------------- |
| `KeyLoadingException` | Thrown when a PEM-formatted key fails to parse | | `KeyLoadingException` | Thrown when a PEM-formatted key fails to parse |
--- ---
@@ -111,10 +111,10 @@ Statistical computation utilities for working with numeric datasets.
### Core Classes ### Core Classes
| Class | Description | | Class | Description |
|-------------------------|----------------------------------------------------------| | ---------------------- | ------------------------------------------------------------------------------------ |
| `Calculator` | Statistical calculator for mean, median, variance, standard deviation, sum, min, max | | `Calculator` | Statistical calculator for mean, median, variance, standard deviation, sum, min, max |
| `PercentileCalculator` | Percentile computation with configurable interpolation strategies | | `PercentileCalculator` | Percentile computation with configurable interpolation strategies |
**Calculator Example:** **Calculator Example:**
@@ -135,9 +135,9 @@ double p99 = calculator.calculate(99);
### Model ### Model
| Class | Description | | Class | Description |
|-------------------|----------------------------------------------------------------| | ---------------- | ------------------------------------------------------------------ |
| `QuartileBounds` | Holds Q1, median (Q2), Q3 values along with IQR and whisker bounds | | `QuartileBounds` | Holds Q1, median (Q2), Q3 values along with IQR and whisker bounds |
--- ---
@@ -147,12 +147,12 @@ Lightweight generic tuple types for when you need to return multiple values with
### Classes ### Classes
| Class | Type Parameters | Mutability | | Class | Type Parameters | Mutability |
|--------------------|-----------------|------------| | -------------------------- | --------------- | ---------- |
| `Tuple<A, B>` | Two elements | Mutable | | `Tuple<A, B>` | Two elements | Mutable |
| `ImmutableTuple<A, B>` | Two elements | Immutable | | `ImmutableTuple<A, B>` | Two elements | Immutable |
| `Triple<A, B, C>` | Three elements | Mutable | | `Triple<A, B, C>` | Three elements | Mutable |
| `ImmutableTriple<A, B, C>` | Three elements | Immutable | | `ImmutableTriple<A, B, C>` | Three elements | Immutable |
**Example:** **Example:**
@@ -183,10 +183,10 @@ Generates unique identifiers suitable for use as database primary keys or distri
### Implementations ### Implementations
| Class | Description | | Class | Description |
|------------------------------|-----------------------------------------------------------------| | ---------------------------- | ------------------------------------------------------------------- |
| `SequentialUuidGenerator` | Generates time-ordered, sequential UUIDs (optimised for DB indexes) | | `SequentialUuidGenerator` | Generates time-ordered, sequential UUIDs (optimised for DB indexes) |
| `SnowflakeIdentityGenerator` | Snowflake-style distributed unique ID generation | | `SnowflakeIdentityGenerator` | Snowflake-style distributed unique ID generation |
**Sequential UUID:** **Sequential UUID:**
@@ -204,6 +204,6 @@ long id = generator.nextId(); // 64-bit snowflake ID
### Exceptions ### Exceptions
| Class | Description | | Class | Description |
|-------------------|----------------------------------------------------------------| | ----------------- | ------------------------------------------------------------------------ |
| `TimingException` | Thrown when the system clock moves backwards, compromising ID uniqueness | | `TimingException` | Thrown when the system clock moves backwards, compromising ID uniqueness |
@@ -13,7 +13,7 @@ The toolkit is available on Maven Central under the `com.onixbyte` group ID. Eac
## Modules ## Modules
| Module | Artifact ID | Description | | Module | Artifact ID | Description |
|--------------------|----------------------|---------------------------------------------------------------------------| | ------------------ | -------------------- | ------------------------------------------------------------------------- |
| Common Toolbox | `common-toolbox` | Everyday utilities — AES, hashing, Base64, collections, branching, ranges | | Common Toolbox | `common-toolbox` | Everyday utilities — AES, hashing, Base64, collections, branching, ranges |
| Crypto Toolbox | `crypto-toolbox` | Public/private key loading for RSA and ECDSA algorithms | | Crypto Toolbox | `crypto-toolbox` | Public/private key loading for RSA and ECDSA algorithms |
| Math Toolbox | `math-toolbox` | Statistical calculation, percentile analysis, quartile computation | | Math Toolbox | `math-toolbox` | Statistical calculation, percentile analysis, quartile computation |
@@ -21,12 +21,12 @@ The library models every ISO 3166-1 country and territory as an enum constant, g
Each region exposes four attributes: Each region exposes four attributes:
| Attribute | Type | Description | Example | | Attribute | Type | Description | Example |
|----------------|--------|---------------------------------|------------------------------------| | -------------- | ------ | ----------------------------------- | ---------------------------------- |
| `abbreviation` | String | ISO 3166-1 alpha-2 abbreviation | `HK`, `US`, `GB`, `RU` | | `abbreviation` | String | ISO 3166-1 alpha-2 abbreviation | `HK`, `US`, `GB`, `RU` |
| `code` | String | ISO 3166-1 alpha-3 code | `HKG`, `USA`, `GBR`, `RUS` | | `code` | String | ISO 3166-1 alpha-3 code | `HKG`, `USA`, `GBR`, `RUS` |
| `callingCode` | String | E.164 international dialling prefix | `852`, `1`, `44`, `7` | | `callingCode` | String | E.164 international dialling prefix | `852`, `1`, `44`, `7` |
| `locale` | String | Default language and region tag | `zh_HK`, `en_US`, `en_GB`, `ru_RU` | | `locale` | String | Default language and region tag | `zh_HK`, `en_US`, `en_GB`, `ru_RU` |
## Installation ## Installation
@@ -20,16 +20,8 @@ This plugin detects the situation at startup and prints a clear, colour-coded wa
## Installation ## Installation
<Tabs> <Tabs>
<Tab label="npm"> <Tab label="npm">```bash npm install vite-plugin-port-checker -D ```</Tab>
```bash <Tab label="pnpm">```bash pnpm add vite-plugin-port-checker -D ```</Tab>
npm install vite-plugin-port-checker -D
```
</Tab>
<Tab label="pnpm">
```bash
pnpm add vite-plugin-port-checker -D
```
</Tab>
</Tabs> </Tabs>
## Usage ## Usage
+16 -16
View File
@@ -10,22 +10,22 @@ author:
## 调料投放时机 ## 调料投放时机
| 调料类型 | 放调料时机 | 适用菜品 | 作用 | | 调料类型 | 放调料时机 | 适用菜品 | 作用 |
|--------------|----------|----------------|---------------------| | -------------------- | ---------------- | ---------------------------- | -------------------------------------- |
| **盐** | 最后放 | 菜叶子 | 防止菜叶变软出水,保证口感干爽脆嫩 | | **盐** | 最后放 | 菜叶子 | 防止菜叶变软出水,保证口感干爽脆嫩 |
| | 中间放 | 土豆丝、豆角、蒜苔等 | 更好入味 | | | 中间放 | 土豆丝、豆角、蒜苔等 | 更好入味 |
| **味精、生抽、蚝油** | 偏后放 | 各类需要增鲜提香的菜品 | 增鲜提香,避免高温破坏鲜味和香味 | | **味精、生抽、蚝油** | 偏后放 | 各类需要增鲜提香的菜品 | 增鲜提香,避免高温破坏鲜味和香味 |
| **料酒** | 和食材一起放 | 需要去腥的食材 | 焯水时去腥效果更好 | | **料酒** | 和食材一起放 | 需要去腥的食材 | 焯水时去腥效果更好 |
| | 锅温最高的时候放 | 需要炒菜去腥的食材 | 炒菜时高温促进酒气挥发,炝锅去腥效果好 | | | 锅温最高的时候放 | 需要炒菜去腥的食材 | 炒菜时高温促进酒气挥发,炝锅去腥效果好 |
| **醋** | 提前放 | 酸辣土豆丝、酸辣大白菜 | 使土豆丝变脆更入味 | | **醋** | 提前放 | 酸辣土豆丝、酸辣大白菜 | 使土豆丝变脆更入味 |
| | 出锅前锅边醋 | 辣椒炒肉等需要猛火爆炒类菜品 | 大火炝锅增香 | | | 出锅前锅边醋 | 辣椒炒肉等需要猛火爆炒类菜品 | 大火炝锅增香 |
| **老抽** | 炒半熟后放 | 需要上色的菜品 | 达到最佳上色效果 | | **老抽** | 炒半熟后放 | 需要上色的菜品 | 达到最佳上色效果 |
## 快速参考 ## 快速参考
| 调料 | 投放时机 | 作用 | 示例 | | 调料 | 投放时机 | 作用 | 示例 |
|-----------------|----------------|------------------------|------------------------| | ----------------------- | ------------------- | -------------------------------------------- | ------------------------------------- |
| **盐** | 出锅前 10 - 15 秒放 | 避免提前放盐导致菜叶出水、变软,保持脆嫩口感 | 炒生菜、菠菜时,关火前撒盐翻炒均匀 | | **盐** | 出锅前 10 - 15 秒放 | 避免提前放盐导致菜叶出水、变软,保持脆嫩口感 | 炒生菜、菠菜时,关火前撒盐翻炒均匀 |
| **蒜末 / 姜末** | 热油后先放 | 爆香提味,激发香味 | 油热后下蒜末炝锅,再放青菜翻炒 | | **蒜末 / 姜末** | 热油后先放 | 爆香提味,激发香味 | 油热后下蒜末炝锅,再放青菜翻炒 |
| **生抽 / 蚝油**(可选) | 盐之后放 | 增鲜提味,少量即可,避免盖过蔬菜原味 | 炒上海青时,加盐后滴 1 - 2 滴生抽翻炒 | | **生抽 / 蚝油**(可选) | 盐之后放 | 增鲜提味,少量即可,避免盖过蔬菜原味 | 炒上海青时,加盐后滴 1 - 2 滴生抽翻炒 |
| **食用油** | 热锅热油 | 油热后快速炒,锁住水分 | 大火快炒时,油稍多一点,油温要高 | | **食用油** | 热锅热油 | 油热后快速炒,锁住水分 | 大火快炒时,油稍多一点,油温要高 |
@@ -17,13 +17,13 @@ author:
```javascript ```javascript
function processResponse(response) { function processResponse(response) {
if (response.status === 200 && response.data) { if (response.status === 200 && response.data) {
return { success: true, data: response.data }; return { success: true, data: response.data }
} else if (response.status === 404) { } else if (response.status === 404) {
return { success: false, error: 'Not found' }; return { success: false, error: "Not found" }
} else if (response.status >= 500) { } else if (response.status >= 500) {
return { success: false, error: 'Server error' }; return { success: false, error: "Server error" }
} else { } else {
return { success: false, error: 'Unknown error' }; return { success: false, error: "Unknown error" }
} }
} }
``` ```
+11 -11
View File
@@ -17,17 +17,17 @@ author:
## 场景速查表 ## 场景速查表
| 场景 | ❌ 别用(听起来……) | ✅ 改用(听起来……) | 为什么有效 | | 场景 | ❌ 别用(听起来……) | ✅ 改用(听起来……) | 为什么有效 |
|----------|--------------------------------------------------------------|----------------------------------------------------------|-----------------------------------------------| | ---------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| 回复晚了 | **Sorry for the delay**<br/>(抱歉回复晚了) | **Thanks for your patience**<br/>(感谢你的耐心等待) | 前者在道歉,后者在感谢——从「我做错了」转变为「你太好了」,既表达了歉意又让对方感到被尊重 | | 回复晚了 | **Sorry for the delay**<br/>(抱歉回复晚了) | **Thanks for your patience**<br/>(感谢你的耐心等待) | 前者在道歉,后者在感谢——从「我做错了」转变为「你太好了」,既表达了歉意又让对方感到被尊重 |
| 约时间 | **What works best for you?**<br/>(你什么时候方便?) | **Could you do …?**<br/>(你可以在某某时间吗?) | 前者把决定权完全推给对方,后者主动给出选项,减少来回沟通的成本 | | 约时间 | **What works best for you?**<br/>(你什么时候方便?) | **Could you do …?**<br/>(你可以在某某时间吗?) | 前者把决定权完全推给对方,后者主动给出选项,减少来回沟通的成本 |
| 帮助了别人之后 | **No problem / No worries**<br/>(没问题 / 别担心) | **Always happy to help**<br/>(乐于效劳) | 前者暗示这件事"本来可能是个问题",后者传递出你乐在其中、下次还愿意帮 | | 帮助了别人之后 | **No problem / No worries**<br/>(没问题 / 别担心) | **Always happy to help**<br/>(乐于效劳) | 前者暗示这件事"本来可能是个问题",后者传递出你乐在其中、下次还愿意帮 |
| 提出建议 | **I think maybe we should …**<br/>(我觉得也许我们应该……) | **It'd be best if we …**<br/>(最好是……) | 前者透着犹豫和不自信,后者直接给出判断——像有经验的人在做决策 | | 提出建议 | **I think maybe we should …**<br/>(我觉得也许我们应该……) | **It'd be best if we …**<br/>(最好是……) | 前者透着犹豫和不自信,后者直接给出判断——像有经验的人在做决策 |
| 文字沟通费劲 | **_花 30 分钟反复修改一封邮件_** | **It'd be easier to discuss in person**<br/>(当面/电话聊会更轻松) | 意识到沟通渠道本身就是问题,果断切换方式反而是效率最高的选择 | | 文字沟通费劲 | **_花 30 分钟反复修改一封邮件_** | **It'd be easier to discuss in person**<br/>(当面/电话聊会更轻松) | 意识到沟通渠道本身就是问题,果断切换方式反而是效率最高的选择 |
| 确认对方是否理解 | **Hopefully that makes sense?**<br/>(希望我说清楚了?) | **Let me know if you have questions**<br/>(有问题随时问我) | 前者在怀疑自己的表达能力,后者把责任合理地分担给对方,姿态更稳 | | 确认对方是否理解 | **Hopefully that makes sense?**<br/>(希望我说清楚了?) | **Let me know if you have questions**<br/>(有问题随时问我) | 前者在怀疑自己的表达能力,后者把责任合理地分担给对方,姿态更稳 |
| 催进度 | **Just wanted to check in**<br/>(只是想确认一下) | **When can I expect an update?**<br/>(我什么时候可以收到更新?) | 前者显得小心翼翼不敢打扰,后者直接问时间节点——清楚、礼貌、专业 | | 催进度 | **Just wanted to check in**<br/>(只是想确认一下) | **When can I expect an update?**<br/>(我什么时候可以收到更新?) | 前者显得小心翼翼不敢打扰,后者直接问时间节点——清楚、礼貌、专业 |
| 犯了小错 | **Ahh sorry my bad totally missed that**<br/>(啊啊抱歉我的错,完全漏掉了) | **Thanks for letting me know**<br/>(感谢提醒) | 前者过度道歉反而让对方尴尬,后者承认了问题但把焦点放在了"解决"上 | | 犯了小错 | **Ahh sorry my bad totally missed that**<br/>(啊啊抱歉我的错,完全漏掉了) | **Thanks for letting me know**<br/>(感谢提醒) | 前者过度道歉反而让对方尴尬,后者承认了问题但把焦点放在了"解决"上 |
| 需要早退 | **Could I possibly leave early?**<br/>(我能提前走吗?) | **I will need to leave at …**<br/>(我需要在某某时间离开) | 前者在请求批准,后者在陈述安排——你是专业人士,不需要为合理需求道歉 | | 需要早退 | **Could I possibly leave early?**<br/>(我能提前走吗?) | **I will need to leave at …**<br/>(我需要在某某时间离开) | 前者在请求批准,后者在陈述安排——你是专业人士,不需要为合理需求道歉 |
## 核心原则 ## 核心原则
@@ -21,6 +21,6 @@ tags:
于是对于 PowerNap 这个功能来说,我们只能从命令行下手,首先使用 **`pmset -g`** 命令查看当前状态,找到 **`powernap`** 的值,如果不是 0 ,说明是启用的状态,使用命令 **`sudo pmset -a powernap 0`** 关掉它。 于是对于 PowerNap 这个功能来说,我们只能从命令行下手,首先使用 **`pmset -g`** 命令查看当前状态,找到 **`powernap`** 的值,如果不是 0 ,说明是启用的状态,使用命令 **`sudo pmset -a powernap 0`** 关掉它。
同时,还有另外一个 **`tcpkeepalive`** ,这个默认应该也不是 0 ,也要关掉,它决定了你的 rmbp 在休眠时是否要保持 tcp 连接。 **`sudo pmset -a tcpkeepalive 0`** ,执行这条命令会导致终端提示:***Warning: This option disables TCP Keep Alive mechanism when system is sleeping. This will result in some critical features like 'Find My Mac' not to function properly.*** 同时,还有另外一个 **`tcpkeepalive`** ,这个默认应该也不是 0 ,也要关掉,它决定了你的 rmbp 在休眠时是否要保持 tcp 连接。 **`sudo pmset -a tcpkeepalive 0`** ,执行这条命令会导致终端提示:**_Warning: This option disables TCP Keep Alive mechanism when system is sleeping. This will result in some critical features like 'Find My Mac' not to function properly._**
大概是说关了的话某些功能会受到限制,其实就是系统功能不能休眠时联网了,我相信真有人偷了你 Mac,它也连不上网的。 大概是说关了的话某些功能会受到限制,其实就是系统功能不能休眠时联网了,我相信真有人偷了你 Mac,它也连不上网的。
@@ -11,20 +11,20 @@ author:
--- ---
| 中文字号 | 英文字号(磅)/pt | 毫米/mm | 像素/px | | 中文字号 | 英文字号(磅)/pt | 毫米/mm | 像素/px |
|------|------------|-------|-------| | -------- | ----------------- | ------- | ------- |
| 初号 | 42 | 14.82 | 56 | | 初号 | 42 | 14.82 | 56 |
| 小初 | 36 | 12.7 | 48 | | 小初 | 36 | 12.7 | 48 |
| 一号 | 26 | 9.17 | 34.7 | | 一号 | 26 | 9.17 | 34.7 |
| 小一 | 24 | 8.47 | 32 | | 小一 | 24 | 8.47 | 32 |
| 二号 | 22 | 7.76 | 29.3 | | 二号 | 22 | 7.76 | 29.3 |
| 小二 | 18 | 6.35 | 24 | | 小二 | 18 | 6.35 | 24 |
| 三号 | 16 | 5.64 | 21.3 | | 三号 | 16 | 5.64 | 21.3 |
| 小三 | 15 | 5.29 | 20 | | 小三 | 15 | 5.29 | 20 |
| 四号 | 14 | 4.94 | 18.7 | | 四号 | 14 | 4.94 | 18.7 |
| 小四 | 12 | 4.23 | 16 | | 小四 | 12 | 4.23 | 16 |
| 五号 | 10.5 | 3.7 | 14 | | 五号 | 10.5 | 3.7 | 14 |
| 小五 | 9 | 3.18 | 12 | | 小五 | 9 | 3.18 | 12 |
| 六号 | 7.5 | 2.56 | 10 | | 六号 | 7.5 | 2.56 | 10 |
| 小六 | 6.5 | 2.29 | 8.7 | | 小六 | 6.5 | 2.29 | 8.7 |
| 七号 | 5.5 | 1.94 | 7.3 | | 七号 | 5.5 | 1.94 | 7.3 |
| 八号 | 5 | 1.76 | 6.7 | | 八号 | 5 | 1.76 | 6.7 |
@@ -68,7 +68,7 @@ createRoot(document.getElementById("root")!).render(
<StyleProvider> <StyleProvider>
<ConfigProvider> <ConfigProvider>
<App> <App>
<RouterProvider/> <RouterProvider />
</App> </App>
</ConfigProvider> </ConfigProvider>
</StyleProvider> </StyleProvider>
+1 -1
View File
@@ -14,7 +14,7 @@ GitLab 在代码中通过 `SWITCHER_MINIMUM_TRANSLATION_LEVEL` 变量将翻译
可以前往 `/path/to/gitlab/embedded/service/gitlab-rails/app/helpers/preferred_language_switcher_helper.rb` 中将代码修改如下: 可以前往 `/path/to/gitlab/embedded/service/gitlab-rails/app/helpers/preferred_language_switcher_helper.rb` 中将代码修改如下:
```diff title="/path/to/gitlab/embedded/service/gitlab-rails/app/helpers/preferred_language_switcher_helper.rb" ```diff title="/path/to/gitlab/embedded/service/gitlab-rails/app/helpers/preferred_language_switcher_helper.rb"
- SWITCHER_MINIMUM_TRANSLATION_LEVEL = 90 - SWITCHER_MINIMUM_TRANSLATION_LEVEL = 90
+ SWITCHER_MINIMUM_TRANSLATION_LEVEL = 80 + SWITCHER_MINIMUM_TRANSLATION_LEVEL = 80
``` ```
@@ -99,11 +99,11 @@ N+1 问题是指 JPA 先执行 1 条查询获取父实体,再为每条父实
**修复方案:** **修复方案:**
| 方式 | 适用场景 | | 方式 | 适用场景 |
|---------------------------|----------------------| | ---------------------------- | ---------------------------------- |
| `@EntityGraph` | 声明式,适合为特定实体定制抓取计划 | | `@EntityGraph` | 声明式,适合为特定实体定制抓取计划 |
| `@Query` 中使用 `JOIN FETCH` | 按查询精细控制 | | `@Query` 中使用 `JOIN FETCH` | 按查询精细控制 |
| `@BatchSize` | 将 N+1 降低为 N/k+1,批量加载 | | `@BatchSize` | 将 N+1 降低为 N/k+1,批量加载 |
```java ```java
// 方式一:EntityGraph // 方式一:EntityGraph
@@ -139,6 +139,7 @@ post.setAuthor(userRepository.getReferenceById(userId));
@Query("SELECT new com.example.UserDto(u.id, u.name) FROM User u WHERE u.id = :id") @Query("SELECT new com.example.UserDto(u.id, u.name) FROM User u WHERE u.id = :id")
UserDto findUserDtoById(@Param("id") long id); UserDto findUserDtoById(@Param("id") long id);
``` ```
3. **在 Service 方法上使用 `@Transactional(readOnly = true)`** — 使 Session 在整个方法作用域内保持打开。 3. **在 Service 方法上使用 `@Transactional(readOnly = true)`** — 使 Session 在整个方法作用域内保持打开。
### 什么时候该用 `@Transactional(readOnly = true)`? ### 什么时候该用 `@Transactional(readOnly = true)`?
@@ -270,7 +271,7 @@ public class Product {
### 如何选择 ### 如何选择
| 场景 | 注解 | | 场景 | 注解 |
|----------------------------------------|--------------------| | -------------------------------------- | ---------------- |
| 平铺字段固定的嵌套对象(如地址、配置) | `@JsonUnwrapped` | | 平铺字段固定的嵌套对象(如地址、配置) | `@JsonUnwrapped` |
| 平铺 key 不固定的动态键值对 | `@JsonAnyGetter` | | 平铺 key 不固定的动态键值对 | `@JsonAnyGetter` |
@@ -119,7 +119,7 @@ author:
- 数据操作约定: - 数据操作约定:
- 对于简单数据库操作,使用 JPA。 - 对于简单数据库操作,使用 JPA。
- 对于复杂数据库操作,使用 MyBatis。 - 对于复杂数据库操作,使用 MyBatis。
- 当进行分页查询时,页码应从 0 开始(*兼容 Spring Data JPA*)。 - 当进行分页查询时,页码应从 0 开始(_兼容 Spring Data JPA_)。
- **数据操作方法命名约定**: - **数据操作方法命名约定**:
- 对于**查询数据列表**:方法**必须**以 `selectListBy` 开头,紧随其后是筛选条件(例如,`selectListByUserId`、`selectListByDepartmentIdAndStatus`)。这些方法也必须包含一个 `PageRequest` 参数用于分页。 - 对于**查询数据列表**:方法**必须**以 `selectListBy` 开头,紧随其后是筛选条件(例如,`selectListByUserId`、`selectListByDepartmentIdAndStatus`)。这些方法也必须包含一个 `PageRequest` 参数用于分页。
- 对于查询**单个数据记录**:方法**必须**以 `selectOne` 开头(例如,`selectOneById`、`selectOneByUsername`)。 - 对于查询**单个数据记录**:方法**必须**以 `selectOne` 开头(例如,`selectOneById`、`selectOneByUsername`)。
+2 -2
View File
@@ -5,8 +5,8 @@ tags:
- json - json
- database - database
author: author:
name: Zihlu Wang name: Zihlu Wang
email: real@zihluwang.me email: real@zihluwang.me
--- ---
MySQL (自 5.7 版本开始) **不直接支持名为 `jsonb` 的数据类型**。`jsonb` 是 PostgreSQL 数据库特有的数据类型,它以二进制格式存储 JSON 数据,并对其进行预解析,以便在查询时更快地访问和操作。 MySQL (自 5.7 版本开始) **不直接支持名为 `jsonb` 的数据类型**。`jsonb` 是 PostgreSQL 数据库特有的数据类型,它以二进制格式存储 JSON 数据,并对其进行预解析,以便在查询时更快地访问和操作。
+10 -11
View File
@@ -25,11 +25,11 @@ https://gravatar.com/avatar/<hash>
### Node.js ### Node.js
```js ```js
import { createHash } from "node:crypto"; import { createHash } from "node:crypto"
const email = "user@example.com".trim().toLowerCase(); const email = "user@example.com".trim().toLowerCase()
const hash = createHash("sha256").update(email).digest("hex"); const hash = createHash("sha256").update(email).digest("hex")
const url = `https://gravatar.com/avatar/${hash}`; const url = `https://gravatar.com/avatar/${hash}`
``` ```
### Python ### Python
@@ -52,11 +52,11 @@ echo -n "user@example.com" | tr '[:upper:]' '[:lower:]' | sha256sum | cut -d ' '
`/avatar/` 端点接受以下查询参数来定制结果: `/avatar/` 端点接受以下查询参数来定制结果:
| 参数 | 描述 | 示例 | | 参数 | 描述 | 示例 |
|-----|------------------------|----------------| | ---- | ------------------------------- | -------------- |
| `s` | 图片尺寸(像素,默认 80) | `?s=200` | | `s` | 图片尺寸(像素,默认 80) | `?s=200` |
| `d` | 未找到 Gravatar 时的默认图片 | `?d=identicon` | | `d` | 未找到 Gravatar 时的默认图片 | `?d=identicon` |
| `r` | 内容分级(`g`、`pg`、`r`、`x`) | `?r=g` | | `r` | 内容分级(`g`、`pg`、`r`、`x`) | `?r=g` |
### 默认图片选项(`d`) ### 默认图片选项(`d`)
@@ -76,8 +76,7 @@ echo -n "user@example.com" | tr '[:upper:]' '[:lower:]' | sha256sum | cut -d ' '
src="https://gravatar.com/avatar/a3b4c5d6e7f8?s=160&d=robohash&r=g" src="https://gravatar.com/avatar/a3b4c5d6e7f8?s=160&d=robohash&r=g"
alt="用户头像" alt="用户头像"
width="160" width="160"
height="160" height="160" />
/>
``` ```
始终传入 `d` 参数以避免未设置 Gravatar 的用户出现裂图。`identicon` 和 `robohash` 是常用的选择,因为它们能为每个哈希值生成唯一且易于辨识的图片。 始终传入 `d` 参数以避免未设置 Gravatar 的用户出现裂图。`identicon` 和 `robohash` 是常用的选择,因为它们能为每个哈希值生成唯一且易于辨识的图片。
@@ -6,16 +6,16 @@ title: Calendar Toolbox API
库的代码组织在基础包 `com.onixbyte.calendar` 下,结构如下: 库的代码组织在基础包 `com.onixbyte.calendar` 下,结构如下:
| 包 | 描述 | | 包 | 描述 |
|---|---| | ------------------------------------------ | ---------------------------------- |
| `com.onixbyte.calendar` | 根日历对象 | | `com.onixbyte.calendar` | 根日历对象 |
| `com.onixbyte.calendar.component` | iCalendar 组件(VEVENT、VTODO 等) | | `com.onixbyte.calendar.component` | iCalendar 组件(VEVENT、VTODO 等) |
| `com.onixbyte.calendar.component.property` | 组件级属性 | | `com.onixbyte.calendar.component.property` | 组件级属性 |
| `com.onixbyte.calendar.property` | 日历级属性 | | `com.onixbyte.calendar.property` | 日历级属性 |
| `com.onixbyte.calendar.parameter` | iCalendar 属性参数 | | `com.onixbyte.calendar.parameter` | iCalendar 属性参数 |
| `com.onixbyte.calendar.recurrence` | 重复规则类型 | | `com.onixbyte.calendar.recurrence` | 重复规则类型 |
| `com.onixbyte.calendar.value` | 值类型 | | `com.onixbyte.calendar.value` | 值类型 |
| `com.onixbyte.calendar.util` | 格式化工具类 | | `com.onixbyte.calendar.util` | 格式化工具类 |
--- ---
@@ -46,19 +46,19 @@ String icsContent = calendar.formatted();
所有日历级属性位于 `com.onixbyte.calendar.property`,实现 `CalendarProperty` 接口。 所有日历级属性位于 `com.onixbyte.calendar.property`,实现 `CalendarProperty` 接口。
| 类 | iCalendar 属性 | 描述 | | 类 | iCalendar 属性 | 描述 |
|---|---|---| | ------------------------ | -------------------- | --------------------------------------- |
| `CalendarScale` | `CALSCALE` | 日历系统(例如 GREGORIAN) | | `CalendarScale` | `CALSCALE` | 日历系统(例如 GREGORIAN) |
| `Method` | `METHOD` | iCalendar 方法(例如 PUBLISH、REQUEST) | | `Method` | `METHOD` | iCalendar 方法(例如 PUBLISH、REQUEST) |
| `ProductIdentifier` | `PRODID` | 日历的产品标识符 | | `ProductIdentifier` | `PRODID` | 日历的产品标识符 |
| `Version` | `VERSION` | iCalendar 版本(例如 2.0) | | `Version` | `VERSION` | iCalendar 版本(例如 2.0) |
| `Owner` | `X-OWNER` | 日历所有者 | | `Owner` | `X-OWNER` | 日历所有者 |
| `PrimaryCalendar` | `X-PRIMARY-CALENDAR` | 是否为主日历 | | `PrimaryCalendar` | `X-PRIMARY-CALENDAR` | 是否为主日历 |
| `PublishedTTL` | `X-PUBLISHED-TTL` | 已发布日历的生存时间 | | `PublishedTTL` | `X-PUBLISHED-TTL` | 已发布日历的生存时间 |
| `CalendarDescription` | `X-CALENDAR-DESC` | 日历描述 | | `CalendarDescription` | `X-CALENDAR-DESC` | 日历描述 |
| `CalendarName` | `X-CALENDAR-NAME` | 日历名称 | | `CalendarName` | `X-CALENDAR-NAME` | 日历名称 |
| `CalendarId` | `X-CALENDAR-ID` | 日历标识符 | | `CalendarId` | `X-CALENDAR-ID` | 日历标识符 |
| `CustomCalendarProperty` | `X-*` | 自定义日历属性 | | `CustomCalendarProperty` | `X-*` | 自定义日历属性 |
自定义属性使用 Builder 创建: 自定义属性使用 Builder 创建:
@@ -276,50 +276,50 @@ Alarm emailAlarm = Alarm.builder()
组件属性位于 `com.onixbyte.calendar.component.property`,实现 `ComponentProperty` 接口。 组件属性位于 `com.onixbyte.calendar.component.property`,实现 `ComponentProperty` 接口。
| 类 | iCalendar 属性 | 描述 | | 类 | iCalendar 属性 | 描述 |
|---|---|---| | ------------------------- | ------------------ | ---------------------------------------------- |
| `Action` | `ACTION` | 闹钟操作类型(AUDIO、DISPLAY、EMAIL) | | `Action` | `ACTION` | 闹钟操作类型(AUDIO、DISPLAY、EMAIL) |
| `Attachment` | `ATTACH` | 文档附件 | | `Attachment` | `ATTACH` | 文档附件 |
| `Attendee` | `ATTENDEE` | 事件/任务参与者 | | `Attendee` | `ATTENDEE` | 事件/任务参与者 |
| `Categories` | `CATEGORIES` | 类别或标签 | | `Categories` | `CATEGORIES` | 类别或标签 |
| `Classification` | `CLASS` | 访问分类(PUBLIC、PRIVATE、CONFIDENTIAL) | | `Classification` | `CLASS` | 访问分类(PUBLIC、PRIVATE、CONFIDENTIAL) |
| `Comment` | `COMMENT` | 评论 | | `Comment` | `COMMENT` | 评论 |
| `Contact` | `CONTACT` | 联系信息 | | `Contact` | `CONTACT` | 联系信息 |
| `DateTimeCompleted` | `COMPLETED` | 完成日期时间 | | `DateTimeCompleted` | `COMPLETED` | 完成日期时间 |
| `DateTimeCreated` | `CREATED` | 创建日期时间 | | `DateTimeCreated` | `CREATED` | 创建日期时间 |
| `DateTimeDue` | `DUE` | 截止日期时间 | | `DateTimeDue` | `DUE` | 截止日期时间 |
| `DateTimeEnd` | `DTEND` | 结束日期时间 | | `DateTimeEnd` | `DTEND` | 结束日期时间 |
| `DateTimeStamp` | `DTSTAMP` | 日期时间戳 | | `DateTimeStamp` | `DTSTAMP` | 日期时间戳 |
| `DateTimeStart` | `DTSTART` | 开始日期时间 | | `DateTimeStart` | `DTSTART` | 开始日期时间 |
| `Description` | `DESCRIPTION` | 描述 | | `Description` | `DESCRIPTION` | 描述 |
| `ExceptionDateTimes` | `EXDATE` | 例外日期时间 | | `ExceptionDateTimes` | `EXDATE` | 例外日期时间 |
| `FreeBusyTime` | `FREEBUSY` | 空闲/忙碌时间段 | | `FreeBusyTime` | `FREEBUSY` | 空闲/忙碌时间段 |
| `GeographicPosition` | `GEO` | 地理位置(纬度/经度) | | `GeographicPosition` | `GEO` | 地理位置(纬度/经度) |
| `LastModified` | `LAST-MODIFIED` | 最后修改日期时间 | | `LastModified` | `LAST-MODIFIED` | 最后修改日期时间 |
| `Location` | `LOCATION` | 地点 | | `Location` | `LOCATION` | 地点 |
| `Organiser` | `ORGANIZER` | 组织者 | | `Organiser` | `ORGANIZER` | 组织者 |
| `PercentComplete` | `PERCENT-COMPLETE` | 完成百分比 | | `PercentComplete` | `PERCENT-COMPLETE` | 完成百分比 |
| `Priority` | `PRIORITY` | 优先级 | | `Priority` | `PRIORITY` | 优先级 |
| `RecurrenceDateTimes` | `RDATE` | 重复日期时间 | | `RecurrenceDateTimes` | `RDATE` | 重复日期时间 |
| `RecurrenceId` | `RECURRENCE-ID` | 重复实例标识符 | | `RecurrenceId` | `RECURRENCE-ID` | 重复实例标识符 |
| `RecurrenceRule` | `RRULE` | 重复规则 | | `RecurrenceRule` | `RRULE` | 重复规则 |
| `RelatedTo` | `RELATED-TO` | 相关组件引用 | | `RelatedTo` | `RELATED-TO` | 相关组件引用 |
| `RepeatCount` | `REPEAT` | 闹钟重复次数 | | `RepeatCount` | `REPEAT` | 闹钟重复次数 |
| `RequestStatus` | `REQUEST-STATUS` | 请求状态 | | `RequestStatus` | `REQUEST-STATUS` | 请求状态 |
| `Resources` | `RESOURCES` | 资源 | | `Resources` | `RESOURCES` | 资源 |
| `SequenceNumber` | `SEQUENCE` | 序列号(修订版本) | | `SequenceNumber` | `SEQUENCE` | 序列号(修订版本) |
| `Status` | `STATUS` | 组件状态(TENTATIVE、CONFIRMED、CANCELLED 等) | | `Status` | `STATUS` | 组件状态(TENTATIVE、CONFIRMED、CANCELLED 等) |
| `Summary` | `SUMMARY` | 标题或摘要 | | `Summary` | `SUMMARY` | 标题或摘要 |
| `TimeTransparency` | `TRANSP` | 时间透明度(OPAQUE、TRANSPARENT) | | `TimeTransparency` | `TRANSP` | 时间透明度(OPAQUE、TRANSPARENT) |
| `TimeZoneIdentifier` | `TZID` | 时区标识符 | | `TimeZoneIdentifier` | `TZID` | 时区标识符 |
| `TimeZoneName` | `TZNAME` | 时区名称 | | `TimeZoneName` | `TZNAME` | 时区名称 |
| `TimeZoneOffsetFrom` | `TZOFFSETFROM` | 相对于 UTC 的时区偏移(原) | | `TimeZoneOffsetFrom` | `TZOFFSETFROM` | 相对于 UTC 的时区偏移(原) |
| `TimeZoneOffsetTo` | `TZOFFSETTO` | 相对于 UTC 的时区偏移(目标) | | `TimeZoneOffsetTo` | `TZOFFSETTO` | 相对于 UTC 的时区偏移(目标) |
| `TimeZoneUrl` | `TZURL` | 时区 URL | | `TimeZoneUrl` | `TZURL` | 时区 URL |
| `Trigger` | `TRIGGER` | 闹钟触发器 | | `Trigger` | `TRIGGER` | 闹钟触发器 |
| `UniformResourceLocator` | `URL` | 关联 URL | | `UniformResourceLocator` | `URL` | 关联 URL |
| `UniqueIdentifier` | `UID` | 唯一标识符 | | `UniqueIdentifier` | `UID` | 唯一标识符 |
| `CustomComponentProperty` | `X-*` | 自定义组件属性 | | `CustomComponentProperty` | `X-*` | 自定义组件属性 |
--- ---
@@ -327,28 +327,28 @@ Alarm emailAlarm = Alarm.builder()
参数位于 `com.onixbyte.calendar.parameter`,为组件属性提供额外限定。 参数位于 `com.onixbyte.calendar.parameter`,为组件属性提供额外限定。
| 类 | iCalendar 参数 | 描述 | | 类 | iCalendar 参数 | 描述 |
|---|---|---| | ----------------------------- | ---------------- | ---------------------------------------------- |
| `AlarmTriggerRelationship` | `RELATED` | 触发器关联(START、END) | | `AlarmTriggerRelationship` | `RELATED` | 触发器关联(START、END) |
| `AlternateTextRepresentation` | `ALTREP` | 替代文本 URI | | `AlternateTextRepresentation` | `ALTREP` | 替代文本 URI |
| `CalendarUserType` | `CUTYPE` | 日历用户类型(INDIVIDUAL、GROUP、RESOURCE 等) | | `CalendarUserType` | `CUTYPE` | 日历用户类型(INDIVIDUAL、GROUP、RESOURCE 等) |
| `CommonName` | `CN` | 通用名称 | | `CommonName` | `CN` | 通用名称 |
| `Delegatees` | `DELEGATED-TO` | 受托人 | | `Delegatees` | `DELEGATED-TO` | 受托人 |
| `Delegators` | `DELEGATED-FROM` | 委托人 | | `Delegators` | `DELEGATED-FROM` | 委托人 |
| `DirectoryEntryReference` | `DIR` | 目录条目 URI | | `DirectoryEntryReference` | `DIR` | 目录条目 URI |
| `FormatType` | `FMTTYPE` | 格式类型(MIME 类型) | | `FormatType` | `FMTTYPE` | 格式类型(MIME 类型) |
| `FreeBusyTimeType` | `FBTYPE` | 空闲/忙碌时间类型 | | `FreeBusyTimeType` | `FBTYPE` | 空闲/忙碌时间类型 |
| `InlineEncoding` | `ENCODING` | 内联编码(BASE64) | | `InlineEncoding` | `ENCODING` | 内联编码(BASE64) |
| `Language` | `LANGUAGE` | 语言 | | `Language` | `LANGUAGE` | 语言 |
| `Membership` | `MEMBER` | 组成员 | | `Membership` | `MEMBER` | 组成员 |
| `ParticipationRole` | `ROLE` | 参与角色(CHAIR、REQ-PARTICIPANT 等) | | `ParticipationRole` | `ROLE` | 参与角色(CHAIR、REQ-PARTICIPANT 等) |
| `ParticipationStatus` | `PARTSTAT` | 参与状态(ACCEPTED、DECLINED 等) | | `ParticipationStatus` | `PARTSTAT` | 参与状态(ACCEPTED、DECLINED 等) |
| `RecurrenceIdentifierRange` | `RANGE` | 重复范围(THISANDPRIOR、THISANDFUTURE) | | `RecurrenceIdentifierRange` | `RANGE` | 重复范围(THISANDPRIOR、THISANDFUTURE) |
| `RelationshipType` | `RELTYPE` | 关系类型(PARENT、CHILD、SIBLING) | | `RelationshipType` | `RELTYPE` | 关系类型(PARENT、CHILD、SIBLING) |
| `RsvpExpectation` | `RSVP` | RSVP 期望(TRUE、FALSE) | | `RsvpExpectation` | `RSVP` | RSVP 期望(TRUE、FALSE) |
| `SentBy` | `SENT-BY` | 发送者 | | `SentBy` | `SENT-BY` | 发送者 |
| `TimeZoneIdentifier` | `TZID` | 时区标识符 | | `TimeZoneIdentifier` | `TZID` | 时区标识符 |
| `ValueDataType` | `VALUE` | 值数据类型(DATE、DATE-TIME 等) | | `ValueDataType` | `VALUE` | 值数据类型(DATE、DATE-TIME 等) |
--- ---
@@ -356,10 +356,10 @@ Alarm emailAlarm = Alarm.builder()
位于 `com.onixbyte.calendar.recurrence`。 位于 `com.onixbyte.calendar.recurrence`。
| 类 | 描述 | | 类 | 描述 |
|---|---| | ------------ | ------------------------------------------------------------ |
| `Frequency` | 重复频率常量(DAILY、WEEKLY、MONTHLY、YEARLY),用于重复规则 | | `Frequency` | 重复频率常量(DAILY、WEEKLY、MONTHLY、YEARLY),用于重复规则 |
| `WeekdayNum` | 重复规则的星期序号(例如第 2 个星期一) | | `WeekdayNum` | 重复规则的星期序号(例如第 2 个星期一) |
--- ---
@@ -367,12 +367,12 @@ Alarm emailAlarm = Alarm.builder()
位于 `com.onixbyte.calendar.value`。 位于 `com.onixbyte.calendar.value`。
| 类 | 描述 | | 类 | 描述 |
|---|---| | ------------------- | ------------------------------------- |
| `FreeBusyTimeValue` | 表示空闲/忙碌时间值 | | `FreeBusyTimeValue` | 表示空闲/忙碌时间值 |
| `PeriodOfTime` | 表示一个时间段,包含开始和结束 | | `PeriodOfTime` | 表示一个时间段,包含开始和结束 |
| `PropertyValue` | 属性的基础值类型 | | `PropertyValue` | 属性的基础值类型 |
| `UtcOffset` | UTC 偏移值(例如 `-05:00`、`+01:00`) | | `UtcOffset` | UTC 偏移值(例如 `-05:00`、`+01:00`) |
--- ---
@@ -22,10 +22,8 @@ Calendar Toolbox 是一个 Java 库,旨在简化遵循 iCalendar 规范(RFC
<Tabs> <Tabs>
<Tab label="Gradle with Kotlin DSL"> <Tab label="Gradle with Kotlin DSL">
```kotlin title="build.gradle.kts" ```kotlin title="build.gradle.kts" dependencies{" "}
dependencies { {implementation("com.onixbyte:calendar-toolbox:1.1.0")}
implementation("com.onixbyte:calendar-toolbox:1.1.0")
}
``` ```
</Tab> </Tab>
<Tab label="Maven"> <Tab label="Maven">
+50 -50
View File
@@ -6,19 +6,19 @@ title: Captcha API
库的代码组织在基础包 `com.onixbyte.captcha` 下,结构如下: 库的代码组织在基础包 `com.onixbyte.captcha` 下,结构如下:
| 包 | 描述 | | 包 | 描述 |
|---|---| | -------------------------------------- | ---------------------- |
| `com.onixbyte.captcha` | 核心接口 | | `com.onixbyte.captcha` | 核心接口 |
| `com.onixbyte.captcha.impl` | 默认实现 | | `com.onixbyte.captcha.impl` | 默认实现 |
| `com.onixbyte.captcha.text` | 文本生成器和渲染器接口 | | `com.onixbyte.captcha.text` | 文本生成器和渲染器接口 |
| `com.onixbyte.captcha.text.impl` | 文本生成器和渲染器实现 | | `com.onixbyte.captcha.text.impl` | 文本生成器和渲染器实现 |
| `com.onixbyte.captcha.text.enums` | 文本相关枚举 | | `com.onixbyte.captcha.text.enums` | 文本相关枚举 |
| `com.onixbyte.captcha.background` | 背景生成器接口 | | `com.onixbyte.captcha.background` | 背景生成器接口 |
| `com.onixbyte.captcha.background.impl` | 背景生成器实现 | | `com.onixbyte.captcha.background.impl` | 背景生成器实现 |
| `com.onixbyte.captcha.noise` | 噪点生成器接口 | | `com.onixbyte.captcha.noise` | 噪点生成器接口 |
| `com.onixbyte.captcha.noise.impl` | 噪点生成器实现 | | `com.onixbyte.captcha.noise.impl` | 噪点生成器实现 |
| `com.onixbyte.captcha.gimpy` | 扭曲引擎接口 | | `com.onixbyte.captcha.gimpy` | 扭曲引擎接口 |
| `com.onixbyte.captcha.gimpy.impl` | 扭曲引擎实现 | | `com.onixbyte.captcha.gimpy.impl` | 扭曲引擎实现 |
--- ---
@@ -45,17 +45,17 @@ BufferedImage image = captcha.createImage(text);
### DefaultCaptchaProducer Builder ### DefaultCaptchaProducer Builder
| 方法 | 默认值 | 描述 | | 方法 | 默认值 | 描述 |
|---|---|---| | -------------------- | --------------------------------------------- | -------------- |
| `textProducer` | `DefaultTextProducer.builder().build()` | 文本生成器 | | `textProducer` | `DefaultTextProducer.builder().build()` | 文本生成器 |
| `wordRenderer` | `DefaultWordRenderer.builder().build()` | 文字渲染器 | | `wordRenderer` | `DefaultWordRenderer.builder().build()` | 文字渲染器 |
| `gimpyEngine` | `WaterRipple.builder().build()` | 扭曲引擎 | | `gimpyEngine` | `WaterRipple.builder().build()` | 扭曲引擎 |
| `backgroundProducer` | `DefaultBackgroundProducer.builder().build()` | 背景生成器 | | `backgroundProducer` | `DefaultBackgroundProducer.builder().build()` | 背景生成器 |
| `width` | `200` | 验证码图片宽度 | | `width` | `200` | 验证码图片宽度 |
| `height` | `50` | 验证码图片高度 | | `height` | `50` | 验证码图片高度 |
| `borderDrawn` | `true` | 是否绘制边框 | | `borderDrawn` | `true` | 是否绘制边框 |
| `borderColour` | `Color.BLACK` | 边框颜色 | | `borderColour` | `Color.BLACK` | 边框颜色 |
| `borderThickness` | `1` | 边框厚度 | | `borderThickness` | `1` | 边框厚度 |
--- ---
@@ -82,10 +82,10 @@ DefaultTextProducer textProducer = DefaultTextProducer.builder()
.build(); .build();
``` ```
| 方法 | 默认值 | 描述 | | 方法 | 默认值 | 描述 |
|---|---|---| | -------- | ----------------------------------- | -------------------- |
| `length` | `6` | 生成文本的长度 | | `length` | `6` | 生成文本的长度 |
| `chars` | `a-z`、`A-Z`、`0-9`(共 62 个字符) | 生成文本使用的字符集 | | `chars` | `a-z`、`A-Z`、`0-9`(共 62 个字符) | 生成文本使用的字符集 |
### WordRenderer ### WordRenderer
@@ -111,24 +111,24 @@ DefaultWordRenderer wordRenderer = DefaultWordRenderer.builder()
.build(); .build();
``` ```
| 方法 | 默认值 | 描述 | | 方法 | 默认值 | 描述 |
|---|---|---| | ------------ | ---------------------- | ------------------ |
| `fontSize` | `40` | 字号(像素) | | `fontSize` | `40` | 字号(像素) |
| `fonts` | `["Arial", "Courier"]` | 字体族(随机选择) | | `fonts` | `["Arial", "Courier"]` | 字体族(随机选择) |
| `fontColour` | `Color.BLACK` | 字体颜色 | | `fontColour` | `Color.BLACK` | 字体颜色 |
| `charSpace` | `2` | 字符间距(像素) | | `charSpace` | `2` | 字符间距(像素) |
| `fontStyle` | `FontStyle.BOLD` | 字体样式 | | `fontStyle` | `FontStyle.BOLD` | 字体样式 |
### FontStyle ### FontStyle
定义 CAPTCHA 文字渲染支持的字体样式。 定义 CAPTCHA 文字渲染支持的字体样式。
| 值 | 对应的 AWT 常量 | 描述 | | 值 | 对应的 AWT 常量 | 描述 |
|---|---|---| | ------------- | ---------------------- | ------------ | ------ |
| `PLAIN` | `java.awt.Font.PLAIN` | 普通样式 | | `PLAIN` | `java.awt.Font.PLAIN` | 普通样式 |
| `BOLD` | `java.awt.Font.BOLD` | 粗体 | | `BOLD` | `java.awt.Font.BOLD` | 粗体 |
| `ITALIC` | `java.awt.Font.ITALIC` | 斜体 | | `ITALIC` | `java.awt.Font.ITALIC` | 斜体 |
| `BOLD_ITALIC` | `java.awt.Font.BOLD | Font.ITALIC` | 粗斜体 | | `BOLD_ITALIC` | `java.awt.Font.BOLD | Font.ITALIC` | 粗斜体 |
--- ---
@@ -152,8 +152,8 @@ WaterRipple gimpy = WaterRipple.builder()
.build(); .build();
``` ```
| 方法 | 默认值 | 描述 | | 方法 | 默认值 | 描述 |
|---|---|---| | --------------- | ---------------------------------------- | ---------- |
| `noiseProducer` | `DefaultNoiseProducer.builder().build()` | 噪点生成器 | | `noiseProducer` | `DefaultNoiseProducer.builder().build()` | 噪点生成器 |
### FishEyeGimpy ### FishEyeGimpy
@@ -175,8 +175,8 @@ ShadowGimpy gimpy = ShadowGimpy.builder()
.build(); .build();
``` ```
| 方法 | 默认值 | 描述 | | 方法 | 默认值 | 描述 |
|---|---|---| | --------------- | ---------------------------------------- | ---------- |
| `noiseProducer` | `DefaultNoiseProducer.builder().build()` | 噪点生成器 | | `noiseProducer` | `DefaultNoiseProducer.builder().build()` | 噪点生成器 |
--- ---
@@ -204,8 +204,8 @@ DefaultNoiseProducer noise = DefaultNoiseProducer.builder()
.build(); .build();
``` ```
| 方法 | 默认值 | 描述 | | 方法 | 默认值 | 描述 |
|---|---|---| | ------------- | ------------- | -------- |
| `noiseColour` | `Color.BLACK` | 噪点颜色 | | `noiseColour` | `Color.BLACK` | 噪点颜色 |
#### NoNoiseProducer #### NoNoiseProducer
@@ -242,7 +242,7 @@ DefaultBackgroundProducer background = DefaultBackgroundProducer.builder()
.build(); .build();
``` ```
| 方法 | 默认值 | 描述 | | 方法 | 默认值 | 描述 |
|---|---|---| | ------------ | ------------------ | ------------ |
| `colourFrom` | `Color.LIGHT_GRAY` | 渐变起始颜色 | | `colourFrom` | `Color.LIGHT_GRAY` | 渐变起始颜色 |
| `colourTo` | `Color.WHITE` | 渐变结束颜色 | | `colourTo` | `Color.WHITE` | 渐变结束颜色 |
@@ -23,10 +23,7 @@ Captcha 是一个用于生成 CAPTCHA 验证码图片的 Java 库。本项目是
<Tabs> <Tabs>
<Tab label="Gradle with Kotlin DSL"> <Tab label="Gradle with Kotlin DSL">
```kotlin title="build.gradle.kts" ```kotlin title="build.gradle.kts" dependencies {implementation("com.onixbyte:captcha:1.0.0")}
dependencies {
implementation("com.onixbyte:captcha:1.0.0")
}
``` ```
</Tab> </Tab>
<Tab label="Maven"> <Tab label="Maven">
@@ -23,6 +23,7 @@ Skills 会被直接部署到 Claude Code 自身的 Skills 目录(`~/.claude/sk
| Windows (amd64) | `ccsm-windows-amd64.exe` | | Windows (amd64) | `ccsm-windows-amd64.exe` |
将二进制文件放到 `PATH` 中的某个目录,并重命名为 `ccsm`(Windows 上为 `ccsm.exe`)。 将二进制文件放到 `PATH` 中的某个目录,并重命名为 `ccsm`(Windows 上为 `ccsm.exe`)。
</Tab> </Tab>
<Tab label="Cargo 安装"> <Tab label="Cargo 安装">
```bash ```bash
@@ -109,10 +110,10 @@ ccsm marketplace remove my-marketplace
CCSM 支持两种部署模式: CCSM 支持两种部署模式:
| 模式 | 行为 | 适用场景 | | 模式 | 行为 | 适用场景 |
|------|------|----------| | -------------- | ------------------------------------------- | ------------------------------------- |
| `copy`(默认) | 将文件物理复制到 `~/.claude/skills/<name>/` | 日常使用 — 安全且自包含 | | `copy`(默认) | 将文件物理复制到 `~/.claude/skills/<name>/` | 日常使用 — 安全且自包含 |
| `link` | 创建指向本地存储的符号链接 | Skill 开发 — 对源文件的修改会立即生效 | | `link` | 创建指向本地存储的符号链接 | Skill 开发 — 对源文件的修改会立即生效 |
可在配置中设置默认模式,也可在安装时单独指定: 可在配置中设置默认模式,也可在安装时单独指定:
@@ -158,21 +159,21 @@ your-marketplace-repo/
**顶层字段:** **顶层字段:**
| 字段 | 必需 | 说明 | | 字段 | 必需 | 说明 |
|------|------|------| | --------- | ---- | ----------------------------- |
| `name` | 是 | Marketplace 的显示名称 | | `name` | 是 | Marketplace 的显示名称 |
| `version` | 否 | 用于自行追踪的任意版本号 | | `version` | 否 | 用于自行追踪的任意版本号 |
| `skills` | 否 | Skill 条目数组(默认为 `[]`) | | `skills` | 否 | Skill 条目数组(默认为 `[]`) |
**Skill 条目字段:** **Skill 条目字段:**
| 字段 | 必需 | 说明 | | 字段 | 必需 | 说明 |
|------|------|------| | ------------- | ---- | ----------------------------------------------------------- |
| `name` | 是 | Skill 的唯一标识符(用于 `ccsm install <name>`) | | `name` | 是 | Skill 的唯一标识符(用于 `ccsm install <name>`) |
| `description` | 是 | 在 `ccsm search` 输出中显示的一行摘要 | | `description` | 是 | 在 `ccsm search` 输出中显示的一行摘要 |
| `repository` | 是 | Skill 的公开 Git 仓库 URL | | `repository` | 是 | Skill 的公开 Git 仓库 URL |
| `branch` | 否 | 安装时使用的 Git 分支(先尝试 `main`,再回退到 `master`) | | `branch` | 否 | 安装时使用的 Git 分支(先尝试 `main`,再回退到 `master`) |
| `path` | 否 | 仓库中 Skill 文件所在的子目录 — 支持单个仓库托管多个 Skills | | `path` | 否 | 仓库中 Skill 文件所在的子目录 — 支持单个仓库托管多个 Skills |
### 工作原理 ### 工作原理
@@ -202,12 +203,12 @@ my-skill-repo/
## 数据存储位置 ## 数据存储位置
| 路径 | 用途 | | 路径 | 用途 |
|------|------| | ------------------------------ | ----------------------------------------------- |
| `~/.claude/skills/` | Claude Code 的 Skills 目录 —— Skills 的部署目标 | | `~/.claude/skills/` | Claude Code 的 Skills 目录 —— Skills 的部署目标 |
| `~/.config/ccsm/config.json` | CCSM 配置文件(已注册的 Marketplace、默认模式) | | `~/.config/ccsm/config.json` | CCSM 配置文件(已注册的 Marketplace、默认模式) |
| `~/.config/ccsm/marketplaces/` | 缓存的 Marketplace 清单 | | `~/.config/ccsm/marketplaces/` | 缓存的 Marketplace 清单 |
| `~/.config/ccsm/skills/` | 下载的 Skill 文件(本地存储) | | `~/.config/ccsm/skills/` | 下载的 Skill 文件(本地存储) |
在 Windows 上,`~/.config/` 对应 `C:\Users\<user>\AppData\Roaming\`。 在 Windows 上,`~/.config/` 对应 `C:\Users\<user>\AppData\Roaming\`。
@@ -6,64 +6,64 @@ title: Delta Force Guide API
后端是基于 Spring Boot 3 的应用程序,代码组织在基础包 `com.onixbyte.deltaforceguide` 下: 后端是基于 Spring Boot 3 的应用程序,代码组织在基础包 `com.onixbyte.deltaforceguide` 下:
| 包 | 描述 | | 包 | 描述 |
|---|---| | ------------- | --------------------------- |
| `controller` | REST API 端点控制器 | | `controller` | REST API 端点控制器 |
| `service` | 业务逻辑层 | | `service` | 业务逻辑层 |
| `repository` | MyBatis 数据访问层 | | `repository` | MyBatis 数据访问层 |
| `domain` | JPA 实体与领域对象 | | `domain` | JPA 实体与领域对象 |
| `config` | Spring 配置(安全、缓存等) | | `config` | Spring 配置(安全、缓存等) |
| `properties` | 配置属性类 | | `properties` | 配置属性类 |
| `enumeration` | 枚举类型 | | `enumeration` | 枚举类型 |
| `shared` | 共享工具类 | | `shared` | 共享工具类 |
## API 端点 ## API 端点
### 认证 ### 认证
| 方法 | 路径 | 描述 | 认证 | | 方法 | 路径 | 描述 | 认证 |
|---|---|---|---| | ------ | ------------------- | ----------------- | ---- |
| `POST` | `/api/auth/login` | 使用凭证登录 | 否 | | `POST` | `/api/auth/login` | 使用凭证登录 | 否 |
| `POST` | `/api/auth/refresh` | 刷新 access token | 否 | | `POST` | `/api/auth/refresh` | 刷新 access token | 否 |
| `GET` | `/api/auth/profile` | 获取当前用户信息 | 是 | | `GET` | `/api/auth/profile` | 获取当前用户信息 | 是 |
### 枪械 ### 枪械
| 方法 | 路径 | 描述 | | 方法 | 路径 | 描述 |
|---|---|---| | -------- | ------------------- | ------------------------------ |
| `GET` | `/api/firearms` | 枪械列表(分页,可按类型筛选) | | `GET` | `/api/firearms` | 枪械列表(分页,可按类型筛选) |
| `GET` | `/api/firearms/:id` | 按 ID 获取枪械详情 | | `GET` | `/api/firearms/:id` | 按 ID 获取枪械详情 |
| `POST` | `/api/firearms` | 创建新枪械 | | `POST` | `/api/firearms` | 创建新枪械 |
| `PUT` | `/api/firearms/:id` | 更新枪械信息 | | `PUT` | `/api/firearms/:id` | 更新枪械信息 |
| `DELETE` | `/api/firearms/:id` | 删除枪械 | | `DELETE` | `/api/firearms/:id` | 删除枪械 |
### 改装 ### 改装
| 方法 | 路径 | 描述 | | 方法 | 路径 | 描述 |
|---|---|---| | -------- | --------------------------------- | ------------------------------------ |
| `GET` | `/api/modifications` | 改装列表(分页,可按枪械和标签筛选) | | `GET` | `/api/modifications` | 改装列表(分页,可按枪械和标签筛选) |
| `GET` | `/api/modifications/:id` | 按 ID 获取改装详情 | | `GET` | `/api/modifications/:id` | 按 ID 获取改装详情 |
| `POST` | `/api/modifications` | 创建改装 | | `POST` | `/api/modifications` | 创建改装 |
| `POST` | `/api/modifications/batch` | 批量创建改装 | | `POST` | `/api/modifications/batch` | 批量创建改装 |
| `PUT` | `/api/modifications/:id` | 更新改装 | | `PUT` | `/api/modifications/:id` | 更新改装 |
| `DELETE` | `/api/modifications/:id` | 删除改装 | | `DELETE` | `/api/modifications/:id` | 删除改装 |
| `DELETE` | `/api/modifications/batch-delete` | 批量删除改装 | | `DELETE` | `/api/modifications/batch-delete` | 批量删除改装 |
### 标签 ### 标签
| 方法 | 路径 | 描述 | | 方法 | 路径 | 描述 |
|---|---|---| | ----- | ----------- | ------------ |
| `GET` | `/api/tags` | 获取所有标签 | | `GET` | `/api/tags` | 获取所有标签 |
## 前端路由 ## 前端路由
| 路径 | 布局 | 页面 | 描述 | | 路径 | 布局 | 页面 | 描述 |
|---|---|---|---| | ------------ | -------- | -------- | --------------- |
| `/` | 主布局 | 枪械浏览 | 首页 — 枪械列表 | | `/` | 主布局 | 枪械浏览 | 首页 — 枪械列表 |
| `/firearms` | 主布局 | 枪械浏览 | 枪械详情与搜索 | | `/firearms` | 主布局 | 枪械浏览 | 枪械详情与搜索 |
| `/mod-codes` | 主布局 | 改装代码 | 改装代码库 | | `/mod-codes` | 主布局 | 改装代码 | 改装代码库 |
| `/legal` | 主布局 | 法律信息 | 法律与隐私信息 | | `/legal` | 主布局 | 法律信息 | 法律与隐私信息 |
| `/login` | 空白布局 | 登录 | 用户认证页面 | | `/login` | 空白布局 | 登录 | 用户认证页面 |
## 前端状态管理 ## 前端状态管理
@@ -82,10 +82,10 @@ Redux Toolkit 结合 Redux Persist 管理以下状态:
## 基础设施 ## 基础设施
| 组件 | 技术 | | 组件 | 技术 |
|---|---| | -------- | ---------------------------------------- |
| 数据库 | PostgreSQL(使用 Flyway 进行数据库迁移) | | 数据库 | PostgreSQL(使用 Flyway 进行数据库迁移) |
| 缓存 | Redis(会话和 token 存储) | | 缓存 | Redis(会话和 token 存储) |
| 文件存储 | AWS S3(枪械图片) | | 文件存储 | AWS S3(枪械图片) |
| 容器化 | Docker | | 容器化 | Docker |
| 部署 | Docker Compose + Nginx | | 部署 | Docker Compose + Nginx |
@@ -25,12 +25,12 @@ Delta Force Guide 是一个全栈 Web 应用,为游戏 Delta Force(三角洲
## 架构 ## 架构
| 组件 | 技术栈 | | 组件 | 技术栈 |
|---|---| | -------- | --------------------------------------------------------------------------------------------------- |
| **前端** | React 19、TypeScript、Tailwind CSS 4、Ant Design 6、React Router 7、Redux Toolkit、TanStack Virtual | | **前端** | React 19、TypeScript、Tailwind CSS 4、Ant Design 6、React Router 7、Redux Toolkit、TanStack Virtual |
| **后端** | Spring Boot 3、Java 21、PostgreSQL、MyBatis、Flyway、Redis | | **后端** | Spring Boot 3、Java 21、PostgreSQL、MyBatis、Flyway、Redis |
| **认证** | JWT(access 和 refresh token)、验证码集成 | | **认证** | JWT(access 和 refresh token)、验证码集成 |
| **存储** | AWS S3(图片上传) | | **存储** | AWS S3(图片上传) |
前端是由 Vite 构建的静态站点,通过 API 与 Spring Boot 后端通信。认证采用 JWT access 和 refresh token 机制,refresh token 通过 Redis 管理。 前端是由 Vite 构建的静态站点,通过 API 与 Spring Boot 后端通信。认证采用 JWT access 和 refresh token 机制,refresh token 通过 Redis 管理。
@@ -6,12 +6,12 @@ title: OnixByte Toolbox API
所有模块均在 `com.onixbyte` group ID 下发布。每个模块有自己独立的基础包: 所有模块均在 `com.onixbyte` group ID 下发布。每个模块有自己独立的基础包:
| 模块 | Maven Artifact | 基础包 | | 模块 | Maven Artifact | 基础包 |
|---|---|---| | ------------------ | -------------------- | -------------------------------- |
| Common Toolbox | `common-toolbox` | `com.onixbyte.common` | | Common Toolbox | `common-toolbox` | `com.onixbyte.common` |
| Crypto Toolbox | `crypto-toolbox` | `com.onixbyte.crypto` | | Crypto Toolbox | `crypto-toolbox` | `com.onixbyte.crypto` |
| Math Toolbox | `math-toolbox` | `com.onixbyte.math` | | Math Toolbox | `math-toolbox` | `com.onixbyte.math` |
| Tuple | `tuple` | `com.onixbyte.tuple` | | Tuple | `tuple` | `com.onixbyte.tuple` |
| Identity Generator | `identity-generator` | `com.onixbyte.identitygenerator` | | Identity Generator | `identity-generator` | `com.onixbyte.identitygenerator` |
--- ---
@@ -28,16 +28,16 @@ import com.onixbyte.common.adapter.ObjectMapAdapter;
### 工具类 ### 工具类
| 类 | 描述 | | 类 | 描述 |
|---|---| | ---------------- | ------------------------------------------- |
| `AesUtil` | AES 对称加密/解密,支持密钥生成 | | `AesUtil` | AES 对称加密/解密,支持密钥生成 |
| `Base64Util` | Base64 编解码,支持多种字符集 | | `Base64Util` | Base64 编解码,支持多种字符集 |
| `BoolUtil` | 布尔值解析与转换工具 | | `BoolUtil` | 布尔值解析与转换工具 |
| `BranchUtil` | 条件分支辅助,支持函数式链式调用 | | `BranchUtil` | 条件分支辅助,支持函数式链式调用 |
| `CollectionUtil` | 集合操作 — 分组、分区、合并 | | `CollectionUtil` | 集合操作 — 分组、分区、合并 |
| `HashUtil` | 哈希工具,支持 MD5、SHA-1、SHA-256、SHA-512 | | `HashUtil` | 哈希工具,支持 MD5、SHA-1、SHA-256、SHA-512 |
| `MapUtil` | Map 构建、合并和转换辅助 | | `MapUtil` | Map 构建、合并和转换辅助 |
| `RangeUtil` | 数值范围操作,支持边界处理 | | `RangeUtil` | 数值范围操作,支持边界处理 |
**AesUtil 示例:** **AesUtil 示例:**
@@ -70,12 +70,12 @@ var grouped = CollectionUtil.groupBy(users, User::getDepartment);
### 密钥加载器 ### 密钥加载器
| 类 | 描述 | | 类 | 描述 |
|---|---| | --------------------- | ---------------------------- |
| `RSAPrivateKeyLoader` | 从 PEM 字符串加载 RSA 私钥 | | `RSAPrivateKeyLoader` | 从 PEM 字符串加载 RSA 私钥 |
| `RSAPublicKeyLoader` | 从 PEM 字符串加载 RSA 公钥 | | `RSAPublicKeyLoader` | 从 PEM 字符串加载 RSA 公钥 |
| `ECPrivateKeyLoader` | 从 PEM 字符串加载 ECDSA 私钥 | | `ECPrivateKeyLoader` | 从 PEM 字符串加载 ECDSA 私钥 |
| `ECPublicKeyLoader` | 从 PEM 字符串加载 ECDSA 公钥 | | `ECPublicKeyLoader` | 从 PEM 字符串加载 ECDSA 公钥 |
所有密钥加载器实现 `PrivateKeyLoader` 或 `PublicKeyLoader` 接口。 所有密钥加载器实现 `PrivateKeyLoader` 或 `PublicKeyLoader` 接口。
@@ -99,8 +99,8 @@ var ecPublicKey = new ECPublicKeyLoader().load(pemString);
### 异常 ### 异常
| 类 | 描述 | | 类 | 描述 |
|---|---| | --------------------- | -------------------------- |
| `KeyLoadingException` | PEM 格式密钥解析失败时抛出 | | `KeyLoadingException` | PEM 格式密钥解析失败时抛出 |
--- ---
@@ -111,10 +111,10 @@ var ecPublicKey = new ECPublicKeyLoader().load(pemString);
### 核心类 ### 核心类
| 类 | 描述 | | 类 | 描述 |
|---|---| | ---------------------- | ------------------------------------------------------------- |
| `Calculator` | 统计计算器 — 均值、中位数、方差、标准差、总和、最小值、最大值 | | `Calculator` | 统计计算器 — 均值、中位数、方差、标准差、总和、最小值、最大值 |
| `PercentileCalculator` | 百分位数计算器,支持可配置的插值策略 | | `PercentileCalculator` | 百分位数计算器,支持可配置的插值策略 |
**Calculator 示例:** **Calculator 示例:**
@@ -135,8 +135,8 @@ double p99 = calculator.calculate(99);
### 数据模型 ### 数据模型
| 类 | 描述 | | 类 | 描述 |
|---|---| | ---------------- | --------------------------------------------- |
| `QuartileBounds` | 包含 Q1、中位数(Q2)、Q3 值及 IQR 和须线边界 | | `QuartileBounds` | 包含 Q1、中位数(Q2)、Q3 值及 IQR 和须线边界 |
--- ---
@@ -147,11 +147,11 @@ double p99 = calculator.calculate(99);
### 类 ### 类
| 类 | 类型参数 | 可变性 | | 类 | 类型参数 | 可变性 |
|---|---|---| | -------------------------- | -------- | ------ |
| `Tuple<A, B>` | 两个元素 | 可变 | | `Tuple<A, B>` | 两个元素 | 可变 |
| `ImmutableTuple<A, B>` | 两个元素 | 不可变 | | `ImmutableTuple<A, B>` | 两个元素 | 不可变 |
| `Triple<A, B, C>` | 三个元素 | 可变 | | `Triple<A, B, C>` | 三个元素 | 可变 |
| `ImmutableTriple<A, B, C>` | 三个元素 | 不可变 | | `ImmutableTriple<A, B, C>` | 三个元素 | 不可变 |
**示例:** **示例:**
@@ -183,10 +183,10 @@ Integer c = triple.getThird();
### 实现 ### 实现
| 类 | 描述 | | 类 | 描述 |
|---|---| | ---------------------------- | --------------------------------------------- |
| `SequentialUuidGenerator` | 生成时间排序的顺序 UUID(优化数据库索引性能) | | `SequentialUuidGenerator` | 生成时间排序的顺序 UUID(优化数据库索引性能) |
| `SnowflakeIdentityGenerator` | 雪花算法分布式唯一 ID 生成 | | `SnowflakeIdentityGenerator` | 雪花算法分布式唯一 ID 生成 |
**顺序 UUID:** **顺序 UUID:**
@@ -204,6 +204,6 @@ long id = generator.nextId(); // 64 位雪花 ID
### 异常 ### 异常
| 类 | 描述 | | 类 | 描述 |
|---|---| | ----------------- | ------------------------------------------ |
| `TimingException` | 系统时钟回拨时抛出,保证 ID 唯一性不受影响 | | `TimingException` | 系统时钟回拨时抛出,保证 ID 唯一性不受影响 |
@@ -12,14 +12,14 @@ OnixByte Toolbox 是一个面向 Java 应用的开发工具包,提供了一系
## 模块 ## 模块
| 模块 | Artifact ID | 描述 | | 模块 | Artifact ID | 描述 |
|--------------------|----------------------|-----------------------------------| | ------------------ | -------------------- | ----------------------------------------------------- |
| Common Toolbox | `common-toolbox` | 日常工具类 — AES 加密、哈希、Base64、集合、分支、范围 | | Common Toolbox | `common-toolbox` | 日常工具类 — AES 加密、哈希、Base64、集合、分支、范围 |
| Crypto Toolbox | `crypto-toolbox` | RSA 和 ECDSA 算法的公私钥加载 | | Crypto Toolbox | `crypto-toolbox` | RSA 和 ECDSA 算法的公私钥加载 |
| Math Toolbox | `math-toolbox` | 统计计算、百分位数分析、四分位数计算 | | Math Toolbox | `math-toolbox` | 统计计算、百分位数分析、四分位数计算 |
| Tuple | `tuple` | 泛型二元组和三元组,含不可变版本 | | Tuple | `tuple` | 泛型二元组和三元组,含不可变版本 |
| Identity Generator | `identity-generator` | 雪花算法和顺序 UUID 唯一标识符生成 | | Identity Generator | `identity-generator` | 雪花算法和顺序 UUID 唯一标识符生成 |
| Version Catalogue | `version-catalogue` | OnixByte 项目的集中式 Gradle 版本目录 | | Version Catalogue | `version-catalogue` | OnixByte 项目的集中式 Gradle 版本目录 |
## 安装 ## 安装
@@ -23,12 +23,12 @@ import { Tabs, Tab } from "@rspress/core/theme"
每个地区暴露四个属性: 每个地区暴露四个属性:
| 属性 | 类型 | 描述 | 示例 | | 属性 | 类型 | 描述 | 示例 |
|---|---|---|---| | -------------- | ------ | ----------------------- | ---------------------------------- |
| `abbreviation` | String | ISO 3166-1 alpha-2 缩写 | `HK`、`US`、`GB`、`RU` | | `abbreviation` | String | ISO 3166-1 alpha-2 缩写 | `HK`、`US`、`GB`、`RU` |
| `code` | String | ISO 3166-1 alpha-3 代码 | `HKG`、`USA`、`GBR`、`RUS` | | `code` | String | ISO 3166-1 alpha-3 代码 | `HKG`、`USA`、`GBR`、`RUS` |
| `callingCode` | String | E.164 国际拨号前缀 | `852`、`1`、`44`、`7` | | `callingCode` | String | E.164 国际拨号前缀 | `852`、`1`、`44`、`7` |
| `locale` | String | 默认语言和地区标签 | `zh_HK`、`en_US`、`en_GB`、`ru_RU` | | `locale` | String | 默认语言和地区标签 | `zh_HK`、`en_US`、`en_GB`、`ru_RU` |
## 安装 ## 安装
@@ -20,16 +20,8 @@ import { Tabs, Tab } from "@rspress/core/theme"
## 安装 ## 安装
<Tabs> <Tabs>
<Tab label="npm"> <Tab label="npm">```bash npm install vite-plugin-port-checker -D ```</Tab>
```bash <Tab label="pnpm">```bash pnpm add vite-plugin-port-checker -D ```</Tab>
npm install vite-plugin-port-checker -D
```
</Tab>
<Tab label="pnpm">
```bash
pnpm add vite-plugin-port-checker -D
```
</Tab>
</Tabs> </Tabs>
## 使用 ## 使用
+1 -1
View File
@@ -2,4 +2,4 @@ export default {
plugins: { plugins: {
"@tailwindcss/postcss": {}, "@tailwindcss/postcss": {},
}, },
}; }
+3 -3
View File
@@ -1,9 +1,9 @@
declare module '*.css'; declare module "*.css"
interface ImportMetaEnv { interface ImportMetaEnv {
readonly SSG_MD: boolean; readonly SSG_MD: boolean
} }
interface ImportMeta { interface ImportMeta {
readonly env: ImportMetaEnv; readonly env: ImportMetaEnv
} }