wally-ui 1.13.1 → 1.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/playground/showcase/package-lock.json +48 -0
- package/playground/showcase/package.json +1 -0
- package/playground/showcase/src/app/app.routes.server.ts +4 -0
- package/playground/showcase/src/app/components/ai/ai-chat/ai-chat.html +7 -2
- package/playground/showcase/src/app/components/ai/ai-chat/ai-chat.ts +12 -1
- package/playground/showcase/src/app/components/ai/ai-chat.service.spec.ts +16 -0
- package/playground/showcase/src/app/components/ai/ai-chat.service.ts +6 -0
- package/playground/showcase/src/app/components/ai/ai-composer/ai-composer.html +14 -7
- package/playground/showcase/src/app/components/ai/ai-composer/ai-composer.ts +3 -1
- package/playground/showcase/src/app/components/ai/ai-message/ai-message.css +0 -0
- package/playground/showcase/src/app/components/ai/ai-message/ai-message.html +165 -0
- package/playground/showcase/src/app/components/ai/ai-message/ai-message.spec.ts +23 -0
- package/playground/showcase/src/app/components/ai/ai-message/ai-message.ts +51 -0
- package/playground/showcase/src/app/components/button/button.html +1 -1
- package/playground/showcase/src/app/components/button/button.ts +3 -3
- package/playground/showcase/src/app/components/selection-popover/selection-popover.css +0 -0
- package/playground/showcase/src/app/components/selection-popover/selection-popover.html +33 -0
- package/playground/showcase/src/app/components/selection-popover/selection-popover.spec.ts +23 -0
- package/playground/showcase/src/app/components/selection-popover/selection-popover.ts +269 -0
- package/playground/showcase/src/app/pages/documentation/chat-sdk/chat-sdk.html +1 -1
- package/playground/showcase/src/app/pages/documentation/components/button-docs/button-docs.examples.ts +10 -10
- package/playground/showcase/src/app/pages/documentation/components/button-docs/button-docs.html +2 -2
- package/playground/showcase/src/app/pages/documentation/components/components.html +27 -0
- package/playground/showcase/src/app/pages/documentation/components/components.routes.ts +4 -0
- package/playground/showcase/src/app/pages/documentation/components/selection-popover-docs/selection-popover-docs.css +1 -0
- package/playground/showcase/src/app/pages/documentation/components/selection-popover-docs/selection-popover-docs.examples.ts +328 -0
- package/playground/showcase/src/app/pages/documentation/components/selection-popover-docs/selection-popover-docs.html +555 -0
- package/playground/showcase/src/app/pages/documentation/components/selection-popover-docs/selection-popover-docs.ts +97 -0
- package/playground/showcase/src/app/pages/home/home.html +2 -2
- package/playground/showcase/src/styles.css +1 -0
package/package.json
CHANGED
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"@angular/build": "^20.0.5",
|
|
30
30
|
"@angular/cli": "^20.0.5",
|
|
31
31
|
"@angular/compiler-cli": "^20.0.0",
|
|
32
|
+
"@tailwindcss/typography": "^0.5.19",
|
|
32
33
|
"@types/express": "^5.0.1",
|
|
33
34
|
"@types/jasmine": "~5.1.0",
|
|
34
35
|
"@types/node": "^20.17.19",
|
|
@@ -4100,6 +4101,19 @@
|
|
|
4100
4101
|
"tailwindcss": "4.1.13"
|
|
4101
4102
|
}
|
|
4102
4103
|
},
|
|
4104
|
+
"node_modules/@tailwindcss/typography": {
|
|
4105
|
+
"version": "0.5.19",
|
|
4106
|
+
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.19.tgz",
|
|
4107
|
+
"integrity": "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==",
|
|
4108
|
+
"dev": true,
|
|
4109
|
+
"license": "MIT",
|
|
4110
|
+
"dependencies": {
|
|
4111
|
+
"postcss-selector-parser": "6.0.10"
|
|
4112
|
+
},
|
|
4113
|
+
"peerDependencies": {
|
|
4114
|
+
"tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1"
|
|
4115
|
+
}
|
|
4116
|
+
},
|
|
4103
4117
|
"node_modules/@tufjs/canonical-json": {
|
|
4104
4118
|
"version": "2.0.0",
|
|
4105
4119
|
"resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz",
|
|
@@ -5205,6 +5219,19 @@
|
|
|
5205
5219
|
"url": "https://github.com/sponsors/fb55"
|
|
5206
5220
|
}
|
|
5207
5221
|
},
|
|
5222
|
+
"node_modules/cssesc": {
|
|
5223
|
+
"version": "3.0.0",
|
|
5224
|
+
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
|
5225
|
+
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
|
|
5226
|
+
"dev": true,
|
|
5227
|
+
"license": "MIT",
|
|
5228
|
+
"bin": {
|
|
5229
|
+
"cssesc": "bin/cssesc"
|
|
5230
|
+
},
|
|
5231
|
+
"engines": {
|
|
5232
|
+
"node": ">=4"
|
|
5233
|
+
}
|
|
5234
|
+
},
|
|
5208
5235
|
"node_modules/custom-event": {
|
|
5209
5236
|
"version": "1.0.1",
|
|
5210
5237
|
"resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz",
|
|
@@ -8824,6 +8851,20 @@
|
|
|
8824
8851
|
"dev": true,
|
|
8825
8852
|
"license": "MIT"
|
|
8826
8853
|
},
|
|
8854
|
+
"node_modules/postcss-selector-parser": {
|
|
8855
|
+
"version": "6.0.10",
|
|
8856
|
+
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
|
|
8857
|
+
"integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
|
|
8858
|
+
"dev": true,
|
|
8859
|
+
"license": "MIT",
|
|
8860
|
+
"dependencies": {
|
|
8861
|
+
"cssesc": "^3.0.0",
|
|
8862
|
+
"util-deprecate": "^1.0.2"
|
|
8863
|
+
},
|
|
8864
|
+
"engines": {
|
|
8865
|
+
"node": ">=4"
|
|
8866
|
+
}
|
|
8867
|
+
},
|
|
8827
8868
|
"node_modules/prismjs": {
|
|
8828
8869
|
"version": "1.30.0",
|
|
8829
8870
|
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
|
|
@@ -10208,6 +10249,13 @@
|
|
|
10208
10249
|
"node": ">=6"
|
|
10209
10250
|
}
|
|
10210
10251
|
},
|
|
10252
|
+
"node_modules/util-deprecate": {
|
|
10253
|
+
"version": "1.0.2",
|
|
10254
|
+
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
|
10255
|
+
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
|
10256
|
+
"dev": true,
|
|
10257
|
+
"license": "MIT"
|
|
10258
|
+
},
|
|
10211
10259
|
"node_modules/utils-merge": {
|
|
10212
10260
|
"version": "1.0.1",
|
|
10213
10261
|
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
|
@@ -33,6 +33,10 @@ export const serverRoutes: ServerRoute[] = [
|
|
|
33
33
|
path: 'documentation/components/dropdown-menu',
|
|
34
34
|
renderMode: RenderMode.Prerender,
|
|
35
35
|
},
|
|
36
|
+
{
|
|
37
|
+
path: 'documentation/components/selection-popover',
|
|
38
|
+
renderMode: RenderMode.Prerender,
|
|
39
|
+
},
|
|
36
40
|
{
|
|
37
41
|
path: 'documentation/chat-sdk',
|
|
38
42
|
renderMode: RenderMode.Prerender,
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
|
+
<div class="mb-2">
|
|
3
|
+
<wally-ai-message (textSelected)="handleTextSelected($event)"></wally-ai-message>
|
|
4
|
+
</div>
|
|
5
|
+
<div
|
|
6
|
+
class="w-full border bg-white/50 border-neutral-300 dark:bg-[#1b1b1b] dark:border-neutral-700 shadow-lg rounded-4xl p-1">
|
|
2
7
|
<div class="w-full">
|
|
3
|
-
<wally-ai-composer></wally-ai-composer>
|
|
8
|
+
<wally-ai-composer [textSelected]="textSelected()"></wally-ai-composer>
|
|
4
9
|
</div>
|
|
5
10
|
</div>
|
|
@@ -1,17 +1,28 @@
|
|
|
1
|
-
import { Component } from '@angular/core';
|
|
1
|
+
import { Component, signal } from '@angular/core';
|
|
2
2
|
|
|
3
3
|
import { AiPromptInput } from '../ai-prompt-input/ai-prompt-input';
|
|
4
4
|
import { AiComposer } from '../ai-composer/ai-composer';
|
|
5
|
+
import { AiChatService } from '../ai-chat.service';
|
|
6
|
+
import { AiMessage } from '../ai-message/ai-message';
|
|
5
7
|
|
|
6
8
|
@Component({
|
|
7
9
|
selector: 'wally-ai-chat',
|
|
8
10
|
imports: [
|
|
9
11
|
AiComposer,
|
|
10
12
|
AiPromptInput,
|
|
13
|
+
AiMessage
|
|
14
|
+
],
|
|
15
|
+
providers: [
|
|
16
|
+
AiChatService
|
|
11
17
|
],
|
|
12
18
|
templateUrl: './ai-chat.html',
|
|
13
19
|
styleUrl: './ai-chat.css'
|
|
14
20
|
})
|
|
15
21
|
export class AiChat {
|
|
22
|
+
textSelected = signal<string>('');
|
|
16
23
|
|
|
24
|
+
handleTextSelected(text: string): void {
|
|
25
|
+
console.log('📝 Texto selecionado:', text);
|
|
26
|
+
this.textSelected.set(text);
|
|
27
|
+
}
|
|
17
28
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { TestBed } from '@angular/core/testing';
|
|
2
|
+
|
|
3
|
+
import { AiChatService } from './ai-chat.service';
|
|
4
|
+
|
|
5
|
+
describe('AiChatService', () => {
|
|
6
|
+
let service: AiChatService;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
TestBed.configureTestingModule({});
|
|
10
|
+
service = TestBed.inject(AiChatService);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('should be created', () => {
|
|
14
|
+
expect(service).toBeTruthy();
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -1,25 +1,31 @@
|
|
|
1
1
|
<div class="w-full flex flex-col" role="region" aria-label="AI message composer">
|
|
2
2
|
<!-- Selected Text Context -->
|
|
3
|
+
@if (textSelected()) {
|
|
3
4
|
<div class="p-0.5">
|
|
4
5
|
<div id="selected-context"
|
|
5
|
-
class="w-full flex items-
|
|
6
|
+
class="w-full max-h-36 overflow-auto flex items-start gap-4 justify-between border bg-neutral-100 border-neutral-300 dark:bg-[#121212] dark:border-neutral-700 rounded-t-3xl rounded-b-lg px-3 py-4"
|
|
6
7
|
role="status" aria-live="polite">
|
|
7
|
-
|
|
8
|
+
<!-- Ícone Esquerda -->
|
|
9
|
+
<div class="flex items-center shrink-0">
|
|
8
10
|
<svg viewBox="0 0 20 20" fill="currentColor" xmlns="http://www.w3.org/2000/svg"
|
|
9
11
|
class="size-5 text-[#0a0a0a] dark:text-white" aria-hidden="true">
|
|
10
12
|
<path
|
|
11
13
|
d="M12.5293 6.5293C12.7566 6.30203 13.1081 6.27383 13.3662 6.44434L13.4707 6.5293L17.4707 10.5293C17.7304 10.789 17.7304 11.211 17.4707 11.4707L13.4707 15.4707C13.211 15.7304 12.789 15.7304 12.5293 15.4707C12.2696 15.211 12.2696 14.789 12.5293 14.5293L15.3936 11.665H6C3.97588 11.665 2.33496 10.0241 2.33496 8V4.5C2.33496 4.13273 2.63273 3.83496 3 3.83496C3.36727 3.83496 3.66504 4.13273 3.66504 4.5V8C3.66504 9.28958 4.71042 10.335 6 10.335H15.3936L12.5293 7.4707L12.4443 7.36621C12.2738 7.10808 12.302 6.75657 12.5293 6.5293Z">
|
|
12
14
|
</path>
|
|
13
15
|
</svg>
|
|
16
|
+
</div>
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
<!-- Texto Central -->
|
|
19
|
+
<div class="flex-1 flex items-center">
|
|
20
|
+
<p class="text-sm text-[#0a0a0a] dark:text-white line-clamp-2" id="context-label">
|
|
21
|
+
"{{ textSelected() }}"
|
|
17
22
|
</p>
|
|
18
23
|
</div>
|
|
19
24
|
|
|
20
|
-
|
|
25
|
+
<!-- Botão Direita -->
|
|
26
|
+
<div class="flex items-center h-5">
|
|
21
27
|
<wally-button [variant]="'ghost'" [rounded]="true" [ariaLabel]="'Clear selected context'">
|
|
22
|
-
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="
|
|
28
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2"
|
|
23
29
|
stroke="currentColor" class="size-5" aria-hidden="true">
|
|
24
30
|
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
|
|
25
31
|
</svg>
|
|
@@ -27,13 +33,14 @@
|
|
|
27
33
|
</div>
|
|
28
34
|
</div>
|
|
29
35
|
</div>
|
|
36
|
+
}
|
|
30
37
|
|
|
31
38
|
<div class="w-full px-4 pt-4">
|
|
32
39
|
<wally-ai-prompt-input></wally-ai-prompt-input>
|
|
33
40
|
</div>
|
|
34
41
|
|
|
35
42
|
<!-- Action Buttons -->
|
|
36
|
-
<div class="w-full flex justify-between
|
|
43
|
+
<div class="w-full flex items-center justify-between p-1" role="toolbar" aria-label="Composer actions">
|
|
37
44
|
<div class="w-full flex items-center gap-1">
|
|
38
45
|
<div class="size-10">
|
|
39
46
|
<wally-dropdown-menu>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Component } from '@angular/core';
|
|
1
|
+
import { Component, input } from '@angular/core';
|
|
2
2
|
|
|
3
3
|
import { DropdownMenuSubTrigger } from '../../dropdown-menu/dropdown-menu-sub-trigger/dropdown-menu-sub-trigger';
|
|
4
4
|
import { DropdownMenuSubContent } from '../../dropdown-menu/dropdown-menu-sub-content/dropdown-menu-sub-content';
|
|
@@ -35,6 +35,8 @@ import { Button } from '../../button/button';
|
|
|
35
35
|
styleUrl: './ai-composer.css'
|
|
36
36
|
})
|
|
37
37
|
export class AiComposer {
|
|
38
|
+
textSelected = input<string>('');
|
|
39
|
+
|
|
38
40
|
onItemClick(): void {
|
|
39
41
|
console.log('Item clicked');
|
|
40
42
|
}
|
|
File without changes
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
<wally-selection-popover (textSelected)="onAskAbout($event)">
|
|
2
|
+
<!-- Ações customizadas do popover -->
|
|
3
|
+
<div popoverActions class="flex gap-2">
|
|
4
|
+
<wally-button variant="ghost" (buttonClick)="askChat()">
|
|
5
|
+
<div class="flex items-center gap-2">
|
|
6
|
+
<svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor" xmlns="http://www.w3.org/2000/svg"
|
|
7
|
+
class="icon">
|
|
8
|
+
<path
|
|
9
|
+
d="M6.24992 11.0417C6.65578 11.0417 7.04227 10.9587 7.39339 10.809C7.05099 11.7065 6.35374 12.9827 5.29864 13.1333C4.84303 13.1985 4.52644 13.6206 4.59153 14.0762C4.65662 14.5318 5.07873 14.8484 5.53434 14.7832C7.66169 14.4794 8.93792 12.0592 9.30742 10.1591C9.50975 9.11833 9.48792 7.98412 9.04084 7.03938C8.53525 5.97097 7.43538 5.20335 6.23748 5.20834C4.63238 5.21504 3.33325 6.5183 3.33325 8.12498C3.33325 9.73583 4.63909 11.0417 6.24992 11.0417Z">
|
|
10
|
+
</path>
|
|
11
|
+
<path
|
|
12
|
+
d="M13.4832 11.0417C13.889 11.0417 14.2755 10.9587 14.6267 10.809C14.2843 11.7065 13.587 12.9827 12.5319 13.1333C12.0763 13.1985 11.7597 13.6206 11.8248 14.0762C11.8899 14.5318 12.312 14.8484 12.7676 14.7832C14.8949 14.4794 16.1712 12.0592 16.5407 10.1591C16.743 9.11833 16.7212 7.98412 16.2741 7.03938C15.7685 5.97097 14.6687 5.20335 13.4708 5.20834C11.8656 5.21504 10.5665 6.5183 10.5665 8.12498C10.5665 9.73583 11.8724 11.0417 13.4832 11.0417Z">
|
|
13
|
+
</path>
|
|
14
|
+
</svg>
|
|
15
|
+
|
|
16
|
+
<span class="text-base">
|
|
17
|
+
Ask chat
|
|
18
|
+
</span>
|
|
19
|
+
</div>
|
|
20
|
+
</wally-button>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<article class="prose dark:prose-invert lg:prose-lg prose-neutral w-full break-words">
|
|
24
|
+
<h1>Garlic bread with cheese: What the science tells us</h1>
|
|
25
|
+
<h2>Garlic bread with cheese: What the science tells us</h2>
|
|
26
|
+
<p>
|
|
27
|
+
For years parents have espoused the health benefits of eating garlic bread with cheese to their children,
|
|
28
|
+
with
|
|
29
|
+
the
|
|
30
|
+
food earning such an iconic status in our culture that kids will often dress up as warm, cheesy loaf for
|
|
31
|
+
Halloween.
|
|
32
|
+
</p>
|
|
33
|
+
<p>
|
|
34
|
+
For years parents have espoused the health benefits of eating garlic bread with cheese to their children,
|
|
35
|
+
with
|
|
36
|
+
the
|
|
37
|
+
food earning such an iconic status in our culture that kids will often dress up as warm, cheesy loaf for
|
|
38
|
+
Halloween.
|
|
39
|
+
</p>
|
|
40
|
+
<p>
|
|
41
|
+
But a recent study shows that the celebrated appetizer may be linked to a series of rabies cases springing
|
|
42
|
+
up
|
|
43
|
+
around
|
|
44
|
+
the country.
|
|
45
|
+
</p>
|
|
46
|
+
|
|
47
|
+
<div class="w-full flex gap-1">
|
|
48
|
+
<div class="size-10">
|
|
49
|
+
<wally-tooltip text="Copy to clipboard" [position]="'bottom'">
|
|
50
|
+
<wally-button variant="ghost">
|
|
51
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2"
|
|
52
|
+
stroke="currentColor" class="size-5">
|
|
53
|
+
<path stroke-linecap="round" stroke-linejoin="round"
|
|
54
|
+
d="M15.666 3.888A2.25 2.25 0 0 0 13.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 0 1-.75.75H9a.75.75 0 0 1-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 0 1-2.25 2.25H6.75A2.25 2.25 0 0 1 4.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 0 1 1.927-.184" />
|
|
55
|
+
</svg>
|
|
56
|
+
</wally-button>
|
|
57
|
+
</wally-tooltip>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<div class="size-10">
|
|
61
|
+
<wally-tooltip text="Satisfactory answer" [position]="'bottom'">
|
|
62
|
+
<wally-button variant="ghost">
|
|
63
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2"
|
|
64
|
+
stroke="currentColor" class="size-5">
|
|
65
|
+
<path stroke-linecap="round" stroke-linejoin="round"
|
|
66
|
+
d="M6.633 10.25c.806 0 1.533-.446 2.031-1.08a9.041 9.041 0 0 1 2.861-2.4c.723-.384 1.35-.956 1.653-1.715a4.498 4.498 0 0 0 .322-1.672V2.75a.75.75 0 0 1 .75-.75 2.25 2.25 0 0 1 2.25 2.25c0 1.152-.26 2.243-.723 3.218-.266.558.107 1.282.725 1.282m0 0h3.126c1.026 0 1.945.694 2.054 1.715.045.422.068.85.068 1.285a11.95 11.95 0 0 1-2.649 7.521c-.388.482-.987.729-1.605.729H13.48c-.483 0-.964-.078-1.423-.23l-3.114-1.04a4.501 4.501 0 0 0-1.423-.23H5.904m10.598-9.75H14.25M5.904 18.5c.083.205.173.405.27.602.197.4-.078.898-.523.898h-.908c-.889 0-1.713-.518-1.972-1.368a12 12 0 0 1-.521-3.507c0-1.553.295-3.036.831-4.398C3.387 9.953 4.167 9.5 5 9.5h1.053c.472 0 .745.556.5.96a8.958 8.958 0 0 0-1.302 4.665c0 1.194.232 2.333.654 3.375Z" />
|
|
67
|
+
</svg>
|
|
68
|
+
|
|
69
|
+
</wally-button>
|
|
70
|
+
</wally-tooltip>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<div class="size-10">
|
|
74
|
+
<wally-tooltip text="Unsatisfactory answer" [position]="'bottom'">
|
|
75
|
+
<wally-button variant="ghost">
|
|
76
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2"
|
|
77
|
+
stroke="currentColor" class="size-5">
|
|
78
|
+
<path stroke-linecap="round" stroke-linejoin="round"
|
|
79
|
+
d="M7.498 15.25H4.372c-1.026 0-1.945-.694-2.054-1.715a12.137 12.137 0 0 1-.068-1.285c0-2.848.992-5.464 2.649-7.521C5.287 4.247 5.886 4 6.504 4h4.016a4.5 4.5 0 0 1 1.423.23l3.114 1.04a4.5 4.5 0 0 0 1.423.23h1.294M7.498 15.25c.618 0 .991.724.725 1.282A7.471 7.471 0 0 0 7.5 19.75 2.25 2.25 0 0 0 9.75 22a.75.75 0 0 0 .75-.75v-.633c0-.573.11-1.14.322-1.672.304-.76.93-1.33 1.653-1.715a9.04 9.04 0 0 0 2.86-2.4c.498-.634 1.226-1.08 2.032-1.08h.384m-10.253 1.5H9.7m8.075-9.75c.01.05.027.1.05.148.593 1.2.925 2.55.925 3.977 0 1.487-.36 2.89-.999 4.125m.023-8.25c-.076-.365.183-.75.575-.75h.908c.889 0 1.713.518 1.972 1.368.339 1.11.521 2.287.521 3.507 0 1.553-.295 3.036-.831 4.398-.306.774-1.086 1.227-1.918 1.227h-1.053c-.472 0-.745-.556-.5-.96a8.95 8.95 0 0 0 .303-.54" />
|
|
80
|
+
</svg>
|
|
81
|
+
</wally-button>
|
|
82
|
+
</wally-tooltip>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
<div class="size-10">
|
|
86
|
+
<wally-tooltip text="Share" [position]="'bottom'">
|
|
87
|
+
<wally-button variant="ghost">
|
|
88
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2"
|
|
89
|
+
stroke="currentColor" class="size-5">
|
|
90
|
+
<path stroke-linecap="round" stroke-linejoin="round"
|
|
91
|
+
d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5M16.5 12 12 16.5m0 0L7.5 12m4.5 4.5V3" />
|
|
92
|
+
</svg>
|
|
93
|
+
</wally-button>
|
|
94
|
+
</wally-tooltip>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
<div class="size-10">
|
|
98
|
+
<wally-tooltip text="Regenerate response" [position]="'bottom'">
|
|
99
|
+
<wally-button variant="ghost">
|
|
100
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2"
|
|
101
|
+
stroke="currentColor" class="size-5">
|
|
102
|
+
<path stroke-linecap="round" stroke-linejoin="round"
|
|
103
|
+
d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182m0-4.991v4.99" />
|
|
104
|
+
</svg>
|
|
105
|
+
</wally-button>
|
|
106
|
+
</wally-tooltip>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<div class="size-10">
|
|
110
|
+
<wally-dropdown-menu>
|
|
111
|
+
<wally-dropdown-menu-trigger>
|
|
112
|
+
<wally-tooltip text="More options" [position]="'bottom'">
|
|
113
|
+
<wally-button variant="ghost">
|
|
114
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2"
|
|
115
|
+
stroke="currentColor" class="size-5">
|
|
116
|
+
<path stroke-linecap="round" stroke-linejoin="round"
|
|
117
|
+
d="M6.75 12a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0ZM12.75 12a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0ZM18.75 12a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0Z" />
|
|
118
|
+
</svg>
|
|
119
|
+
</wally-button>
|
|
120
|
+
</wally-tooltip>
|
|
121
|
+
</wally-dropdown-menu-trigger>
|
|
122
|
+
|
|
123
|
+
<wally-dropdown-menu-content>
|
|
124
|
+
<wally-dropdown-menu-group>
|
|
125
|
+
<wally-dropdown-menu-item>
|
|
126
|
+
<div class="flex items-center gap-3">
|
|
127
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
|
128
|
+
stroke-width="2" stroke="currentColor" class="size-5">
|
|
129
|
+
<path stroke-linecap="round" stroke-linejoin="round"
|
|
130
|
+
d="M19.114 5.636a9 9 0 0 1 0 12.728M16.463 8.288a5.25 5.25 0 0 1 0 7.424M6.75 8.25l4.72-4.72a.75.75 0 0 1 1.28.53v15.88a.75.75 0 0 1-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9.009 9.009 0 0 1 2.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25H6.75Z" />
|
|
131
|
+
</svg>
|
|
132
|
+
|
|
133
|
+
<span>
|
|
134
|
+
Read aloud
|
|
135
|
+
</span>
|
|
136
|
+
</div>
|
|
137
|
+
</wally-dropdown-menu-item>
|
|
138
|
+
</wally-dropdown-menu-group>
|
|
139
|
+
</wally-dropdown-menu-content>
|
|
140
|
+
</wally-dropdown-menu>
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
<div>
|
|
144
|
+
<wally-button variant="ghost" [rounded]="true">
|
|
145
|
+
<div class="flex items-center gap-2">
|
|
146
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" class="size-5">
|
|
147
|
+
<path fill="#FFC107"
|
|
148
|
+
d="M43.611,20.083H42V20H24v8h11.303c-1.649,4.657-6.08,8-11.303,8c-6.627,0-12-5.373-12-12c0-6.627,5.373-12,12-12c3.059,0,5.842,1.154,7.961,3.039l5.657-5.657C34.046,6.053,29.268,4,24,4C12.955,4,4,12.955,4,24c0,11.045,8.955,20,20,20c11.045,0,20-8.955,20-20C44,22.659,43.862,21.35,43.611,20.083z" />
|
|
149
|
+
<path fill="#FF3D00"
|
|
150
|
+
d="M6.306,14.691l6.571,4.819C14.655,15.108,18.961,12,24,12c3.059,0,5.842,1.154,7.961,3.039l5.657-5.657C34.046,6.053,29.268,4,24,4C16.318,4,9.656,8.337,6.306,14.691z" />
|
|
151
|
+
<path fill="#4CAF50"
|
|
152
|
+
d="M24,44c5.166,0,9.86-1.977,13.409-5.192l-6.19-5.238C29.211,35.091,26.715,36,24,36c-5.202,0-9.619-3.317-11.283-7.946l-6.522,5.025C9.505,39.556,16.227,44,24,44z" />
|
|
153
|
+
<path fill="#1976D2"
|
|
154
|
+
d="M43.611,20.083H42V20H24v8h11.303c-0.792,2.237-2.231,4.166-4.087,5.571c0.001-0.001,0.002-0.001,0.003-0.002l6.19,5.238C36.971,39.205,44,34,44,24C44,22.659,43.862,21.35,43.611,20.083z" />
|
|
155
|
+
</svg>
|
|
156
|
+
|
|
157
|
+
<span>
|
|
158
|
+
Sources
|
|
159
|
+
</span>
|
|
160
|
+
</div>
|
|
161
|
+
</wally-button>
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
</article>
|
|
165
|
+
</wally-selection-popover>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
+
|
|
3
|
+
import { AiMessage } from './ai-message';
|
|
4
|
+
|
|
5
|
+
describe('AiMessage', () => {
|
|
6
|
+
let component: AiMessage;
|
|
7
|
+
let fixture: ComponentFixture<AiMessage>;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
await TestBed.configureTestingModule({
|
|
11
|
+
imports: [AiMessage]
|
|
12
|
+
})
|
|
13
|
+
.compileComponents();
|
|
14
|
+
|
|
15
|
+
fixture = TestBed.createComponent(AiMessage);
|
|
16
|
+
component = fixture.componentInstance;
|
|
17
|
+
fixture.detectChanges();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should create', () => {
|
|
21
|
+
expect(component).toBeTruthy();
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Component, input, InputSignal, output, OutputEmitterRef } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
import { Button } from '../../button/button';
|
|
4
|
+
import { Tooltip } from '../../tooltip/tooltip';
|
|
5
|
+
import { DropdownMenu } from '../../dropdown-menu/dropdown-menu';
|
|
6
|
+
import { DropdownMenuContent } from '../../dropdown-menu/dropdown-menu-content/dropdown-menu-content';
|
|
7
|
+
import { DropdownMenuTrigger } from '../../dropdown-menu/dropdown-menu-trigger/dropdown-menu-trigger';
|
|
8
|
+
import { DropdownMenuItem } from '../../dropdown-menu/dropdown-menu-item/dropdown-menu-item';
|
|
9
|
+
import { DropdownMenuGroup } from '../../dropdown-menu/dropdown-menu-group/dropdown-menu-group';
|
|
10
|
+
import { SelectionPopover } from '../../selection-popover/selection-popover';
|
|
11
|
+
|
|
12
|
+
type role = 'user' | 'assistant';
|
|
13
|
+
|
|
14
|
+
@Component({
|
|
15
|
+
selector: 'wally-ai-message',
|
|
16
|
+
imports: [
|
|
17
|
+
Button,
|
|
18
|
+
Tooltip,
|
|
19
|
+
DropdownMenu,
|
|
20
|
+
DropdownMenuTrigger,
|
|
21
|
+
DropdownMenuContent,
|
|
22
|
+
DropdownMenuGroup,
|
|
23
|
+
DropdownMenuItem,
|
|
24
|
+
SelectionPopover
|
|
25
|
+
],
|
|
26
|
+
templateUrl: './ai-message.html',
|
|
27
|
+
styleUrl: './ai-message.css'
|
|
28
|
+
})
|
|
29
|
+
export class AiMessage {
|
|
30
|
+
inputRole: InputSignal<role> = input<role>('user');
|
|
31
|
+
|
|
32
|
+
textSelected: OutputEmitterRef<string> = output<string>();
|
|
33
|
+
|
|
34
|
+
onAskAbout(text: string): void {
|
|
35
|
+
// console.log('📝 Texto selecionado:', text);
|
|
36
|
+
// console.log('📏 Tamanho do texto:', text.length);
|
|
37
|
+
this.textSelected.emit(text);
|
|
38
|
+
// Aqui você pode emitir um evento para o componente pai (ai-chat)
|
|
39
|
+
// ou chamar um serviço para adicionar a pergunta ao chat
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
askChat(): void {
|
|
43
|
+
// console.log('🤖 Botão "Ask chat" clicado');
|
|
44
|
+
// Este método agora é redundante - o texto é emitido via textSelected
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
copyText(): void {
|
|
48
|
+
// console.log('Copiando texto selecionado');
|
|
49
|
+
// Lógica para copiar o texto para a área de transferência
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<button [type]="type()" [disabled]="disabled() || loading()" [attr.aria-label]="ariaLabel() || null"
|
|
2
2
|
[attr.aria-describedby]="ariaDescribedBy() || null" [attr.aria-pressed]="ariaPressed()" [attr.aria-busy]="loading()"
|
|
3
|
-
(click)="handleClick()" [class]="'group relative w-full flex items-center justify-center gap-2 text-sm font-medium text-white active:scale-95 disabled:pointer-events-none p-2.5 disabled:active:scale-100 transition-all duration-300 ease-in-out antialiased cursor-pointer ' + variantClasses()" [ngClass]="{
|
|
3
|
+
(click)="handleClick($event)" [class]="'group relative w-full flex items-center justify-center gap-2 text-sm font-medium text-white active:scale-95 disabled:pointer-events-none p-2.5 disabled:active:scale-100 transition-all duration-300 ease-in-out antialiased cursor-pointer ' + variantClasses()" [ngClass]="{
|
|
4
4
|
'rounded-md': !rounded(),
|
|
5
5
|
'rounded-full': rounded()
|
|
6
6
|
}">
|
|
@@ -28,7 +28,7 @@ export class Button {
|
|
|
28
28
|
ariaDescribedBy: InputSignal<string> = input<string>('');
|
|
29
29
|
ariaPressed: InputSignal<boolean | undefined> = input<boolean | undefined>(undefined);
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
buttonClick: OutputEmitterRef<void> = output<void>();
|
|
32
32
|
|
|
33
33
|
// Computed classes based on variant
|
|
34
34
|
variantClasses: Signal<string> = computed(() => {
|
|
@@ -44,7 +44,7 @@ export class Button {
|
|
|
44
44
|
return variantMap[this.variant()] || variantMap.primary;
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
-
handleClick(): void {
|
|
47
|
+
handleClick(event: MouseEvent): void {
|
|
48
48
|
if (this.variant() === 'link' && this.href()) {
|
|
49
49
|
if (this.href().startsWith('http://') || this.href().startsWith('https://')) {
|
|
50
50
|
window.open(this.href(), '_blank');
|
|
@@ -53,6 +53,6 @@ export class Button {
|
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
this.
|
|
56
|
+
this.buttonClick.emit();
|
|
57
57
|
}
|
|
58
58
|
}
|
|
File without changes
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<div class="relative">
|
|
2
|
+
<!-- Original content (where user will select text) -->
|
|
3
|
+
<ng-content></ng-content>
|
|
4
|
+
|
|
5
|
+
<!-- Floating popover -->
|
|
6
|
+
@if (isVisible()) {
|
|
7
|
+
<div #popover
|
|
8
|
+
class="fixed z-50 bg-white dark:bg-neutral-900 shadow-lg rounded-xl border border-neutral-300 dark:border-neutral-700 transition-opacity duration-150"
|
|
9
|
+
[class.p-1]="!isMobile()"
|
|
10
|
+
[class.p-2]="isMobile()"
|
|
11
|
+
[class.opacity-0]="!isPositioned()"
|
|
12
|
+
[class.opacity-100]="isPositioned()"
|
|
13
|
+
[style.top.px]="adjustedPosition().top" [style.left.px]="adjustedPosition().left" role="dialog"
|
|
14
|
+
aria-label="Selection actions"
|
|
15
|
+
(click)="onPopoverClick()">
|
|
16
|
+
|
|
17
|
+
<!-- Customizable content via content projection (always rendered) -->
|
|
18
|
+
<div #customActionsSlot [class.hidden]="!hasCustomActionsSignal()">
|
|
19
|
+
<ng-content select="[popoverActions]"></ng-content>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<!-- Fallback: default button (hidden if custom actions exist) -->
|
|
23
|
+
<button [class.hidden]="hasCustomActionsSignal()"
|
|
24
|
+
class="text-[#0a0a0a] text-sm font-mono hover:bg-neutral-100 dark:text-white dark:hover:bg-neutral-800 rounded transition-colors cursor-pointer"
|
|
25
|
+
[class.px-3]="!isMobile()"
|
|
26
|
+
[class.py-2]="!isMobile()"
|
|
27
|
+
[class.px-4]="isMobile()"
|
|
28
|
+
[class.py-3]="isMobile()">
|
|
29
|
+
Default Action
|
|
30
|
+
</button>
|
|
31
|
+
</div>
|
|
32
|
+
}
|
|
33
|
+
</div>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
2
|
+
|
|
3
|
+
import { SelectionPopover } from './selection-popover';
|
|
4
|
+
|
|
5
|
+
describe('SelectionPopover', () => {
|
|
6
|
+
let component: SelectionPopover;
|
|
7
|
+
let fixture: ComponentFixture<SelectionPopover>;
|
|
8
|
+
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
await TestBed.configureTestingModule({
|
|
11
|
+
imports: [SelectionPopover]
|
|
12
|
+
})
|
|
13
|
+
.compileComponents();
|
|
14
|
+
|
|
15
|
+
fixture = TestBed.createComponent(SelectionPopover);
|
|
16
|
+
component = fixture.componentInstance;
|
|
17
|
+
fixture.detectChanges();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should create', () => {
|
|
21
|
+
expect(component).toBeTruthy();
|
|
22
|
+
});
|
|
23
|
+
});
|