Skip to content

Comments

feat(icon): add colorful option for icon rendering#217

Merged
TalexDreamSoul merged 4 commits intomasterfrom
fix/2.4.7-251129
Nov 29, 2025
Merged

feat(icon): add colorful option for icon rendering#217
TalexDreamSoul merged 4 commits intomasterfrom
fix/2.4.7-251129

Conversation

@TalexDreamSoul
Copy link
Contributor

@TalexDreamSoul TalexDreamSoul commented Nov 29, 2025

Summary by CodeRabbit

  • New Features

    • Per-item icon coloration toggle for URL items (colorful or plain).
    • Clipboard auto-fill exposes an explicit reset control.
  • Improvements

    • Clipboard handling: configurable caching/expiration, more robust timestamp logic, safer parsing, periodic cleanup, and streamlined dismissal/remember behavior.

✏️ Tip: You can customize this high-level summary in your review settings.

Introduces a new `colorful` boolean property to the `ITuffIcon` type and `TuffIcon` component. This allows for explicit control over whether URL-based icons, particularly SVGs, should be rendered in their original colors or as monochrome template icons.

The URL provider for browser actions has been updated to set `colorful: false`, ensuring browser icons are displayed monochromatically for a more consistent UI.
This commit refactors the `useClipboard` hook to enhance code quality and readability.

- Replaced magic numbers related to autofill logic with named constants (e.g., `AUTOFILL_SHORT_TEXT_LIMIT`, `AUTOFILL_TIMESTAMP_TTL`).
- Simplified conditional logic and function implementations for better conciseness.
- Revised comments to be more direct and removed verbose JSDoc blocks, improving overall code clarity.
- Increased the character limit for short text autofill from 25 to 80.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 29, 2025

Walkthrough

Added an optional colorful icon flag and propagated it into URL items and rendering logic; substantially refactored clipboard autofill handling with new constants, TTL/cleanup semantics, improved error handling, and a new public resetAutoPasteState hook member.

Changes

Cohort / File(s) Summary
Icon type definition & provider
packages/utils/types/icon.ts, apps/core-app/src/main/modules/box-tool/addon/url/url-provider.ts
Added optional colorful?: boolean to ITuffIcon; URL provider now sets colorful: false when creating URL icons.
Icon rendering & usage
apps/core-app/src/renderer/src/components/base/TuffIcon.vue, apps/core-app/src/renderer/src/components/render/BoxItem.vue
TuffIcon rendering now requires isSvg && colorful to render SVG path; BoxItem binds :colorful="render?.basic?.icon?.colorful ?? true" to allow per-item coloration.
Clipboard autofill hook
apps/core-app/src/renderer/src/modules/box/adapter/hooks/useClipboard.ts
Major rewrite: added constants (TTL/limits/cleanup), timestamp normalization, updated canAutoPaste semantics (supports -1/0 modes), probabilistic/periodic cleanup, UI-only autofill behavior, expanded file/text handling and error handling, and exposed new public resetAutoPasteState.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Pay special attention to:
    • useClipboard.ts: TTL semantics, normalizeTimestamp, canAutoPaste (-1/0), probabilistic cleanup, JSON parsing/error paths, and the newly exported resetAutoPasteState.
    • TuffIcon.vue / BoxItem.vue: changed SVG rendering condition and default colorful binding—verify visual regressions for non-colorful SVGs.
    • packages/utils/types/icon.ts and url-provider.ts: consumers handling of the new optional colorful property.

Poem

🐇 I hopped through code with cheerful paws,

Gave tiny icons a colorful cause,
Clipboards now tidy with TTL and care,
Resets and timestamps kept orderly there,
A rabbit's tweak — light as air 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.56% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: adding a colorful option for icon rendering across multiple components and the type definition.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/2.4.7-251129

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
apps/core-app/src/renderer/src/modules/box/adapter/hooks/useClipboard.ts (3)

46-74: Use normalizeTimestamp in canAutoPaste to avoid runtime errors on invalid timestamps.

Right now canAutoPaste does:

  • const copiedTime = new Date(clipboardOptions.last.timestamp).getTime()
  • Logs new Date(copiedTime).toISOString()

If clipboardOptions.last.timestamp is malformed, copiedTime becomes NaN, and new Date(NaN).toISOString() will throw a RangeError, breaking auto‑paste.

