@allbluecn/web-app 0.22.0 → 0.22.3
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 +6 -8
- package/public/logo/allblue-logo-app-icon.svg +1 -1
- package/src/components/changelog/changelog-page.tsx +24 -5
- package/src/components/desktop/cloud-login.tsx +91 -0
- package/src/components/desktop/conflict-center.tsx +148 -0
- package/src/components/desktop/renamed-toast.tsx +23 -0
- package/src/components/desktop/shell-widgets.tsx +19 -0
- package/src/components/desktop/sync-indicator.tsx +60 -0
- package/src/components/desktop/sync-status-card.tsx +60 -0
- package/src/components/layout/sidebar-03/app-sidebar.tsx +38 -3
- package/src/components/shared-kit/avatars/__tests__/avatar-shape.test.ts +17 -1
- package/src/components/shared-kit/avatars/avatar-shape.ts +10 -0
- package/src/components/shared-kit/avatars/user-avatar.tsx +2 -2
- package/src/lib/auth/auth.config.ts +4 -1
- package/src/lib/changelog/sdk-versions.ts +33 -2
- package/src/lib/desktop-bridge-types.ts +30 -0
- package/src/lib/desktop-detect.ts +2 -0
- package/src/lib/vector-store/__tests__/factory.test.ts +0 -7
- package/src/lib/vector-store/index.ts +2 -4
- package/src/paraglide/messages/_index.d.ts +1 -1
- package/src/paraglide/messages/_index.js +32 -16
- package/src/paraglide/messages/common_sidebar_usermenu_loginsaas3.d.ts +1 -1
- package/src/paraglide/messages/common_sidebar_usermenu_loginsaas3.js +3 -3
- package/src/paraglide/messages/en.js +11 -6
- package/src/paraglide/messages/{tasks_ai_desktop_notice.d.ts → settings_tabs_sync.d.ts} +5 -5
- package/src/paraglide/messages/settings_tabs_sync.js +29 -0
- package/src/paraglide/messages/zh.js +11 -6
- package/src/providers/index.tsx +6 -0
- package/src/routeTree.gen.ts +107 -49
- package/src/routes/_nav/route.lazy.tsx +12 -0
- package/src/routes/_nav/settings/-settings-tabs.tsx +1 -1
- package/src/routes/_nav/settings/sync/route.lazy.tsx +40 -0
- package/src/routes/_nav/settings/sync/route.tsx +15 -0
- package/src/routes/_nav/tasks/index.lazy.tsx +1 -11
- package/src/routes/api/billing-webhook.ts +6 -2
- package/src/routes/api/sync/bootstrap.ts +38 -0
- package/src/routes/api/sync/pull.ts +96 -0
- package/src/routes/api/sync/push.ts +169 -0
- package/src/routes/api/sync/snapshot.ts +104 -0
- package/src/server/__tests__/fetch-guard.test.ts +2 -2
- package/src/server/__tests__/resource-registry.test.ts +4 -2
- package/src/server/__tests__/sandbox-entitlement.test.ts +5 -0
- package/src/server/__tests__/security-headers.test.ts +25 -0
- package/src/server/__tests__/task-assigned-query.test.ts +0 -11
- package/src/server/__tests__/task-serverfn-crud.test.ts +1 -22
- package/src/server/__tests__/task-serverfn-queries.test.ts +1 -9
- package/src/server/ai-gateway/__tests__/chat-stream-builder-memory.test.ts +0 -11
- package/src/server/ai-gateway/__tests__/embedding-request.test.ts +1 -1
- package/src/server/ai-gateway/__tests__/provider-adapter.test.ts +2 -2
- package/src/server/ai-gateway/__tests__/run-store-durable.test.ts +1 -1
- package/src/server/ai-gateway/durability/__tests__/pg-stream.test.ts +1 -1
- package/src/server/ai-gateway/durability/__tests__/reaper-real-db.test.ts +1 -1
- package/src/server/ai-gateway/memory/index.ts +2 -2
- package/src/server/ai-gateway/sandbox/__tests__/instance-store.test.ts +1 -1
- package/src/server/ai-task/assigned-query.ts +4 -15
- package/src/server/desktop-guest.ts +12 -0
- package/src/server/fetch-guard.ts +3 -3
- package/src/server/middlewares/desktop-token.ts +23 -0
- package/src/server/remote-auth.ts +6 -0
- package/src/server/serverFns/__tests__/billing.test.ts +58 -1
- package/src/server/serverFns/__tests__/update-seats.test.ts +8 -8
- package/src/server/serverFns/agents/agent-crud.ts +3 -1
- package/src/server/serverFns/auth/session.ts +9 -0
- package/src/server/serverFns/billing.ts +10 -4
- package/src/server/serverFns/bug/bug-crud.ts +7 -2
- package/src/server/serverFns/story/__tests__/labels.test.ts +37 -15
- package/src/server/serverFns/story/story-comments.ts +2 -1
- package/src/server/serverFns/story/story-crud/activities.ts +1 -1
- package/src/server/serverFns/story/story-crud/user-brief.ts +1 -1
- package/src/server/serverFns/story/story-crud.ts +2 -1
- package/src/server/serverFns/story/story-transfer.ts +2 -1
- package/src/server/serverFns/tasks/task-crud.ts +0 -13
- package/src/server/serverFns/tasks/task-queries.ts +1 -12
- package/src/server/serverFns/testcase/plans.ts +1 -1
- package/src/server/serverFns/testcase/run-cases.ts +1 -1
- package/src/server/serverFns/testcase/runs.ts +1 -1
- package/src/server/serverFns/testcase/testcase-activities.ts +1 -1
- package/src/server/serverFns/testcase/testcase-board.ts +1 -1
- package/src/server/serverFns/testcase/testcase-crud.ts +1 -1
- package/src/server/serverFns/workspace.ts +1 -1
- package/src/server/story/types.ts +7 -22
- package/src/server/story/user-brief.ts +48 -0
- package/src/server/sync/scopes.ts +29 -0
- package/src/start.ts +10 -2
- package/src/styles/globals.css +10 -0
- package/src/__tests__/desktop-connection-validate.test.ts +0 -66
- package/src/lib/vector-store/__tests__/sqlite-vec-store.test.ts +0 -143
- package/src/lib/vector-store/sqlite-vec-store.ts +0 -224
- package/src/paraglide/messages/tasks_ai_desktop_notice.js +0 -29
- package/src/routes/__desktop/onboarding/route.lazy.tsx +0 -175
- package/src/routes/__desktop/onboarding/route.tsx +0 -11
- package/src/routes/_nav/settings/connection/route.lazy.tsx +0 -64
- package/src/routes/_nav/settings/connection/route.tsx +0 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@allbluecn/web-app",
|
|
3
|
-
"version": "0.22.
|
|
3
|
+
"version": "0.22.3",
|
|
4
4
|
"license": "AGPL-3.0-or-later",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"@ag-ui/core": "1.0.0",
|
|
62
62
|
"@allbluecn/collab-core": "0.2.0",
|
|
63
|
-
"@allbluecn/database": "0.
|
|
63
|
+
"@allbluecn/database": "0.12.1",
|
|
64
64
|
"@allbluecn/kernel": "0.12.0",
|
|
65
65
|
"@ariakit/react": "^0.4.28",
|
|
66
66
|
"@auth/core": "^0.41.2",
|
|
@@ -134,11 +134,11 @@
|
|
|
134
134
|
"@tanstack/ai-sandbox-vercel": "1.0.0",
|
|
135
135
|
"@tanstack/ai-skills": "1.0.0",
|
|
136
136
|
"@tanstack/ai-utils": "1.0.0",
|
|
137
|
-
"@tanstack/db": "0.
|
|
137
|
+
"@tanstack/db": "1.0.0-rc.1",
|
|
138
138
|
"@tanstack/hotkeys": "^0.18.0",
|
|
139
139
|
"@tanstack/markdown": "^1.0.0",
|
|
140
140
|
"@tanstack/pacer": "^0.23.1",
|
|
141
|
-
"@tanstack/query-db-collection": "1.
|
|
141
|
+
"@tanstack/query-db-collection": "1.5.0",
|
|
142
142
|
"@tanstack/react-form": "2.0.0-alpha.3",
|
|
143
143
|
"@tanstack/react-hotkeys": "^0.17.0",
|
|
144
144
|
"@tanstack/react-pacer": "^0.24.1",
|
|
@@ -187,7 +187,6 @@
|
|
|
187
187
|
"@vercel/speed-insights": "^2.0.0",
|
|
188
188
|
"@zumer/snapdom": "^2.12.9",
|
|
189
189
|
"bcryptjs": "^3.0.3",
|
|
190
|
-
"better-sqlite3": "^13.0.1",
|
|
191
190
|
"class-variance-authority": "^0.7.1",
|
|
192
191
|
"clsx": "^2.1.1",
|
|
193
192
|
"cmdk": "^1.1.1",
|
|
@@ -235,12 +234,12 @@
|
|
|
235
234
|
"remark-gfm": "^4.0.1",
|
|
236
235
|
"sanitize-html": "^2.18.0",
|
|
237
236
|
"shiki": "^4.2.0",
|
|
238
|
-
"sqlite-vec": "^0.1.9",
|
|
239
237
|
"start-authjs": "^1.0.2",
|
|
240
238
|
"tailwind-merge": "^3.5.0",
|
|
241
239
|
"tw-animate-css": "^1.4.0",
|
|
242
240
|
"yjs": "^13.6.33",
|
|
243
|
-
"zod": "^4.6.7"
|
|
241
|
+
"zod": "^4.6.7",
|
|
242
|
+
"@allbluecn/sync-core": "0.5.4"
|
|
244
243
|
},
|
|
245
244
|
"devDependencies": {
|
|
246
245
|
"@babel/parser": "^8.0.4",
|
|
@@ -261,7 +260,6 @@
|
|
|
261
260
|
"@testing-library/react": "^16.3.2",
|
|
262
261
|
"@testing-library/user-event": "^14.6.1",
|
|
263
262
|
"@types/bcryptjs": "^2.4.6",
|
|
264
|
-
"@types/better-sqlite3": "^7.6.13",
|
|
265
263
|
"@types/file-saver": "^2.0.7",
|
|
266
264
|
"@types/is-hotkey": "^0.1.10",
|
|
267
265
|
"@types/jsdom": "^28.0.3",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" width="1024" height="1024">
|
|
2
2
|
<rect width="1024" height="1024" rx="229" fill="#ffffff"/>
|
|
3
|
-
<g transform="translate(
|
|
3
|
+
<g transform="translate(123.41 146.54) scale(0.85)">
|
|
4
4
|
<path d="M912.82 421.43C912.92 423.04 913.03 424.64 913.13 426.24C913.41 436.02 913.68 445.8 913.96 455.57C913.82 457.16 913.67 458.75 913.53 460.34C913.39 464.09 913.25 467.85 913.11 471.61C913.01 475.69 912.9 479.76 912.8 483.84C905.03 614.78 836.48 739.86 721.87 806.58C713.75 811.31 705.22 814.88 696.81 818.82C681.84 825.84 666.92 832.57 651.26 838.16C619.75 849.42 586.6 854.7 553.54 858.44C552.2 858.57 550.85 858.7 549.51 858.83C545.18 858.97 540.85 859.11 536.52 859.25C533.01 859.29 529.5 859.33 525.98 859.38C520.6 859.19 515.22 859 509.83 858.81C507.65 858.71 505.47 858.6 503.29 858.49C501.39 858.34 499.48 858.18 497.58 858.02C496.24 857.91 494.9 857.8 493.57 857.69C461.02 854.33 429.45 847.25 399.28 834.59C396.56 833.45 393.57 832.92 390.9 831.7C389.25 830.95 388.09 829.59 386.42 828.83C375.89 824.08 364.9 820.07 354.95 814.1C348.49 810.22 340.67 802.9 334.06 800.22C327.67 797.62 320.09 797.29 313.41 795.58C302.7 792.84 291.97 788.87 281.95 784.16C218.81 754.5 165.31 704.82 137.47 640.13C125.41 612.09 117.59 582.91 112.22 552.9C105.08 513.03 104.34 471.16 86.98 433.78C80.1 418.98 71.48 405.92 59 395.26C43.31 381.86 27.91 375.15 13.87 358.51C8.99 352.73 4.81 346.43 2.3 339.24C1.43 336.74 1.16 333.94 0.14 331.58C0.14 326.94 0.14 322.31 0.14 317.67C1.35 314.89 1.52 311.38 2.36 308.37C4.36 301.27 6.96 294.29 10.08 287.6C22.74 260.44 38.53 234.64 55.66 210.06C139.44 89.85 287.99 10.93 433.59 2.96C435.46 2.86 437.32 2.77 439.19 2.68C470.99 -0.23 503.07 3.72 534.54 7.84C556.37 10.7 577.66 15.99 598.74 22.39C618.17 28.3 637.25 34.9 655.82 43.23C745.71 83.53 819.5 157.02 863.49 244.8C879.78 277.29 893.81 312.84 901.66 348.38C907 372.56 910.26 396.87 912.82 421.43ZM493.57 182.93C494.93 182.82 496.3 182.72 497.66 182.61C505.74 182.61 513.82 182.61 521.9 182.61C524.07 182.72 526.24 182.82 528.41 182.92C561.81 185.44 594.93 192.49 625.58 206.35C635.18 210.69 644.79 215.62 653.81 221.07C658.57 223.94 663.03 228.33 668.22 230.26C673.63 226.68 683.25 209.59 686.8 203.36C703.34 174.33 713.07 141.46 709.77 107.79C709.63 106.48 709.5 105.18 709.37 103.87C709.14 100.94 709.21 96.23 707.38 93.76C704.78 90.25 698.39 87.71 694.72 85.35C685.43 79.4 676.09 73.48 666.3 68.38C624.09 46.36 577.59 31.74 530.61 24.81C522.06 23.54 513.55 22.01 504.89 21.63C503.54 21.51 502.2 21.38 500.86 21.25C493.3 20.83 485.75 20.42 478.2 20C476.56 19.89 474.93 19.78 473.3 19.68C450.82 17.58 427.93 20.22 405.63 22.9C371.88 26.95 341.56 33.48 309.14 43.67C296.56 47.62 283.89 52.16 271.81 57.45C268.64 58.83 261.75 60.48 260.51 63.62C264.92 66.44 271.7 66.57 276.75 68.08C286.8 71.09 296.71 75.83 305.56 81.45C336.54 101.12 361.81 126.41 378.02 159.73C382.31 168.53 386.57 177.57 389.71 186.86C391.66 192.64 392.43 199.06 394.6 204.64C398.04 204.62 401.67 202.32 404.95 201.13C413.17 198.15 421.55 195.3 430 193.03C450.85 187.43 472.07 184.53 493.57 182.93ZM187.04 542.19C186.91 540.33 186.78 538.46 186.65 536.59C186.84 532.9 186.2 529.26 186.27 525.58C186.54 511.2 187.22 496.43 189.24 482.19C199.47 410.09 228.1 344.53 276.74 290.11C281.56 284.72 286.16 278.95 291.42 273.99C309.99 256.47 329.78 240.3 351.93 227.51C359.31 223.25 366.61 218.8 374.29 215.08C378.39 213.1 384.07 211.94 386.99 208.33C387.19 198.94 377.68 178.18 373.32 169.26C355.91 133.67 330.18 103.03 294.94 83.94C284.52 78.3 273.61 73.83 262.29 70.33C258.28 69.09 252.68 66.98 248.55 68.23C244.02 69.6 239.34 72.89 235.11 75.04C221.9 81.75 209.46 89.66 197.15 97.86C189.2 103.16 180.88 107.88 173.15 113.55C122.61 150.58 78.65 197.8 47.79 252.38C40.47 265.32 33.02 277.98 27.12 291.68C22.72 301.89 16.43 318.01 18.24 329.1C21.06 346.46 41.16 360.45 54.63 369.63C72.08 381.53 88.02 396.33 98.55 414.87C122.95 457.88 122.14 510.27 131.72 557.48C135.8 577.54 139.77 597.71 146.98 616.95C164.32 663.26 195.1 705.41 234.93 734.76C247.38 743.94 259.84 752.27 273.72 759.2C277.82 761.24 284.79 766.61 289.13 765.47C287.16 761.37 278.25 754.14 274.66 750.17C263.65 737.97 252.77 725.18 243.61 711.49C207.97 658.28 192.06 605.7 187.04 542.19ZM718.15 117.52C718.18 120.49 718.2 123.46 718.22 126.43C717.46 136.86 716.88 147.28 714.61 157.54C710.82 174.7 702.85 191 694.8 206.49C691.82 212.22 688.37 217.72 684.34 222.76C681.34 226.51 676.68 230.85 675.33 235.44C680.4 240.92 687.28 245.04 692.79 250.16C704.7 261.25 715.61 273.36 725.75 286.09C749.66 316.1 767.03 353.54 774.82 391.04C777.72 404.95 778.9 418.75 780.67 432.78C780.72 438.18 780.77 443.59 780.83 448.99C780.62 451.46 780.42 453.92 780.22 456.39C780.1 458.52 779.98 460.64 779.87 462.77C779.99 464.5 779.95 464.53 781.34 465.43C784.02 465.54 786.7 465.66 789.38 465.77C801.15 466.66 813.28 466.98 825.04 465.79C826.96 465.68 828.88 465.57 830.79 465.47C850.7 465.45 874.53 460.05 892.99 452.64C895.33 445.14 893.36 435.28 893.03 427.51C892.24 408.85 890.32 389.91 886.51 371.61C875.06 316.67 855.45 263.3 825.31 215.75C812.61 195.72 799.1 175.14 782.34 158.19C767.47 143.15 752.77 128.48 736.46 114.95C732.34 111.54 721.34 101.22 716.84 100.67C717.28 106.29 717.72 111.9 718.15 117.52ZM514.64 364.49C519.5 364.49 524.36 364.48 529.22 364.48C563.64 368.65 596.33 384.31 611.49 417.37C616.99 429.35 619.78 441.71 621.78 454.66C621.9 456.05 622.03 457.44 622.16 458.83C622.17 460.94 622.18 463.05 622.18 465.17C622.04 466.8 621.9 468.43 621.76 470.06C618.66 503.22 595.23 538.24 560.43 543.86C552.87 545.08 544.33 545.22 536.87 543.15C509.15 535.43 492.16 506.74 509.02 480.29C512.66 474.57 519.84 467.6 527.17 471.71C528.62 472.52 530.07 473.94 530.56 475.58C531.59 479.06 528.57 481.57 526.76 484.19C523.83 488.43 521.98 492.45 521.68 497.65C521.68 499.83 521.67 502.01 521.67 504.19C523.6 527.88 550.02 530.06 567.56 522.56C587.94 513.84 600.08 489.72 601.61 468.48C601.62 464.39 601.62 460.29 601.62 456.2C601.57 450.19 600.01 444.32 598.49 438.54C594.42 423.05 585.53 407.69 572.6 397.9C550.9 381.46 518.84 377.57 493.2 385.06C450.3 397.59 419.32 435.92 407.95 477.96C403.48 494.49 401.61 512.55 402.5 529.63C403.14 541.91 406.11 554.6 409.85 566.28C432.39 636.77 500.5 676.66 572.99 670.86C574.32 670.73 575.66 670.61 576.99 670.48C585.6 670.28 594.5 667.86 602.79 665.69C619.35 661.34 634.41 655.75 649.35 647.3C718.36 608.26 757.1 538.55 759.19 459.93C759.4 452.24 760.69 444.53 759.98 436.83C759.86 433.88 759.75 430.92 759.64 427.96C759.49 425.25 759.34 422.53 759.2 419.81C759.08 418.45 758.96 417.09 758.84 415.73C757.72 395.27 751.43 375.82 744.23 356.82C719.23 290.85 664.95 240.51 599.81 215.16C592.66 212.38 585.11 210.77 577.73 208.75C562.15 204.48 545.42 201.03 529.18 200.74C527.3 200.61 525.43 200.47 523.55 200.34C508.74 198.48 493.06 200 478.27 201.41C432.35 205.78 387.73 222.74 349.49 248.57C337.91 256.39 326.64 265.14 316.44 274.71C313.21 277.74 308.64 280.6 306.1 284.06C309.51 288.45 323.4 292.81 329.01 296.26C346.82 307.23 364.64 318.41 380.18 332.61C395 346.15 408.61 361.07 421.19 376.68C424.81 381.17 427.82 386.38 430.99 391.21C432.51 393.54 433.34 396.53 435.62 397.97C438.8 396.11 441.43 392.85 444.37 390.49C448.2 387.42 452.75 384.46 457.07 382.13C476.8 371.52 492.29 366.73 514.64 364.49ZM206.07 543.81C206.18 545.13 206.3 546.45 206.42 547.77C206.56 549.42 206.7 551.08 206.84 552.73C206.97 554.09 207.1 555.46 207.22 556.82C208.75 587.42 217.32 618.74 229.86 646.59C233.67 655.06 237.48 668.35 244.3 674.7C248.5 674.4 253.61 670.6 257.46 668.8C267.12 664.28 277.07 660.52 286.69 655.88C304.27 647.41 321.86 638.41 338.62 628.38C346.33 623.76 353.75 618.59 361.45 613.92C373.55 606.58 386.27 599.78 397.53 591.17C396.7 584.09 392.15 576.86 390.09 569.96C386.6 558.27 385.08 546.09 383.12 534.09C383.01 532.7 382.91 531.32 382.8 529.93C382.8 525.39 382.8 520.85 382.8 516.31C382.92 514.13 383.03 511.95 383.15 509.77C383.3 507.85 383.44 505.92 383.59 503.99C383.72 502.68 383.86 501.36 383.99 500.05C387.62 476.41 393.83 452.71 406.6 432.14C410.26 426.24 414.34 420.51 418.96 415.31C422.09 411.79 426.6 408.12 428.75 404.01C423.15 392.59 414.55 382.49 406.58 372.65C386.28 347.58 361.41 325.17 333.7 308.56C325.97 303.92 307.5 291.43 299.46 290.31C293.08 294.95 288.07 302.54 283.01 308.55C272.5 321.03 262.6 333.83 254.46 348.01C227.76 394.48 212.55 439.71 207.31 493.16C205.65 510.12 204.84 526.78 206.07 543.81ZM816.94 474.28C810.72 474.33 804.51 474.38 798.29 474.42C794.01 474.23 789.72 474.04 785.44 473.85C783.65 473.78 781.85 473.71 780.06 473.65C779.98 473.8 779.9 473.96 779.82 474.11C778.27 480.1 778.41 486.95 777.39 493.16C775.21 506.55 771.82 519.48 767.88 532.43C764.89 542.25 760.29 551.72 755.84 560.94C739.05 595.71 716.36 625.15 684.87 647.99C672.14 657.21 658.79 665.87 644.45 672.36C640.55 674.12 636.43 675.95 632.35 677.25C629.55 678.14 626.58 678.11 624.02 679.56C624.13 683.59 626.94 686.68 628.68 690.3C631.87 696.92 634.91 703.67 637.71 710.45C646.43 731.52 653.6 753.15 658.57 775.43C660.45 783.85 661.15 792.48 662.45 800.99C662.98 804.48 662.8 808.75 664.98 811.45C672.92 809.52 679.75 804.41 687.05 800.92C704.15 792.76 720.13 783.37 735.63 772.49C806.65 722.63 859.72 644.07 880.43 559.79C886.19 536.35 891.08 512.51 891.99 488.3C892.24 481.78 894.47 467.13 892.62 461.55C888.62 461.6 884.42 464.05 880.53 465.2C875.2 466.79 869.75 467.99 864.32 469.2C848.64 472.68 832.81 472.65 816.94 474.28ZM517.98 839.83C521.68 839.84 525.39 839.86 529.1 839.87C570.52 840.69 615.92 830.57 654.44 815.48C656.78 805.45 652.44 794.04 650.83 784.11C646.7 758.8 638.19 733.8 628.28 710.2C625.69 704.03 622.06 698.44 619.35 692.36C617.98 689.3 617.09 685.12 614.73 682.76C605.25 683.65 595.84 686.76 586.36 688.03C574.03 689.68 561.94 689.08 549.6 689.43C547.67 689.29 545.74 689.15 543.81 689.01C501.53 683.39 462.28 669.63 431.63 638.56C422.63 629.44 415.77 619.45 408.57 608.93C406.16 605.41 404.3 601.27 401.58 598.04C394.71 599.37 378.41 613 371.47 617.46C347.08 633.15 322.05 647.46 296.12 660.41C286.68 665.13 276.85 669.6 267.03 673.45C261.1 675.77 253.05 677.55 248.4 682.08C249.64 688.81 255.93 696.23 259.72 701.88C270.21 717.54 282.56 731.83 295.93 745.1C345.45 794.22 410.48 826.76 479.4 837C492.01 838.87 505.24 839.34 517.98 839.83Z" fill="#1a1a1a" fill-rule="evenodd" stroke="#1a1a1a" stroke-width="60" stroke-linejoin="round"/>
|
|
5
5
|
<path d="M439.19 2.68C437.32 2.77 435.46 2.86 433.59 2.96C433.73 2.78 433.86 2.61 434 2.43C435.73 2.51 437.46 2.6 439.19 2.68ZM473.3 19.68C474.93 19.78 476.56 19.89 478.2 20C476.7 20.09 475.2 20.17 473.71 20.26C473.57 20.07 473.44 19.87 473.3 19.68ZM500.86 21.25C502.2 21.38 503.54 21.51 504.89 21.63C503.68 21.72 502.47 21.8 501.26 21.88C501.13 21.67 500.99 21.46 500.86 21.25ZM709.37 103.87C709.5 105.18 709.63 106.48 709.77 107.79C709.56 107.66 709.35 107.52 709.15 107.38C709.22 106.21 709.29 105.04 709.37 103.87ZM718.15 117.52C718.39 117.65 718.63 117.79 718.87 117.92C718.87 120.62 718.87 123.32 718.87 126.03C718.65 126.16 718.44 126.3 718.22 126.43C718.2 123.46 718.18 120.49 718.15 117.52ZM497.66 182.61C496.3 182.72 494.93 182.82 493.57 182.93C493.7 182.73 493.84 182.54 493.97 182.35C495.2 182.44 496.43 182.53 497.66 182.61ZM521.9 182.61C523.94 182.53 525.97 182.44 528.01 182.35C528.14 182.54 528.28 182.73 528.41 182.92C526.24 182.82 524.07 182.72 521.9 182.61ZM523.55 200.34C525.43 200.47 527.3 200.61 529.18 200.74C527.44 200.82 525.7 200.91 523.96 200.99C523.82 200.77 523.69 200.56 523.55 200.34ZM529.22 364.48C524.36 364.48 519.5 364.49 514.64 364.49C514.77 364.29 514.91 364.09 515.04 363.89C519.63 363.89 524.23 363.89 528.82 363.89C528.95 364.09 529.09 364.28 529.22 364.48ZM758.84 415.73C758.96 417.09 759.08 418.45 759.2 419.81C758.99 419.68 758.79 419.54 758.58 419.41C758.67 418.18 758.75 416.96 758.84 415.73ZM912.82 421.43C913.01 421.57 913.19 421.71 913.38 421.84C913.3 423.31 913.21 424.77 913.13 426.24C913.03 424.64 912.92 423.04 912.82 421.43ZM759.64 427.96C759.75 430.92 759.86 433.88 759.98 436.83C759.78 436.7 759.59 436.56 759.39 436.43C759.47 433.61 759.56 430.78 759.64 427.96ZM780.67 432.78C780.72 438.18 780.77 443.59 780.83 448.99C780.77 443.59 780.72 438.18 780.67 432.78ZM621.78 454.66C621.99 454.8 622.21 454.93 622.43 455.07C622.34 456.32 622.25 457.57 622.16 458.83C622.03 457.44 621.9 456.05 621.78 454.66ZM913.96 455.57C914.04 457.03 914.11 458.48 914.19 459.93C913.97 460.07 913.75 460.2 913.53 460.34C913.67 458.75 913.82 457.16 913.96 455.57ZM601.62 456.2C601.62 460.29 601.62 464.39 601.61 468.48C601.62 464.39 601.62 460.29 601.62 456.2ZM780.22 456.39C780.3 458.38 780.38 460.37 780.47 462.36C780.27 462.5 780.07 462.63 779.87 462.77C779.98 460.64 780.1 458.52 780.22 456.39ZM622.18 465.17C622.26 466.66 622.35 468.16 622.43 469.66C622.2 469.79 621.98 469.93 621.76 470.06C621.9 468.43 622.04 466.8 622.18 465.17ZM781.34 465.43C783.89 465.35 786.43 465.28 788.97 465.2C789.11 465.39 789.24 465.58 789.38 465.77C786.7 465.66 784.02 465.54 781.34 465.43ZM830.79 465.47C828.88 465.57 826.96 465.68 825.04 465.79C825.17 465.59 825.31 465.4 825.45 465.2C827.23 465.29 829.01 465.38 830.79 465.47ZM913.11 471.61C913.2 475.55 913.29 479.49 913.38 483.43C913.19 483.57 912.99 483.7 912.8 483.84C912.9 479.76 913.01 475.69 913.11 471.61ZM785.44 473.85C783.57 473.94 781.69 474.02 779.82 474.11C779.9 473.96 779.98 473.8 780.06 473.65C781.85 473.71 783.65 473.78 785.44 473.85ZM798.29 474.42C804.51 474.38 810.72 474.33 816.94 474.28C814.3 475.59 801.18 475.53 798.29 474.42ZM521.68 497.65C521.68 499.83 521.67 502.01 521.67 504.19C521.67 502.01 521.68 499.83 521.68 497.65ZM383.99 500.05C383.86 501.36 383.72 502.68 383.59 503.99C383.5 502.81 383.42 501.63 383.34 500.45C383.56 500.32 383.78 500.18 383.99 500.05ZM383.15 509.77C383.03 511.95 382.92 514.13 382.8 516.31C382.71 514.27 382.62 512.22 382.53 510.18C382.74 510.04 382.95 509.91 383.15 509.77ZM382.8 529.93C382.91 531.32 383.01 532.7 383.12 534.09C382.92 533.95 382.73 533.82 382.53 533.68C382.62 532.43 382.71 531.18 382.8 529.93ZM186.65 536.59C186.78 538.46 186.91 540.33 187.04 542.19C186.82 542.06 186.61 541.92 186.4 541.79C186.49 540.06 186.57 538.33 186.65 536.59ZM206.07 543.81C206.27 543.95 206.47 544.08 206.67 544.22C206.58 545.4 206.5 546.59 206.42 547.77C206.3 546.45 206.18 545.13 206.07 543.81ZM206.84 552.73C207.05 552.86 207.26 553 207.48 553.13C207.39 554.36 207.31 555.59 207.22 556.82C207.1 555.46 206.97 554.09 206.84 552.73ZM576.99 670.48C575.66 670.61 574.32 670.73 572.99 670.86C573.12 670.66 573.26 670.45 573.39 670.24C574.59 670.32 575.79 670.4 576.99 670.48ZM543.81 689.01C545.74 689.15 547.67 689.29 549.6 689.43C547.81 689.52 546.01 689.6 544.22 689.69C544.08 689.47 543.95 689.24 543.81 689.01ZM517.98 839.83C521.68 839.84 525.39 839.86 529.1 839.87C525.39 839.86 521.68 839.84 517.98 839.83ZM493.57 857.69C494.9 857.8 496.24 857.91 497.58 858.02C496.37 858.1 495.17 858.19 493.97 858.27C493.84 858.08 493.7 857.88 493.57 857.69ZM503.29 858.49C505.47 858.6 507.65 858.71 509.83 858.81C507.79 858.9 505.74 858.99 503.7 859.08C503.56 858.88 503.43 858.69 503.29 858.49ZM553.54 858.44C553.4 858.66 553.27 858.87 553.13 859.08C551.93 858.99 550.72 858.91 549.51 858.83C550.85 858.7 552.2 858.57 553.54 858.44ZM525.98 859.38C529.5 859.33 533.01 859.29 536.52 859.25C536.38 859.46 536.25 859.67 536.11 859.89C532.87 859.89 529.63 859.89 526.39 859.89C526.25 859.72 526.12 859.55 525.98 859.38Z" fill="#1a1a1a" fill-rule="evenodd" stroke="#1a1a1a" stroke-width="60" stroke-linejoin="round"/>
|
|
6
6
|
</g>
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
|
|
18
18
|
import { cn } from '@/lib/utils'
|
|
19
|
-
import { ScrollText, AlertTriangle, ArrowLeft, ChevronDown } from 'lucide-react'
|
|
19
|
+
import { ScrollText, AlertTriangle, ArrowLeft, ChevronDown, Cpu } from 'lucide-react'
|
|
20
20
|
import ReactMarkdown from 'react-markdown'
|
|
21
21
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|
22
22
|
import { Route } from '@/routes/_nav/changelog/route'
|
|
@@ -426,11 +426,30 @@ function SdkGroupSection({
|
|
|
426
426
|
/>
|
|
427
427
|
</span>
|
|
428
428
|
) : (
|
|
429
|
-
|
|
429
|
+
// 其他核心组:lucide Cpu 图标(size-4 对齐前两组 h-4 图形字标,
|
|
430
|
+
// 用户裁决弃用 emoji)
|
|
431
|
+
<Cpu className="size-4 mt-0.5 shrink-0 text-foreground/80" strokeWidth={3} />
|
|
430
432
|
)}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
433
|
+
{/* 纯文本分组标题(其他核心)拆两段:「其他核心/Other Core」承当字标
|
|
434
|
+
角色用 text-base,SDK 尾缀与其余两组(图形字标 + SDK)保持同号
|
|
435
|
+
text-sm;z/en 文案均以 " SDK" 结尾,正则拆分双语言通吃 */}
|
|
436
|
+
{(() => {
|
|
437
|
+
const label = SDK_GROUP_LABELS[group.label]?.() ?? group.label
|
|
438
|
+
const m = label.match(/^(.*\S)\s+(SDK)$/)
|
|
439
|
+
if (!m) {
|
|
440
|
+
return (
|
|
441
|
+
<span className="font-brand text-sm mt-0.5 font-semibold text-foreground/80">
|
|
442
|
+
{label}
|
|
443
|
+
</span>
|
|
444
|
+
)
|
|
445
|
+
}
|
|
446
|
+
return (
|
|
447
|
+
<span className="mt-0.5 flex items-baseline gap-1.5 font-semibold text-foreground/80">
|
|
448
|
+
<span className="font-brand text-lg">{m[1]}</span>
|
|
449
|
+
<span className="font-brand text-sm">{m[2]}</span>
|
|
450
|
+
</span>
|
|
451
|
+
)
|
|
452
|
+
})()}
|
|
434
453
|
</span>
|
|
435
454
|
{isAccordion && (
|
|
436
455
|
<ChevronDown
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// AllBlue - 理想之海
|
|
3
|
+
// Copyright (C) 2026 AllBlue Contributors
|
|
4
|
+
|
|
5
|
+
// 桌面云端登录桥(迁移设计 §3.6):表单调用 desktopBridge.cloudLogin
|
|
6
|
+
// (main 侧 desktopLogin 走云端 credentials),成功后引擎自动拾取会话
|
|
7
|
+
// (makeFetchApi 每次同步重读 cloud-session.json)。
|
|
8
|
+
// 独立于 web 登录页——web 表单走本地 serverFn authjs,二者不混用。
|
|
9
|
+
import { useEffect, useState } from "react"
|
|
10
|
+
import { getDesktopBridge } from "@/lib/desktop-bridge-types"
|
|
11
|
+
import { Button } from "@/components/ui/button"
|
|
12
|
+
import { Input } from "@/components/ui/input"
|
|
13
|
+
import { Label } from "@/components/ui/label"
|
|
14
|
+
|
|
15
|
+
export function DesktopCloudLogin() {
|
|
16
|
+
const [email, setEmail] = useState("")
|
|
17
|
+
const [password, setPassword] = useState("")
|
|
18
|
+
const [busy, setBusy] = useState(false)
|
|
19
|
+
const [error, setError] = useState<string | null>(null)
|
|
20
|
+
const [ok, setOk] = useState(false)
|
|
21
|
+
const [mounted, setMounted] = useState(false)
|
|
22
|
+
|
|
23
|
+
// 环境自判(与 sync-indicator/conflict-center 同构,mounted 模式保证 SSR/客户端
|
|
24
|
+
// 首帧一致):仅桌面壳(存在 desktopBridge)时渲染。web/云端部署下本组件由
|
|
25
|
+
// shell-widgets 无条件挂载,缺此守卫会让登录表单以 fixed 容器悬浮在浏览器
|
|
26
|
+
// 每个页面右下角遮挡内容区交互(2026-10-30 实锤)。
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
setMounted(true)
|
|
29
|
+
}, [])
|
|
30
|
+
|
|
31
|
+
const submit = async () => {
|
|
32
|
+
const bridge = getDesktopBridge()
|
|
33
|
+
if (!bridge || !email || !password || busy) return
|
|
34
|
+
setBusy(true)
|
|
35
|
+
setError(null)
|
|
36
|
+
try {
|
|
37
|
+
const res = await bridge.cloudLogin(email, password)
|
|
38
|
+
if (res.ok) {
|
|
39
|
+
setOk(true)
|
|
40
|
+
} else {
|
|
41
|
+
setError(res.error ?? "登录失败")
|
|
42
|
+
}
|
|
43
|
+
} catch (e) {
|
|
44
|
+
setError(e instanceof Error ? e.message : String(e))
|
|
45
|
+
} finally {
|
|
46
|
+
setBusy(false)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!mounted || !getDesktopBridge()) return null
|
|
51
|
+
|
|
52
|
+
if (ok) {
|
|
53
|
+
return (
|
|
54
|
+
<p className="text-xs text-emerald-600" data-testid="desktop-cloud-login-ok">
|
|
55
|
+
云端登录成功,同步已启用
|
|
56
|
+
</p>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<div className="space-y-2" data-testid="desktop-cloud-login">
|
|
62
|
+
<p className="text-xs text-muted-foreground">登录云端账号以启用多端同步</p>
|
|
63
|
+
<div className="space-y-1.5">
|
|
64
|
+
<Label htmlFor="desktop-cloud-email" className="text-xs">邮箱</Label>
|
|
65
|
+
<Input
|
|
66
|
+
id="desktop-cloud-email"
|
|
67
|
+
type="email"
|
|
68
|
+
value={email}
|
|
69
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
70
|
+
placeholder="you@example.com"
|
|
71
|
+
className="h-8 text-xs"
|
|
72
|
+
/>
|
|
73
|
+
</div>
|
|
74
|
+
<div className="space-y-1.5">
|
|
75
|
+
<Label htmlFor="desktop-cloud-password" className="text-xs">密码</Label>
|
|
76
|
+
<Input
|
|
77
|
+
id="desktop-cloud-password"
|
|
78
|
+
type="password"
|
|
79
|
+
value={password}
|
|
80
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
81
|
+
className="h-8 text-xs"
|
|
82
|
+
onKeyDown={(e) => e.key === "Enter" && void submit()}
|
|
83
|
+
/>
|
|
84
|
+
</div>
|
|
85
|
+
{error && <p className="text-xs text-destructive">{error}</p>}
|
|
86
|
+
<Button size="sm" className="w-full" disabled={busy || !email || !password} onClick={() => void submit()}>
|
|
87
|
+
{busy ? "登录中…" : "登录云端"}
|
|
88
|
+
</Button>
|
|
89
|
+
</div>
|
|
90
|
+
)
|
|
91
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// AllBlue - 理想之海
|
|
3
|
+
// Copyright (C) 2026 AllBlue Contributors
|
|
4
|
+
|
|
5
|
+
// 桌面冲突中心(迁移设计 §3.5):列出未决冲突(双方载荷),三选操作
|
|
6
|
+
// (采用我的 / 保留云端 / 都保留副本)。数据源 desktopBridge.conflictRpc。
|
|
7
|
+
// 仅 DEPLOY_TARGET=desktop 挂载;云端部署零成本。
|
|
8
|
+
import { useCallback, useEffect, useState } from "react"
|
|
9
|
+
import { getDesktopBridge } from "@/lib/desktop-bridge-types"
|
|
10
|
+
import { Button } from "@/components/ui/button"
|
|
11
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
|
12
|
+
import { Badge } from "@/components/ui/badge"
|
|
13
|
+
|
|
14
|
+
interface ConflictItem {
|
|
15
|
+
id: string
|
|
16
|
+
table: string
|
|
17
|
+
pk: string
|
|
18
|
+
reason: string
|
|
19
|
+
localPayload: Record<string, unknown> | null
|
|
20
|
+
remotePayload: Record<string, unknown> | null
|
|
21
|
+
detectedAt: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const REASON_LABELS: Record<string, string> = {
|
|
25
|
+
versionDiverged: "双方均已修改",
|
|
26
|
+
localDirtyGuard: "本地有未同步修改",
|
|
27
|
+
uniqueViolation: "编号冲突",
|
|
28
|
+
forbidden: "权限不足",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function payloadTitle(p: Record<string, unknown> | null): string {
|
|
32
|
+
if (!p) return "(无)"
|
|
33
|
+
return String(p.name ?? p.title ?? p.identifier ?? p.pk ?? "未知")
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function DesktopConflictCenter() {
|
|
37
|
+
const [items, setItems] = useState<ConflictItem[]>([])
|
|
38
|
+
const [busyId, setBusyId] = useState<string | null>(null)
|
|
39
|
+
const [error, setError] = useState<string | null>(null)
|
|
40
|
+
const [loaded, setLoaded] = useState(false)
|
|
41
|
+
|
|
42
|
+
const refresh = useCallback(async () => {
|
|
43
|
+
const bridge = getDesktopBridge()
|
|
44
|
+
if (!bridge) return
|
|
45
|
+
try {
|
|
46
|
+
const list = (await bridge.conflictRpc("list")) as ConflictItem[]
|
|
47
|
+
setItems(list)
|
|
48
|
+
setLoaded(true)
|
|
49
|
+
} catch (e) {
|
|
50
|
+
setError(e instanceof Error ? e.message : String(e))
|
|
51
|
+
}
|
|
52
|
+
}, [])
|
|
53
|
+
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
void refresh()
|
|
56
|
+
}, [refresh])
|
|
57
|
+
|
|
58
|
+
const resolve = useCallback(
|
|
59
|
+
async (action: "applyLocal" | "keepRemote", item: ConflictItem) => {
|
|
60
|
+
setBusyId(item.id)
|
|
61
|
+
try {
|
|
62
|
+
await getDesktopBridge()!.conflictRpc(action, item.id, item.table)
|
|
63
|
+
await refresh()
|
|
64
|
+
} catch (e) {
|
|
65
|
+
setError(e instanceof Error ? e.message : String(e))
|
|
66
|
+
} finally {
|
|
67
|
+
setBusyId(null)
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
[refresh],
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
const keepBoth = useCallback(
|
|
74
|
+
async (item: ConflictItem) => {
|
|
75
|
+
setBusyId(item.id)
|
|
76
|
+
try {
|
|
77
|
+
const newPk = `${item.pk}-copy-${Date.now()}`
|
|
78
|
+
await getDesktopBridge()!.conflictRpc("keepBoth", item.id, item.table, newPk)
|
|
79
|
+
await refresh()
|
|
80
|
+
} catch (e) {
|
|
81
|
+
setError(e instanceof Error ? e.message : String(e))
|
|
82
|
+
} finally {
|
|
83
|
+
setBusyId(null)
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
[refresh],
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
if (!loaded) return null
|
|
90
|
+
if (items.length === 0) return null
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<div className="space-y-3 p-4" data-testid="desktop-conflict-center">
|
|
94
|
+
<div className="flex items-center gap-2">
|
|
95
|
+
<h3 className="text-sm font-semibold">同步冲突</h3>
|
|
96
|
+
<Badge variant="destructive">{items.length}</Badge>
|
|
97
|
+
</div>
|
|
98
|
+
{error && <p className="text-xs text-destructive">{error}</p>}
|
|
99
|
+
{items.map((item) => (
|
|
100
|
+
<Card key={item.id}>
|
|
101
|
+
<CardHeader className="p-3 pb-0">
|
|
102
|
+
<CardTitle className="text-xs font-medium text-muted-foreground">
|
|
103
|
+
{REASON_LABELS[item.reason] ?? item.reason} · {item.table} · {item.pk}
|
|
104
|
+
</CardTitle>
|
|
105
|
+
</CardHeader>
|
|
106
|
+
<CardContent className="space-y-2 p-3 pt-2">
|
|
107
|
+
<div className="grid grid-cols-2 gap-2 text-xs">
|
|
108
|
+
<div className="rounded border p-2">
|
|
109
|
+
<p className="mb-1 font-medium">本地</p>
|
|
110
|
+
<p className="text-muted-foreground">{payloadTitle(item.localPayload)}</p>
|
|
111
|
+
</div>
|
|
112
|
+
<div className="rounded border p-2">
|
|
113
|
+
<p className="mb-1 font-medium">云端</p>
|
|
114
|
+
<p className="text-muted-foreground">{payloadTitle(item.remotePayload)}</p>
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
<div className="flex gap-2">
|
|
118
|
+
<Button
|
|
119
|
+
size="sm"
|
|
120
|
+
variant="outline"
|
|
121
|
+
disabled={busyId === item.id}
|
|
122
|
+
onClick={() => void resolve("applyLocal", item)}
|
|
123
|
+
>
|
|
124
|
+
采用我的
|
|
125
|
+
</Button>
|
|
126
|
+
<Button
|
|
127
|
+
size="sm"
|
|
128
|
+
variant="outline"
|
|
129
|
+
disabled={busyId === item.id}
|
|
130
|
+
onClick={() => void resolve("keepRemote", item)}
|
|
131
|
+
>
|
|
132
|
+
保留云端
|
|
133
|
+
</Button>
|
|
134
|
+
<Button
|
|
135
|
+
size="sm"
|
|
136
|
+
variant="outline"
|
|
137
|
+
disabled={busyId === item.id}
|
|
138
|
+
onClick={() => void keepBoth(item)}
|
|
139
|
+
>
|
|
140
|
+
都保留
|
|
141
|
+
</Button>
|
|
142
|
+
</div>
|
|
143
|
+
</CardContent>
|
|
144
|
+
</Card>
|
|
145
|
+
))}
|
|
146
|
+
</div>
|
|
147
|
+
)
|
|
148
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// AllBlue - 理想之海
|
|
3
|
+
// Copyright (C) 2026 AllBlue Contributors
|
|
4
|
+
|
|
5
|
+
// 编号重发号提示(迁移设计 §3.5):订阅 desktopBridge.onRenamed,服务端
|
|
6
|
+
// 因离线撞号重发号后提示「编号已调整为 X」。数据层回写已由引擎完成
|
|
7
|
+
// (sync-core 0.5.3+),此处仅补用户感知。
|
|
8
|
+
import { useEffect } from "react"
|
|
9
|
+
import { toast } from "@/components/ui/toast"
|
|
10
|
+
import { getDesktopBridge } from "@/lib/desktop-bridge-types"
|
|
11
|
+
|
|
12
|
+
export function DesktopRenamedToast() {
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
const bridge = getDesktopBridge()
|
|
15
|
+
if (!bridge?.onRenamed) return
|
|
16
|
+
return bridge.onRenamed((info) => {
|
|
17
|
+
const label = info.table === "bugs" ? "BUG" : "需求"
|
|
18
|
+
toast.info(`${label}编号已调整:${info.from} → ${info.to}`)
|
|
19
|
+
})
|
|
20
|
+
}, [])
|
|
21
|
+
|
|
22
|
+
return null
|
|
23
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// AllBlue - 理想之海
|
|
3
|
+
// Copyright (C) 2026 AllBlue Contributors
|
|
4
|
+
|
|
5
|
+
// 桌面壳挂载组件(迁移设计 §3.7):DEPLOY_TARGET=desktop 时挂载同步状态
|
|
6
|
+
// 指示器 + 冲突中心。云端部署零成本(组件内部自判返回 null)。
|
|
7
|
+
// 云登录入口在侧边栏用户菜单(isLocalGuest → /login)与 /settings/sync
|
|
8
|
+
// 设置页卡片,不在壳层悬浮表单(2026-10-31 用户裁决移除右下角表单)。
|
|
9
|
+
import { DesktopSyncIndicator } from "@/components/desktop/sync-indicator"
|
|
10
|
+
import { DesktopConflictCenter } from "@/components/desktop/conflict-center"
|
|
11
|
+
|
|
12
|
+
export function DesktopShellWidgets() {
|
|
13
|
+
return (
|
|
14
|
+
<div className="fixed right-4 bottom-4 z-50 w-80 space-y-3">
|
|
15
|
+
<DesktopSyncIndicator />
|
|
16
|
+
<DesktopConflictCenter />
|
|
17
|
+
</div>
|
|
18
|
+
)
|
|
19
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// AllBlue - 理想之海
|
|
3
|
+
// Copyright (C) 2026 AllBlue Contributors
|
|
4
|
+
|
|
5
|
+
// 桌面同步状态指示器(迁移设计 §3.7 最小 UI):顶栏小圆点 + 同步状态。
|
|
6
|
+
// 仅 DEPLOY_TARGET=desktop 渲染;云端部署零成本(组件返回 null)。
|
|
7
|
+
// 数据源:desktopBridge.onSyncState(backend parentPort 上报的引擎状态机)。
|
|
8
|
+
import { useEffect, useState } from "react"
|
|
9
|
+
import { getDesktopBridge } from "@/lib/desktop-bridge-types"
|
|
10
|
+
|
|
11
|
+
interface SyncState {
|
|
12
|
+
phase: "idle" | "pushing" | "pulling"
|
|
13
|
+
pending: number
|
|
14
|
+
lastError: string | null
|
|
15
|
+
lastSyncedAt: string | null
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const LABELS: Record<SyncState["phase"], string> = {
|
|
19
|
+
idle: "已同步",
|
|
20
|
+
pushing: "推送中",
|
|
21
|
+
pulling: "拉取中",
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function DesktopSyncIndicator() {
|
|
25
|
+
const [state, setState] = useState<SyncState | null>(null)
|
|
26
|
+
const [mounted, setMounted] = useState(false)
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
setMounted(true)
|
|
30
|
+
const bridge = getDesktopBridge()
|
|
31
|
+
if (!bridge) return
|
|
32
|
+
const off = bridge.onSyncState((s) => setState(s as SyncState))
|
|
33
|
+
return off
|
|
34
|
+
}, [])
|
|
35
|
+
|
|
36
|
+
if (!mounted || !getDesktopBridge()) return null
|
|
37
|
+
if (!state) return null
|
|
38
|
+
|
|
39
|
+
const hasError = state.lastError !== null
|
|
40
|
+
const busy = state.phase === "pushing" || state.phase === "pulling"
|
|
41
|
+
const dotColor = hasError ? "bg-destructive" : busy ? "bg-amber-500 animate-pulse" : "bg-emerald-500"
|
|
42
|
+
const label = hasError
|
|
43
|
+
? "同步失败"
|
|
44
|
+
: busy
|
|
45
|
+
? LABELS[state.phase]
|
|
46
|
+
: state.pending > 0
|
|
47
|
+
? `待同步 ${state.pending}`
|
|
48
|
+
: LABELS.idle
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<div
|
|
52
|
+
className="flex items-center gap-1.5 px-2 py-1 text-xs text-muted-foreground"
|
|
53
|
+
title={hasError ? `同步失败:${state.lastError}` : `上次同步 ${state.lastSyncedAt ?? "—"}`}
|
|
54
|
+
data-testid="desktop-sync-indicator"
|
|
55
|
+
>
|
|
56
|
+
<span className={`inline-block size-2 rounded-full ${dotColor}`} aria-hidden />
|
|
57
|
+
<span>{label}</span>
|
|
58
|
+
</div>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
// AllBlue - 理想之海
|
|
3
|
+
// Copyright (C) 2026 AllBlue Contributors
|
|
4
|
+
|
|
5
|
+
// 同步状态卡片(设置页内嵌版,比顶栏指示器信息更全):订阅
|
|
6
|
+
// desktopBridge.onSyncState,展示阶段/待同步数/上次同步/错误。
|
|
7
|
+
import { useEffect, useState } from "react"
|
|
8
|
+
import { getDesktopBridge } from "@/lib/desktop-bridge-types"
|
|
9
|
+
import { Badge } from "@/components/ui/badge"
|
|
10
|
+
|
|
11
|
+
interface SyncState {
|
|
12
|
+
phase: "idle" | "pushing" | "pulling"
|
|
13
|
+
pending: number
|
|
14
|
+
lastError: string | null
|
|
15
|
+
lastSyncedAt: string | null
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const PHASE_LABELS: Record<SyncState["phase"], string> = {
|
|
19
|
+
idle: "空闲",
|
|
20
|
+
pushing: "推送中",
|
|
21
|
+
pulling: "拉取中",
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function DesktopSyncStatusCard() {
|
|
25
|
+
const [state, setState] = useState<SyncState | null>(null)
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
const bridge = getDesktopBridge()
|
|
29
|
+
if (!bridge) return
|
|
30
|
+
return bridge.onSyncState((s) => setState(s as SyncState))
|
|
31
|
+
}, [])
|
|
32
|
+
|
|
33
|
+
if (!getDesktopBridge()) {
|
|
34
|
+
return <p className="text-xs text-muted-foreground">仅桌面客户端可用</p>
|
|
35
|
+
}
|
|
36
|
+
if (!state) {
|
|
37
|
+
return <p className="text-xs text-muted-foreground">等待同步状态…</p>
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<dl className="grid grid-cols-[auto_1fr] gap-x-4 gap-y-1.5 text-xs">
|
|
42
|
+
<dt className="text-muted-foreground">状态</dt>
|
|
43
|
+
<dd>
|
|
44
|
+
<Badge variant={state.lastError ? "destructive" : "secondary"}>
|
|
45
|
+
{state.lastError ? "异常" : PHASE_LABELS[state.phase]}
|
|
46
|
+
</Badge>
|
|
47
|
+
</dd>
|
|
48
|
+
<dt className="text-muted-foreground">待同步</dt>
|
|
49
|
+
<dd>{state.pending} 项</dd>
|
|
50
|
+
<dt className="text-muted-foreground">上次同步</dt>
|
|
51
|
+
<dd>{state.lastSyncedAt ?? "—"}</dd>
|
|
52
|
+
{state.lastError && (
|
|
53
|
+
<>
|
|
54
|
+
<dt className="text-muted-foreground">错误</dt>
|
|
55
|
+
<dd className="text-destructive">{state.lastError}</dd>
|
|
56
|
+
</>
|
|
57
|
+
)}
|
|
58
|
+
</dl>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
@@ -37,6 +37,7 @@ import { PresenceAvatarStack } from "@/components/layout/sidebar-03/presence-ava
|
|
|
37
37
|
import { SidebarSearchTrigger } from "@/components/layout/sidebar-03/nav-search-trigger"
|
|
38
38
|
import { NavSecondary } from "@/components/layout/sidebar-03/nav-secondary"
|
|
39
39
|
import { PluginNavItems } from '@/components/plugin-slots/plugin-nav-items'
|
|
40
|
+
import { isDesktopEdition } from "@/lib/edition-desktop"
|
|
40
41
|
|
|
41
42
|
// presence 渲染工厂(§8.2 消费者两文件之一):nav-main 按 route.id 查
|
|
42
43
|
// MODULE_ROUTES 得 module key 后回调;module 级单例 map,identity 稳定。
|
|
@@ -48,18 +49,25 @@ const routes: Route[] = workspaceRoutes.map((route) => ({
|
|
|
48
49
|
export function DashboardSidebar() {
|
|
49
50
|
const { state } = useSidebar();
|
|
50
51
|
const isCollapsed = state === "collapsed";
|
|
52
|
+
// 桌面壳(Electron hiddenInset):红绿灯悬浮在窗口左上角,顶栏重排为
|
|
53
|
+
// 「第一行红绿灯让位 + 右侧按钮组(整行可拖动窗口)、第二行 logo」。
|
|
54
|
+
// web/云端 SaaS 布局不变(isDesktopEdition 为构建期常量,死分支消除)。
|
|
55
|
+
const isDesktop = isDesktopEdition();
|
|
51
56
|
|
|
52
57
|
return (
|
|
53
58
|
<Sidebar variant="floating" collapsible="icon">
|
|
54
59
|
<SidebarHeader
|
|
55
60
|
className={cn(
|
|
56
61
|
"flex flex-col items-center justify-center gap-2 py-2",
|
|
57
|
-
!isCollapsed && "flex-row justify-between py-2 md:pt-3.5"
|
|
62
|
+
!isCollapsed && !isDesktop && "flex-row justify-between py-2 md:pt-3.5",
|
|
63
|
+
// 桌面展开态:整块 header 为拖动区(第二行 logo 左对齐),按钮逐个 no-drag
|
|
64
|
+
!isCollapsed && isDesktop && "app-region-drag w-full items-stretch justify-start gap-0 py-1",
|
|
65
|
+
isCollapsed && isDesktop && "app-region-drag pt-8",
|
|
58
66
|
)}
|
|
59
67
|
>
|
|
60
68
|
{isCollapsed ? (
|
|
61
69
|
<div className="flex flex-col items-center gap-2">
|
|
62
|
-
<div className="flex flex-col items-center gap-2">
|
|
70
|
+
<div className={cn("flex flex-col items-center gap-2", isDesktop && "app-region-no-drag")}>
|
|
63
71
|
<img
|
|
64
72
|
src="/logo/allblue-logo-dark.svg"
|
|
65
73
|
alt="AllBlue"
|
|
@@ -71,11 +79,38 @@ export function DashboardSidebar() {
|
|
|
71
79
|
className="hidden h-6 w-6 dark:block"
|
|
72
80
|
/>
|
|
73
81
|
</div>
|
|
74
|
-
<div className="flex flex-col-reverse items-center gap-1">
|
|
82
|
+
<div className={cn("flex flex-col-reverse items-center gap-1", isDesktop && "app-region-no-drag")}>
|
|
75
83
|
<NotificationsBell />
|
|
76
84
|
<SidebarTrigger />
|
|
77
85
|
</div>
|
|
78
86
|
</div>
|
|
87
|
+
) : isDesktop ? (
|
|
88
|
+
<>
|
|
89
|
+
{/* 第一行:左侧让位 macOS 红绿灯(hiddenInset + trafficLightPosition
|
|
90
|
+
y=18 → 圆心 24px,与 h-8 行中心 8+16=24 对齐),右侧按钮组;
|
|
91
|
+
relative z-50 压过 _nav 的顶缘拖动条(否则按钮点击被 drag 吞);
|
|
92
|
+
空区拖动窗口 */}
|
|
93
|
+
<div className="app-region-drag relative z-50 flex h-8 w-full items-center justify-end gap-2 pl-20">
|
|
94
|
+
<div className="app-region-no-drag flex items-center gap-2">
|
|
95
|
+
<NotificationsBell />
|
|
96
|
+
<AnimatedThemeToggler />
|
|
97
|
+
<SidebarTrigger />
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
{/* 第二行:logo(左对齐,与红绿灯行错层) */}
|
|
101
|
+
<a href="#" className="app-region-no-drag flex items-center gap-2 px-1 pb-1">
|
|
102
|
+
<img
|
|
103
|
+
src="/logo/allblue-logo-text-dark.svg"
|
|
104
|
+
alt="AllBlue"
|
|
105
|
+
className="h-5 dark:hidden"
|
|
106
|
+
/>
|
|
107
|
+
<img
|
|
108
|
+
src="/logo/allblue-logo-text-light.svg"
|
|
109
|
+
alt="AllBlue"
|
|
110
|
+
className="hidden h-5 dark:block"
|
|
111
|
+
/>
|
|
112
|
+
</a>
|
|
113
|
+
</>
|
|
79
114
|
) : (
|
|
80
115
|
<>
|
|
81
116
|
<a href="#" className="flex items-center gap-2">
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
|
|
18
18
|
import { describe, it, expect } from "vitest";
|
|
19
|
-
import { AVATAR_SHAPES, toAvatarShape } from "../avatar-shape";
|
|
19
|
+
import { AVATAR_SHAPES, hasAvatarConfig, toAvatarShape } from "../avatar-shape";
|
|
20
20
|
|
|
21
21
|
describe("AVATAR_SHAPES", () => {
|
|
22
22
|
it("与 kernel ShapeType 三值一致", () => {
|
|
@@ -42,3 +42,19 @@ describe("toAvatarShape", () => {
|
|
|
42
42
|
expect(toAvatarShape(undefined)).toBeNull();
|
|
43
43
|
});
|
|
44
44
|
});
|
|
45
|
+
|
|
46
|
+
describe("hasAvatarConfig", () => {
|
|
47
|
+
it("空对象(DB 列默认值)不算已配置", () => {
|
|
48
|
+
expect(hasAvatarConfig({})).toBe(false);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("空值不算已配置", () => {
|
|
52
|
+
expect(hasAvatarConfig(null)).toBe(false);
|
|
53
|
+
expect(hasAvatarConfig(undefined)).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("非空对象算已配置", () => {
|
|
57
|
+
expect(hasAvatarConfig({ face: 0 })).toBe(true);
|
|
58
|
+
expect(hasAvatarConfig({ face: 3, hair: 1 })).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
});
|