-
Notifications
You must be signed in to change notification settings - Fork 482
feat(a11y): Accessibility Studio portlet + AI agent platform (@dotcms/ai, MCP server, agent UI) #36641
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat(a11y): Accessibility Studio portlet + AI agent platform (@dotcms/ai, MCP server, agent UI) #36641
Changes from all commits
f86fb60
eb2ac73
0f552d5
a373801
89fcf4b
c4db65e
44f4e70
f247900
3f4e1f8
1c3fb9b
fd93b91
a12f4d9
b5e2272
b3abb0d
51b5930
8c250e7
2702547
71114d7
31106ef
fa1de8b
199c064
8fd63f5
7f7bf25
0d83931
aa07699
6f321b6
89b3796
bc08ac5
50bc6ca
13a45bc
215e2fe
0aa4911
0dab40a
c060a85
b60e8e1
83ca684
d7465a2
35c2b18
bf0122d
8c66b7a
0198e49
4bb91c3
c6d8d9a
79e62ad
595ecc8
7e5f1c5
e491199
b0d33f7
685ff9b
ed3e173
8f8a4ed
56aa313
70b5adc
d531560
119355a
5b98fa8
264c235
32bcd7a
ed6143c
7fc6588
dca4b46
3431cdf
eafa4ae
330bc9d
d3579a3
4429323
f3174a2
fb7d978
3a3bf92
5fe3888
289c9d8
33706d0
5c3b042
b0a7506
365918f
cd9cab9
e5e2c6a
73208be
d42f1fc
1656901
3f9c4bf
1e1943c
93afadd
611c311
4423656
802f894
b16f903
2061827
f99c20a
a714776
82638ec
9ae1420
1fd9321
02d17b9
93ba456
bbdad3b
682f584
52bdbbb
f93cdbc
4c48b07
9cfa5bd
02ddb5b
027773b
f25f579
87173c5
f524dc7
43961e5
e3b5b0f
bf050ab
e1b51d5
e117304
71ff98b
b2d0617
a098405
b2de03a
261e796
8080d70
1b40e40
0fecbf9
9315126
c655426
e72b789
f0de57e
a0d0a48
cd54ebd
2899213
445c2a7
5d956a6
a052f2d
15c1439
1789765
8ed65ba
ec8925a
d92395f
e4d93de
2c5b061
46715b5
e639ba0
9996d84
ca7917d
88a6d60
a2c6bfc
043af7b
ed0702a
b5f031c
e02235f
b142e43
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| .env | ||
| /libs/agentic-tools/src/generated | ||
|
|
||
| vite.config.*.timestamp* | ||
| vitest.config.*.timestamp* | ||
| vitest.config.*.timestamp* | ||
|
|
||
| .angular |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -185,6 +185,12 @@ const PORTLETS_ANGULAR: Route[] = [ | |
| (m) => m.dotPublishingQueueRoutes | ||
| ) | ||
| }, | ||
| { | ||
| path: 'agents', | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the only top-level portlet route in this array without
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixing. Will be in the next commit
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Confirmed — the omission is real, and it's portlet authorization that's bypassed, not authentication. We're merging without it for now — tracking it as follow-up rather than adding it in this PR. Flagging for anyone reading this later: the guard is necessary but not sufficient on its own, since the backend half ( |
||
| data: { reuseRoute: false }, | ||
| loadChildren: () => | ||
| import('@dotcms/portlets/dot-agents/portlet').then((m) => m.dotAgentsRoutes) | ||
| }, | ||
|
zJaaal marked this conversation as resolved.
|
||
| { | ||
| path: 'users', | ||
| canActivate: [MenuGuardService], | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| import { includeMatcher, splitIncludePatterns } from './assets-transfer'; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This spec covers Would it be worth adding a few tests around those paths, given they're the behavior changes the rework is for? |
||
|
|
||
| describe('splitIncludePatterns', () => { | ||
| it('splits comma-separated patterns and trims them', () => { | ||
| expect(splitIncludePatterns('*.vtl, *.scss')).toEqual(['*.vtl', '*.scss']); | ||
| }); | ||
|
|
||
| it('does NOT split on a comma inside a brace group', () => { | ||
| expect(splitIncludePatterns('*.{png,webp,jpg}')).toEqual(['*.{png,webp,jpg}']); | ||
| }); | ||
|
|
||
| it('splits around a brace group but keeps the group intact', () => { | ||
| expect(splitIncludePatterns('*.{png,jpg},*.vtl')).toEqual(['*.{png,jpg}', '*.vtl']); | ||
| }); | ||
|
|
||
| it('drops empty entries and returns [] for undefined', () => { | ||
| expect(splitIncludePatterns('*.png,,')).toEqual(['*.png']); | ||
| expect(splitIncludePatterns(undefined)).toEqual([]); | ||
| }); | ||
| }); | ||
|
|
||
| describe('includeMatcher', () => { | ||
| it('matches everything when no include is given', () => { | ||
| const m = includeMatcher(); | ||
| expect(m('a.png')).toBe(true); | ||
| expect(m('deep/nested/a.vtl')).toBe(true); | ||
| }); | ||
|
|
||
| // The three repro cases from the bug report — files live directly in the source dir. | ||
| describe('bug report repro (top-level files)', () => { | ||
| it('brace expansion matches top-level files (was: 0 matched)', () => { | ||
| const m = includeMatcher('*.{png,webp,jpg}'); | ||
| expect(m('amazon-logo.png')).toBe(true); | ||
| expect(m('book1.webp')).toBe(true); | ||
| expect(m('cover.jpg')).toBe(true); | ||
| expect(m('notes.txt')).toBe(false); | ||
| }); | ||
|
|
||
| it('** globstar matches a top-level file too (was: 0 matched)', () => { | ||
| const m = includeMatcher('**/*.png'); | ||
| expect(m('amazon-logo.png')).toBe(true); // no subdirectory — must still match | ||
| expect(m('img/hero.png')).toBe(true); | ||
| expect(m('a/b/c/deep.png')).toBe(true); | ||
| expect(m('a/b/c/deep.webp')).toBe(false); | ||
| }); | ||
|
|
||
| it('plain top-level glob still works', () => { | ||
| const m = includeMatcher('*.png'); | ||
| expect(m('amazon-logo.png')).toBe(true); | ||
| expect(m('book1.webp')).toBe(false); | ||
| }); | ||
| }); | ||
|
|
||
| describe('single-star does not cross directories', () => { | ||
| it('"*.png" (no slash) matches a basename anywhere in the tree', () => { | ||
| const m = includeMatcher('*.png'); | ||
| expect(m('a.png')).toBe(true); | ||
| expect(m('deep/dir/a.png')).toBe(true); // basename match, unanchored | ||
| }); | ||
|
|
||
| it('an anchored "img/*.png" only matches that one directory level', () => { | ||
| const m = includeMatcher('img/*.png'); | ||
| expect(m('img/a.png')).toBe(true); | ||
| expect(m('img/sub/a.png')).toBe(false); // * does not cross / | ||
| expect(m('other/a.png')).toBe(false); | ||
| }); | ||
| }); | ||
|
|
||
| describe('** globstar depth', () => { | ||
| it('"img/**/*.png" matches zero or more intermediate dirs', () => { | ||
| const m = includeMatcher('img/**/*.png'); | ||
| expect(m('img/a.png')).toBe(true); // zero intermediate dirs | ||
| expect(m('img/sub/a.png')).toBe(true); | ||
| expect(m('img/a/b/c.png')).toBe(true); | ||
| expect(m('other/a.png')).toBe(false); | ||
| }); | ||
| }); | ||
|
|
||
| describe('? single char', () => { | ||
| it('matches exactly one non-slash char', () => { | ||
| const m = includeMatcher('file?.txt'); | ||
| expect(m('file1.txt')).toBe(true); | ||
| expect(m('fileA.txt')).toBe(true); | ||
| expect(m('file.txt')).toBe(false); | ||
| expect(m('file12.txt')).toBe(false); | ||
| }); | ||
| }); | ||
|
|
||
| describe('literals are escaped', () => { | ||
| it('a dot in the pattern is literal, not "any char"', () => { | ||
| const m = includeMatcher('*.png'); | ||
| expect(m('axpng')).toBe(false); // the "." must be a real dot | ||
| expect(m('a.png')).toBe(true); | ||
| }); | ||
|
|
||
| it('multiple patterns OR together', () => { | ||
| const m = includeMatcher('*.vtl,*.scss'); | ||
| expect(m('theme.vtl')).toBe(true); | ||
| expect(m('styles.scss')).toBe(true); | ||
| expect(m('image.png')).toBe(false); | ||
| }); | ||
| }); | ||
|
|
||
| it('is case-insensitive', () => { | ||
| const m = includeMatcher('*.PNG'); | ||
| expect(m('photo.png')).toBe(true); | ||
| }); | ||
| }); | ||
Uh oh!
There was an error while loading. Please reload this page.