You already have normalizeTimestamp; reusing it here makes this path robust and consistent with handlePaste.

   function canAutoPaste(): boolean {
-    if (!clipboardOptions.last || !clipboardOptions.last.timestamp) return false
-    if (!appSetting.tools.autoPaste.enable) return false
+    if (!clipboardOptions.last || !clipboardOptions.last.timestamp) return false
+    if (!appSetting.tools.autoPaste.enable) return false
@@
-    const limit = appSetting.tools.autoPaste.time
-    if (limit === -1) return false
-    if (limit === 0) return true
-
-    // Use database timestamp (real copy time) instead of detectedAt
-    const copiedTime = new Date(clipboardOptions.last.timestamp).getTime()
-    const now = Date.now()
-    const elapsed = now - copiedTime
+    const limit = appSetting.tools.autoPaste.time
+    if (limit === -1) return false
+    if (limit === 0) return true
+
+    // Use database timestamp (real copy time) instead of detectedAt
+    const copiedTime = normalizeTimestamp(clipboardOptions.last.timestamp)
+    if (copiedTime === null) {
+      console.error('[Clipboard] Invalid clipboard timestamp, disabling auto-paste for this item')
+      return false
+    }
+
+    const now = Date.now()
+    const elapsed = now - copiedTime
@@
-      copiedAt: new Date(copiedTime).toISOString(),
+      copiedAt: new Date(copiedTime).toISOString(),
       now: new Date(now).toISOString(),
       elapsed,
       limit: limit * 1000,
       canPaste: elapsed <= limit * 1000
     })

You may also want to audit any other direct new Date(...).toISOString() usage for the same issue; here normalizeTimestamp already covers it.


236-258: clearClipboard dismissal mirroring into autoPastedTimestamps is consistent; just tweak brace style.

The logic to:

  • Set lastClearedTimestamp when remember is true, and
  • Mirror the normalized timestamp into autoPastedTimestamps,

nicely aligns manual dismissals with auto‑paste suppression. The else if (!remember) branch correctly resets lastClearedTimestamp when we’re not “remembering” a dismissal.

Only style issues remain:

  • Line 248 } else if (!remember) { is flagged by brace-style (prefer }\nelse if).
  • This function otherwise looks ready.

260-285: System clipboard event handler and new resetAutoPasteState export look good; address minor lint.

  • The early if (!data?.type) return is a good guard against malformed events.
  • Normalizing incomingTimestamp and lastClearedTimestamp reuses the shared timestamp logic.
  • Resetting lastClearedTimestamp and detectedAt on each new event, and exposing resetAutoPasteState from the hook, aligns with the new auto‑paste state model.

Remaining lints:

  • Line 262: if (!data?.type) return hits antfu/if-newline.
  • console.debug on Line 277 is another no-console violation; consider the same mitigation strategy as in handleAutoFill.

Also applies to: 287-293

🧹 Nitpick comments (2)
apps/core-app/src/renderer/src/modules/box/adapter/hooks/useClipboard.ts (1)

76-122: Auto‑fill behavior is clear; double‑check cleanup probability and address no-console lints.

  • The guard clauses (!clipboardOptions.last, !canAutoPaste(), autoPastedTimestamps.has(timestamp)) make the auto‑fill path easy to follow and avoid duplicate UI updates.
  • File handling and short/long text handling look consistent with the new options; using AUTOFILL_SHORT_TEXT_LIMIT and AUTOFILL_LOG_PREVIEW_LENGTH is a good improvement.

Two follow‑ups:

  1. Cleanup probability
    AUTOFILL_CLEANUP_PROBABILITY = 0.1 means a 10% chance per call:

    if (Math.random() < AUTOFILL_CLEANUP_PROBABILITY) {
      cleanupAutoPastedRecords()
    }

    If your original intent was a rare 0.1% cleanup, this constant should be 0.001 instead. Please confirm the desired frequency.

  2. no-console ESLint violations
    The console.debug calls here (Lines 85, 114, 135, 148) are flagged by no-console (only warn/error allowed). Either:

    • Swap them to a project logging helper (preferred), or
    • Change them to console.warn/console.error where appropriate, or
    • Gate them with an environment check and disable the rule locally.

Also applies to: 124-159

apps/core-app/src/renderer/src/components/base/TuffIcon.vue (1)

93-104: isSvg && colorful gate changes behavior for existing callers; verify they’re updated or okay with <img> fallback.

The new branch:

<template v-else-if="addressable">
  <template v-if="isSvg && colorful">
    <i class="TuffIcon-Svg colorful" :alt="alt" :style="{ '--un-icon': `url('${dataurl}')` }" />
  </template>
  <template v-else>
    <img :alt="alt" :src="url" />
  </template>
</template>

means:

  • Only icons with both isSvg === true and colorful === true use the SVG mask path.
  • All other addressable icons (including SVGs with colorful missing/false) now render as plain <img>.

