Skip to content

Conversation

@arjansingh
Copy link
Contributor

@arjansingh arjansingh commented Sep 16, 2025

Summary

These are all the components and updated api logic for our Asset Browser. Next step is wiring up the flow end to end!

Changes

  1. Modal Components + Storybook tests.
  2. Related Compostables.

Not in this PR:

  1. Hooking up to asset widget + running the workflow (next PR).
  2. Pagination of assets + useCacheRequest work (PR after that).

Review Focus

Architecture. Did I Vue it right?

Screenshots (if applicable)

You can download this branch and also run the storybook stories

Screenshot 2025-09-16 at 5 29 05 PM Screenshot 2025-09-16 at 5 28 45 PM Screenshot 2025-09-16 at 5 28 31 PM Screenshot 2025-09-16 at 5 34 04 PM Screenshot 2025-09-16 at 5 33 52 PM

┆Issue is synchronized with this Notion page by Unito

@github-actions
Copy link

github-actions bot commented Sep 16, 2025

🎭 Playwright Test Results

All tests passed!

⏰ Completed at: 09/17/2025, 06:44:07 PM UTC

📈 Summary

  • Total Tests: 450
  • Passed: 421 ✅
  • Failed: 0
  • Flaky: 0
  • Skipped: 29 ⏭️

📊 Test Reports by Browser

  • chromium: View Report • ✅ 414 / ❌ 0 / ⚠️ 0 / ⏭️ 29
  • chromium-2x: View Report • ✅ 2 / ❌ 0 / ⚠️ 0 / ⏭️ 0
  • chromium-0.5x: View Report • ✅ 1 / ❌ 0 / ⚠️ 0 / ⏭️ 0
  • mobile-chrome: View Report • ✅ 4 / ❌ 0 / ⚠️ 0 / ⏭️ 0

🎉 Click on the links above to view detailed test results for each browser configuration.

@arjansingh arjansingh force-pushed the feat/asset-browser-modal-wiring branch from f345a2d to 8ef528b Compare September 16, 2025 22:09
// Replace plugins entirely to avoid inheritance issues
plugins: [
// Only include plugins we explicitly need for Storybook
tailwindcss(),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

the filtering above was filtering out tailwind apparently.

@arjansingh arjansingh marked this pull request as ready for review September 17, 2025 00:35
@dosubot dosubot bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Sep 17, 2025
@arjansingh arjansingh force-pushed the feat/asset-browser-modal-wiring branch from b395af9 to 81a0046 Compare September 17, 2025 00:35
@arjansingh arjansingh changed the title DRAFT - Asset Browser Modal Component and API Data Logic Asset Browser Modal Component Sep 17, 2025
@DrJKL DrJKL assigned DrJKL and unassigned DrJKL Sep 17, 2025
Comment on lines +9 to +18
safelist: [
'icon-[lucide--folder]',
'icon-[lucide--package]',
'icon-[lucide--image]',
'icon-[lucide--video]',
'icon-[lucide--box]',
'icon-[lucide--audio-waveform]',
'icon-[lucide--message-circle]'
],

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think you'll need this if you use the pattern in #5453

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right about the iconify plugin from PR #5453, but that approach only works for static template usage like:

<i  class="icon-[lucide--folder]">

The safelist is still needed for dynamic usage where icons are assigned as strings in JavaScript objects:

{ icon: 'icon-[lucide--folder]' }  // Tailwind can't detect this

We have 3 icons used this way in useAssetBrowser.ts (we have to map them to api categories) and menu composables. However, 4 icons in the safelist are unused and can be removed.

The iconify plugin can't solve the dynamic string assignment problem. Tha'ts why we need the safelist.

// Compute content title from selected category
const contentTitle = computed(() => {
if (selectedCategory.value === 'all') {
return 'All Models'
Copy link

Choose a reason for hiding this comment

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

[quality] medium Priority

Issue: Hardcoded strings 'All Models' and 'Assets' are not internationalized
Context: Project guidelines require all user-facing strings use vue-i18n for consistency and localization
Suggestion: Replace with i18n keys like $t('assetBrowser.allModels') and $t('assetBrowser.assets')

Copy link
Contributor

Choose a reason for hiding this comment

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

This one might be harder to catch in a linter

// Extract description from metadata or create from tags
const typeTag = asset.tags.find((tag) => tag !== 'models')
const description =
asset.user_metadata?.description || `${typeTag || 'Unknown'} model`
Copy link

Choose a reason for hiding this comment

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

[quality] medium Priority

Issue: Hardcoded 'Unknown' string in template literal is not internationalized
Context: Project guidelines require all user-facing strings use vue-i18n
Suggestion: Replace with $t('assetBrowser.unknownModel') or similar i18n key

"
@click="interactive && $emit('select', asset)"
@keydown.enter="interactive && $emit('select', asset)"
@keydown.space.prevent="interactive && $emit('select', asset)"
Copy link

Choose a reason for hiding this comment

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

[quality] medium Priority

Issue: Keyboard space handler uses prevent modifier which may interfere with screen reader functionality
Context: For button elements, space key should use natural button behavior rather than preventing default
Suggestion: Remove .prevent from space handler or ensure proper button semantics are maintained for accessibility

created_at: z.string(),
updated_at: z.string(),
last_access_time: z.string(),
user_metadata: z.record(z.any()).optional(),
Copy link

Choose a reason for hiding this comment

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

[quality] low Priority

Issue: z.any() usage in user_metadata field reduces type safety
Context: z.any() defeats the purpose of schema validation and TypeScript type checking
Suggestion: Consider using z.record(z.string()) or a more specific schema for user_metadata to maintain type safety

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ironically this isn't low, this is probably the most important one.


// Create display stats from API data
const stats = {
formattedDate: new Date(asset.created_at).toLocaleDateString(),
Copy link

Choose a reason for hiding this comment

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

[architecture] medium Priority

Issue: Date parsing with new Date() lacks error handling for invalid date strings
Context: If asset.created_at has invalid date format, this will create Invalid Date objects leading to 'Invalid Date' display
Suggestion: Add date validation or fallback: new Date(asset.created_at).toLocaleDateString() || 'Unknown date'

Copy link
Contributor

Choose a reason for hiding this comment

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

Quick warning, the suggested fix would not behave as described

Copy link
Contributor

Choose a reason for hiding this comment

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

Our i18n library has date localization as well

Copy link

@claude claude bot left a comment

Choose a reason for hiding this comment

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

Comprehensive PR Review

This review is generated by Claude. It may not always be accurate, as with human reviewers. If you believe that any of the comments are invalid or incorrect, please state why for each. For others, please implement the changes in one way or another.

Review Summary

PR: Asset Browser Modal Component (#5607)
Impact: 2518 additions, 61 deletions across 25 files

Issue Distribution

  • Critical: 0
  • High: 1
  • Medium: 6
  • Low: 1

Category Breakdown

  • Architecture: 1 issue
  • Security: 0 issues
  • Performance: 1 issue
  • Code Quality: 6 issues

Key Findings

Architecture & Design

The asset browser implementation follows Vue 3 Composition API patterns well and maintains proper separation of concerns between components and composables. The modal system integration is architected correctly using the dialog store. However, there's a missing error handling pattern for date parsing that could lead to poor user experience.

Security Considerations

No critical security issues identified. The implementation properly uses TypeScript interfaces and schema validation with Zod, though the use of z.any() reduces type safety marginally.

Performance Impact

Asset transformation occurs on every filter operation which could be expensive for large lists. Consider memoizing the transformation results to avoid redundant processing. Bundle size impact appears minimal with good component chunking.

Integration Points

Good integration with existing UI components (SearchBox, LeftSidePanel, BaseModalLayout). Proper use of PrimeVue components and following established patterns. The composable architecture allows for good reusability.

Positive Observations

  • Excellent use of Vue 3 Composition API patterns
  • Good component decomposition and reusability
  • Proper TypeScript typing throughout
  • Comprehensive test coverage including Storybook stories
  • Good use of existing UI components rather than reinventing
  • Proper Tailwind utility usage with dark mode support
  • Clean modal dialog integration

Critical Issues to Address

  1. Class merging patterns: The AssetCard component uses array-based class merging which violates project guidelines. Must use cn() utility instead.

References

Next Steps

  1. Address the high-priority class merging issue before merge
  2. Consider internationalization for all hardcoded strings
  3. Add error handling for date parsing
  4. Consider performance optimization for large asset lists
  5. Add tests for error scenarios

This is a comprehensive automated review. For architectural decisions requiring human judgment, please request additional manual review.

@arjansingh arjansingh force-pushed the feat/asset-browser-modal-wiring branch from 5499489 to de5bb01 Compare September 17, 2025 02:50
@arjansingh
Copy link
Contributor Author

@DrJKL You're absolutely correct about the cn() usage in AssetCard.vue:7. The code is properly using cn() with the correct pattern. The automated review may have been looking at an earlier commit or different section.

Regarding the tailwind config safelist comment: You're right that with the iconify plugin from PR #5453, those hardcoded icon names in the safelist might not be necessary since the plugin handles dynamic icon selection. Should I remove those from the safelist?

:class="
cn(
// Base layout and container styles (always applied)
'rounded-xl overflow-hidden transition-all duration-200 min-w-60 max-w-64',
Copy link
Member

Choose a reason for hiding this comment

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

The min-width and max-width CSS on the button should be removed. It’s better to let the width adjust dynamically based on the browser size, with the parent component controlling the layout using grid-template. Removing those two properties makes the screen look like this.

Before
before1
After
after1
Before
before2
After
after2

As a side note, if the card looks too wide, you can set a smaller minWidth in createGridStyle to balance it out.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i will remove the min/max for now and if we run into issues talking to design on the size of hte cards we'll adjust minWidth in createGridStyle

DrJKL
DrJKL previously approved these changes Sep 17, 2025
}
},
template: `
<div class="flex items-center justify-center min-h-screen bg-stone-200 dark-theme:bg-stone-200 p-4">
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
<div class="flex items-center justify-center min-h-screen bg-stone-200 dark-theme:bg-stone-200 p-4">
<div class="flex items-center justify-center min-h-screen bg-stone-200 p-4">

return { ...args, onAssetSelect, onClose, assets: singleTypeAssets }
},
template: `
<div class="flex items-center justify-center min-h-screen bg-stone-200 dark-theme:bg-stone-200 p-4">
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
<div class="flex items-center justify-center min-h-screen bg-stone-200 dark-theme:bg-stone-200 p-4">
<div class="flex items-center justify-center min-h-screen bg-stone-200 p-4">

Copy link
Contributor

Choose a reason for hiding this comment

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

Here and elsewhere. Don't want to confuse the robots with this pattern.

@update:model-value="handleFilterChange"
>
<template #icon>
<i-lucide:arrow-up-down class="size-3" />
Copy link
Contributor

Choose a reason for hiding this comment

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

We should be able to add a lint rule to catch these.

{
id: 'all',
label: t('assetBrowser.allModels'),
icon: 'icon-[lucide--folder]'
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this cause them to be picked up by Tailwind?

Suggested change
icon: 'icon-[lucide--folder]'
icon: cn('icon-[lucide--folder]')

.sort()
.map((category) => ({
id: category,
label: category.charAt(0).toUpperCase() + category.slice(1),
Copy link
Contributor

Choose a reason for hiding this comment

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

This would also probably be an i18n issue.

@DrJKL DrJKL removed their assignment Sep 17, 2025
@arjansingh arjansingh enabled auto-merge (squash) September 17, 2025 18:44
Copy link
Contributor

@christian-byrne christian-byrne left a comment

Choose a reason for hiding this comment

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

LGTM!

Comment on lines +6 to +15
label="File formats"
:options="fileFormatOptions"
:class="selectClasses"
data-component-id="asset-filter-file-formats"
@update:model-value="handleFilterChange"
/>

<MultiSelect
v-model="baseModels"
label="Base models"
Copy link
Contributor

Choose a reason for hiding this comment

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

Are the labels translated?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Another good catch. I will make sure that happens.

@arjansingh arjansingh merged commit dfa1cbb into main Sep 17, 2025
21 checks passed
@arjansingh arjansingh deleted the feat/asset-browser-modal-wiring branch September 17, 2025 23:17
Myestery pushed a commit that referenced this pull request Sep 19, 2025
* [ci] ignore playwright mcp directory

* [feat] add AssetBrowserModal

And all related sub components

* [feat] reactive filter functions

* [ci] clean up storybook config

* [feat] add sematic AssetCard

* [fix] i love lucide

* [fix] AssetCard layout issues

* [fix] add AssetBadge type

* [fix] simplify useAssetBrowser

* [fix] modal layout

* [fix] simplify useAssetBrowserDialog

* [fix] add tailwind back to storybook

* [fix] better reponsive layout

* [fix] missed i18n string

* [fix] missing i18n translations

* [fix] remove erroneous prevent on keyboard.space

* [feat] add asset metadata validation utilities

* [fix] remove erroneous test code

* [fix] remove forced min and max width on AssetCard

* [fix] import statement nits
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:assets claude-review Add to trigger a PR code review from Claude Code size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants