Skip to content

Commit 2b90d03

Browse files
authored
Merge pull request #323 from nextcloud/feat/sticker
feat: add sticker generation smart picker
2 parents daea893 + ea2cfa1 commit 2b90d03

File tree

11 files changed

+207
-1
lines changed

11 files changed

+207
-1
lines changed

lib/AppInfo/Application.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@
2222
use OCA\Assistant\Listener\TaskOutputFileReferenceListener;
2323
use OCA\Assistant\Listener\TaskSuccessfulListener;
2424
use OCA\Assistant\Listener\Text2Image\Text2ImageReferenceListener;
25+
use OCA\Assistant\Listener\Text2Image\Text2StickerListener;
2526
use OCA\Assistant\Notification\Notifier;
2627
use OCA\Assistant\Reference\FreePromptReferenceProvider;
2728
use OCA\Assistant\Reference\SpeechToTextReferenceProvider;
2829
use OCA\Assistant\Reference\TaskOutputFileReferenceProvider;
2930
use OCA\Assistant\Reference\Text2ImageReferenceProvider;
31+
use OCA\Assistant\Reference\Text2StickerProvider;
3032
use OCA\Assistant\TaskProcessing\AudioToAudioChatProvider;
3133
use OCA\Assistant\TaskProcessing\ContextAgentAudioInteractionProvider;
3234
use OCA\Files\Event\LoadAdditionalScriptsEvent;
@@ -60,11 +62,13 @@ public function register(IRegistrationContext $context): void {
6062
$context->registerCapability(Capabilities::class);
6163

6264
$context->registerReferenceProvider(Text2ImageReferenceProvider::class);
65+
$context->registerReferenceProvider(Text2StickerProvider::class);
6366
$context->registerReferenceProvider(FreePromptReferenceProvider::class);
6467
$context->registerReferenceProvider(SpeechToTextReferenceProvider::class);
6568
$context->registerReferenceProvider(TaskOutputFileReferenceProvider::class);
6669

6770
$context->registerEventListener(RenderReferenceEvent::class, Text2ImageReferenceListener::class);
71+
$context->registerEventListener(RenderReferenceEvent::class, Text2StickerListener::class);
6872
$context->registerEventListener(RenderReferenceEvent::class, FreePromptReferenceListener::class);
6973
$context->registerEventListener(RenderReferenceEvent::class, SpeechToTextReferenceListener::class);
7074
$context->registerEventListener(RenderReferenceEvent::class, TaskOutputFileReferenceListener::class);

lib/Listener/Text2Image/Text2ImageReferenceListener.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public function handle(Event $event): void {
3838
if ($this->appConfig->getValueString(Application::APP_ID, 'text_to_image_picker_enabled', '1') === '1'
3939
&& $this->config->getUserValue($this->userId, Application::APP_ID, 'text_to_image_picker_enabled', '1') === '1') {
4040

41-
// Double check that atleast one provider is registered
41+
// Double check that at least one provider is registered
4242
$availableTaskTypes = $this->taskProcessingManager->getAvailableTaskTypes();
4343
$textToImageAvailable = array_key_exists(TextToImage::ID, $availableTaskTypes);
4444
if ($textToImageAvailable) {
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
/**
4+
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
8+
namespace OCA\Assistant\Listener\Text2Image;
9+
10+
use OCA\Assistant\AppInfo\Application;
11+
use OCP\Collaboration\Reference\RenderReferenceEvent;
12+
use OCP\EventDispatcher\Event;
13+
use OCP\EventDispatcher\IEventListener;
14+
use OCP\IAppConfig;
15+
use OCP\IConfig;
16+
use OCP\TaskProcessing\IManager as ITaskProcessingManager;
17+
use OCP\TaskProcessing\TaskTypes\TextToImage;
18+
use OCP\Util;
19+
20+
/**
21+
* @template-implements IEventListener<Event>
22+
*/
23+
class Text2StickerListener implements IEventListener {
24+
public function __construct(
25+
private IConfig $config,
26+
private IAppConfig $appConfig,
27+
private ?string $userId,
28+
private ITaskProcessingManager $taskProcessingManager,
29+
) {
30+
}
31+
32+
public function handle(Event $event): void {
33+
34+
if (!$event instanceof RenderReferenceEvent) {
35+
return;
36+
}
37+
38+
if ($this->appConfig->getValueString(Application::APP_ID, 'text_to_sticker_picker_enabled', '1') === '1'
39+
&& $this->config->getUserValue($this->userId, Application::APP_ID, 'text_to_sticker_picker_enabled', '1') === '1') {
40+
41+
// Double check that at least one provider is registered
42+
$availableTaskTypes = $this->taskProcessingManager->getAvailableTaskTypes();
43+
$textToImageAvailable = array_key_exists(TextToImage::ID, $availableTaskTypes);
44+
if ($textToImageAvailable) {
45+
Util::addScript(Application::APP_ID, Application::APP_ID . '-stickerGeneration');
46+
}
47+
}
48+
}
49+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<?php
2+
3+
/**
4+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
8+
namespace OCA\Assistant\Reference;
9+
10+
use OCA\Assistant\AppInfo\Application;
11+
use OCP\Collaboration\Reference\ADiscoverableReferenceProvider;
12+
use OCP\Collaboration\Reference\IReference;
13+
use OCP\Collaboration\Reference\IReferenceManager;
14+
use OCP\IL10N;
15+
use OCP\IURLGenerator;
16+
17+
class Text2StickerProvider extends ADiscoverableReferenceProvider {
18+
19+
public function __construct(
20+
private IL10N $l10n,
21+
private IURLGenerator $urlGenerator,
22+
private IReferenceManager $referenceManager,
23+
private ?string $userId,
24+
) {
25+
}
26+
27+
/**
28+
* @inheritDoc
29+
*/
30+
public function getId(): string {
31+
return 'assistant_sticker_generation';
32+
}
33+
34+
/**
35+
* @inheritDoc
36+
*/
37+
public function getTitle(): string {
38+
return $this->l10n->t('AI sticker generation');
39+
}
40+
41+
/**
42+
* @inheritDoc
43+
*/
44+
public function getOrder(): int {
45+
return 10;
46+
}
47+
48+
/**
49+
* @inheritDoc
50+
*/
51+
public function getIconUrl(): string {
52+
return $this->urlGenerator->getAbsoluteURL(
53+
$this->urlGenerator->imagePath(Application::APP_ID, 'app-dark.svg')
54+
);
55+
}
56+
57+
/**
58+
* @inheritDoc
59+
*/
60+
public function matchReference(string $referenceText): bool {
61+
return false;
62+
}
63+
64+
/**
65+
* @inheritDoc
66+
*/
67+
public function resolveReference(string $referenceText): ?IReference {
68+
return null;
69+
}
70+
71+
/**
72+
* @inheritDoc
73+
*/
74+
public function getCachePrefix(string $referenceId): string {
75+
return $this->userId ?? '';
76+
}
77+
78+
/**
79+
* @inheritDoc
80+
*/
81+
public function getCacheKey(string $referenceId): ?string {
82+
return $referenceId;
83+
}
84+
85+
/**
86+
* @param string $userId
87+
* @return void
88+
*/
89+
public function invalidateUserCache(string $userId): void {
90+
$this->referenceManager->invalidateCache($userId);
91+
}
92+
93+
94+
}

lib/Settings/Admin.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ public function getForm(): TemplateResponse {
4242

4343
$freePromptPickerEnabled = $this->appConfig->getValueString(Application::APP_ID, 'free_prompt_picker_enabled', '1') === '1';
4444
$textToImagePickerEnabled = $this->appConfig->getValueString(Application::APP_ID, 'text_to_image_picker_enabled', '1') === '1';
45+
$textToStickerPickerEnabled = $this->appConfig->getValueString(Application::APP_ID, 'text_to_sticker_picker_enabled', '1') === '1';
4546

4647
$speechToTextEnabled = $this->appConfig->getValueString(Application::APP_ID, 'speech_to_text_picker_enabled', '1') === '1';
4748
$chattyLLMUserInstructions = $this->appConfig->getValueString(Application::APP_ID, 'chat_user_instructions', Application::CHAT_USER_INSTRUCTIONS) ?: Application::CHAT_USER_INSTRUCTIONS;
@@ -53,6 +54,7 @@ public function getForm(): TemplateResponse {
5354
'assistant_enabled' => $assistantEnabled,
5455
'text_to_image_picker_available' => $textToImageAvailable,
5556
'text_to_image_picker_enabled' => $textToImagePickerEnabled,
57+
'text_to_sticker_picker_enabled' => $textToStickerPickerEnabled,
5658
'free_prompt_task_type_available' => $freePromptTaskTypeAvailable,
5759
'free_prompt_picker_enabled' => $freePromptPickerEnabled,
5860
'speech_to_text_picker_available' => $speechToTextAvailable,

lib/Settings/Personal.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ public function getForm(): TemplateResponse {
5252
$textToImagePickerAvailable = $textToImageAvailable && $this->appConfig->getValueString(Application::APP_ID, 'text_to_image_picker_enabled', '1') === '1';
5353
$textToImagePickerEnabled = $this->config->getUserValue($this->userId, Application::APP_ID, 'text_to_image_picker_enabled', '1') === '1';
5454

55+
$textToStickerPickerAvailable = $textToImageAvailable && $this->appConfig->getValueString(Application::APP_ID, 'text_to_sticker_picker_enabled', '1') === '1';
56+
$textToStickerPickerEnabled = $this->config->getUserValue($this->userId, Application::APP_ID, 'text_to_sticker_picker_enabled', '1') === '1';
57+
5558
$freePromptPickerAvailable = $freePromptTaskTypeAvailable && $this->appConfig->getValueString(Application::APP_ID, 'free_prompt_picker_enabled', '1') === '1';
5659
$freePromptPickerEnabled = $this->config->getUserValue($this->userId, Application::APP_ID, 'free_prompt_picker_enabled', '1') === '1';
5760

@@ -63,6 +66,8 @@ public function getForm(): TemplateResponse {
6366
'assistant_enabled' => $assistantEnabled,
6467
'text_to_image_picker_available' => $textToImagePickerAvailable,
6568
'text_to_image_picker_enabled' => $textToImagePickerEnabled,
69+
'text_to_sticker_picker_available' => $textToStickerPickerAvailable,
70+
'text_to_sticker_picker_enabled' => $textToStickerPickerEnabled,
6671
'free_prompt_picker_available' => $freePromptPickerAvailable,
6772
'free_prompt_picker_enabled' => $freePromptPickerEnabled,
6873
'speech_to_text_picker_available' => $speechToTextPickerAvailable,

src/components/AdminSettings.vue

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,14 @@
5151
{{ t('assistant', 'Enable text-to-image in smart picker') }}
5252
</div>
5353
</NcCheckboxRadioSwitch>
54+
<NcCheckboxRadioSwitch
55+
:model-value="state.text_to_sticker_picker_enabled"
56+
:disabled="!state.text_to_image_picker_available"
57+
@update:model-value="onCheckboxChanged($event, 'text_to_sticker_picker_enabled')">
58+
<div class="checkbox-text">
59+
{{ t('assistant', 'Enable text-to-sticker in smart picker') }}
60+
</div>
61+
</NcCheckboxRadioSwitch>
5462
<NcNoteCard v-if="!state.text_to_image_picker_available" type="info">
5563
<div class="checkbox-text">
5664
<span>

src/components/PersonalSettings.vue

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@
3737
{{ t('assistant', 'Enable AI image generation in smart picker') }}
3838
</div>
3939
</NcCheckboxRadioSwitch>
40+
<NcCheckboxRadioSwitch v-if="state.text_to_sticker_picker_available"
41+
:model-value="state.text_to_sticker_picker_enabled"
42+
@update:model-value="onCheckboxChanged($event, 'text_to_image_picker_enabled')">
43+
<div class="checkbox-text">
44+
{{ t('assistant', 'Enable AI sticker generation in smart picker') }}
45+
</div>
46+
</NcCheckboxRadioSwitch>
4047
<NcCheckboxRadioSwitch v-if="state.speech_to_text_picker_available"
4148
:model-value="state.speech_to_text_picker_enabled"
4249
@update:model-value="onCheckboxChanged($event, 'speech_to_text_picker_enabled')">

src/stickerGeneration.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import { registerCustomPickerElement, NcCustomPickerRenderResult } from '@nextcloud/vue/components/NcRichText'
7+
8+
registerCustomPickerElement('assistant_sticker_generation', async (el, { providerId, accessible }) => {
9+
const { createApp } = await import('vue')
10+
const { default: ImageResultCustomPickerElement } = await import('./views/ImageResultCustomPickerElement.vue')
11+
const app = createApp(
12+
ImageResultCustomPickerElement,
13+
{
14+
inputs: {
15+
input: t('assistant', 'cartoon, neutral background, sticker of '),
16+
},
17+
providerId,
18+
accessible,
19+
taskType: 'core:text2image',
20+
outputKey: 'images',
21+
multipleImages: true,
22+
},
23+
)
24+
app.mixin({ methods: { t, n } })
25+
app.mount(el)
26+
27+
return new NcCustomPickerRenderResult(el, app)
28+
}, (el, renderResult) => {
29+
renderResult.object.unmount()
30+
})

src/views/ImageResultCustomPickerElement.vue

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ export default {
3939
type: Boolean,
4040
default: false,
4141
},
42+
inputs: {
43+
type: Object,
44+
default: () => ({}),
45+
required: false,
46+
},
4247
},
4348
4449
emits: ['cancel', 'submit'],
@@ -56,6 +61,7 @@ export default {
5661
5762
mounted() {
5863
OCA.Assistant.openAssistantForm({
64+
inputs: this.inputs,
5965
appId: 'assistant',
6066
taskType: this.taskType,
6167
closeOnResult: false,

0 commit comments

Comments
 (0)