Skip to content

feat(tags): implement auto-creation of tags and ensure system tags exist - #683

Open
storm1er wants to merge 4 commits into
icereed:mainfrom
storm1er:fix-659-autotag
Open

feat(tags): implement auto-creation of tags and ensure system tags exist#683
storm1er wants to merge 4 commits into
icereed:mainfrom
storm1er:fix-659-autotag

Conversation

@storm1er

@storm1er storm1er commented Oct 5, 2025

Copy link
Copy Markdown

TL;DR:

  • Added ensureSystemTagsExist function to create critical system tags if they don't exist.
  • Updated UpdateDocuments method to support auto-creation of tags based on settings.
  • Introduced TagSettings component for managing tag auto-creation settings.
  • Enhanced UI to prevent deletion of last tag and provide user feedback.
  • Updated styles for last tag indication and tooltip.

This pull request introduces robust validation and management for document tags in both backend and frontend, ensuring that every document always has at least one tag and that system tags are reliably present. It also adds a user-facing setting for automatic tag creation and improves the UI to prevent accidental removal of all tags from a document.

Backend improvements:

  • System tag bootstrap and validation: Added ensureSystemTagsExist in main.go to guarantee all required system tags exist in Paperless-ngx before processing documents. The app now fails fast if any system tag cannot be created. [1] [2]
  • Automatic tag creation logic: Updated UpdateDocuments in paperless.go to auto-create missing tags when enabled, always auto-creating system tags, and to prevent documents from having zero tags by applying a fallback system tag. [1] [2]
  • Settings support for tag auto-creation: Added TagsAutoCreate to the backend settings (types.go, settings.go) and documented its usage in tag creation logic. [1] [2] [3]

Frontend improvements:

  • Tag auto-creation setting UI: Added a new TagSettings component, integrated into the main settings page, allowing users to enable/disable automatic tag creation. [1] [2] [3] [4]
  • Tag validation and UI safeguards: The document processor and tag deletion logic now prevent removing all tags from a document, displaying errors if attempted. The UI visually highlights the last remaining tag and shows a tooltip explaining why it cannot be deleted. [1] [2] [3] [4]

Summary by CodeRabbit

  • New Features

    • New Tag Settings section with a toggle to automatically create tags from AI suggestions; saves setting via partial updates.
    • Ensures required system tags are created on startup to avoid missing-tag errors.
  • Bug Fixes

    • Prevents saving updates that would leave a document without tags; falls back to the OCR-complete tag when available.
    • Blocks deletion of the last remaining tag on a document.
  • Chores

    • Add development watch to auto-rebuild on file changes.

…ist.

- Added `ensureSystemTagsExist` function to create critical system tags if they don't exist.
- Updated `UpdateDocuments` method to support auto-creation of tags based on settings.
- Introduced `TagSettings` component for managing tag auto-creation settings.
- Enhanced UI to prevent deletion of last tag and provide user feedback.
- Updated styles for last tag indication and tooltip.
@coderabbitai

coderabbitai Bot commented Oct 5, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Adds startup bootstrap to ensure required system tags exist; introduces Settings.TagsAutoCreate with UI toggle and backend support; updates tag mapping/creation and fallbacks in UpdateDocuments; enforces at-least-one-tag in UI and backend; prevents deleting the last tag; makes settings endpoint partial-update; updates getSuggestedTags to respect TagsAutoCreate; adds dev watch.

Changes