This is correct for the new URL icons where you set colorful: false, but it changes behavior for any existing TuffIcon usages that previously relied on SVG masking without passing colorful. Those will now lose the mask/theming.

I recommend quickly scanning other <TuffIcon> call sites and either:

  • Pass :colorful="true" where you still want the masked SVG behavior, or
  • Confirm that the <img> fallback is acceptable there.

(Optionally, you could also include colorful in the watch dependencies if you ever want to avoid fetching SVG content when colorful is always false.)

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1b8b1d5 and 59b274f.

📒 Files selected for processing (5)
  • apps/core-app/src/main/modules/box-tool/addon/url/url-provider.ts (1 hunks)
  • apps/core-app/src/renderer/src/components/base/TuffIcon.vue (1 hunks)
  • apps/core-app/src/renderer/src/components/render/BoxItem.vue (2 hunks)
  • apps/core-app/src/renderer/src/modules/box/adapter/hooks/useClipboard.ts (11 hunks)
  • packages/utils/types/icon.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/core-app/src/renderer/src/modules/box/adapter/hooks/useClipboard.ts (4)
apps/core-app/src/renderer/src/modules/box/adapter/hooks/index.ts (3)
  • useClipboard (3-3)
  • IClipboardOptions (1-1)
  • IClipboardHook (1-1)
apps/core-app/src/renderer/src/modules/box/adapter/types.ts (1)
  • IBoxOptions (14-24)
apps/core-app/src/renderer/src/modules/box/adapter/hooks/types.ts (2)
  • IClipboardOptions (27-34)
  • IClipboardHook (39-47)
apps/core-app/src/renderer/src/modules/channel/storage/index.ts (1)
  • appSetting (95-95)
🪛 ESLint
apps/core-app/src/renderer/src/modules/box/adapter/hooks/useClipboard.ts

[error] 14-14: Expect newline after if

(antfu/if-newline)


[error] 53-53: Expect newline after if

(antfu/if-newline)


[error] 54-54: Expect newline after if

(antfu/if-newline)


[error] 57-57: Expect newline after if

(antfu/if-newline)


[error] 58-58: Expect newline after if

(antfu/if-newline)


[error] 78-78: Expect newline after if

(antfu/if-newline)


[error] 79-79: Expect newline after if

(antfu/if-newline)


[error] 85-85: Unexpected console statement. Only these console methods are allowed: warn, error.

(no-console)


[error] 114-114: Unexpected console statement. Only these console methods are allowed: warn, error.

(no-console)


[error] 118-118: Closing curly brace appears on the same line as the subsequent block.

(style/brace-style)


[error] 135-135: Unexpected console statement. Only these console methods are allowed: warn, error.

(no-console)


[error] 148-148: Unexpected console statement. Only these console methods are allowed: warn, error.

(no-console)


[error] 192-192: '=' should be placed at the beginning of the line.

(style/operator-linebreak)


[error] 222-222: Expect newline after if

(antfu/if-newline)


[error] 230-230: Closing curly brace appears on the same line as the subsequent block.

(style/brace-style)


[error] 248-248: Closing curly brace appears on the same line as the subsequent block.

(style/brace-style)


[error] 262-262: Expect newline after if

(antfu/if-newline)

🔇 Additional comments (4)
apps/core-app/src/renderer/src/modules/box/adapter/hooks/useClipboard.ts (1)

172-218: handlePaste timestamp handling and dismissal logic look correct.

Using normalizeTimestamp for clipboard.timestamp, currentTimestamp, and lastClearedTimestamp makes the comparisons safe. The isSameClipboard / isDismissed / shouldRefresh flags correctly avoid re‑showing dismissed content while still allowing overrides via overrideDismissed, and you always fall through to handleAutoFill() when appropriate.

If you haven’t already, it’s worth adding a small test or manual check for the case where clipboard.timestamp is an invalid string to ensure the clearClipboard() early‑return path behaves as expected.

apps/core-app/src/main/modules/box-tool/addon/url/url-provider.ts (1)

47-52: Explicit colorful: false for browser URL icons aligns with new TuffIcon behavior.

Using:

.setIcon({ type: 'url', value: browser.icon, colorful: false })

means these browser icons will bypass the SVG mask path (isSvg && colorful) and render as plain <img>, preserving their original logo appearance. This fits the new colorful flag semantics and the BoxItem binding.

If there are other URL icons that should also be de‑colorized, make sure they set colorful: false consistently so their rendering matches this behavior.

apps/core-app/src/renderer/src/components/render/BoxItem.vue (1)

118-126: Dynamic colorful binding for TuffIcon is reasonable and backwards‑friendly.

:colorful="render?.basic?.icon?.colorful ?? true" ensures:

  • Existing icons without a colorful flag still behave as “colorful” (true).
  • Callers like the URL provider can explicitly set colorful: false to switch to the non‑colorful path.

This matches the new TuffIcon logic that checks isSvg && colorful.

It’s worth sanity‑checking that any non‑URL icons that rely on special coloring also set render.basic.icon.colorful as needed, since this binding looks directly at the render metadata rather than the displayIcon computed.

packages/utils/types/icon.ts (1)

40-46: ITuffIcon.colorful extension is clean and backwards‑compatible.

Adding an optional colorful?: boolean with clear docs keeps existing icons valid while enabling the new rendering mode for URL icons. No issues from a type/interface perspective.

Elevates the log level for clipboard operations from `debug` to `warn` to increase their visibility during development and aid in debugging auto-paste behavior.

Additionally, this commit improves code formatting by consistently using block statements for conditionals and try-catch blocks, enhancing readability.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/core-app/src/renderer/src/modules/box/adapter/hooks/useClipboard.ts (1)

210-211: Fix operator linebreak style.

The assignment operator should be placed at the beginning of the continuation line per the project's ESLint style guide.

Apply this diff:

-    const isDismissed =
-      !overrideDismissed && dismissedTimestamp !== null && dismissedTimestamp === clipboardTimestamp
+    const isDismissed
+      = !overrideDismissed && dismissedTimestamp !== null && dismissedTimestamp === clipboardTimestamp
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2eaf792 and 0c5f7a5.

📒 Files selected for processing (1)
  • apps/core-app/src/renderer/src/modules/box/adapter/hooks/useClipboard.ts (12 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/core-app/src/renderer/src/modules/box/adapter/hooks/useClipboard.ts (4)
apps/core-app/src/renderer/src/modules/box/adapter/types.ts (1)
  • IBoxOptions (14-24)
apps/core-app/src/renderer/src/modules/box/adapter/hooks/types.ts (3)
  • IClipboardOptions (27-34)
  • IClipboardHook (39-47)
  • IClipboardItem (11-22)
apps/core-app/src/renderer/src/modules/channel/storage/index.ts (1)
  • appSetting (95-95)
apps/core-app/src/main/modules/clipboard.ts (1)
  • IClipboardItem (63-74)
🪛 ESLint
apps/core-app/src/renderer/src/modules/box/adapter/hooks/useClipboard.ts

[error] 210-210: '=' should be placed at the beginning of the line.

(style/operator-linebreak)

🔇 Additional comments (9)
apps/core-app/src/renderer/src/modules/box/adapter/hooks/useClipboard.ts (9)

7-10: LGTM! Constants improve maintainability.

The extracted constants eliminate magic numbers and make the autofill behavior more configurable and maintainable.


12-29: LGTM! Robust timestamp normalization.

The function correctly handles multiple timestamp formats (number, Date, string) with proper validation, making the autofill logic more resilient to varied input types.


31-47: LGTM! Clean TTL-based cache management.

The autofill cache with TTL-based cleanup and the exposed resetAutoPasteState hook provide a solid foundation for managing auto-paste state across CoreBox sessions.


55-87: LGTM! Comprehensive auto-paste eligibility logic.

The function correctly handles all autofill modes (-1, 0, positive seconds) and uses the database timestamp for accurate time-based filtering.


89-188: LGTM! Well-structured autofill logic with proper guards.

The function correctly handles files, short text (≤80 chars to input), long text (callback only), and images with appropriate guard clauses and duplicate prevention. The probabilistic cleanup (10% chance) provides amortized performance without overhead on every call.


238-255: LGTM! Robust IPC call with error handling.

The function correctly applies the clipboard item to the active application with proper try/catch error handling and boolean success indication.


257-280: LGTM! Clear dismissal logic with dual tracking.

The function correctly clears clipboard UI state and, when remember is true, mirrors the dismissal to both lastClearedTimestamp and autoPastedTimestamps to prevent the item from ever auto-loading again.


282-309: LGTM! Proper system clipboard event handling.

The listener correctly guards against invalid items, respects dismissed state, and updates the clipboard options with appropriate logging and callbacks.


311-317: LGTM! New public API member correctly exposed.

The resetAutoPasteState function is now properly exposed as a public hook member, enabling explicit reset behavior on CoreBox mount as documented in the types file.

@TalexDreamSoul TalexDreamSoul merged commit 7b6bbf3 into master Nov 29, 2025
4 checks passed
@TalexDreamSoul TalexDreamSoul deleted the fix/2.4.7-251129 branch November 29, 2025 07:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant