Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
2ca984d
Can edit channel tags from settings page
catherineluse Oct 18, 2025
f816f22
Add in-place tag editor to channel sidebar
catherineluse Oct 18, 2025
4c17d47
Fix tag creation constraint violation
catherineluse Oct 18, 2025
acc1198
Fix hydration errors in channel navigation
catherineluse Oct 18, 2025
5a592d3
Fix tabs hydration mismatch in ChannelTabs
catherineluse Oct 18, 2025
ab55e95
Fix tabs hydration by simplifying SSR fallback
catherineluse Oct 18, 2025
d6bed4f
Add in-place tag editing for discussions
catherineluse Oct 18, 2025
55a2129
Refactor tag editor component architecture
catherineluse Oct 18, 2025
f63f72f
Fix hydration error on discussion detail page
catherineluse Oct 18, 2025
0a05d94
On event detail page, can edit tags in place
gennitdev Oct 18, 2025
ea987c9
Admin name wraps properly
gennitdev Oct 18, 2025
7c1219f
Don't allow multiselect in download edit form
gennitdev Oct 18, 2025
a1930f6
Tab title no longer says undefined
gennitdev Oct 19, 2025
244aade
Forum picker always shows channel unique name
gennitdev Oct 19, 2025
c32e962
Debounce forum picker search
gennitdev Oct 19, 2025
7d4d62b
Show 'Comments in other forums' on download detail pages
gennitdev Oct 19, 2025
5a1754e
Bookmark icon is the correct color
gennitdev Oct 19, 2025
7916f74
Favorite discussion list item is more compact
gennitdev Oct 19, 2025
34fb13d
Fix console error
gennitdev Oct 19, 2025
18faf13
Separate downloads from discussions in user contribution list
gennitdev Oct 19, 2025
1177235
Add pinning feature for discussions and wiki pages
gennitdev Oct 20, 2025
653cb99
Refactor Comment and EventHeader to use useChannelPermissions composable
gennitdev Oct 20, 2025
7c45655
Add AGENTS.md
gennitdev Oct 21, 2025
51012a1
Permissions util handles undefined values
gennitdev Oct 22, 2025
7d1fabb
Fix programmatic Cypress auth
gennitdev Oct 22, 2025
cdb5acb
Create/edit channels test passes
gennitdev Oct 22, 2025
91c3822
Add programmatic auth to more Cypress tests
gennitdev Oct 22, 2025
0ad9d41
Filter discussions by tag test passes
gennitdev Oct 22, 2025
828ba79
Bump tailwindcss from 3.4.17 to 4.1.16
dependabot[bot] Oct 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Repository Guidelines

## Project Structure & Module Organization
The Nuxt 4 app lives at the repo root. Vue route files sit in `pages/`, shared components in `components/`, and cross-cutting logic in `composables/` and `stores/`. Domain-specific GraphQL documents are under `graphQLData/`, while reusable utilities and types live in `utils/` and `types/`. Cypress specs reside in `cypress/e2e`, unit tests in `tests/unit`, and static assets in `public/` and `assets/`. Reference `docs/` and `CLAUDE.md` for deeper architectural notes.

## Build, Test, and Development Commands
- `npm run dev`: Launch the Nuxt dev server on `127.0.0.1`.
- `npm run build`: Produce the production bundle.
- `npm run preview`: Inspect the production build locally.
- `npm run test:unit` / `npm run test:unit -- --run path/to/spec`: Run Vitest suites.
- `npm run coverage`: Generate HTML and text coverage reports in `coverage/`.
- `npm run test` or `npx cypress run --spec cypress/e2e/...`: Execute Cypress interactively or headless.
- `npm run lint` / `npm run lint:a11y`: Apply ESLint rules, including Vue accessibility checks.
- `npm run verify`: Type-check then run unit tests; mirrors the pre-commit hook.

## Coding Style & Naming Conventions
TypeScript and Vue single-file components use two-space indentation. Vue components and composables follow `PascalCase` and `useThing` naming; Pinia stores live in `stores/` with `useXStore`. Prefer type-only imports (`import type`) as enforced by ESLint. Run `npx eslint --fix file.vue` for quick fixes. Tailwind classes should stay sorted via the Prettier Tailwind plugin.

## Testing Guidelines
Vitest runs in a `happy-dom` environment with setup in `tests/setup.ts`; keep assertions focused on observable behavior. Maintain ≥80% coverage across lines, branches, functions, and statements as enforced by `vitest.config.ts`. End-to-end specs use the `.spec.cy.ts` suffix; rely on helpers like `setupTestData()` and `loginUser()` from Cypress support, and alias GraphQL operations to guard for status 200 responses.

## Commit & Pull Request Guidelines
Commit history favors concise, imperative subjects (e.g., “Add pinning feature…”). Keep commits scoped and allow the `verify` hook to pass. Pull requests should describe scope, list critical commands run, and link related issues; include screenshots or recordings for UI changes. Confirm you ran `npm run lint` plus relevant tests before requesting review, and surface follow-up tasks in the PR description.

## Agent Collaboration Notes
Read `CLAUDE.md` for nuanced component, testing, and permission guidance. When collaborating asynchronously, cite sections from that guide, call out remaining TODOs explicitly, and prefer incremental patches so automation hooks stay fast.
64 changes: 46 additions & 18 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,6 @@

### Cypress Test Optimizations

- **Replace arbitrary timeouts with network waits**:

```javascript
// Before: Fixed timeout that might be too short or too long
cy.get('button').contains('Save').click().wait(3000);

// After: Wait for the actual network request to complete
cy.intercept('POST', '**/graphql').as('graphqlRequest');
cy.get('button').contains('Save').click();
cy.wait('@graphqlRequest').its('response.statusCode').should('eq', 200);
```

- **Validate network responses**: Add status code checks to ensure operations completed successfully

```javascript
Expand Down Expand Up @@ -87,6 +75,52 @@
- Test files: Run the specific test that changed
- To skip pre-commit hooks temporarily: `git commit --no-verify`

## Component Architecture Preferences:

1. Separation of Concerns

- Child components are ignorant of parent state/layout - they never manage showEditor,
hideEditor, or similar display state
- Each component has a single, clear responsibility

1. Reusable UI Components (like InPlaceTagEditor)

- Pure UI only - no mutations, no business logic
- Edit mode only - view mode belongs in parent components
- Emit generic, simple events: save, cancel
- Accept minimal props: data needed for editing, loading/error states
- Use props as ref defaults: ref([...props.existingTags]) not onMounted

1. Wrapper Components (like ChannelTagEditor, DiscussionTagEditor)

- Handle domain-specific mutations (UPDATE_CHANNEL, UPDATE_DISCUSSION)
- Translate between child events and mutation lifecycle
- Emit domain-specific events:
- done on successful save (via onDone hook)
- cancel passed through from child
- refetch for data updates
- Still don't manage display state - that's the parent's job

1. Parent Components (like ChannelSidebar, DiscussionBody)

- Manage display state with refs (showTagEditor)
- Render both view mode (tags + edit button) and edit mode (wrapper component)
- Listen to events from wrappers (@done, @cancel) to close editor
- Control the entire UX flow

1. Event Naming

- Be clear and specific - no misleading names
- done = successful completion
- cancel = user cancelled
- Don't call cancel "done" or vice versa

1. Avoid Unnecessary Complexity

- Use onDone hooks, not watchers
- Use props as ref defaults, not onMounted
- Keep it simple and direct

## Code Style Guidelines

- **TypeScript**: Use strict typing whenever possible, proper interfaces in `types/` directory
Expand Down Expand Up @@ -276,12 +310,10 @@ The application has two separate but related permission systems:
### User Permission Levels

1. **Standard Users**:

- Use the DefaultChannelRole for the channel (or DefaultServerRole as fallback)
- Have permissions like createDiscussion, createComment, upvoteContent, etc.

2. **Channel Admins/Owners**:

- Users in the `Channel.Admins` list
- Have all user and moderator permissions automatically

Expand All @@ -292,14 +324,12 @@ The application has two separate but related permission systems:
### Moderator Permission Levels

1. **Standard/Normal Moderators**:

- All authenticated users are considered standard moderators by default
- Not explicitly included in `Channel.Moderators` list, not in `Channel.SuspendedMods`
- Can perform basic moderation actions (report, give feedback) based on DefaultModRole
- These permissions are controlled by the DefaultModRole configuration

2. **Elevated Moderators**:

- Explicitly included in the `Channel.Moderators` list
- Have additional permissions beyond standard moderators
- Can typically archive content, manage other moderators, etc.
Expand All @@ -320,9 +350,7 @@ The application has two separate but related permission systems:
- Channel owners/admins bypass all permission checks (both user and mod)
- Suspended status overrides all other status for that permission type
- **Fallback Chain**:

- Channel-specific roles -> Server default roles -> Deny access

- **User vs. Mod Actions**:
- Some UI actions require BOTH user and mod permissions
- For example, to archive content: need canHideDiscussion (mod) AND be an elevated mod or admin
Expand Down
5 changes: 3 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,14 +149,15 @@ describe('Feature workflow', () => {
setupTestData();

// Authenticate before each test
loginUser('loginWithCreateEventButton');
loginUser('loginProgrammatically');

it('completes a user workflow successfully', () => {
// Set up request interception for GraphQL calls
cy.intercept('POST', '**/graphql').as('graphqlRequest');

// Navigate to the starting point
cy.visit('/forums/cats');
cy.syncAuthState();

// Interact with the UI
cy.get('[data-testid="create-discussion-button"]').click();
Expand Down Expand Up @@ -253,7 +254,7 @@ import { DISCUSSION_CREATION_FORM } from '../constants';

describe('Discussion CRUD operations', () => {
setupTestData();
loginUser('loginWithCreateEventButton');
loginUser('loginProgrammatically');

it('creates, edits, and deletes a discussion', () => {
// Set up GraphQL interception
Expand Down
Loading