Cohort / File(s) Summary
Startup system tag bootstrap
main.go
Adds ensureSystemTagsExist and invokes it during startup; exits on failure.
Backend tag mapping, creation & fallbacks
paperless.go
Maps tag names→IDs, auto-creates missing system/user tags based on Settings.TagsAutoCreate, caches created IDs, and applies fallback to pdfOCRCompleteTag when resulting tag lists would be empty. Adds logging and error wrapping.
Settings model & defaults
types.go, settings.go
Adds TagsAutoCreate bool to Settings and initializes default to false in loadDefaultSettings.
Settings API partial update
app_http_handlers.go
updateSettingsHandler accepts partial updates (map), merges typed fields including tags_auto_create, saves settings, and responds with combined settings + custom_fields.
LLM suggestions respect setting
app_llm.go
getSuggestedTags reads TagsAutoCreate under a read lock and, when enabled, returns suggested tags without filtering against available tags; otherwise preserves existing filtering.
Document update / UI guardrails
web-app/src/DocumentProcessor.tsx, web-app/src/components/SuggestionCard.tsx, web-app/src/index.css
Validates every suggestion has ≥1 tag before update; prevents deletion of the last tag (sets error); adjusts tag CSS and adds tooltip for non-deletable last tag; minor class change for last-tag rendering.
Tag settings UI
web-app/src/components/TagSettings.tsx, web-app/src/components/Settings.tsx, web-app/src/components/CustomFieldsEditor.tsx
Adds TagSettings component, wires it into Settings UI, extends SettingsData with tags_auto_create, fetches and POSTs the tags_auto_create field (partial save).
Dev tooling
docker-compose.yml
Adds develop.watch file-matching to trigger rebuilds for the app service (ignores node_modules/ and .git/).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant M as main.go
  participant P as Paperless API
  participant Cache as Local Tag Cache

  M->>P: GET /tags (fetch all tags)
  P-->>M: Tag list
  M->>Cache: Update known tags
  loop For each required system tag
    M->>P: POST /tags (create if missing)
    alt Created
      P-->>M: 201 Created
      M->>Cache: Add created tag
    else Exists
      Note over M,Cache: No action
    else Error
      P-->>M: Error
      M-->>M: Fatal exit
    end
  end
  Note over M: Continue startup/background tasks
Loading
sequenceDiagram
  autonumber
  participant UI as Web App
  participant S as Settings API
  participant B as Backend UpdateDocuments
  participant P as Paperless API

  rect rgba(230,245,255,0.5)
  note over UI,S: Tag auto-create toggle
  UI->>S: GET ./api/settings
  S-->>UI: settings { tags_auto_create }
  UI->>S: POST ./api/settings (partial: tags_auto_create)
  S-->>UI: 200 OK
  end

  rect rgba(240,255,240,0.5)
  note over UI,B: Update documents with tags
  UI-->>UI: Validate: each doc has ≥1 tag
  UI->>B: UpdateDocuments(finalTagNames)
  B-->>B: Map names→IDs (cache)
  alt Missing tag
    B-->>B: If system tag or TagsAutoCreate=true → create
    B->>P: POST /tags
    P-->>B: Created/ID
  end
  alt Resulting tag list empty
    B-->>B: Fallback to pdfOCRCompleteTag or return error
  end
  B-->>UI: Success/Errors
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

In burrows of code I hop and plug,
I plant new tags with gentle bug.
A toggle flipped, a last-tag hug,
I nibble faults and tidy smug.
Hop on, small app — tag gardens bloom! 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 clearly and concisely summarizes the primary enhancements in this pull request—adding tag auto-creation and bootstrapping required system tags—without extraneous details, making it easy for team members to understand the main change at a glance.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@storm1er

storm1er commented Oct 5, 2025

Copy link
Copy Markdown
Author

Should fix #659

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (2)
paperless.go (1)

1254-1266: Handle “tag already exists” races when auto-creating tags

If another server instance or concurrent request creates the tag after our initial GetAllTags snapshot, Paperless returns a 400/409 (“tag with this name already exists”). Today we bubble that up, causing the whole update (or even startup via ensureSystemTagsExist) to fail. Please treat this race as success by refreshing the tag list and returning the existing ID before giving up.

A minimal fix inside CreateTag:

-	if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK {
-		bodyBytes, _ := io.ReadAll(resp.Body)
-		return 0, fmt.Errorf("error creating tag: %d, %s", resp.StatusCode, string(bodyBytes))
-	}
+	if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK {
+		bodyBytes, _ := io.ReadAll(resp.Body)
+		if (resp.StatusCode == http.StatusBadRequest || resp.StatusCode == http.StatusConflict) &&
+			strings.Contains(strings.ToLower(string(bodyBytes)), "already exists") {
+			if refreshed, err := client.GetAllTags(ctx); err == nil {
+				if existingID, ok := refreshed[tagName]; ok {
+					return existingID, nil
+				}
+			}
+		}
+		return 0, fmt.Errorf("error creating tag: %d, %s", resp.StatusCode, string(bodyBytes))
+	}
web-app/src/components/CustomFieldsEditor.tsx (1)

9-14: Extract SettingsData interface to a shared types file.

The SettingsData interface is duplicated in both CustomFieldsEditor.tsx (lines 9-14) and TagSettings.tsx (lines 4-9). This violates the DRY principle and creates a maintenance burden—changes to the settings structure must be synchronized across multiple files.

Create a shared types file (e.g., web-app/src/types/settings.ts) and export the interface from there:

// web-app/src/types/settings.ts
export interface SettingsData {
  custom_fields_enable: boolean;
  custom_fields_selected_ids: number[];
  custom_fields_write_mode: 'append' | 'replace' | 'update';
  tags_auto_create: boolean;
}

Then import it in both components:

-interface SettingsData {
-  custom_fields_enable: boolean;
-  custom_fields_selected_ids: number[];
-  custom_fields_write_mode: 'append' | 'replace' | 'update';
-  tags_auto_create: boolean;
-}
+import { SettingsData } from '../types/settings';
♻️ Duplicate comments (1)
web-app/src/components/TagSettings.tsx (1)

4-9: Extract SettingsData interface to a shared types file.

This interface is duplicated between TagSettings.tsx and CustomFieldsEditor.tsx. See the comment on CustomFieldsEditor.tsx (lines 9-14) for the detailed refactoring suggestion.

🧹 Nitpick comments (2)
web-app/src/DocumentProcessor.tsx (1)

209-228: Consider persisting the error message when preventing last tag deletion.

The error message "Cannot remove all tags..." is set using the shared error state, which may be cleared by other operations (e.g., line 145 in handleUpdateDocuments). If the user attempts to delete the last tag and then immediately clicks "Update Documents," the deletion error could be overwritten before the user sees it.

Consider one of these approaches:

Option 1: Use a separate error state for tag deletion

const [tagDeletionError, setTagDeletionError] = useState<string | null>(null);

// In handleTagDeletion
if (currentTags.length <= 1) {
  setTagDeletionError('Cannot remove all tags. Documents must have at least one tag.');
  setTimeout(() => setTagDeletionError(null), 3000); // Auto-clear after 3s
  return doc;
}

// Display both errors in the UI
{error && <div className="mb-4 p-4 bg-red-100 ...">{error}</div>}
{tagDeletionError && <div className="mb-4 p-4 bg-red-100 ...">{tagDeletionError}</div>}

Option 2: Use toast/notification system
If you have a toast notification library, use that for transient validation messages like tag deletion prevention, reserving the main error state for critical failures.

web-app/src/components/TagSettings.tsx (1)

73-75: Loading state could be more informative.

The loading text "Loading settings..." uses a generic gray color that may not be immediately visible in dark mode contexts. Consider using the same styling pattern as the error state for consistency.

 if (loading) {
-  return <div className="text-gray-400">Loading settings...</div>;
+  return (
+    <div className="bg-gray-800 p-4 rounded-lg">
+      <p className="text-gray-300">Loading settings...</p>
+    </div>
+  );
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7cd6626 and 99adc1e.

📒 Files selected for processing (10)
  • main.go (2 hunks)
  • paperless.go (3 hunks)
  • settings.go (1 hunks)
  • types.go (1 hunks)
  • web-app/src/DocumentProcessor.tsx (2 hunks)
  • web-app/src/components/CustomFieldsEditor.tsx (1 hunks)
  • web-app/src/components/Settings.tsx (2 hunks)
  • web-app/src/components/SuggestionCard.tsx (1 hunks)
  • web-app/src/components/TagSettings.tsx (1 hunks)
  • web-app/src/index.css (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Ensure all Go source files are formatted with gofmt before committing

Files:

  • types.go
  • settings.go
  • paperless.go
  • main.go
web-app/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Fix all frontend lint/TypeScript issues by running npm run lint in web-app before committing

Files:

  • web-app/src/components/Settings.tsx
  • web-app/src/components/SuggestionCard.tsx
  • web-app/src/components/CustomFieldsEditor.tsx
  • web-app/src/DocumentProcessor.tsx
  • web-app/src/components/TagSettings.tsx
🧬 Code graph analysis (2)
web-app/src/components/Settings.tsx (1)
web-app/src/components/TagSettings.tsx (1)
  • TagSettings (11-137)
main.go (1)
paperless.go (1)
  • PaperlessClient (34-39)
🔇 Additional comments (4)
web-app/src/components/Settings.tsx (1)

4-4: LGTM!

The integration of the TagSettings component follows the established pattern and is correctly positioned between PromptsEditor and CustomFieldsEditor sections.

Also applies to: 13-16

web-app/src/components/SuggestionCard.tsx (1)

103-105: LGTM!

The conditional styling correctly applies the react-tags__tag--last class when only one tag remains, which aligns with the UI requirement to visually indicate the last non-deletable tag.

web-app/src/DocumentProcessor.tsx (1)

148-159: LGTM!

The pre-update validation correctly ensures all documents have at least one tag before proceeding with the update, preventing invalid states and providing clear user feedback.

web-app/src/components/TagSettings.tsx (1)

1-137: Verify lint and TypeScript compliance

Ensure web-app/src/components/TagSettings.tsx passes the project’s frontend checks by running in your repo root:

cd web-app
npm run lint -- src/components/TagSettings.tsx

Comment thread web-app/src/components/TagSettings.tsx
@storm1er
storm1er marked this pull request as draft October 6, 2025 07:13
@storm1er storm1er changed the title wip: feat(tags): implement auto-creation of tags and ensure system tags exist feat(tags): implement auto-creation of tags and ensure system tags exist Oct 6, 2025
@storm1er
storm1er marked this pull request as ready for review October 6, 2025 07:30
@storm1er

storm1er commented Oct 6, 2025

Copy link
Copy Markdown
Author

Some UI issue remains, I have more tests to do

@storm1er
storm1er marked this pull request as draft October 6, 2025 07:53
@storm1er
storm1er marked this pull request as ready for review October 6, 2025 08:39
@storm1er

storm1er commented Oct 6, 2025

Copy link
Copy Markdown
Author

@icereed
Ready for Review / QA :)

@storm1er

storm1er commented Oct 6, 2025

Copy link
Copy Markdown
Author

If any of @xJylin, @onestopjs, @martinenko7 or @seafish1972 can help by testing this PR it would be nice. I only tested locally using mistral.

Steps:

  • if possible, check that it creates system tags properly (but I guess most of you already has them, it works on my side)
  • => Should work even with tag creation disallowed
  • go to tag prompt and update as you like so it tries to create new tags
  • => try to generate tag, you should not get new tags, only existing ones
  • go to settings, update the allow creation tag settings
  • => retry generate tag, you should now get new tags (if prompt allows it)

enjoy =)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
web-app/src/components/TagSettings.tsx (1)

4-9: Consider narrowing the interface to managed fields only.

The SettingsData interface includes all settings fields, but this component only manages tags_auto_create. While the partial update (line 54) correctly prevents overwrites, including unused fields in the interface can be confusing for maintainers.

Consider creating a focused interface:

 interface SettingsData {
-  custom_fields_enable: boolean;
-  custom_fields_selected_ids: number[];
-  custom_fields_write_mode: 'append' | 'replace';
   tags_auto_create: boolean;
 }

Alternatively, if you need the full settings for display purposes, rename it to clarify scope:

interface FullSettingsData {
  custom_fields_enable: boolean;
  custom_fields_selected_ids: number[];
  custom_fields_write_mode: 'append' | 'replace';
  tags_auto_create: boolean;
}

interface TagSettingsData {
  tags_auto_create: boolean;
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 99adc1e and 0aac71d.

📒 Files selected for processing (6)
  • app_http_handlers.go (2 hunks)
  • app_llm.go (1 hunks)
  • docker-compose.yml (1 hunks)
  • web-app/src/components/CustomFieldsEditor.tsx (2 hunks)
  • web-app/src/components/Settings.tsx (2 hunks)
  • web-app/src/components/TagSettings.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • web-app/src/components/Settings.tsx
🧰 Additional context used
📓 Path-based instructions (2)
web-app/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Fix all frontend lint/TypeScript issues by running npm run lint in web-app before committing

Files:

  • web-app/src/components/CustomFieldsEditor.tsx
  • web-app/src/components/TagSettings.tsx
**/*.go

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Ensure all Go source files are formatted with gofmt before committing

Files:

  • app_llm.go
  • app_http_handlers.go
🔇 Additional comments (9)
docker-compose.yml (1)

10-16: LGTM! Development workflow enhancement.

The addition of develop.watch enables automatic rebuilds during development, improving developer experience. The ignore patterns for node_modules/ and .git/ are appropriate.

app_llm.go (1)

153-161: LGTM! Correct implementation of TagsAutoCreate gating.

The function properly reads TagsAutoCreate under a read lock and conditionally skips filtering when auto-creation is enabled. The early return pattern is appropriate and maintains backward compatibility when the setting is disabled.

web-app/src/components/CustomFieldsEditor.tsx (2)

13-13: LGTM! Type definition extended appropriately.

The tags_auto_create field is correctly added to the SettingsData interface for type consistency with the backend settings model.


65-73: LGTM! Correct partial update implementation.

The component correctly sends only the fields it manages (custom_fields_enable, custom_fields_selected_ids, custom_fields_write_mode), avoiding overwriting the tags_auto_create field managed by the TagSettings component. This follows the partial update pattern that was previously discussed and resolved.

app_http_handlers.go (2)

131-164: LGTM! Partial update implementation with proper type handling.

The handler correctly:

  • Parses incoming JSON as a map to support partial updates
  • Type-checks each field with safe existence checks
  • Converts JSON number types (float64) to integers appropriately for array indices
  • Includes the new tags_auto_create field handling

The approach enables concurrent editing of different settings sections without overwrites.


173-181: POST /api/settings consumer ignores response body, so safe
The only consumer (CustomFieldsEditor.tsx) checks response.ok and never calls response.json() on success, so returning the full settings object won’t break existing code.

web-app/src/components/TagSettings.tsx (3)

51-70: LGTM! Partial update correctly addresses past review concerns.

The component correctly implements partial updates by:

  1. Sending only tags_auto_create in the POST body (lines 53-55)
  2. Updating local state from the server response to maintain consistency (lines 58-60)

This prevents overwriting settings managed by other components, addressing the concern raised in previous reviews.

Based on past review comments.


84-119: LGTM! Well-designed user interface with appropriate warnings.

The UI implementation is clear and user-friendly:

  • Checkbox properly labeled and accessible
  • Descriptive text explains the feature clearly
  • Warning box appropriately alerts users about side effects
  • Good use of dark mode styling

122-142: LGTM! Proper save flow with user feedback.

The save button and feedback implementation is correct:

  • Button disabled when no changes or saving in progress
  • Clear visual states for enabled/disabled
  • Success and error messages displayed appropriately
  • Message cleared after 3 seconds

Comment thread web-app/src/components/TagSettings.tsx Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0aac71d and 0a2c1df.

📒 Files selected for processing (1)
  • web-app/src/components/TagSettings.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
web-app/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Fix all frontend lint/TypeScript issues by running npm run lint in web-app before committing

Files:

  • web-app/src/components/TagSettings.tsx

Comment on lines +47 to +69
setSaving(true);
setMessage('');
setError('');

try {
// Only send the field this component manages (partial update)
const response = await axios.post('./api/settings', {
tags_auto_create: settings.tags_auto_create
});
setMessage('Settings saved successfully');
// Update both settings and initialSettings with the server response
const settingsData = response.data.settings as SettingsData;
setSettings(settingsData);
setInitialSettings(settingsData);

// Clear message after 3 seconds
setTimeout(() => setMessage(''), 3000);
} catch (err) {
console.error('Error saving settings:', err);
setError('Failed to save settings');
} finally {
setSaving(false);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Clear the success-message timeout to avoid state updates after unmount.

If the user navigates away before the 3 s timer fires (or saves repeatedly), the pending setTimeout still runs and calls setMessage on an unmounted component, triggering React warnings and leaking timers. Store the timeout ID in a ref, clear it before scheduling a new one, and tear it down on unmount.

@@
-import { useEffect, useState } from 'react';
+import { useEffect, useRef, useState } from 'react';
@@
   const [message, setMessage] = useState('');
   const [error, setError] = useState('');
+  const messageTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
+
+  useEffect(() => {
+    return () => {
+      if (messageTimeoutRef.current) {
+        clearTimeout(messageTimeoutRef.current);
+      }
+    };
+  }, []);
@@
-      // Clear message after 3 seconds
-      setTimeout(() => setMessage(''), 3000);
+      // Clear message after 3 seconds
+      if (messageTimeoutRef.current) {
+        clearTimeout(messageTimeoutRef.current);
+      }
+      messageTimeoutRef.current = setTimeout(() => setMessage(''), 3000);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
setSaving(true);
setMessage('');
setError('');
try {
// Only send the field this component manages (partial update)
const response = await axios.post('./api/settings', {
tags_auto_create: settings.tags_auto_create
});
setMessage('Settings saved successfully');
// Update both settings and initialSettings with the server response
const settingsData = response.data.settings as SettingsData;
setSettings(settingsData);
setInitialSettings(settingsData);
// Clear message after 3 seconds
setTimeout(() => setMessage(''), 3000);
} catch (err) {
console.error('Error saving settings:', err);
setError('Failed to save settings');
} finally {
setSaving(false);
}
// In web-app/src/components/TagSettings.tsx
import { useEffect, useRef, useState } from 'react';
// … other imports
function TagSettings(/* props */) {
- const [message, setMessage] = useState('');
const [message, setMessage] = useState('');
const [error, setError] = useState('');
const messageTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
useEffect(() => {
return () => {
if (messageTimeoutRef.current) {
clearTimeout(messageTimeoutRef.current);
}
};
}, []);
const saveSettings = async () => {
setSaving(true);
setMessage('');
setError('');
try {
// Only send the field this component manages (partial update)
const response = await axios.post('./api/settings', {
tags_auto_create: settings.tags_auto_create
});
setMessage('Settings saved successfully');
// Update both settings and initialSettings with the server response
const settingsData = response.data.settings as SettingsData;
setSettings(settingsData);
setInitialSettings(settingsData);
- // Clear message after 3 seconds
// Clear message after 3 seconds
if (messageTimeoutRef.current) {
clearTimeout(messageTimeoutRef.current);
}
messageTimeoutRef.current = setTimeout(() => setMessage(''), 3000);
} catch (err) {
console.error('Error saving settings:', err);
setError('Failed to save settings');
} finally {
setSaving(false);
}
};
// … rest of component
}
🤖 Prompt for AI Agents
In web-app/src/components/TagSettings.tsx around lines 47 to 69, the success
message timeout is scheduled with setTimeout but never cleared, which can cause
state updates after unmount and timer leaks; change the implementation to store
the timeout ID in a ref, clear any existing timeout before scheduling a new one,
and clear the timeout in a useEffect cleanup on unmount (use window.clearTimeout
with the ref-held ID and set the ref to null after clearing) so repeated saves
or navigation away won't trigger setMessage on an unmounted component.

@seafish1972

Copy link
Copy Markdown

Hi, where do I see the Auto-Generate Tag button in the frontend?

**"Frontend improvements:

Tag auto-creation setting UI: Added a new TagSettings component, integrated into the main settings page, allowing users to enable/disable automatic tag creation."**

@storm1er

storm1er commented Oct 6, 2025

Copy link
Copy Markdown
Author

@seafish1972 There's a new ui where you can set it, you can also use app/config/settings.json

image

@seafish1972

Copy link
Copy Markdown

Thanks. I'm pretty new to github. I cloned your forked version. There I can't see this new GUI.

@storm1er

storm1er commented Oct 7, 2025

Copy link
Copy Markdown
Author

@seafish1972

docker compose build
docker compose up

You need to have a valid .env file with valid variables from README#environment-variables

You should be able to navigate to http://localhost:8080 once the stack is up

@seafish1972

Copy link
Copy Markdown

THX. It's working. But maybe I'm wrong but my expectation was placing the tag "paperless-gpt-ocr-auto" would do both tagging, title generating, finding creation, date, ..... AND doing a OCR. Am I wrong?

Placing this tag "only" doing OCR and afterwards setting a tag "paperless-gpt-ocr-complete". No title generated, no creation date, no correspondent.

@storm1er

Copy link
Copy Markdown
Author

I don't remember the variable names, but if you want them all, there's 2 way: put 2 tags in paperless for inbox, or setup papergpt to use the tag that triggers the title and stuff as the completed tag of ocr completion

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.

2 participants