@audiofool/moondog 0.2.0-beta.5 → 0.2.0-beta.7
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/README.md +3 -3
- package/assets/brand/moondog-logo.svg +1 -0
- package/package.json +2 -2
- package/src/core/moondog-application.mjs +26 -5
- package/src/i18n/screen/es.mjs +10 -5
- package/src/i18n/screen/ja.mjs +10 -5
- package/src/i18n/screen/pt.mjs +10 -5
- package/src/i18n/screen/zh.mjs +10 -5
- package/src/memory/local-memory-store.mjs +48 -8
- package/src/surfaces/brand/logo.mjs +237 -0
- package/src/surfaces/cli/format-output.mjs +1 -1
- package/src/surfaces/cli/terminal-art.mjs +24 -66
- package/src/surfaces/cli/tui.mjs +41 -7
- package/assets/brand/moondog-lunar-record/moondog-logo-1x1.png +0 -0
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="assets/brand/moondog-
|
|
2
|
+
<img src="assets/brand/moondog-logo.svg" width="156" alt="Moondog: a dog in headphones inside a moon that is also a record">
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<h1 align="center">Moondog</h1>
|
|
6
6
|
|
|
7
|
-
<p align="center">Source candidate 0.2.0-beta.
|
|
7
|
+
<p align="center">Source candidate 0.2.0-beta.7</p>
|
|
8
8
|
|
|
9
9
|
<p align="center"><strong>A listening room that shows its work.</strong></p>
|
|
10
10
|
|
|
@@ -46,7 +46,7 @@ See [updating and release channels](docs/UPDATES.md), including the one-time upg
|
|
|
46
46
|
It shares your profile, settings, sign-ins, and conversations with `moondog`, and draws its own conversation, now playing, and up next.
|
|
47
47
|
Run it from this checkout with `cd app && npm ci && npm start`.
|
|
48
48
|
|
|
49
|
-
The [project page](
|
|
49
|
+
The [project page](https://moondog.audiofool.ai) plays a short scene in this window, in a browser, with a made-up listener ([how](site/README.md)).
|
|
50
50
|
|
|
51
51
|
## The sleeve
|
|
52
52
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><style>svg{color:#141414}@media (prefers-color-scheme:dark){svg{color:#f2f2f2}}</style><defs><mask id="moondog-logo-clear" maskUnits="userSpaceOnUse" x="0" y="0" width="1024" height="1024"><rect width="1024" height="1024" fill="#fff"/><g fill="#000" stroke="#000" stroke-width="12" stroke-linejoin="round"><path d="M447.6 281C447.6 281 469.5 347.5 469.5 347.5C469.5 347.5 477.18 355.23 481 359.8C485.34 364.99 488.83 372.3 494 377.2C499.16 382.09 505.02 385.53 512 389.2C520.53 393.69 531.66 396.8 542 401.2C553.27 405.99 566.45 411 577 417C586.59 422.46 597.08 428.92 603 435.2C607.23 439.69 608.52 444.63 611.5 449C614.37 453.21 617.59 456.92 620.5 461C623.42 465.09 625.62 469.76 629 473.5C632.44 477.31 636.66 480.78 641 483.6C645.33 486.41 649.57 487.98 655 490.4C662.29 493.65 672.51 497.89 681 501C688.95 503.91 697.34 506.69 704.4 508.6C710.08 510.14 715.57 510.59 720 512C723.47 513.1 726.8 513.88 729 515.8C730.97 517.51 732.26 520.06 733 522.5C733.75 524.99 733.69 527.86 733.4 530.6C733.09 533.52 732.21 536.28 731 539.5C729.44 543.64 726.27 548.68 724.3 553.2C722.44 557.47 721.37 561.84 719.4 565.9C717.44 569.94 715.59 573.52 712.5 577.5C708.49 582.67 702.17 588.95 696.5 593.6C691.11 598.02 685.65 601.75 679.4 604.9C672.73 608.26 664.66 610.85 657.5 612.9C650.88 614.79 645.91 616.08 638 617C626.05 618.39 603.34 616.7 592.5 618.1C586.34 618.9 582.32 619.54 578.2 621.5C574.46 623.28 571.15 626.26 568.6 628.9C566.43 631.14 563.5 636 563.5 636C563.5 636 562.99 652.36 563.3 661C563.63 670.32 565.1 679.65 565.6 690C566.18 701.87 566.3 728.3 566.3 728.3C566.3 728.3 552.89 737.15 546.2 740.3C540.12 743.17 528 746.8 528 746.8C528 746.8 525.33 775.16 525.2 790C525.06 805.77 527.5 838.8 527.5 838.8C527.5 838.8 495.67 835.82 480 833.5C464.5 831.21 448.92 828.4 434 825C419.61 821.72 405.95 817.75 392 813.6C377.95 809.42 363.25 806.36 350 800C336.56 793.55 324.56 783.37 312 775C299.56 766.7 286.57 758.33 275 750C264.35 742.33 254.16 735.47 245 727C235.94 718.62 220.3 699.5 220.3 699.5C220.3 699.5 230.67 675.67 236 664C241.24 652.51 247.02 641.02 252 630C256.64 619.72 261.07 610.41 265 600C269.11 589.12 272.18 577.16 276 566C279.75 555.06 284.75 541.79 287.7 533.7C289.48 528.83 292 522 292 522C292 522 330 505 330 505C330 505 358 455 358 455C358 455 359.1 453.67 359.5 452C360.98 445.73 358.92 417.16 359.6 404.95C360.04 397.12 360.65 392.03 361.72 385.75C362.78 379.57 364.21 373.42 365.96 367.57C367.66 361.92 369.78 356.39 372.02 351.21C374.15 346.31 376.4 341.81 379 337.26C381.65 332.6 384.41 327.99 387.82 323.58C391.44 318.88 400.28 310 400.28 310C400.28 310 412.44 299.33 418.61 294.8C424.35 290.56 430.58 285.73 435.99 283.48C440.08 281.78 447.6 281 447.6 281Z"/><path d="M334 452C334 452 334.37 428.09 334.6 418C334.78 409.9 334.47 403.56 335.2 396.1C335.97 388.18 337.4 379.3 339.3 371.8C341 365.08 343.29 359.13 345.7 353.1C348.04 347.24 350.39 341.82 353.5 336.1C356.92 329.83 361.17 323.05 365.6 317.1C369.95 311.26 375.4 305.38 379.8 300.7C383.31 296.96 386.2 293.98 389.7 291C393.17 288.05 396.82 285.42 400.7 282.9C404.73 280.28 409.19 278.07 413.5 275.6C417.92 273.07 422.88 270.29 426.9 267.9C430.24 265.91 433.32 263.03 436 262.3C437.83 261.8 439.62 261.66 441 262.4C442.56 263.24 443.57 265.56 444.6 267.6C445.85 270.08 447.5 273.85 447.6 276.4C447.68 278.32 447.52 280.18 446.3 281.5C444.61 283.33 440.24 283.07 436.7 284.8C431.66 287.26 425.19 291.81 419.5 296C413.39 300.5 406.63 306.03 401.3 311.1C396.63 315.54 392.58 319.87 389 324.5C385.64 328.85 382.92 333.4 380.3 338C377.73 342.51 375.5 346.96 373.4 351.8C371.18 356.93 369.08 362.4 367.4 368C365.66 373.79 364.25 379.89 363.2 386C362.14 392.22 361.54 397.24 361.1 405C360.42 417.16 361 452 361 452C361 452 334 452 334 452Z"/><circle cx="362.5" cy="504" r="67"/><path d="M1017.45 38.94L1028.89 47.84L929.37 175.67L917.92 166.76Z"/><path d="M914.04 165.64L931.4 179.16L926.18 185.86L908.82 172.35Z"/><path d="M902.77 169.54L930.39 191.04L898.75 231.68L869.16 208.64Z"/><path d="M863.2 206.53L900.68 235.71L872.68 268.41L835.2 239.23Z"/><path d="M900.35 228.81L929.55 251.54L924.33 258.24L895.13 235.51Z"/><path d="M845.72 249.32L863.08 262.84L845.33 281.97L831.53 271.22Z"/><path d="M835.27 274.14L841.59 279.05L836.41 283.26L832.46 280.19Z"/><circle cx="926.94" cy="254.89" r="4.25"/></g><circle cx="321.5" cy="236.7" r="49" fill="#000"/><circle cx="712.8" cy="277.2" r="46" fill="#000"/><circle cx="212" cy="490.9" r="37.5" fill="#000"/><circle cx="816.5" cy="618.4" r="26.5" fill="#000"/><circle cx="256.5" cy="786.9" r="40.5" fill="#000"/><circle cx="640.8" cy="848.7" r="50" fill="#000"/></mask><mask id="moondog-logo-cut" maskUnits="userSpaceOnUse" x="0" y="0" width="1024" height="1024"><rect width="1024" height="1024" fill="#fff"/><circle cx="362.5" cy="504" r="72.5" fill="none" stroke="#000" stroke-width="11"/><path d="M459.61 282.74C459.61 282.74 451.67 285.49 448.09 286.7C444.97 287.76 442.59 288.06 439.32 289.64C434.54 291.95 428.27 296.37 422.78 300.41C416.85 304.79 410.21 310.22 405.06 315.12C400.58 319.37 396.74 323.47 393.34 327.88C390.14 332.03 387.57 336.35 385.08 340.73C382.62 345.04 380.47 349.32 378.45 353.97C376.31 358.9 374.29 364.18 372.67 369.57C370.99 375.15 369.63 381.03 368.62 386.92C367.59 392.92 366.96 398.59 366.59 405.25C366.16 413.21 366.6 431.5 366.6 431.5" fill="none" stroke="#000" stroke-width="11"/><circle cx="362.5" cy="504" r="5" fill="none" stroke="#000" stroke-width="3.2"/><circle cx="362.5" cy="504" r="14" fill="none" stroke="#000" stroke-width="3.2"/><circle cx="362.5" cy="504" r="23" fill="none" stroke="#000" stroke-width="3.2"/><circle cx="362.5" cy="504" r="32" fill="none" stroke="#000" stroke-width="3.2"/><circle cx="362.5" cy="504" r="41" fill="none" stroke="#000" stroke-width="3.2"/><circle cx="362.5" cy="504" r="50" fill="none" stroke="#000" stroke-width="3.2"/><circle cx="362.5" cy="504" r="59" fill="none" stroke="#000" stroke-width="3.2"/></mask><mask id="moondog-logo-arm" maskUnits="userSpaceOnUse" x="0" y="0" width="1024" height="1024"><rect width="1024" height="1024" fill="#fff"/><circle cx="907.21" cy="192.32" r="7.5" fill="none" stroke="#000" stroke-width="1.8"/><circle cx="859.72" cy="232.97" r="5.5" fill="#000"/><circle cx="875.11" cy="244.95" r="5.5" fill="#000"/><path d="M859.72 232.97L875.11 244.95" stroke="#000" stroke-width="1.6"/></mask></defs><g mask="url(#moondog-logo-clear)"><circle cx="512" cy="512" r="426.7" fill="none" stroke="currentColor" stroke-width="2.24"/><circle cx="512" cy="512" r="416.95" fill="none" stroke="currentColor" stroke-width="1.4"/><circle cx="512" cy="512" r="407.2" fill="none" stroke="currentColor" stroke-width="1.4"/><circle cx="512" cy="512" r="397.45" fill="none" stroke="currentColor" stroke-width="1.4"/><circle cx="512" cy="512" r="387.7" fill="none" stroke="currentColor" stroke-width="1.4"/><circle cx="512" cy="512" r="377.95" fill="none" stroke="currentColor" stroke-width="1.4"/><circle cx="512" cy="512" r="368.2" fill="none" stroke="currentColor" stroke-width="1.4"/><circle cx="512" cy="512" r="358.45" fill="none" stroke="currentColor" stroke-width="1.4"/><circle cx="512" cy="512" r="348.7" fill="none" stroke="currentColor" stroke-width="1.4"/><circle cx="512" cy="512" r="338.95" fill="none" stroke="currentColor" stroke-width="1.4"/><circle cx="512" cy="512" r="329.2" fill="none" stroke="currentColor" stroke-width="1.4"/><circle cx="512" cy="512" r="319.45" fill="none" stroke="currentColor" stroke-width="1.4"/><circle cx="512" cy="512" r="309.7" fill="none" stroke="currentColor" stroke-width="1.4"/><circle cx="512" cy="512" r="299.95" fill="none" stroke="currentColor" stroke-width="1.4"/><circle cx="512" cy="512" r="290.2" fill="none" stroke="currentColor" stroke-width="1.4"/><circle cx="512" cy="512" r="280.45" fill="none" stroke="currentColor" stroke-width="1.4"/><circle cx="512" cy="512" r="270.7" fill="none" stroke="currentColor" stroke-width="1.4"/><circle cx="512" cy="512" r="260.95" fill="none" stroke="currentColor" stroke-width="1.4"/><circle cx="512" cy="512" r="251.2" fill="none" stroke="currentColor" stroke-width="1.4"/><circle cx="512" cy="512" r="241.45" fill="none" stroke="currentColor" stroke-width="1.4"/><circle cx="512" cy="512" r="231.7" fill="none" stroke="currentColor" stroke-width="1.4"/></g><path d="M311.61 233.1A10.53 10.53 0 1 1 317.9 246.59" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/><circle cx="321.5" cy="236.7" r="17.94" fill="none" stroke="currentColor" stroke-width="1.61"/><circle cx="321.5" cy="236.7" r="28.08" fill="none" stroke="currentColor" stroke-width="1.4" stroke-dasharray="19.5 4.68" stroke-dashoffset="7.8"/><circle cx="321.5" cy="236.7" r="39" fill="none" stroke="currentColor" stroke-width="1.19" stroke-dasharray="10.92 3.9 2.34 3.9"/><path d="M703.67 273.88A9.72 9.72 0 1 1 709.48 286.33" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/><circle cx="712.8" cy="277.2" r="16.56" fill="none" stroke="currentColor" stroke-width="1.61"/><circle cx="712.8" cy="277.2" r="25.92" fill="none" stroke="currentColor" stroke-width="1.4" stroke-dasharray="18 4.32" stroke-dashoffset="7.2"/><circle cx="712.8" cy="277.2" r="36" fill="none" stroke="currentColor" stroke-width="1.19" stroke-dasharray="10.08 3.6 2.16 3.6"/><path d="M205.02 488.36A7.43 7.43 0 1 1 209.46 497.88" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/><circle cx="212" cy="490.9" r="12.65" fill="none" stroke="currentColor" stroke-width="1.61"/><circle cx="212" cy="490.9" r="19.8" fill="none" stroke="currentColor" stroke-width="1.4" stroke-dasharray="13.75 3.3" stroke-dashoffset="5.5"/><circle cx="212" cy="490.9" r="27.5" fill="none" stroke="currentColor" stroke-width="1.19" stroke-dasharray="7.7 2.75 1.65 2.75"/><path d="M812.31 616.88A4.46 4.46 0 1 1 814.98 622.59" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/><circle cx="816.5" cy="618.4" r="7.59" fill="none" stroke="currentColor" stroke-width="1.61"/><circle cx="816.5" cy="618.4" r="16.5" fill="none" stroke="currentColor" stroke-width="1.4" stroke-dasharray="8.25 1.98" stroke-dashoffset="3.3"/><path d="M248.76 784.08A8.24 8.24 0 1 1 253.68 794.64" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/><circle cx="256.5" cy="786.9" r="14.03" fill="none" stroke="currentColor" stroke-width="1.61"/><circle cx="256.5" cy="786.9" r="21.96" fill="none" stroke="currentColor" stroke-width="1.4" stroke-dasharray="15.25 3.66" stroke-dashoffset="6.1"/><circle cx="256.5" cy="786.9" r="30.5" fill="none" stroke="currentColor" stroke-width="1.19" stroke-dasharray="8.54 3.05 1.83 3.05"/><path d="M630.65 845.01A10.8 10.8 0 1 1 637.11 858.85" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/><circle cx="640.8" cy="848.7" r="18.4" fill="none" stroke="currentColor" stroke-width="1.61"/><circle cx="640.8" cy="848.7" r="28.8" fill="none" stroke="currentColor" stroke-width="1.4" stroke-dasharray="20 4.8" stroke-dashoffset="8"/><circle cx="640.8" cy="848.7" r="40" fill="none" stroke="currentColor" stroke-width="1.19" stroke-dasharray="11.2 4 2.4 4"/><g mask="url(#moondog-logo-cut)" fill="currentColor"><path d="M447.6 281C447.6 281 469.5 347.5 469.5 347.5C469.5 347.5 477.18 355.23 481 359.8C485.34 364.99 488.83 372.3 494 377.2C499.16 382.09 505.02 385.53 512 389.2C520.53 393.69 531.66 396.8 542 401.2C553.27 405.99 566.45 411 577 417C586.59 422.46 597.08 428.92 603 435.2C607.23 439.69 608.52 444.63 611.5 449C614.37 453.21 617.59 456.92 620.5 461C623.42 465.09 625.62 469.76 629 473.5C632.44 477.31 636.66 480.78 641 483.6C645.33 486.41 649.57 487.98 655 490.4C662.29 493.65 672.51 497.89 681 501C688.95 503.91 697.34 506.69 704.4 508.6C710.08 510.14 715.57 510.59 720 512C723.47 513.1 726.8 513.88 729 515.8C730.97 517.51 732.26 520.06 733 522.5C733.75 524.99 733.69 527.86 733.4 530.6C733.09 533.52 732.21 536.28 731 539.5C729.44 543.64 726.27 548.68 724.3 553.2C722.44 557.47 721.37 561.84 719.4 565.9C717.44 569.94 715.59 573.52 712.5 577.5C708.49 582.67 702.17 588.95 696.5 593.6C691.11 598.02 685.65 601.75 679.4 604.9C672.73 608.26 664.66 610.85 657.5 612.9C650.88 614.79 645.91 616.08 638 617C626.05 618.39 603.34 616.7 592.5 618.1C586.34 618.9 582.32 619.54 578.2 621.5C574.46 623.28 571.15 626.26 568.6 628.9C566.43 631.14 563.5 636 563.5 636C563.5 636 562.99 652.36 563.3 661C563.63 670.32 565.1 679.65 565.6 690C566.18 701.87 566.3 728.3 566.3 728.3C566.3 728.3 552.89 737.15 546.2 740.3C540.12 743.17 528 746.8 528 746.8C528 746.8 525.33 775.16 525.2 790C525.06 805.77 527.5 838.8 527.5 838.8C527.5 838.8 495.67 835.82 480 833.5C464.5 831.21 448.92 828.4 434 825C419.61 821.72 405.95 817.75 392 813.6C377.95 809.42 363.25 806.36 350 800C336.56 793.55 324.56 783.37 312 775C299.56 766.7 286.57 758.33 275 750C264.35 742.33 254.16 735.47 245 727C235.94 718.62 220.3 699.5 220.3 699.5C220.3 699.5 230.67 675.67 236 664C241.24 652.51 247.02 641.02 252 630C256.64 619.72 261.07 610.41 265 600C269.11 589.12 272.18 577.16 276 566C279.75 555.06 284.75 541.79 287.7 533.7C289.48 528.83 292 522 292 522C292 522 330 505 330 505C330 505 358 455 358 455C358 455 359.1 453.67 359.5 452C360.98 445.73 358.92 417.16 359.6 404.95C360.04 397.12 360.65 392.03 361.72 385.75C362.78 379.57 364.21 373.42 365.96 367.57C367.66 361.92 369.78 356.39 372.02 351.21C374.15 346.31 376.4 341.81 379 337.26C381.65 332.6 384.41 327.99 387.82 323.58C391.44 318.88 400.28 310 400.28 310C400.28 310 412.44 299.33 418.61 294.8C424.35 290.56 430.58 285.73 435.99 283.48C440.08 281.78 447.6 281 447.6 281Z"/><path d="M334 452C334 452 334.37 428.09 334.6 418C334.78 409.9 334.47 403.56 335.2 396.1C335.97 388.18 337.4 379.3 339.3 371.8C341 365.08 343.29 359.13 345.7 353.1C348.04 347.24 350.39 341.82 353.5 336.1C356.92 329.83 361.17 323.05 365.6 317.1C369.95 311.26 375.4 305.38 379.8 300.7C383.31 296.96 386.2 293.98 389.7 291C393.17 288.05 396.82 285.42 400.7 282.9C404.73 280.28 409.19 278.07 413.5 275.6C417.92 273.07 422.88 270.29 426.9 267.9C430.24 265.91 433.32 263.03 436 262.3C437.83 261.8 439.62 261.66 441 262.4C442.56 263.24 443.57 265.56 444.6 267.6C445.85 270.08 447.5 273.85 447.6 276.4C447.68 278.32 447.52 280.18 446.3 281.5C444.61 283.33 440.24 283.07 436.7 284.8C431.66 287.26 425.19 291.81 419.5 296C413.39 300.5 406.63 306.03 401.3 311.1C396.63 315.54 392.58 319.87 389 324.5C385.64 328.85 382.92 333.4 380.3 338C377.73 342.51 375.5 346.96 373.4 351.8C371.18 356.93 369.08 362.4 367.4 368C365.66 373.79 364.25 379.89 363.2 386C362.14 392.22 361.54 397.24 361.1 405C360.42 417.16 361 452 361 452C361 452 334 452 334 452Z"/><circle cx="362.5" cy="504" r="67"/></g><g fill="currentColor" mask="url(#moondog-logo-arm)"><path d="M1017.45 38.94L1028.89 47.84L929.37 175.67L917.92 166.76Z"/><path d="M914.04 165.64L931.4 179.16L926.18 185.86L908.82 172.35Z"/><path d="M902.77 169.54L930.39 191.04L898.75 231.68L869.16 208.64Z"/><path d="M863.2 206.53L900.68 235.71L872.68 268.41L835.2 239.23Z"/><path d="M900.35 228.81L929.55 251.54L924.33 258.24L895.13 235.51Z"/><path d="M845.72 249.32L863.08 262.84L845.33 281.97L831.53 271.22Z"/><path d="M835.27 274.14L841.59 279.05L836.41 283.26L832.46 280.19Z"/><circle cx="926.94" cy="254.89" r="4.25"/></g></svg>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@audiofool/moondog",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Local-first personal AI music agent.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"moondog": "scripts/moondog"
|
|
19
19
|
},
|
|
20
20
|
"files": [
|
|
21
|
-
"assets/brand/moondog-
|
|
21
|
+
"assets/brand/moondog-logo.svg",
|
|
22
22
|
"contracts/v1",
|
|
23
23
|
"contracts/v2",
|
|
24
24
|
"scripts/contract-lib.mjs",
|
|
@@ -986,7 +986,7 @@ export class MoondogApplication {
|
|
|
986
986
|
// in generic memory, automatic recall or reflection.
|
|
987
987
|
const result = this.transientSpotifyContext ? this.commitTransientPrompt(memoryMutations, user)
|
|
988
988
|
: this.memoryStore ? this.memoryStore.commitCompletedPrompt(this.ensureMemorySession().session_id,
|
|
989
|
-
{ user, assistant, memoryMutations }) : { recorded: false, memory_results: [] };
|
|
989
|
+
{ user, assistant, memoryMutations, turnId: this.conversationEntry.entryId }) : { recorded: false, memory_results: [] };
|
|
990
990
|
status = 'completed';
|
|
991
991
|
return result;
|
|
992
992
|
} finally {
|
|
@@ -1304,20 +1304,41 @@ export class MoondogApplication {
|
|
|
1304
1304
|
return structuredClone(row.pending);
|
|
1305
1305
|
}
|
|
1306
1306
|
|
|
1307
|
+
// Edit, in place: the message and everything after it leave this conversation, and the message
|
|
1308
|
+
// comes back as the draft. Returns { session_id, draft }.
|
|
1307
1309
|
rewindConversation(entryId) {
|
|
1308
1310
|
if (this.conversationEntry || this.pendingPlaylistPromptTransaction) throw new Error("Wait for the current turn to finish before rewinding");
|
|
1309
1311
|
const sessionId = this.ensureMemorySession().session_id;
|
|
1312
|
+
let rewound;
|
|
1313
|
+
if (this.memoryStore) rewound = this.memoryStore.rewindConversation(sessionId, entryId);
|
|
1314
|
+
else {
|
|
1315
|
+
const row = this.localConversations.get(sessionId);
|
|
1316
|
+
const index = row.entries.findIndex(entry => entry.entry_id === entryId);
|
|
1317
|
+
if (index < 0) throw new Error("The selected turn is no longer available");
|
|
1318
|
+
rewound = { session_id: sessionId, draft: row.entries[index].user_text };
|
|
1319
|
+
row.entries.splice(index);
|
|
1320
|
+
row.pending.draft = rewound.draft;
|
|
1321
|
+
}
|
|
1322
|
+
this.#clearConversationState();
|
|
1323
|
+
return structuredClone(rewound);
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
// Branch: a new conversation that goes on from a reply (the latest by default), with a copy of
|
|
1327
|
+
// everything up to it. The original stays as it is. Returns the new conversation's session.
|
|
1328
|
+
branchConversation(entryId = this.latestConversationEntryId()) {
|
|
1329
|
+
if (this.conversationEntry || this.pendingPlaylistPromptTransaction) throw new Error("Wait for the current turn to finish before branching");
|
|
1330
|
+
if (!entryId) throw new Error("There is nothing to branch from yet");
|
|
1331
|
+
const sessionId = this.ensureMemorySession().session_id;
|
|
1310
1332
|
let next;
|
|
1311
|
-
if (this.memoryStore) next = this.memoryStore.
|
|
1333
|
+
if (this.memoryStore) next = this.memoryStore.branchConversation(sessionId, entryId, { routeKey: this.memoryRouteKey });
|
|
1312
1334
|
else {
|
|
1313
1335
|
const original = this.localConversations.get(sessionId);
|
|
1314
1336
|
const index = original.entries.findIndex(entry => entry.entry_id === entryId);
|
|
1315
1337
|
if (index < 0) throw new Error("The selected turn is no longer available");
|
|
1316
|
-
next = { ...this.#newLocalConversation(), parent_session_id: sessionId
|
|
1338
|
+
next = { ...this.#newLocalConversation(), parent_session_id: sessionId };
|
|
1317
1339
|
const branch = this.localConversations.get(next.session_id);
|
|
1318
1340
|
branch.parent_session_id = sessionId;
|
|
1319
|
-
branch.entries = structuredClone(original.entries.slice(0, index)).map(entry => ({ ...entry, entry_id: randomUUID() }));
|
|
1320
|
-
branch.pending.draft = next.draft;
|
|
1341
|
+
branch.entries = structuredClone(original.entries.slice(0, index + 1)).map(entry => ({ ...entry, entry_id: randomUUID() }));
|
|
1321
1342
|
}
|
|
1322
1343
|
this.memorySession = next;
|
|
1323
1344
|
this.#clearConversationState();
|
package/src/i18n/screen/es.mjs
CHANGED
|
@@ -176,7 +176,7 @@ export default {
|
|
|
176
176
|
"Memory": "Memoria",
|
|
177
177
|
"Right now I can't change anything in your music accounts.": "Ahora mismo no puedo cambiar nada en tus cuentas de música.",
|
|
178
178
|
"I can make changes in your connected accounts, and I always ask first.": "Puedo hacer cambios en tus cuentas conectadas, y siempre pregunto antes.",
|
|
179
|
-
"# Every command\n\nFrom your shell:\n\n- `moondog --version` - show the installed version\n- `moondog update [--check] [--channel latest|beta]` - check for or install a published release\n- `moondog web status|search|read [arguments] [--json]` - look things up on public music sites through DeepSeek, Brave Search or the Codex CLI\n- `moondog demo-history --output <absolute-file.zip> [--json]` - make a fictional Spotify history ZIP to try the importer with\n- `moondog auth login <provider>` - sign in to a model provider; the key is typed out of sight, and openai-codex uses your ChatGPT sign-in\n- `moondog auth status [provider]` - see which sign-ins are saved\n- `moondog auth logout <provider>` - sign out of a provider\n- `moondog spotify help` - set up and control Spotify\n- `moondog spotify login [client-id]` - connect Spotify in your browser\n- `moondog spotify now|devices|queue` - see what Spotify is playing, where, and what's next\n- `moondog spotify recent` - see what you played lately\n- `moondog spotify sync-recent` - add your latest plays to your profile\n- `moondog spotify sync-library` - add your Spotify library to your profile\n- `moondog spotify play|pause|next|previous` - control Spotify playback\n- `moondog listenbrainz import-history <listen-history.json>` - add a ListenBrainz listens file to your profile\n- `moondog catalog latest-single --artist <name> [--known-release <title> | --from <spotify-history.zip>]` - find an artist's latest single on Apple Music (US), no model needed\n- `moondog status [--json]` - see where things stand\n- `moondog taste [--json]` - your listening report\n- `moondog taste --html [--output <file.html>]` - save your report as a web page that works offline\n- `moondog taste --card [--output <file.html>]` - save a short recap you can check before sharing\n- `moondog taste --from <spotify-history.zip> --html` - a one-off report from a Spotify ZIP, without saving it\n- `moondog taste --from <account-data.zip> --from <extended-history.zip> --html` - a one-off report from both Spotify exports together\n- `moondog taste --from <spotify-history.zip> --save --html` - save the Spotify history, then show your full report\n- `moondog data inspect --scope <listening|apple|profile>` - see what's stored, and get the code needed to reset it\n- `moondog data export --scope <listening|apple|profile> --output <directory>` - export a copy, leaving the original alone\n- `moondog data reset --scope <listening|apple|profile> --confirm <token>` - set stored data aside (it is archived, not deleted)\n- `moondog profile build [--force]` - build or resume a saved profile with your connected model\n- `moondog profile saved [offset]` - read your saved profile\n- `moondog profile explain <number>` - trace a saved finding to its evidence\n- `moondog profile corrections [--all]` - everything you've told me, including earlier choices\n- `moondog profile correct --artist <name> (--like|--avoid)` - like an artist, or keep them out\n- `moondog profile correct --track <title> --by <artist> (--like|--avoid)` - like a track, or keep it out\n- `moondog profile retract <correction-id>` - undo one of your choices\n- `moondog demo --offline [--json]` - a demo with made-up data, no sign-in needed\n- `moondog demo [prompt] [--json]` - the same demo, talking through your model\n- `moondog ask <prompt>` - ask one question and exit\n- `moondog remember <text>` - ask me to remember something\n- `moondog forget <memory-id>` - forget something\n- `moondog memory reflect [--dry-run] [--json]` - let me think over recent conversations now\n- `moondog` - open the listening room\n\nInside the listening room:\n\n- `/update [--check] [--channel latest|beta]` - check for updates, or save and close this session to install one\n- `/status` or `/sources` - see where things stand\n- `/profile` or `/taste` - your profile, and why each song or artist is there\n- `/taste report` - the whole report on one page\n- `/profile build [--force]` - build or resume a saved reading; Ctrl+C keeps progress\n- `/profile saved [offset]` - read your saved profile\n- `/profile explain <number>` - inspect a saved finding's evidence\n- `/profile corrections [--all]` - everything you've told me\n- `/profile correct --track \"<title>\" --by \"<artist>\" (--like|--avoid)` - like a track, or keep it out\n- `/profile correct --artist \"<name>\" (--like|--avoid)` - like an artist, or keep them out\n- `/profile retract <correction-id>` - undo a choice\n- `/memory` - what I remember\n- `/tools` - what I can do right now\n- `/doctor` - check your setup\n- `/model` - pick the model I talk through\n- `/model <provider> <model>` - switch straight to a model\n- `/model refresh [provider]` - update public model choices; keep the selected model\n- `/auth [provider]` - sign in to a model provider\n- `/web status|search|read` - look things up on public music sites\n- `/import [path]` - bring in your history, library, or a playlist\n- `/language` - choose the language Moondog uses\n- `/spotify` - Spotify commands\n- `/spotify login [client-id]` - connect Spotify\n- `/spotify now|devices|queue` - see what's playing, where, and what's next\n- `/spotify recent|sync-recent` - see or save your latest plays\n- `/spotify import-history \"/path/to/spotify-history.zip\"` - add a Spotify ZIP, no sign-in needed\n- `/skip` or `/stop` - move to the next song preview, or stop them\n- `/reload` - reload model settings and sign-ins\n- `/remember [kind] <text>` - ask me to remember something\n- `/forget <memory-id>` - forget something\n- `/rewind` or Alt+R - edit an earlier message on a new branch; the original stays saved\n- `/rewind latest` - edit the most recent message\n- `/resume` - pick up a saved conversation\n- `/resume <session-id>` - go straight to one\n- `/new` - start fresh; the last one stays saved\n- `/quit` - exit\n\nAnything you type that isn't a command goes to the model you picked with `/model`, or the one set in `MOONDOG_PROVIDER` and `MOONDOG_MODEL`.\nSign in with `/auth`, or set the provider's API key in your environment. `/auth openai-codex` uses your ChatGPT sign-in.\nOnce your profile is ready, I can search your library, explain what I see, and plan playlists with you.\nI can only control Spotify after `moondog spotify login`, and I never post, send messages, delete anything, or spend money.\nAsk me to move playback onto a device by name, such as your iPhone.": "# Todos los comandos\n\nDesde tu terminal:\n\n- `moondog --version` - muestra la versión instalada\n- `moondog update [--check] [--channel latest|beta]` - busca o instala una versión publicada\n- `moondog web status|search|read [argumentos] [--json]` - consulta sitios de música públicos a través de DeepSeek, Brave Search o Codex CLI\n- `moondog demo-history --output <archivo-absoluto.zip> [--json]` - crea un ZIP de historial de Spotify ficticio para probar el importador\n- `moondog auth login <proveedor>` - inicia sesión en un proveedor de modelos; la clave se escribe sin mostrarse, y openai-codex usa tu cuenta de ChatGPT\n- `moondog auth status [proveedor]` - mira qué inicios de sesión están guardados\n- `moondog auth logout <proveedor>` - cierra la sesión de un proveedor\n- `moondog spotify help` - configura y controla Spotify\n- `moondog spotify login [client-id]` - conecta Spotify en tu navegador\n- `moondog spotify now|devices|queue` - mira qué suena en Spotify, dónde y qué viene después\n- `moondog spotify recent` - mira lo que escuchaste últimamente\n- `moondog spotify sync-recent` - añade tus últimas reproducciones a tu perfil\n- `moondog spotify sync-library` - añade tu biblioteca de Spotify a tu perfil\n- `moondog spotify play|pause|next|previous` - controla la reproducción de Spotify\n- `moondog listenbrainz import-history <listen-history.json>` - añade un archivo de escuchas de ListenBrainz a tu perfil\n- `moondog catalog latest-single --artist <nombre> [--known-release <título> | --from <spotify-history.zip>]` - encuentra el último sencillo de un artista en Apple Music (EE. UU.), sin modelo\n- `moondog status [--json]` - mira cómo está todo\n- `moondog taste [--json]` - tu informe de escucha\n- `moondog taste --html [--output <archivo.html>]` - guarda tu informe como página web que funciona sin conexión\n- `moondog taste --card [--output <archivo.html>]` - guarda un resumen breve que puedes revisar antes de compartirlo\n- `moondog taste --from <spotify-history.zip> --html` - un informe puntual de un ZIP de Spotify, sin guardarlo\n- `moondog taste --from <account-data.zip> --from <extended-history.zip> --html` - un informe puntual de ambas exportaciones de Spotify\n- `moondog taste --from <spotify-history.zip> --save --html` - guarda el historial de Spotify y muestra tu informe completo\n- `moondog data inspect --scope <listening|apple|profile>` - mira qué hay guardado y obtén el código para restablecerlo\n- `moondog data export --scope <listening|apple|profile> --output <carpeta>` - exporta una copia sin tocar el original\n- `moondog data reset --scope <listening|apple|profile> --confirm <código>` - aparta los datos guardados (se archivan, no se borran)\n- `moondog profile build [--force]` - crea o retoma un perfil guardado con tu modelo conectado\n- `moondog profile saved [desplazamiento]` - lee tu perfil guardado\n- `moondog profile explain <número>` - rastrea un hallazgo guardado hasta su evidencia\n- `moondog profile corrections [--all]` - todo lo que me has dicho, incluidas elecciones anteriores\n- `moondog profile correct --artist <nombre> (--like|--avoid)` - marca que te gusta un artista, o déjalo fuera\n- `moondog profile correct --track <título> --by <artista> (--like|--avoid)` - marca que te gusta una canción, o déjala fuera\n- `moondog profile retract <correction-id>` - deshaz una de tus elecciones\n- `moondog demo --offline [--json]` - una demo con datos inventados, sin iniciar sesión\n- `moondog demo [petición] [--json]` - la misma demo, hablando a través de tu modelo\n- `moondog ask <petición>` - haz una pregunta y sal\n- `moondog remember <texto>` - pídeme que recuerde algo\n- `moondog forget <memory-id>` - olvida algo\n- `moondog memory reflect [--dry-run] [--json]` - déjame repasar ahora las conversaciones recientes\n- `moondog` - abre la sala de escucha\n\nDentro de la sala de escucha:\n\n- `/update [--check] [--channel latest|beta]` - busca actualizaciones, o guarda y cierra esta sesión para instalar una\n- `/status` o `/sources` - mira cómo está todo\n- `/profile` o `/taste` - tu perfil, y por qué está cada canción o artista\n- `/taste report` - el informe completo en una página\n- `/profile build [--force]` - crea o retoma una lectura guardada; Ctrl+C conserva el progreso\n- `/profile saved [desplazamiento]` - lee tu perfil guardado\n- `/profile explain <número>` - revisa la evidencia de un hallazgo guardado\n- `/profile corrections [--all]` - todo lo que me has dicho\n- `/profile correct --track \"<título>\" --by \"<artista>\" (--like|--avoid)` - marca que te gusta una canción, o déjala fuera\n- `/profile correct --artist \"<nombre>\" (--like|--avoid)` - marca que te gusta un artista, o déjalo fuera\n- `/profile retract <correction-id>` - deshaz una elección\n- `/memory` - lo que recuerdo\n- `/tools` - lo que puedo hacer ahora mismo\n- `/doctor` - revisa tu configuración\n- `/model` - elige el modelo con el que hablo\n- `/model <proveedor> <modelo>` - cambia directamente a un modelo\n- `/model refresh [proveedor]` - actualiza las opciones públicas de modelos; mantiene el modelo elegido\n- `/auth [proveedor]` - inicia sesión en un proveedor de modelos\n- `/web status|search|read` - consulta sitios de música públicos\n- `/import [ruta]` - trae tu historial, tu biblioteca o una playlist\n- `/language` - elige el idioma de Moondog\n- `/spotify` - comandos de Spotify\n- `/spotify login [client-id]` - conecta Spotify\n- `/spotify now|devices|queue` - mira qué suena, dónde y qué viene después\n- `/spotify recent|sync-recent` - mira o guarda tus últimas reproducciones\n- `/spotify import-history \"/ruta/a/spotify-history.zip\"` - añade un ZIP de Spotify, sin iniciar sesión\n- `/skip` o `/stop` - pasa al siguiente avance, o detén los avances\n- `/reload` - recarga los ajustes del modelo y los inicios de sesión\n- `/remember [tipo] <texto>` - pídeme que recuerde algo\n- `/forget <memory-id>` - olvida algo\n- `/rewind` o Alt+R - edita un mensaje anterior en una rama nueva; el original queda guardado\n- `/rewind latest` - edita el mensaje más reciente\n- `/resume` - retoma una conversación guardada\n- `/resume <session-id>` - ve directamente a una\n- `/new` - empieza de cero; la anterior queda guardada\n- `/quit` - salir\n\nTodo lo que escribas que no sea un comando va al modelo que elegiste con `/model`, o al definido en `MOONDOG_PROVIDER` y `MOONDOG_MODEL`.\nInicia sesión con `/auth`, o define la clave de API del proveedor en tu entorno. `/auth openai-codex` usa tu cuenta de ChatGPT.\nCuando tu perfil esté listo, puedo buscar en tu biblioteca, explicarte lo que veo y planear playlists contigo.\nSolo puedo controlar Spotify después de `moondog spotify login`, y nunca publico, envío mensajes, borro nada ni gasto dinero.\nPídeme pasar la reproducción a un dispositivo por su nombre, como tu iPhone.",
|
|
179
|
+
"# Every command\n\nFrom your shell:\n\n- `moondog --version` - show the installed version\n- `moondog update [--check] [--channel latest|beta]` - check for or install a published release\n- `moondog web status|search|read [arguments] [--json]` - look things up on public music sites through DeepSeek, Brave Search or the Codex CLI\n- `moondog demo-history --output <absolute-file.zip> [--json]` - make a fictional Spotify history ZIP to try the importer with\n- `moondog auth login <provider>` - sign in to a model provider; the key is typed out of sight, and openai-codex uses your ChatGPT sign-in\n- `moondog auth status [provider]` - see which sign-ins are saved\n- `moondog auth logout <provider>` - sign out of a provider\n- `moondog spotify help` - set up and control Spotify\n- `moondog spotify login [client-id]` - connect Spotify in your browser\n- `moondog spotify now|devices|queue` - see what Spotify is playing, where, and what's next\n- `moondog spotify recent` - see what you played lately\n- `moondog spotify sync-recent` - add your latest plays to your profile\n- `moondog spotify sync-library` - add your Spotify library to your profile\n- `moondog spotify play|pause|next|previous` - control Spotify playback\n- `moondog listenbrainz import-history <listen-history.json>` - add a ListenBrainz listens file to your profile\n- `moondog catalog latest-single --artist <name> [--known-release <title> | --from <spotify-history.zip>]` - find an artist's latest single on Apple Music (US), no model needed\n- `moondog status [--json]` - see where things stand\n- `moondog taste [--json]` - your listening report\n- `moondog taste --html [--output <file.html>]` - save your report as a web page that works offline\n- `moondog taste --card [--output <file.html>]` - save a short recap you can check before sharing\n- `moondog taste --from <spotify-history.zip> --html` - a one-off report from a Spotify ZIP, without saving it\n- `moondog taste --from <account-data.zip> --from <extended-history.zip> --html` - a one-off report from both Spotify exports together\n- `moondog taste --from <spotify-history.zip> --save --html` - save the Spotify history, then show your full report\n- `moondog data inspect --scope <listening|apple|profile>` - see what's stored, and get the code needed to reset it\n- `moondog data export --scope <listening|apple|profile> --output <directory>` - export a copy, leaving the original alone\n- `moondog data reset --scope <listening|apple|profile> --confirm <token>` - set stored data aside (it is archived, not deleted)\n- `moondog profile build [--force]` - build or resume a saved profile with your connected model\n- `moondog profile saved [offset]` - read your saved profile\n- `moondog profile explain <number>` - trace a saved finding to its evidence\n- `moondog profile corrections [--all]` - everything you've told me, including earlier choices\n- `moondog profile correct --artist <name> (--like|--avoid)` - like an artist, or keep them out\n- `moondog profile correct --track <title> --by <artist> (--like|--avoid)` - like a track, or keep it out\n- `moondog profile retract <correction-id>` - undo one of your choices\n- `moondog demo --offline [--json]` - a demo with made-up data, no sign-in needed\n- `moondog demo [prompt] [--json]` - the same demo, talking through your model\n- `moondog ask <prompt>` - ask one question and exit\n- `moondog remember <text>` - ask me to remember something\n- `moondog forget <memory-id>` - forget something\n- `moondog memory reflect [--dry-run] [--json]` - let me think over recent conversations now\n- `moondog` - open the listening room\n\nInside the listening room:\n\n- `/update [--check] [--channel latest|beta]` - check for updates, or save and close this session to install one\n- `/status` or `/sources` - see where things stand\n- `/profile` or `/taste` - your profile, and why each song or artist is there\n- `/taste report` - the whole report on one page\n- `/profile build [--force]` - build or resume a saved reading; Ctrl+C keeps progress\n- `/profile saved [offset]` - read your saved profile\n- `/profile explain <number>` - inspect a saved finding's evidence\n- `/profile corrections [--all]` - everything you've told me\n- `/profile correct --track \"<title>\" --by \"<artist>\" (--like|--avoid)` - like a track, or keep it out\n- `/profile correct --artist \"<name>\" (--like|--avoid)` - like an artist, or keep them out\n- `/profile retract <correction-id>` - undo a choice\n- `/memory` - what I remember\n- `/tools` - what I can do right now\n- `/doctor` - check your setup\n- `/model` - pick the model I talk through\n- `/model <provider> <model>` - switch straight to a model\n- `/model refresh [provider]` - update public model choices; keep the selected model\n- `/auth [provider]` - sign in to a model provider\n- `/web status|search|read` - look things up on public music sites\n- `/import [path]` - bring in your history, library, or a playlist\n- `/language` - choose the language Moondog uses\n- `/spotify` - Spotify commands\n- `/spotify login [client-id]` - connect Spotify\n- `/spotify now|devices|queue` - see what's playing, where, and what's next\n- `/spotify recent|sync-recent` - see or save your latest plays\n- `/spotify import-history \"/path/to/spotify-history.zip\"` - add a Spotify ZIP, no sign-in needed\n- `/skip` or `/stop` - move to the next song preview, or stop them\n- `/reload` - reload model settings and sign-ins\n- `/remember [kind] <text>` - ask me to remember something\n- `/forget <memory-id>` - forget something\n- `/rewind` or Alt+R - edit an earlier message in place; the messages after it are replaced\n- `/branch` - go on from here in a new conversation; this one stays saved\n- `/rewind latest` - edit the most recent message\n- `/resume` - pick up a saved conversation\n- `/resume <session-id>` - go straight to one\n- `/new` - start fresh; the last one stays saved\n- `/quit` - exit\n\nAnything you type that isn't a command goes to the model you picked with `/model`, or the one set in `MOONDOG_PROVIDER` and `MOONDOG_MODEL`.\nSign in with `/auth`, or set the provider's API key in your environment. `/auth openai-codex` uses your ChatGPT sign-in.\nOnce your profile is ready, I can search your library, explain what I see, and plan playlists with you.\nI can only control Spotify after `moondog spotify login`, and I never post, send messages, delete anything, or spend money.\nAsk me to move playback onto a device by name, such as your iPhone.": "# Todos los comandos\n\nDesde tu terminal:\n\n- `moondog --version` - muestra la versión instalada\n- `moondog update [--check] [--channel latest|beta]` - busca o instala una versión publicada\n- `moondog web status|search|read [argumentos] [--json]` - consulta sitios de música públicos a través de DeepSeek, Brave Search o Codex CLI\n- `moondog demo-history --output <archivo-absoluto.zip> [--json]` - crea un ZIP de historial de Spotify ficticio para probar el importador\n- `moondog auth login <proveedor>` - inicia sesión en un proveedor de modelos; la clave se escribe sin mostrarse, y openai-codex usa tu cuenta de ChatGPT\n- `moondog auth status [proveedor]` - mira qué inicios de sesión están guardados\n- `moondog auth logout <proveedor>` - cierra la sesión de un proveedor\n- `moondog spotify help` - configura y controla Spotify\n- `moondog spotify login [client-id]` - conecta Spotify en tu navegador\n- `moondog spotify now|devices|queue` - mira qué suena en Spotify, dónde y qué viene después\n- `moondog spotify recent` - mira lo que escuchaste últimamente\n- `moondog spotify sync-recent` - añade tus últimas reproducciones a tu perfil\n- `moondog spotify sync-library` - añade tu biblioteca de Spotify a tu perfil\n- `moondog spotify play|pause|next|previous` - controla la reproducción de Spotify\n- `moondog listenbrainz import-history <listen-history.json>` - añade un archivo de escuchas de ListenBrainz a tu perfil\n- `moondog catalog latest-single --artist <nombre> [--known-release <título> | --from <spotify-history.zip>]` - encuentra el último sencillo de un artista en Apple Music (EE. UU.), sin modelo\n- `moondog status [--json]` - mira cómo está todo\n- `moondog taste [--json]` - tu informe de escucha\n- `moondog taste --html [--output <archivo.html>]` - guarda tu informe como página web que funciona sin conexión\n- `moondog taste --card [--output <archivo.html>]` - guarda un resumen breve que puedes revisar antes de compartirlo\n- `moondog taste --from <spotify-history.zip> --html` - un informe puntual de un ZIP de Spotify, sin guardarlo\n- `moondog taste --from <account-data.zip> --from <extended-history.zip> --html` - un informe puntual de ambas exportaciones de Spotify\n- `moondog taste --from <spotify-history.zip> --save --html` - guarda el historial de Spotify y muestra tu informe completo\n- `moondog data inspect --scope <listening|apple|profile>` - mira qué hay guardado y obtén el código para restablecerlo\n- `moondog data export --scope <listening|apple|profile> --output <carpeta>` - exporta una copia sin tocar el original\n- `moondog data reset --scope <listening|apple|profile> --confirm <código>` - aparta los datos guardados (se archivan, no se borran)\n- `moondog profile build [--force]` - crea o retoma un perfil guardado con tu modelo conectado\n- `moondog profile saved [desplazamiento]` - lee tu perfil guardado\n- `moondog profile explain <número>` - rastrea un hallazgo guardado hasta su evidencia\n- `moondog profile corrections [--all]` - todo lo que me has dicho, incluidas elecciones anteriores\n- `moondog profile correct --artist <nombre> (--like|--avoid)` - marca que te gusta un artista, o déjalo fuera\n- `moondog profile correct --track <título> --by <artista> (--like|--avoid)` - marca que te gusta una canción, o déjala fuera\n- `moondog profile retract <correction-id>` - deshaz una de tus elecciones\n- `moondog demo --offline [--json]` - una demo con datos inventados, sin iniciar sesión\n- `moondog demo [petición] [--json]` - la misma demo, hablando a través de tu modelo\n- `moondog ask <petición>` - haz una pregunta y sal\n- `moondog remember <texto>` - pídeme que recuerde algo\n- `moondog forget <memory-id>` - olvida algo\n- `moondog memory reflect [--dry-run] [--json]` - déjame repasar ahora las conversaciones recientes\n- `moondog` - abre la sala de escucha\n\nDentro de la sala de escucha:\n\n- `/update [--check] [--channel latest|beta]` - busca actualizaciones, o guarda y cierra esta sesión para instalar una\n- `/status` o `/sources` - mira cómo está todo\n- `/profile` o `/taste` - tu perfil, y por qué está cada canción o artista\n- `/taste report` - el informe completo en una página\n- `/profile build [--force]` - crea o retoma una lectura guardada; Ctrl+C conserva el progreso\n- `/profile saved [desplazamiento]` - lee tu perfil guardado\n- `/profile explain <número>` - revisa la evidencia de un hallazgo guardado\n- `/profile corrections [--all]` - todo lo que me has dicho\n- `/profile correct --track \"<título>\" --by \"<artista>\" (--like|--avoid)` - marca que te gusta una canción, o déjala fuera\n- `/profile correct --artist \"<nombre>\" (--like|--avoid)` - marca que te gusta un artista, o déjalo fuera\n- `/profile retract <correction-id>` - deshaz una elección\n- `/memory` - lo que recuerdo\n- `/tools` - lo que puedo hacer ahora mismo\n- `/doctor` - revisa tu configuración\n- `/model` - elige el modelo con el que hablo\n- `/model <proveedor> <modelo>` - cambia directamente a un modelo\n- `/model refresh [proveedor]` - actualiza las opciones públicas de modelos; mantiene el modelo elegido\n- `/auth [proveedor]` - inicia sesión en un proveedor de modelos\n- `/web status|search|read` - consulta sitios de música públicos\n- `/import [ruta]` - trae tu historial, tu biblioteca o una playlist\n- `/language` - elige el idioma de Moondog\n- `/spotify` - comandos de Spotify\n- `/spotify login [client-id]` - conecta Spotify\n- `/spotify now|devices|queue` - mira qué suena, dónde y qué viene después\n- `/spotify recent|sync-recent` - mira o guarda tus últimas reproducciones\n- `/spotify import-history \"/ruta/a/spotify-history.zip\"` - añade un ZIP de Spotify, sin iniciar sesión\n- `/skip` o `/stop` - pasa al siguiente avance, o detén los avances\n- `/reload` - recarga los ajustes del modelo y los inicios de sesión\n- `/remember [tipo] <texto>` - pídeme que recuerde algo\n- `/forget <memory-id>` - olvida algo\n- `/rewind` o Alt+R - edita un mensaje anterior en esta conversación; los mensajes siguientes se reemplazan\n- `/branch` - sigue desde aquí en una conversación nueva; esta queda guardada\n- `/rewind latest` - edita el mensaje más reciente\n- `/resume` - retoma una conversación guardada\n- `/resume <session-id>` - ve directamente a una\n- `/new` - empieza de cero; la anterior queda guardada\n- `/quit` - salir\n\nTodo lo que escribas que no sea un comando va al modelo que elegiste con `/model`, o al definido en `MOONDOG_PROVIDER` y `MOONDOG_MODEL`.\nInicia sesión con `/auth`, o define la clave de API del proveedor en tu entorno. `/auth openai-codex` usa tu cuenta de ChatGPT.\nCuando tu perfil esté listo, puedo buscar en tu biblioteca, explicarte lo que veo y planear playlists contigo.\nSolo puedo controlar Spotify después de `moondog spotify login`, y nunca publico, envío mensajes, borro nada ni gasto dinero.\nPídeme pasar la reproducción a un dispositivo por su nombre, como tu iPhone.",
|
|
180
180
|
"Apple Music library": "Biblioteca de Apple Music",
|
|
181
181
|
"Model": "Modelo",
|
|
182
182
|
"Finding similar music": "Buscar música parecida",
|
|
@@ -682,10 +682,14 @@ export default {
|
|
|
682
682
|
"Use /rewind or /rewind latest.": "Usa /rewind o /rewind latest.",
|
|
683
683
|
"Conversation rewind isn't available in this runtime.": "Rebobinar la conversación no está disponible en este entorno.",
|
|
684
684
|
"No earlier messages yet.": "Todavía no hay mensajes anteriores.",
|
|
685
|
-
"Rewind to a message ·
|
|
685
|
+
"Rewind to a message · it and what follows are replaced": "Rebobinar hasta un mensaje · ese mensaje y lo que sigue se reemplazan",
|
|
686
686
|
"Rewind cancelled. Queued drafts stay paused.": "Rebobinado cancelado. Los borradores en cola siguen en pausa.",
|
|
687
|
-
"
|
|
688
|
-
"Earlier message restored · edit before sending
|
|
687
|
+
"Rewound to before this message. Edit it below, then send. Spotify actions and saved preferences from the replaced messages have not been undone.": "Rebobinado hasta antes de este mensaje. Edítalo abajo y envíalo. Las acciones de Spotify y las preferencias guardadas de los mensajes reemplazados no se han deshecho.",
|
|
688
|
+
"Earlier message restored · edit before sending": "Mensaje anterior recuperado · edítalo antes de enviar",
|
|
689
|
+
"Use /branch on its own.": "Usa /branch sin nada más.",
|
|
690
|
+
"Branching isn't available in this runtime.": "Las ramas no están disponibles en este entorno.",
|
|
691
|
+
"Nothing to branch from yet.": "Todavía no hay nada desde donde crear una rama.",
|
|
692
|
+
"Branched · the original is in /resume": "Rama creada · el original está en /resume",
|
|
689
693
|
"Use /resume, or /resume <conversation-id>.": "Usa /resume o /resume <id-de-conversación>.",
|
|
690
694
|
"Saved conversations aren't available when Moondog is started this way.": "Las conversaciones guardadas no están disponibles cuando Moondog se abre de esta forma.",
|
|
691
695
|
"No saved conversations yet.": "Todavía no hay conversaciones guardadas.",
|
|
@@ -811,7 +815,8 @@ export default {
|
|
|
811
815
|
"Ask Moondog to remember something": "Pide a Moondog que recuerde algo",
|
|
812
816
|
"Forget one memory by ID": "Olvida un recuerdo por su ID",
|
|
813
817
|
"Pick up a saved conversation": "Retoma una conversación guardada",
|
|
814
|
-
"Edit an earlier message
|
|
818
|
+
"Edit an earlier message in place · Alt+R": "Edita un mensaje anterior en esta conversación · Alt+R",
|
|
819
|
+
"Go on from here in a new conversation; this one stays saved": "Sigue desde aquí en una conversación nueva; esta queda guardada",
|
|
815
820
|
"Start fresh; this conversation stays saved": "Empieza de cero; esta conversación queda guardada",
|
|
816
821
|
"Commands and shortcuts": "Comandos y atajos",
|
|
817
822
|
"Leave Moondog": "Salir de Moondog",
|
package/src/i18n/screen/ja.mjs
CHANGED
|
@@ -176,7 +176,7 @@ export default {
|
|
|
176
176
|
"Memory": "記憶",
|
|
177
177
|
"Right now I can't change anything in your music accounts.": "いまは音楽アカウントの内容を何も変更できません。",
|
|
178
178
|
"I can make changes in your connected accounts, and I always ask first.": "接続したアカウントの内容を変更できます。変更するときは必ず先に確認します。",
|
|
179
|
-
"# Every command\n\nFrom your shell:\n\n- `moondog --version` - show the installed version\n- `moondog update [--check] [--channel latest|beta]` - check for or install a published release\n- `moondog web status|search|read [arguments] [--json]` - look things up on public music sites through DeepSeek, Brave Search or the Codex CLI\n- `moondog demo-history --output <absolute-file.zip> [--json]` - make a fictional Spotify history ZIP to try the importer with\n- `moondog auth login <provider>` - sign in to a model provider; the key is typed out of sight, and openai-codex uses your ChatGPT sign-in\n- `moondog auth status [provider]` - see which sign-ins are saved\n- `moondog auth logout <provider>` - sign out of a provider\n- `moondog spotify help` - set up and control Spotify\n- `moondog spotify login [client-id]` - connect Spotify in your browser\n- `moondog spotify now|devices|queue` - see what Spotify is playing, where, and what's next\n- `moondog spotify recent` - see what you played lately\n- `moondog spotify sync-recent` - add your latest plays to your profile\n- `moondog spotify sync-library` - add your Spotify library to your profile\n- `moondog spotify play|pause|next|previous` - control Spotify playback\n- `moondog listenbrainz import-history <listen-history.json>` - add a ListenBrainz listens file to your profile\n- `moondog catalog latest-single --artist <name> [--known-release <title> | --from <spotify-history.zip>]` - find an artist's latest single on Apple Music (US), no model needed\n- `moondog status [--json]` - see where things stand\n- `moondog taste [--json]` - your listening report\n- `moondog taste --html [--output <file.html>]` - save your report as a web page that works offline\n- `moondog taste --card [--output <file.html>]` - save a short recap you can check before sharing\n- `moondog taste --from <spotify-history.zip> --html` - a one-off report from a Spotify ZIP, without saving it\n- `moondog taste --from <account-data.zip> --from <extended-history.zip> --html` - a one-off report from both Spotify exports together\n- `moondog taste --from <spotify-history.zip> --save --html` - save the Spotify history, then show your full report\n- `moondog data inspect --scope <listening|apple|profile>` - see what's stored, and get the code needed to reset it\n- `moondog data export --scope <listening|apple|profile> --output <directory>` - export a copy, leaving the original alone\n- `moondog data reset --scope <listening|apple|profile> --confirm <token>` - set stored data aside (it is archived, not deleted)\n- `moondog profile build [--force]` - build or resume a saved profile with your connected model\n- `moondog profile saved [offset]` - read your saved profile\n- `moondog profile explain <number>` - trace a saved finding to its evidence\n- `moondog profile corrections [--all]` - everything you've told me, including earlier choices\n- `moondog profile correct --artist <name> (--like|--avoid)` - like an artist, or keep them out\n- `moondog profile correct --track <title> --by <artist> (--like|--avoid)` - like a track, or keep it out\n- `moondog profile retract <correction-id>` - undo one of your choices\n- `moondog demo --offline [--json]` - a demo with made-up data, no sign-in needed\n- `moondog demo [prompt] [--json]` - the same demo, talking through your model\n- `moondog ask <prompt>` - ask one question and exit\n- `moondog remember <text>` - ask me to remember something\n- `moondog forget <memory-id>` - forget something\n- `moondog memory reflect [--dry-run] [--json]` - let me think over recent conversations now\n- `moondog` - open the listening room\n\nInside the listening room:\n\n- `/update [--check] [--channel latest|beta]` - check for updates, or save and close this session to install one\n- `/status` or `/sources` - see where things stand\n- `/profile` or `/taste` - your profile, and why each song or artist is there\n- `/taste report` - the whole report on one page\n- `/profile build [--force]` - build or resume a saved reading; Ctrl+C keeps progress\n- `/profile saved [offset]` - read your saved profile\n- `/profile explain <number>` - inspect a saved finding's evidence\n- `/profile corrections [--all]` - everything you've told me\n- `/profile correct --track \"<title>\" --by \"<artist>\" (--like|--avoid)` - like a track, or keep it out\n- `/profile correct --artist \"<name>\" (--like|--avoid)` - like an artist, or keep them out\n- `/profile retract <correction-id>` - undo a choice\n- `/memory` - what I remember\n- `/tools` - what I can do right now\n- `/doctor` - check your setup\n- `/model` - pick the model I talk through\n- `/model <provider> <model>` - switch straight to a model\n- `/model refresh [provider]` - update public model choices; keep the selected model\n- `/auth [provider]` - sign in to a model provider\n- `/web status|search|read` - look things up on public music sites\n- `/import [path]` - bring in your history, library, or a playlist\n- `/language` - choose the language Moondog uses\n- `/spotify` - Spotify commands\n- `/spotify login [client-id]` - connect Spotify\n- `/spotify now|devices|queue` - see what's playing, where, and what's next\n- `/spotify recent|sync-recent` - see or save your latest plays\n- `/spotify import-history \"/path/to/spotify-history.zip\"` - add a Spotify ZIP, no sign-in needed\n- `/skip` or `/stop` - move to the next song preview, or stop them\n- `/reload` - reload model settings and sign-ins\n- `/remember [kind] <text>` - ask me to remember something\n- `/forget <memory-id>` - forget something\n- `/rewind` or Alt+R - edit an earlier message on a new branch; the original stays saved\n- `/rewind latest` - edit the most recent message\n- `/resume` - pick up a saved conversation\n- `/resume <session-id>` - go straight to one\n- `/new` - start fresh; the last one stays saved\n- `/quit` - exit\n\nAnything you type that isn't a command goes to the model you picked with `/model`, or the one set in `MOONDOG_PROVIDER` and `MOONDOG_MODEL`.\nSign in with `/auth`, or set the provider's API key in your environment. `/auth openai-codex` uses your ChatGPT sign-in.\nOnce your profile is ready, I can search your library, explain what I see, and plan playlists with you.\nI can only control Spotify after `moondog spotify login`, and I never post, send messages, delete anything, or spend money.\nAsk me to move playback onto a device by name, such as your iPhone.": "# すべてのコマンド\n\nターミナルから:\n\n- `moondog --version` - インストール済みのバージョンを表示\n- `moondog update [--check] [--channel latest|beta]` - 公開済みのリリースを確認またはインストール\n- `moondog web status|search|read [引数] [--json]` - DeepSeek、Brave Search、または Codex CLI で公開の音楽サイトを調べる\n- `moondog demo-history --output <絶対パス.zip> [--json]` - 取り込みを試すための架空の Spotify 履歴 ZIP を作る\n- `moondog auth login <provider>` - モデルプロバイダーにサインイン。キーは表示されずに入力でき、openai-codex は ChatGPT のサインインを使います\n- `moondog auth status [provider]` - 保存されているサインインを確認\n- `moondog auth logout <provider>` - プロバイダーからサインアウト\n- `moondog spotify help` - Spotify の設定と操作\n- `moondog spotify login [client-id]` - ブラウザで Spotify を接続\n- `moondog spotify now|devices|queue` - Spotify で再生中のもの、再生先、次の曲を見る\n- `moondog spotify recent` - 最近聴いたものを見る\n- `moondog spotify sync-recent` - 最近の再生をプロフィールに追加\n- `moondog spotify sync-library` - Spotify ライブラリをプロフィールに追加\n- `moondog spotify play|pause|next|previous` - Spotify の再生を操作\n- `moondog listenbrainz import-history <listen-history.json>` - ListenBrainz の再生ファイルをプロフィールに追加\n- `moondog catalog latest-single --artist <名前> [--known-release <タイトル> | --from <spotify-history.zip>]` - Apple Music(米国)でアーティストの最新シングルを探す。モデル不要\n- `moondog status [--json]` - 現在の状況を見る\n- `moondog taste [--json]` - あなたのリスニングレポート\n- `moondog taste --html [--output <ファイル.html>]` - オフラインでも開ける Web ページとして保存\n- `moondog taste --card [--output <ファイル.html>]` - 共有前に確認できる短いまとめを保存\n- `moondog taste --from <spotify-history.zip> --html` - Spotify の ZIP から一度だけのレポートを作る(保存しない)\n- `moondog taste --from <account-data.zip> --from <extended-history.zip> --html` - 2 つの Spotify エクスポートから一度だけのレポートを作る\n- `moondog taste --from <spotify-history.zip> --save --html` - Spotify の履歴を保存してから完全なレポートを表示\n- `moondog data inspect --scope <listening|apple|profile>` - 保存内容と、リセットに必要なコードを確認\n- `moondog data export --scope <listening|apple|profile> --output <フォルダ>` - 元のデータはそのままにコピーを書き出す\n- `moondog data reset --scope <listening|apple|profile> --confirm <コード>` - 保存データを退避(削除ではなくアーカイブ)\n- `moondog profile build [--force]` - 接続中のモデルで保存プロフィールを作成または再開\n- `moondog profile saved [オフセット]` - 保存したプロフィールを読む\n- `moondog profile explain <番号>` - 保存した発見を根拠までたどる\n- `moondog profile corrections [--all]` - 以前のものも含め、教えてもらった選択の一覧\n- `moondog profile correct --artist <名前> (--like|--avoid)` - アーティストを好きにする、または除外する\n- `moondog profile correct --track <曲名> --by <アーティスト> (--like|--avoid)` - 曲を好きにする、または除外する\n- `moondog profile retract <correction-id>` - 選択を取り消す\n- `moondog demo --offline [--json]` - 架空のデータを使ったデモ。サインイン不要\n- `moondog demo [プロンプト] [--json]` - 同じデモをあなたのモデルで\n- `moondog ask <プロンプト>` - 質問をひとつして終了\n- `moondog remember <内容>` - 覚えておくよう頼む\n- `moondog forget <memory-id>` - 忘れてもらう\n- `moondog memory reflect [--dry-run] [--json]` - 最近の会話をいま振り返る\n- `moondog` - リスニングルームを開く\n\nリスニングルームの中で:\n\n- `/update [--check] [--channel latest|beta]` - アップデートを確認、またはセッションを保存して閉じ、インストール\n- `/status` または `/sources` - 現在の状況を見る\n- `/profile` または `/taste` - あなたのプロフィールと、各曲・各アーティストがある理由\n- `/taste report` - 完全なレポートを 1 ページで\n- `/profile build [--force]` - 保存する読み取りを作成または再開。Ctrl+C でも進捗は残ります\n- `/profile saved [オフセット]` - 保存したプロフィールを読む\n- `/profile explain <番号>` - 保存した発見の根拠を見る\n- `/profile corrections [--all]` - 教えてもらった選択の一覧\n- `/profile correct --track \"<曲名>\" --by \"<アーティスト>\" (--like|--avoid)` - 曲を好きにする、または除外する\n- `/profile correct --artist \"<名前>\" (--like|--avoid)` - アーティストを好きにする、または除外する\n- `/profile retract <correction-id>` - 選択を取り消す\n- `/memory` - 覚えていること\n- `/tools` - いまできること\n- `/doctor` - 設定を診断\n- `/model` - 会話に使うモデルを選ぶ\n- `/model <provider> <model>` - そのモデルに直接切り替える\n- `/model refresh [provider]` - 公開のモデル一覧を更新。選んだモデルはそのまま\n- `/auth [provider]` - モデルプロバイダーにサインイン\n- `/web status|search|read` - 公開の音楽サイトで調べる\n- `/import [パス]` - 履歴、ライブラリ、プレイリストを取り込む\n- `/language` - Moondog の言語を選ぶ\n- `/spotify` - Spotify のコマンド\n- `/spotify login [client-id]` - Spotify を接続\n- `/spotify now|devices|queue` - 再生中のもの、再生先、次の曲を見る\n- `/spotify recent|sync-recent` - 最近の再生を見る、または保存\n- `/spotify import-history \"/path/to/spotify-history.zip\"` - Spotify の ZIP を追加。サインイン不要\n- `/skip` または `/stop` - 次のプレビューへ進む、またはプレビューを止める\n- `/reload` - モデル設定とサインインを読み込み直す\n- `/remember [種類] <内容>` - 覚えておくよう頼む\n- `/forget <memory-id>` - 忘れてもらう\n- `/rewind` または Alt+R - 以前のメッセージを新しいブランチで編集。元のものは残ります\n- `/rewind latest` - 直近のメッセージを編集\n- `/resume` - 保存した会話を再開\n- `/resume <session-id>` - その会話を直接開く\n- `/new` - 新しく始める。前の会話は保存されます\n- `/quit` - 終了\n\nコマンド以外の入力は、`/model` で選んだモデル、または `MOONDOG_PROVIDER` と `MOONDOG_MODEL` で指定したモデルに送られます。\n`/auth` でサインインするか、環境変数にプロバイダーの API キーを設定してください。`/auth openai-codex` は ChatGPT のサインインを使います。\nプロフィールの準備ができたら、ライブラリを検索したり、わかったことを説明したり、一緒にプレイリストを考えたりできます。\nSpotify を操作できるのは `moondog spotify login` の後だけです。投稿、メッセージ送信、削除、支払いは一切しません。\niPhone のように名前を言ってもらえれば、そのデバイスに再生を切り替えます。",
|
|
179
|
+
"# Every command\n\nFrom your shell:\n\n- `moondog --version` - show the installed version\n- `moondog update [--check] [--channel latest|beta]` - check for or install a published release\n- `moondog web status|search|read [arguments] [--json]` - look things up on public music sites through DeepSeek, Brave Search or the Codex CLI\n- `moondog demo-history --output <absolute-file.zip> [--json]` - make a fictional Spotify history ZIP to try the importer with\n- `moondog auth login <provider>` - sign in to a model provider; the key is typed out of sight, and openai-codex uses your ChatGPT sign-in\n- `moondog auth status [provider]` - see which sign-ins are saved\n- `moondog auth logout <provider>` - sign out of a provider\n- `moondog spotify help` - set up and control Spotify\n- `moondog spotify login [client-id]` - connect Spotify in your browser\n- `moondog spotify now|devices|queue` - see what Spotify is playing, where, and what's next\n- `moondog spotify recent` - see what you played lately\n- `moondog spotify sync-recent` - add your latest plays to your profile\n- `moondog spotify sync-library` - add your Spotify library to your profile\n- `moondog spotify play|pause|next|previous` - control Spotify playback\n- `moondog listenbrainz import-history <listen-history.json>` - add a ListenBrainz listens file to your profile\n- `moondog catalog latest-single --artist <name> [--known-release <title> | --from <spotify-history.zip>]` - find an artist's latest single on Apple Music (US), no model needed\n- `moondog status [--json]` - see where things stand\n- `moondog taste [--json]` - your listening report\n- `moondog taste --html [--output <file.html>]` - save your report as a web page that works offline\n- `moondog taste --card [--output <file.html>]` - save a short recap you can check before sharing\n- `moondog taste --from <spotify-history.zip> --html` - a one-off report from a Spotify ZIP, without saving it\n- `moondog taste --from <account-data.zip> --from <extended-history.zip> --html` - a one-off report from both Spotify exports together\n- `moondog taste --from <spotify-history.zip> --save --html` - save the Spotify history, then show your full report\n- `moondog data inspect --scope <listening|apple|profile>` - see what's stored, and get the code needed to reset it\n- `moondog data export --scope <listening|apple|profile> --output <directory>` - export a copy, leaving the original alone\n- `moondog data reset --scope <listening|apple|profile> --confirm <token>` - set stored data aside (it is archived, not deleted)\n- `moondog profile build [--force]` - build or resume a saved profile with your connected model\n- `moondog profile saved [offset]` - read your saved profile\n- `moondog profile explain <number>` - trace a saved finding to its evidence\n- `moondog profile corrections [--all]` - everything you've told me, including earlier choices\n- `moondog profile correct --artist <name> (--like|--avoid)` - like an artist, or keep them out\n- `moondog profile correct --track <title> --by <artist> (--like|--avoid)` - like a track, or keep it out\n- `moondog profile retract <correction-id>` - undo one of your choices\n- `moondog demo --offline [--json]` - a demo with made-up data, no sign-in needed\n- `moondog demo [prompt] [--json]` - the same demo, talking through your model\n- `moondog ask <prompt>` - ask one question and exit\n- `moondog remember <text>` - ask me to remember something\n- `moondog forget <memory-id>` - forget something\n- `moondog memory reflect [--dry-run] [--json]` - let me think over recent conversations now\n- `moondog` - open the listening room\n\nInside the listening room:\n\n- `/update [--check] [--channel latest|beta]` - check for updates, or save and close this session to install one\n- `/status` or `/sources` - see where things stand\n- `/profile` or `/taste` - your profile, and why each song or artist is there\n- `/taste report` - the whole report on one page\n- `/profile build [--force]` - build or resume a saved reading; Ctrl+C keeps progress\n- `/profile saved [offset]` - read your saved profile\n- `/profile explain <number>` - inspect a saved finding's evidence\n- `/profile corrections [--all]` - everything you've told me\n- `/profile correct --track \"<title>\" --by \"<artist>\" (--like|--avoid)` - like a track, or keep it out\n- `/profile correct --artist \"<name>\" (--like|--avoid)` - like an artist, or keep them out\n- `/profile retract <correction-id>` - undo a choice\n- `/memory` - what I remember\n- `/tools` - what I can do right now\n- `/doctor` - check your setup\n- `/model` - pick the model I talk through\n- `/model <provider> <model>` - switch straight to a model\n- `/model refresh [provider]` - update public model choices; keep the selected model\n- `/auth [provider]` - sign in to a model provider\n- `/web status|search|read` - look things up on public music sites\n- `/import [path]` - bring in your history, library, or a playlist\n- `/language` - choose the language Moondog uses\n- `/spotify` - Spotify commands\n- `/spotify login [client-id]` - connect Spotify\n- `/spotify now|devices|queue` - see what's playing, where, and what's next\n- `/spotify recent|sync-recent` - see or save your latest plays\n- `/spotify import-history \"/path/to/spotify-history.zip\"` - add a Spotify ZIP, no sign-in needed\n- `/skip` or `/stop` - move to the next song preview, or stop them\n- `/reload` - reload model settings and sign-ins\n- `/remember [kind] <text>` - ask me to remember something\n- `/forget <memory-id>` - forget something\n- `/rewind` or Alt+R - edit an earlier message in place; the messages after it are replaced\n- `/branch` - go on from here in a new conversation; this one stays saved\n- `/rewind latest` - edit the most recent message\n- `/resume` - pick up a saved conversation\n- `/resume <session-id>` - go straight to one\n- `/new` - start fresh; the last one stays saved\n- `/quit` - exit\n\nAnything you type that isn't a command goes to the model you picked with `/model`, or the one set in `MOONDOG_PROVIDER` and `MOONDOG_MODEL`.\nSign in with `/auth`, or set the provider's API key in your environment. `/auth openai-codex` uses your ChatGPT sign-in.\nOnce your profile is ready, I can search your library, explain what I see, and plan playlists with you.\nI can only control Spotify after `moondog spotify login`, and I never post, send messages, delete anything, or spend money.\nAsk me to move playback onto a device by name, such as your iPhone.": "# すべてのコマンド\n\nターミナルから:\n\n- `moondog --version` - インストール済みのバージョンを表示\n- `moondog update [--check] [--channel latest|beta]` - 公開済みのリリースを確認またはインストール\n- `moondog web status|search|read [引数] [--json]` - DeepSeek、Brave Search、または Codex CLI で公開の音楽サイトを調べる\n- `moondog demo-history --output <絶対パス.zip> [--json]` - 取り込みを試すための架空の Spotify 履歴 ZIP を作る\n- `moondog auth login <provider>` - モデルプロバイダーにサインイン。キーは表示されずに入力でき、openai-codex は ChatGPT のサインインを使います\n- `moondog auth status [provider]` - 保存されているサインインを確認\n- `moondog auth logout <provider>` - プロバイダーからサインアウト\n- `moondog spotify help` - Spotify の設定と操作\n- `moondog spotify login [client-id]` - ブラウザで Spotify を接続\n- `moondog spotify now|devices|queue` - Spotify で再生中のもの、再生先、次の曲を見る\n- `moondog spotify recent` - 最近聴いたものを見る\n- `moondog spotify sync-recent` - 最近の再生をプロフィールに追加\n- `moondog spotify sync-library` - Spotify ライブラリをプロフィールに追加\n- `moondog spotify play|pause|next|previous` - Spotify の再生を操作\n- `moondog listenbrainz import-history <listen-history.json>` - ListenBrainz の再生ファイルをプロフィールに追加\n- `moondog catalog latest-single --artist <名前> [--known-release <タイトル> | --from <spotify-history.zip>]` - Apple Music(米国)でアーティストの最新シングルを探す。モデル不要\n- `moondog status [--json]` - 現在の状況を見る\n- `moondog taste [--json]` - あなたのリスニングレポート\n- `moondog taste --html [--output <ファイル.html>]` - オフラインでも開ける Web ページとして保存\n- `moondog taste --card [--output <ファイル.html>]` - 共有前に確認できる短いまとめを保存\n- `moondog taste --from <spotify-history.zip> --html` - Spotify の ZIP から一度だけのレポートを作る(保存しない)\n- `moondog taste --from <account-data.zip> --from <extended-history.zip> --html` - 2 つの Spotify エクスポートから一度だけのレポートを作る\n- `moondog taste --from <spotify-history.zip> --save --html` - Spotify の履歴を保存してから完全なレポートを表示\n- `moondog data inspect --scope <listening|apple|profile>` - 保存内容と、リセットに必要なコードを確認\n- `moondog data export --scope <listening|apple|profile> --output <フォルダ>` - 元のデータはそのままにコピーを書き出す\n- `moondog data reset --scope <listening|apple|profile> --confirm <コード>` - 保存データを退避(削除ではなくアーカイブ)\n- `moondog profile build [--force]` - 接続中のモデルで保存プロフィールを作成または再開\n- `moondog profile saved [オフセット]` - 保存したプロフィールを読む\n- `moondog profile explain <番号>` - 保存した発見を根拠までたどる\n- `moondog profile corrections [--all]` - 以前のものも含め、教えてもらった選択の一覧\n- `moondog profile correct --artist <名前> (--like|--avoid)` - アーティストを好きにする、または除外する\n- `moondog profile correct --track <曲名> --by <アーティスト> (--like|--avoid)` - 曲を好きにする、または除外する\n- `moondog profile retract <correction-id>` - 選択を取り消す\n- `moondog demo --offline [--json]` - 架空のデータを使ったデモ。サインイン不要\n- `moondog demo [プロンプト] [--json]` - 同じデモをあなたのモデルで\n- `moondog ask <プロンプト>` - 質問をひとつして終了\n- `moondog remember <内容>` - 覚えておくよう頼む\n- `moondog forget <memory-id>` - 忘れてもらう\n- `moondog memory reflect [--dry-run] [--json]` - 最近の会話をいま振り返る\n- `moondog` - リスニングルームを開く\n\nリスニングルームの中で:\n\n- `/update [--check] [--channel latest|beta]` - アップデートを確認、またはセッションを保存して閉じ、インストール\n- `/status` または `/sources` - 現在の状況を見る\n- `/profile` または `/taste` - あなたのプロフィールと、各曲・各アーティストがある理由\n- `/taste report` - 完全なレポートを 1 ページで\n- `/profile build [--force]` - 保存する読み取りを作成または再開。Ctrl+C でも進捗は残ります\n- `/profile saved [オフセット]` - 保存したプロフィールを読む\n- `/profile explain <番号>` - 保存した発見の根拠を見る\n- `/profile corrections [--all]` - 教えてもらった選択の一覧\n- `/profile correct --track \"<曲名>\" --by \"<アーティスト>\" (--like|--avoid)` - 曲を好きにする、または除外する\n- `/profile correct --artist \"<名前>\" (--like|--avoid)` - アーティストを好きにする、または除外する\n- `/profile retract <correction-id>` - 選択を取り消す\n- `/memory` - 覚えていること\n- `/tools` - いまできること\n- `/doctor` - 設定を診断\n- `/model` - 会話に使うモデルを選ぶ\n- `/model <provider> <model>` - そのモデルに直接切り替える\n- `/model refresh [provider]` - 公開のモデル一覧を更新。選んだモデルはそのまま\n- `/auth [provider]` - モデルプロバイダーにサインイン\n- `/web status|search|read` - 公開の音楽サイトで調べる\n- `/import [パス]` - 履歴、ライブラリ、プレイリストを取り込む\n- `/language` - Moondog の言語を選ぶ\n- `/spotify` - Spotify のコマンド\n- `/spotify login [client-id]` - Spotify を接続\n- `/spotify now|devices|queue` - 再生中のもの、再生先、次の曲を見る\n- `/spotify recent|sync-recent` - 最近の再生を見る、または保存\n- `/spotify import-history \"/path/to/spotify-history.zip\"` - Spotify の ZIP を追加。サインイン不要\n- `/skip` または `/stop` - 次のプレビューへ進む、またはプレビューを止める\n- `/reload` - モデル設定とサインインを読み込み直す\n- `/remember [種類] <内容>` - 覚えておくよう頼む\n- `/forget <memory-id>` - 忘れてもらう\n- `/rewind` または Alt+R - 以前のメッセージをその場で編集。それ以降のメッセージは置き換わります\n- `/branch` - ここから新しい会話で続ける。この会話は保存されたままです\n- `/rewind latest` - 直近のメッセージを編集\n- `/resume` - 保存した会話を再開\n- `/resume <session-id>` - その会話を直接開く\n- `/new` - 新しく始める。前の会話は保存されます\n- `/quit` - 終了\n\nコマンド以外の入力は、`/model` で選んだモデル、または `MOONDOG_PROVIDER` と `MOONDOG_MODEL` で指定したモデルに送られます。\n`/auth` でサインインするか、環境変数にプロバイダーの API キーを設定してください。`/auth openai-codex` は ChatGPT のサインインを使います。\nプロフィールの準備ができたら、ライブラリを検索したり、わかったことを説明したり、一緒にプレイリストを考えたりできます。\nSpotify を操作できるのは `moondog spotify login` の後だけです。投稿、メッセージ送信、削除、支払いは一切しません。\niPhone のように名前を言ってもらえれば、そのデバイスに再生を切り替えます。",
|
|
180
180
|
"Apple Music library": "Apple Music ライブラリ",
|
|
181
181
|
"Model": "モデル",
|
|
182
182
|
"Finding similar music": "似た音楽を探す",
|
|
@@ -682,10 +682,14 @@ export default {
|
|
|
682
682
|
"Use /rewind or /rewind latest.": "/rewind または /rewind latest を使ってください。",
|
|
683
683
|
"Conversation rewind isn't available in this runtime.": "この実行環境では会話を巻き戻せません。",
|
|
684
684
|
"No earlier messages yet.": "まだ以前のメッセージがありません。",
|
|
685
|
-
"Rewind to a message ·
|
|
685
|
+
"Rewind to a message · it and what follows are replaced": "メッセージまで巻き戻す · そのメッセージ以降は置き換わります",
|
|
686
686
|
"Rewind cancelled. Queued drafts stay paused.": "巻き戻しをキャンセルしました。待ちの下書きは一時停止のままです。",
|
|
687
|
-
"
|
|
688
|
-
"Earlier message restored · edit before sending
|
|
687
|
+
"Rewound to before this message. Edit it below, then send. Spotify actions and saved preferences from the replaced messages have not been undone.": "このメッセージの前まで巻き戻しました。下で編集してから送信してください。置き換えたメッセージでの Spotify の操作や保存した好みは取り消されていません。",
|
|
688
|
+
"Earlier message restored · edit before sending": "以前のメッセージを復元しました · 送信前に編集",
|
|
689
|
+
"Use /branch on its own.": "/branch は単独で使ってください。",
|
|
690
|
+
"Branching isn't available in this runtime.": "この実行環境では分岐できません。",
|
|
691
|
+
"Nothing to branch from yet.": "まだ分岐できる内容がありません。",
|
|
692
|
+
"Branched · the original is in /resume": "分岐しました · 元の会話は /resume にあります",
|
|
689
693
|
"Use /resume, or /resume <conversation-id>.": "/resume、または /resume <会話 ID> を使ってください。",
|
|
690
694
|
"Saved conversations aren't available when Moondog is started this way.": "この起動方法では保存した会話を使えません。",
|
|
691
695
|
"No saved conversations yet.": "保存した会話はまだありません。",
|
|
@@ -811,7 +815,8 @@ export default {
|
|
|
811
815
|
"Ask Moondog to remember something": "Moondog に覚えておくよう頼む",
|
|
812
816
|
"Forget one memory by ID": "ID を指定して記憶を 1 つ忘れる",
|
|
813
817
|
"Pick up a saved conversation": "保存した会話を再開",
|
|
814
|
-
"Edit an earlier message
|
|
818
|
+
"Edit an earlier message in place · Alt+R": "以前のメッセージをその場で編集 · Alt+R",
|
|
819
|
+
"Go on from here in a new conversation; this one stays saved": "ここから新しい会話で続ける。この会話は保存されたままです",
|
|
815
820
|
"Start fresh; this conversation stays saved": "新しく始める。この会話は保存されます",
|
|
816
821
|
"Commands and shortcuts": "コマンドとショートカット",
|
|
817
822
|
"Leave Moondog": "Moondog を終了",
|
package/src/i18n/screen/pt.mjs
CHANGED
|
@@ -176,7 +176,7 @@ export default {
|
|
|
176
176
|
"Memory": "Memória",
|
|
177
177
|
"Right now I can't change anything in your music accounts.": "No momento não consigo mudar nada nas suas contas de música.",
|
|
178
178
|
"I can make changes in your connected accounts, and I always ask first.": "Posso fazer mudanças nas suas contas conectadas, e sempre pergunto antes.",
|
|
179
|
-
"# Every command\n\nFrom your shell:\n\n- `moondog --version` - show the installed version\n- `moondog update [--check] [--channel latest|beta]` - check for or install a published release\n- `moondog web status|search|read [arguments] [--json]` - look things up on public music sites through DeepSeek, Brave Search or the Codex CLI\n- `moondog demo-history --output <absolute-file.zip> [--json]` - make a fictional Spotify history ZIP to try the importer with\n- `moondog auth login <provider>` - sign in to a model provider; the key is typed out of sight, and openai-codex uses your ChatGPT sign-in\n- `moondog auth status [provider]` - see which sign-ins are saved\n- `moondog auth logout <provider>` - sign out of a provider\n- `moondog spotify help` - set up and control Spotify\n- `moondog spotify login [client-id]` - connect Spotify in your browser\n- `moondog spotify now|devices|queue` - see what Spotify is playing, where, and what's next\n- `moondog spotify recent` - see what you played lately\n- `moondog spotify sync-recent` - add your latest plays to your profile\n- `moondog spotify sync-library` - add your Spotify library to your profile\n- `moondog spotify play|pause|next|previous` - control Spotify playback\n- `moondog listenbrainz import-history <listen-history.json>` - add a ListenBrainz listens file to your profile\n- `moondog catalog latest-single --artist <name> [--known-release <title> | --from <spotify-history.zip>]` - find an artist's latest single on Apple Music (US), no model needed\n- `moondog status [--json]` - see where things stand\n- `moondog taste [--json]` - your listening report\n- `moondog taste --html [--output <file.html>]` - save your report as a web page that works offline\n- `moondog taste --card [--output <file.html>]` - save a short recap you can check before sharing\n- `moondog taste --from <spotify-history.zip> --html` - a one-off report from a Spotify ZIP, without saving it\n- `moondog taste --from <account-data.zip> --from <extended-history.zip> --html` - a one-off report from both Spotify exports together\n- `moondog taste --from <spotify-history.zip> --save --html` - save the Spotify history, then show your full report\n- `moondog data inspect --scope <listening|apple|profile>` - see what's stored, and get the code needed to reset it\n- `moondog data export --scope <listening|apple|profile> --output <directory>` - export a copy, leaving the original alone\n- `moondog data reset --scope <listening|apple|profile> --confirm <token>` - set stored data aside (it is archived, not deleted)\n- `moondog profile build [--force]` - build or resume a saved profile with your connected model\n- `moondog profile saved [offset]` - read your saved profile\n- `moondog profile explain <number>` - trace a saved finding to its evidence\n- `moondog profile corrections [--all]` - everything you've told me, including earlier choices\n- `moondog profile correct --artist <name> (--like|--avoid)` - like an artist, or keep them out\n- `moondog profile correct --track <title> --by <artist> (--like|--avoid)` - like a track, or keep it out\n- `moondog profile retract <correction-id>` - undo one of your choices\n- `moondog demo --offline [--json]` - a demo with made-up data, no sign-in needed\n- `moondog demo [prompt] [--json]` - the same demo, talking through your model\n- `moondog ask <prompt>` - ask one question and exit\n- `moondog remember <text>` - ask me to remember something\n- `moondog forget <memory-id>` - forget something\n- `moondog memory reflect [--dry-run] [--json]` - let me think over recent conversations now\n- `moondog` - open the listening room\n\nInside the listening room:\n\n- `/update [--check] [--channel latest|beta]` - check for updates, or save and close this session to install one\n- `/status` or `/sources` - see where things stand\n- `/profile` or `/taste` - your profile, and why each song or artist is there\n- `/taste report` - the whole report on one page\n- `/profile build [--force]` - build or resume a saved reading; Ctrl+C keeps progress\n- `/profile saved [offset]` - read your saved profile\n- `/profile explain <number>` - inspect a saved finding's evidence\n- `/profile corrections [--all]` - everything you've told me\n- `/profile correct --track \"<title>\" --by \"<artist>\" (--like|--avoid)` - like a track, or keep it out\n- `/profile correct --artist \"<name>\" (--like|--avoid)` - like an artist, or keep them out\n- `/profile retract <correction-id>` - undo a choice\n- `/memory` - what I remember\n- `/tools` - what I can do right now\n- `/doctor` - check your setup\n- `/model` - pick the model I talk through\n- `/model <provider> <model>` - switch straight to a model\n- `/model refresh [provider]` - update public model choices; keep the selected model\n- `/auth [provider]` - sign in to a model provider\n- `/web status|search|read` - look things up on public music sites\n- `/import [path]` - bring in your history, library, or a playlist\n- `/language` - choose the language Moondog uses\n- `/spotify` - Spotify commands\n- `/spotify login [client-id]` - connect Spotify\n- `/spotify now|devices|queue` - see what's playing, where, and what's next\n- `/spotify recent|sync-recent` - see or save your latest plays\n- `/spotify import-history \"/path/to/spotify-history.zip\"` - add a Spotify ZIP, no sign-in needed\n- `/skip` or `/stop` - move to the next song preview, or stop them\n- `/reload` - reload model settings and sign-ins\n- `/remember [kind] <text>` - ask me to remember something\n- `/forget <memory-id>` - forget something\n- `/rewind` or Alt+R - edit an earlier message on a new branch; the original stays saved\n- `/rewind latest` - edit the most recent message\n- `/resume` - pick up a saved conversation\n- `/resume <session-id>` - go straight to one\n- `/new` - start fresh; the last one stays saved\n- `/quit` - exit\n\nAnything you type that isn't a command goes to the model you picked with `/model`, or the one set in `MOONDOG_PROVIDER` and `MOONDOG_MODEL`.\nSign in with `/auth`, or set the provider's API key in your environment. `/auth openai-codex` uses your ChatGPT sign-in.\nOnce your profile is ready, I can search your library, explain what I see, and plan playlists with you.\nI can only control Spotify after `moondog spotify login`, and I never post, send messages, delete anything, or spend money.\nAsk me to move playback onto a device by name, such as your iPhone.": "# Todos os comandos\n\nNo seu terminal:\n\n- `moondog --version` - mostra a versão instalada\n- `moondog update [--check] [--channel latest|beta]` - procura ou instala uma versão publicada\n- `moondog web status|search|read [argumentos] [--json]` - pesquisa em sites públicos de música pelo DeepSeek, pelo Brave Search ou pelo Codex CLI\n- `moondog demo-history --output <arquivo-absoluto.zip> [--json]` - cria um ZIP fictício de histórico do Spotify para testar a importação\n- `moondog auth login <provedor>` - faz login em um provedor de modelos; a chave é digitada sem aparecer, e o openai-codex usa seu login do ChatGPT\n- `moondog auth status [provedor]` - mostra quais logins estão salvos\n- `moondog auth logout <provedor>` - sai de um provedor\n- `moondog spotify help` - configura e controla o Spotify\n- `moondog spotify login [client-id]` - conecta o Spotify no navegador\n- `moondog spotify now|devices|queue` - mostra o que o Spotify está tocando, onde e o que vem depois\n- `moondog spotify recent` - mostra o que você ouviu ultimamente\n- `moondog spotify sync-recent` - adiciona suas últimas reproduções ao perfil\n- `moondog spotify sync-library` - adiciona sua biblioteca do Spotify ao perfil\n- `moondog spotify play|pause|next|previous` - controla a reprodução do Spotify\n- `moondog listenbrainz import-history <listen-history.json>` - adiciona um arquivo de escutas do ListenBrainz ao perfil\n- `moondog catalog latest-single --artist <nome> [--known-release <título> | --from <spotify-history.zip>]` - encontra o último single de um artista no Apple Music (EUA), sem modelo\n- `moondog status [--json]` - mostra como estão as coisas\n- `moondog taste [--json]` - seu relatório de escuta\n- `moondog taste --html [--output <arquivo.html>]` - salva o relatório como página web que funciona offline\n- `moondog taste --card [--output <arquivo.html>]` - salva um resumo curto que você pode conferir antes de compartilhar\n- `moondog taste --from <spotify-history.zip> --html` - um relatório único de um ZIP do Spotify, sem salvar\n- `moondog taste --from <account-data.zip> --from <extended-history.zip> --html` - um relatório único das duas exportações do Spotify juntas\n- `moondog taste --from <spotify-history.zip> --save --html` - salva o histórico do Spotify e mostra o relatório completo\n- `moondog data inspect --scope <listening|apple|profile>` - mostra o que está guardado e o código para redefinir\n- `moondog data export --scope <listening|apple|profile> --output <pasta>` - exporta uma cópia sem mexer no original\n- `moondog data reset --scope <listening|apple|profile> --confirm <código>` - deixa de lado os dados guardados (são arquivados, não apagados)\n- `moondog profile build [--force]` - cria ou retoma um perfil salvo com o modelo conectado\n- `moondog profile saved [deslocamento]` - lê seu perfil salvo\n- `moondog profile explain <número>` - rastreia uma descoberta salva até a evidência\n- `moondog profile corrections [--all]` - tudo o que você me disse, inclusive escolhas anteriores\n- `moondog profile correct --artist <nome> (--like|--avoid)` - marca que você curte um artista, ou tira ele das sugestões\n- `moondog profile correct --track <título> --by <artista> (--like|--avoid)` - marca que você curte uma música, ou tira ela das sugestões\n- `moondog profile retract <correction-id>` - desfaz uma das suas escolhas\n- `moondog demo --offline [--json]` - uma demonstração com dados inventados, sem login\n- `moondog demo [pedido] [--json]` - a mesma demonstração, conversando pelo seu modelo\n- `moondog ask <pedido>` - faz uma pergunta e sai\n- `moondog remember <texto>` - pede para eu lembrar de algo\n- `moondog forget <memory-id>` - esquece algo\n- `moondog memory reflect [--dry-run] [--json]` - me deixa repassar agora as conversas recentes\n- `moondog` - abre a sala de escuta\n\nDentro da sala de escuta:\n\n- `/update [--check] [--channel latest|beta]` - procura atualizações, ou salva e fecha esta sessão para instalar uma\n- `/status` ou `/sources` - mostra como estão as coisas\n- `/profile` ou `/taste` - seu perfil, e por que cada música ou artista está ali\n- `/taste report` - o relatório completo em uma página\n- `/profile build [--force]` - cria ou retoma uma leitura salva; Ctrl+C guarda o progresso\n- `/profile saved [deslocamento]` - lê seu perfil salvo\n- `/profile explain <número>` - mostra a evidência de uma descoberta salva\n- `/profile corrections [--all]` - tudo o que você me disse\n- `/profile correct --track \"<título>\" --by \"<artista>\" (--like|--avoid)` - marca que você curte uma música, ou tira ela das sugestões\n- `/profile correct --artist \"<nome>\" (--like|--avoid)` - marca que você curte um artista, ou tira ele das sugestões\n- `/profile retract <correction-id>` - desfaz uma escolha\n- `/memory` - o que eu lembro\n- `/tools` - o que eu consigo fazer agora\n- `/doctor` - verifica sua configuração\n- `/model` - escolhe o modelo com que eu converso\n- `/model <provedor> <modelo>` - troca direto para um modelo\n- `/model refresh [provedor]` - atualiza as opções públicas de modelos; mantém o modelo escolhido\n- `/auth [provedor]` - faz login em um provedor de modelos\n- `/web status|search|read` - pesquisa em sites públicos de música\n- `/import [caminho]` - traz seu histórico, sua biblioteca ou uma playlist\n- `/language` - escolha o idioma do Moondog\n- `/spotify` - comandos do Spotify\n- `/spotify login [client-id]` - conecta o Spotify\n- `/spotify now|devices|queue` - mostra o que está tocando, onde e o que vem depois\n- `/spotify recent|sync-recent` - mostra ou salva suas últimas reproduções\n- `/spotify import-history \"/caminho/para/spotify-history.zip\"` - adiciona um ZIP do Spotify, sem login\n- `/skip` ou `/stop` - pula para a próxima prévia, ou interrompe as prévias\n- `/reload` - recarrega as configurações do modelo e os logins\n- `/remember [tipo] <texto>` - pede para eu lembrar de algo\n- `/forget <memory-id>` - esquece algo\n- `/rewind` ou Alt+R - edita uma mensagem anterior em um novo ramo; a original fica salva\n- `/rewind latest` - edita a mensagem mais recente\n- `/resume` - retoma uma conversa salva\n- `/resume <session-id>` - vai direto para uma\n- `/new` - começa do zero; a anterior fica salva\n- `/quit` - sair\n\nTudo o que você digitar que não for comando vai para o modelo escolhido com `/model`, ou o definido em `MOONDOG_PROVIDER` e `MOONDOG_MODEL`.\nFaça login com `/auth`, ou defina a chave de API do provedor no seu ambiente. `/auth openai-codex` usa seu login do ChatGPT.\nQuando seu perfil estiver pronto, posso buscar na sua biblioteca, explicar o que vejo e planejar playlists com você.\nSó consigo controlar o Spotify depois de `moondog spotify login`, e nunca publico, mando mensagens, apago nada ou gasto dinheiro.\nPeça para eu passar a reprodução para um dispositivo pelo nome, como o seu iPhone.",
|
|
179
|
+
"# Every command\n\nFrom your shell:\n\n- `moondog --version` - show the installed version\n- `moondog update [--check] [--channel latest|beta]` - check for or install a published release\n- `moondog web status|search|read [arguments] [--json]` - look things up on public music sites through DeepSeek, Brave Search or the Codex CLI\n- `moondog demo-history --output <absolute-file.zip> [--json]` - make a fictional Spotify history ZIP to try the importer with\n- `moondog auth login <provider>` - sign in to a model provider; the key is typed out of sight, and openai-codex uses your ChatGPT sign-in\n- `moondog auth status [provider]` - see which sign-ins are saved\n- `moondog auth logout <provider>` - sign out of a provider\n- `moondog spotify help` - set up and control Spotify\n- `moondog spotify login [client-id]` - connect Spotify in your browser\n- `moondog spotify now|devices|queue` - see what Spotify is playing, where, and what's next\n- `moondog spotify recent` - see what you played lately\n- `moondog spotify sync-recent` - add your latest plays to your profile\n- `moondog spotify sync-library` - add your Spotify library to your profile\n- `moondog spotify play|pause|next|previous` - control Spotify playback\n- `moondog listenbrainz import-history <listen-history.json>` - add a ListenBrainz listens file to your profile\n- `moondog catalog latest-single --artist <name> [--known-release <title> | --from <spotify-history.zip>]` - find an artist's latest single on Apple Music (US), no model needed\n- `moondog status [--json]` - see where things stand\n- `moondog taste [--json]` - your listening report\n- `moondog taste --html [--output <file.html>]` - save your report as a web page that works offline\n- `moondog taste --card [--output <file.html>]` - save a short recap you can check before sharing\n- `moondog taste --from <spotify-history.zip> --html` - a one-off report from a Spotify ZIP, without saving it\n- `moondog taste --from <account-data.zip> --from <extended-history.zip> --html` - a one-off report from both Spotify exports together\n- `moondog taste --from <spotify-history.zip> --save --html` - save the Spotify history, then show your full report\n- `moondog data inspect --scope <listening|apple|profile>` - see what's stored, and get the code needed to reset it\n- `moondog data export --scope <listening|apple|profile> --output <directory>` - export a copy, leaving the original alone\n- `moondog data reset --scope <listening|apple|profile> --confirm <token>` - set stored data aside (it is archived, not deleted)\n- `moondog profile build [--force]` - build or resume a saved profile with your connected model\n- `moondog profile saved [offset]` - read your saved profile\n- `moondog profile explain <number>` - trace a saved finding to its evidence\n- `moondog profile corrections [--all]` - everything you've told me, including earlier choices\n- `moondog profile correct --artist <name> (--like|--avoid)` - like an artist, or keep them out\n- `moondog profile correct --track <title> --by <artist> (--like|--avoid)` - like a track, or keep it out\n- `moondog profile retract <correction-id>` - undo one of your choices\n- `moondog demo --offline [--json]` - a demo with made-up data, no sign-in needed\n- `moondog demo [prompt] [--json]` - the same demo, talking through your model\n- `moondog ask <prompt>` - ask one question and exit\n- `moondog remember <text>` - ask me to remember something\n- `moondog forget <memory-id>` - forget something\n- `moondog memory reflect [--dry-run] [--json]` - let me think over recent conversations now\n- `moondog` - open the listening room\n\nInside the listening room:\n\n- `/update [--check] [--channel latest|beta]` - check for updates, or save and close this session to install one\n- `/status` or `/sources` - see where things stand\n- `/profile` or `/taste` - your profile, and why each song or artist is there\n- `/taste report` - the whole report on one page\n- `/profile build [--force]` - build or resume a saved reading; Ctrl+C keeps progress\n- `/profile saved [offset]` - read your saved profile\n- `/profile explain <number>` - inspect a saved finding's evidence\n- `/profile corrections [--all]` - everything you've told me\n- `/profile correct --track \"<title>\" --by \"<artist>\" (--like|--avoid)` - like a track, or keep it out\n- `/profile correct --artist \"<name>\" (--like|--avoid)` - like an artist, or keep them out\n- `/profile retract <correction-id>` - undo a choice\n- `/memory` - what I remember\n- `/tools` - what I can do right now\n- `/doctor` - check your setup\n- `/model` - pick the model I talk through\n- `/model <provider> <model>` - switch straight to a model\n- `/model refresh [provider]` - update public model choices; keep the selected model\n- `/auth [provider]` - sign in to a model provider\n- `/web status|search|read` - look things up on public music sites\n- `/import [path]` - bring in your history, library, or a playlist\n- `/language` - choose the language Moondog uses\n- `/spotify` - Spotify commands\n- `/spotify login [client-id]` - connect Spotify\n- `/spotify now|devices|queue` - see what's playing, where, and what's next\n- `/spotify recent|sync-recent` - see or save your latest plays\n- `/spotify import-history \"/path/to/spotify-history.zip\"` - add a Spotify ZIP, no sign-in needed\n- `/skip` or `/stop` - move to the next song preview, or stop them\n- `/reload` - reload model settings and sign-ins\n- `/remember [kind] <text>` - ask me to remember something\n- `/forget <memory-id>` - forget something\n- `/rewind` or Alt+R - edit an earlier message in place; the messages after it are replaced\n- `/branch` - go on from here in a new conversation; this one stays saved\n- `/rewind latest` - edit the most recent message\n- `/resume` - pick up a saved conversation\n- `/resume <session-id>` - go straight to one\n- `/new` - start fresh; the last one stays saved\n- `/quit` - exit\n\nAnything you type that isn't a command goes to the model you picked with `/model`, or the one set in `MOONDOG_PROVIDER` and `MOONDOG_MODEL`.\nSign in with `/auth`, or set the provider's API key in your environment. `/auth openai-codex` uses your ChatGPT sign-in.\nOnce your profile is ready, I can search your library, explain what I see, and plan playlists with you.\nI can only control Spotify after `moondog spotify login`, and I never post, send messages, delete anything, or spend money.\nAsk me to move playback onto a device by name, such as your iPhone.": "# Todos os comandos\n\nNo seu terminal:\n\n- `moondog --version` - mostra a versão instalada\n- `moondog update [--check] [--channel latest|beta]` - procura ou instala uma versão publicada\n- `moondog web status|search|read [argumentos] [--json]` - pesquisa em sites públicos de música pelo DeepSeek, pelo Brave Search ou pelo Codex CLI\n- `moondog demo-history --output <arquivo-absoluto.zip> [--json]` - cria um ZIP fictício de histórico do Spotify para testar a importação\n- `moondog auth login <provedor>` - faz login em um provedor de modelos; a chave é digitada sem aparecer, e o openai-codex usa seu login do ChatGPT\n- `moondog auth status [provedor]` - mostra quais logins estão salvos\n- `moondog auth logout <provedor>` - sai de um provedor\n- `moondog spotify help` - configura e controla o Spotify\n- `moondog spotify login [client-id]` - conecta o Spotify no navegador\n- `moondog spotify now|devices|queue` - mostra o que o Spotify está tocando, onde e o que vem depois\n- `moondog spotify recent` - mostra o que você ouviu ultimamente\n- `moondog spotify sync-recent` - adiciona suas últimas reproduções ao perfil\n- `moondog spotify sync-library` - adiciona sua biblioteca do Spotify ao perfil\n- `moondog spotify play|pause|next|previous` - controla a reprodução do Spotify\n- `moondog listenbrainz import-history <listen-history.json>` - adiciona um arquivo de escutas do ListenBrainz ao perfil\n- `moondog catalog latest-single --artist <nome> [--known-release <título> | --from <spotify-history.zip>]` - encontra o último single de um artista no Apple Music (EUA), sem modelo\n- `moondog status [--json]` - mostra como estão as coisas\n- `moondog taste [--json]` - seu relatório de escuta\n- `moondog taste --html [--output <arquivo.html>]` - salva o relatório como página web que funciona offline\n- `moondog taste --card [--output <arquivo.html>]` - salva um resumo curto que você pode conferir antes de compartilhar\n- `moondog taste --from <spotify-history.zip> --html` - um relatório único de um ZIP do Spotify, sem salvar\n- `moondog taste --from <account-data.zip> --from <extended-history.zip> --html` - um relatório único das duas exportações do Spotify juntas\n- `moondog taste --from <spotify-history.zip> --save --html` - salva o histórico do Spotify e mostra o relatório completo\n- `moondog data inspect --scope <listening|apple|profile>` - mostra o que está guardado e o código para redefinir\n- `moondog data export --scope <listening|apple|profile> --output <pasta>` - exporta uma cópia sem mexer no original\n- `moondog data reset --scope <listening|apple|profile> --confirm <código>` - deixa de lado os dados guardados (são arquivados, não apagados)\n- `moondog profile build [--force]` - cria ou retoma um perfil salvo com o modelo conectado\n- `moondog profile saved [deslocamento]` - lê seu perfil salvo\n- `moondog profile explain <número>` - rastreia uma descoberta salva até a evidência\n- `moondog profile corrections [--all]` - tudo o que você me disse, inclusive escolhas anteriores\n- `moondog profile correct --artist <nome> (--like|--avoid)` - marca que você curte um artista, ou tira ele das sugestões\n- `moondog profile correct --track <título> --by <artista> (--like|--avoid)` - marca que você curte uma música, ou tira ela das sugestões\n- `moondog profile retract <correction-id>` - desfaz uma das suas escolhas\n- `moondog demo --offline [--json]` - uma demonstração com dados inventados, sem login\n- `moondog demo [pedido] [--json]` - a mesma demonstração, conversando pelo seu modelo\n- `moondog ask <pedido>` - faz uma pergunta e sai\n- `moondog remember <texto>` - pede para eu lembrar de algo\n- `moondog forget <memory-id>` - esquece algo\n- `moondog memory reflect [--dry-run] [--json]` - me deixa repassar agora as conversas recentes\n- `moondog` - abre a sala de escuta\n\nDentro da sala de escuta:\n\n- `/update [--check] [--channel latest|beta]` - procura atualizações, ou salva e fecha esta sessão para instalar uma\n- `/status` ou `/sources` - mostra como estão as coisas\n- `/profile` ou `/taste` - seu perfil, e por que cada música ou artista está ali\n- `/taste report` - o relatório completo em uma página\n- `/profile build [--force]` - cria ou retoma uma leitura salva; Ctrl+C guarda o progresso\n- `/profile saved [deslocamento]` - lê seu perfil salvo\n- `/profile explain <número>` - mostra a evidência de uma descoberta salva\n- `/profile corrections [--all]` - tudo o que você me disse\n- `/profile correct --track \"<título>\" --by \"<artista>\" (--like|--avoid)` - marca que você curte uma música, ou tira ela das sugestões\n- `/profile correct --artist \"<nome>\" (--like|--avoid)` - marca que você curte um artista, ou tira ele das sugestões\n- `/profile retract <correction-id>` - desfaz uma escolha\n- `/memory` - o que eu lembro\n- `/tools` - o que eu consigo fazer agora\n- `/doctor` - verifica sua configuração\n- `/model` - escolhe o modelo com que eu converso\n- `/model <provedor> <modelo>` - troca direto para um modelo\n- `/model refresh [provedor]` - atualiza as opções públicas de modelos; mantém o modelo escolhido\n- `/auth [provedor]` - faz login em um provedor de modelos\n- `/web status|search|read` - pesquisa em sites públicos de música\n- `/import [caminho]` - traz seu histórico, sua biblioteca ou uma playlist\n- `/language` - escolha o idioma do Moondog\n- `/spotify` - comandos do Spotify\n- `/spotify login [client-id]` - conecta o Spotify\n- `/spotify now|devices|queue` - mostra o que está tocando, onde e o que vem depois\n- `/spotify recent|sync-recent` - mostra ou salva suas últimas reproduções\n- `/spotify import-history \"/caminho/para/spotify-history.zip\"` - adiciona um ZIP do Spotify, sem login\n- `/skip` ou `/stop` - pula para a próxima prévia, ou interrompe as prévias\n- `/reload` - recarrega as configurações do modelo e os logins\n- `/remember [tipo] <texto>` - pede para eu lembrar de algo\n- `/forget <memory-id>` - esquece algo\n- `/rewind` ou Alt+R - edita uma mensagem anterior nesta conversa; as mensagens seguintes são substituídas\n- `/branch` - continua daqui em uma conversa nova; esta fica salva\n- `/rewind latest` - edita a mensagem mais recente\n- `/resume` - retoma uma conversa salva\n- `/resume <session-id>` - vai direto para uma\n- `/new` - começa do zero; a anterior fica salva\n- `/quit` - sair\n\nTudo o que você digitar que não for comando vai para o modelo escolhido com `/model`, ou o definido em `MOONDOG_PROVIDER` e `MOONDOG_MODEL`.\nFaça login com `/auth`, ou defina a chave de API do provedor no seu ambiente. `/auth openai-codex` usa seu login do ChatGPT.\nQuando seu perfil estiver pronto, posso buscar na sua biblioteca, explicar o que vejo e planejar playlists com você.\nSó consigo controlar o Spotify depois de `moondog spotify login`, e nunca publico, mando mensagens, apago nada ou gasto dinheiro.\nPeça para eu passar a reprodução para um dispositivo pelo nome, como o seu iPhone.",
|
|
180
180
|
"Apple Music library": "Biblioteca do Apple Music",
|
|
181
181
|
"Model": "Modelo",
|
|
182
182
|
"Finding similar music": "Encontrar música parecida",
|
|
@@ -682,10 +682,14 @@ export default {
|
|
|
682
682
|
"Use /rewind or /rewind latest.": "Use /rewind ou /rewind latest.",
|
|
683
683
|
"Conversation rewind isn't available in this runtime.": "Voltar a conversa não está disponível neste ambiente.",
|
|
684
684
|
"No earlier messages yet.": "Ainda não há mensagens anteriores.",
|
|
685
|
-
"Rewind to a message ·
|
|
685
|
+
"Rewind to a message · it and what follows are replaced": "Voltar até uma mensagem · ela e o que vem depois são substituídos",
|
|
686
686
|
"Rewind cancelled. Queued drafts stay paused.": "Volta cancelada. Os rascunhos na espera continuam pausados.",
|
|
687
|
-
"
|
|
688
|
-
"Earlier message restored · edit before sending
|
|
687
|
+
"Rewound to before this message. Edit it below, then send. Spotify actions and saved preferences from the replaced messages have not been undone.": "Voltei para antes desta mensagem. Edite abaixo e envie. As ações do Spotify e as preferências salvas das mensagens substituídas não foram desfeitas.",
|
|
688
|
+
"Earlier message restored · edit before sending": "Mensagem anterior recuperada · edite antes de enviar",
|
|
689
|
+
"Use /branch on its own.": "Use /branch sem nada depois.",
|
|
690
|
+
"Branching isn't available in this runtime.": "Ramos não estão disponíveis neste ambiente.",
|
|
691
|
+
"Nothing to branch from yet.": "Ainda não há nada para ramificar.",
|
|
692
|
+
"Branched · the original is in /resume": "Ramo criado · a original está em /resume",
|
|
689
693
|
"Use /resume, or /resume <conversation-id>.": "Use /resume ou /resume <id-da-conversa>.",
|
|
690
694
|
"Saved conversations aren't available when Moondog is started this way.": "Conversas salvas não estão disponíveis quando o Moondog é aberto deste jeito.",
|
|
691
695
|
"No saved conversations yet.": "Ainda não há conversas salvas.",
|
|
@@ -811,7 +815,8 @@ export default {
|
|
|
811
815
|
"Ask Moondog to remember something": "Peça ao Moondog para lembrar de algo",
|
|
812
816
|
"Forget one memory by ID": "Esqueça uma lembrança pelo ID",
|
|
813
817
|
"Pick up a saved conversation": "Retome uma conversa salva",
|
|
814
|
-
"Edit an earlier message
|
|
818
|
+
"Edit an earlier message in place · Alt+R": "Edite uma mensagem anterior nesta conversa · Alt+R",
|
|
819
|
+
"Go on from here in a new conversation; this one stays saved": "Continue daqui em uma conversa nova; esta fica salva",
|
|
815
820
|
"Start fresh; this conversation stays saved": "Comece do zero; esta conversa fica salva",
|
|
816
821
|
"Commands and shortcuts": "Comandos e atalhos",
|
|
817
822
|
"Leave Moondog": "Sair do Moondog",
|
package/src/i18n/screen/zh.mjs
CHANGED
|
@@ -176,7 +176,7 @@ export default {
|
|
|
176
176
|
"Memory": "记忆",
|
|
177
177
|
"Right now I can't change anything in your music accounts.": "目前我不能修改你音乐账号里的任何内容。",
|
|
178
178
|
"I can make changes in your connected accounts, and I always ask first.": "我可以修改你已连接的账号,并且每次都会先问你。",
|
|
179
|
-
"# Every command\n\nFrom your shell:\n\n- `moondog --version` - show the installed version\n- `moondog update [--check] [--channel latest|beta]` - check for or install a published release\n- `moondog web status|search|read [arguments] [--json]` - look things up on public music sites through DeepSeek, Brave Search or the Codex CLI\n- `moondog demo-history --output <absolute-file.zip> [--json]` - make a fictional Spotify history ZIP to try the importer with\n- `moondog auth login <provider>` - sign in to a model provider; the key is typed out of sight, and openai-codex uses your ChatGPT sign-in\n- `moondog auth status [provider]` - see which sign-ins are saved\n- `moondog auth logout <provider>` - sign out of a provider\n- `moondog spotify help` - set up and control Spotify\n- `moondog spotify login [client-id]` - connect Spotify in your browser\n- `moondog spotify now|devices|queue` - see what Spotify is playing, where, and what's next\n- `moondog spotify recent` - see what you played lately\n- `moondog spotify sync-recent` - add your latest plays to your profile\n- `moondog spotify sync-library` - add your Spotify library to your profile\n- `moondog spotify play|pause|next|previous` - control Spotify playback\n- `moondog listenbrainz import-history <listen-history.json>` - add a ListenBrainz listens file to your profile\n- `moondog catalog latest-single --artist <name> [--known-release <title> | --from <spotify-history.zip>]` - find an artist's latest single on Apple Music (US), no model needed\n- `moondog status [--json]` - see where things stand\n- `moondog taste [--json]` - your listening report\n- `moondog taste --html [--output <file.html>]` - save your report as a web page that works offline\n- `moondog taste --card [--output <file.html>]` - save a short recap you can check before sharing\n- `moondog taste --from <spotify-history.zip> --html` - a one-off report from a Spotify ZIP, without saving it\n- `moondog taste --from <account-data.zip> --from <extended-history.zip> --html` - a one-off report from both Spotify exports together\n- `moondog taste --from <spotify-history.zip> --save --html` - save the Spotify history, then show your full report\n- `moondog data inspect --scope <listening|apple|profile>` - see what's stored, and get the code needed to reset it\n- `moondog data export --scope <listening|apple|profile> --output <directory>` - export a copy, leaving the original alone\n- `moondog data reset --scope <listening|apple|profile> --confirm <token>` - set stored data aside (it is archived, not deleted)\n- `moondog profile build [--force]` - build or resume a saved profile with your connected model\n- `moondog profile saved [offset]` - read your saved profile\n- `moondog profile explain <number>` - trace a saved finding to its evidence\n- `moondog profile corrections [--all]` - everything you've told me, including earlier choices\n- `moondog profile correct --artist <name> (--like|--avoid)` - like an artist, or keep them out\n- `moondog profile correct --track <title> --by <artist> (--like|--avoid)` - like a track, or keep it out\n- `moondog profile retract <correction-id>` - undo one of your choices\n- `moondog demo --offline [--json]` - a demo with made-up data, no sign-in needed\n- `moondog demo [prompt] [--json]` - the same demo, talking through your model\n- `moondog ask <prompt>` - ask one question and exit\n- `moondog remember <text>` - ask me to remember something\n- `moondog forget <memory-id>` - forget something\n- `moondog memory reflect [--dry-run] [--json]` - let me think over recent conversations now\n- `moondog` - open the listening room\n\nInside the listening room:\n\n- `/update [--check] [--channel latest|beta]` - check for updates, or save and close this session to install one\n- `/status` or `/sources` - see where things stand\n- `/profile` or `/taste` - your profile, and why each song or artist is there\n- `/taste report` - the whole report on one page\n- `/profile build [--force]` - build or resume a saved reading; Ctrl+C keeps progress\n- `/profile saved [offset]` - read your saved profile\n- `/profile explain <number>` - inspect a saved finding's evidence\n- `/profile corrections [--all]` - everything you've told me\n- `/profile correct --track \"<title>\" --by \"<artist>\" (--like|--avoid)` - like a track, or keep it out\n- `/profile correct --artist \"<name>\" (--like|--avoid)` - like an artist, or keep them out\n- `/profile retract <correction-id>` - undo a choice\n- `/memory` - what I remember\n- `/tools` - what I can do right now\n- `/doctor` - check your setup\n- `/model` - pick the model I talk through\n- `/model <provider> <model>` - switch straight to a model\n- `/model refresh [provider]` - update public model choices; keep the selected model\n- `/auth [provider]` - sign in to a model provider\n- `/web status|search|read` - look things up on public music sites\n- `/import [path]` - bring in your history, library, or a playlist\n- `/language` - choose the language Moondog uses\n- `/spotify` - Spotify commands\n- `/spotify login [client-id]` - connect Spotify\n- `/spotify now|devices|queue` - see what's playing, where, and what's next\n- `/spotify recent|sync-recent` - see or save your latest plays\n- `/spotify import-history \"/path/to/spotify-history.zip\"` - add a Spotify ZIP, no sign-in needed\n- `/skip` or `/stop` - move to the next song preview, or stop them\n- `/reload` - reload model settings and sign-ins\n- `/remember [kind] <text>` - ask me to remember something\n- `/forget <memory-id>` - forget something\n- `/rewind` or Alt+R - edit an earlier message on a new branch; the original stays saved\n- `/rewind latest` - edit the most recent message\n- `/resume` - pick up a saved conversation\n- `/resume <session-id>` - go straight to one\n- `/new` - start fresh; the last one stays saved\n- `/quit` - exit\n\nAnything you type that isn't a command goes to the model you picked with `/model`, or the one set in `MOONDOG_PROVIDER` and `MOONDOG_MODEL`.\nSign in with `/auth`, or set the provider's API key in your environment. `/auth openai-codex` uses your ChatGPT sign-in.\nOnce your profile is ready, I can search your library, explain what I see, and plan playlists with you.\nI can only control Spotify after `moondog spotify login`, and I never post, send messages, delete anything, or spend money.\nAsk me to move playback onto a device by name, such as your iPhone.": "# 全部命令\n\n在终端里:\n\n- `moondog --version` - 显示已安装的版本\n- `moondog update [--check] [--channel latest|beta]` - 检查或安装已发布的版本\n- `moondog web status|search|read [参数] [--json]` - 通过 DeepSeek、Brave Search 或 Codex CLI 在公开音乐网站上查资料\n- `moondog demo-history --output <绝对路径.zip> [--json]` - 生成一个虚构的 Spotify 历史 ZIP,用来试用导入\n- `moondog auth login <provider>` - 登录模型服务商;密钥输入时不显示,openai-codex 使用你的 ChatGPT 登录\n- `moondog auth status [provider]` - 查看保存了哪些登录\n- `moondog auth logout <provider>` - 退出某个服务商\n- `moondog spotify help` - 设置并控制 Spotify\n- `moondog spotify login [client-id]` - 在浏览器里连接 Spotify\n- `moondog spotify now|devices|queue` - 查看 Spotify 正在放什么、在哪放、接下来是什么\n- `moondog spotify recent` - 查看你最近听了什么\n- `moondog spotify sync-recent` - 把最近的播放加入你的资料\n- `moondog spotify sync-library` - 把你的 Spotify 曲库加入你的资料\n- `moondog spotify play|pause|next|previous` - 控制 Spotify 播放\n- `moondog listenbrainz import-history <listen-history.json>` - 把 ListenBrainz 收听文件加入你的资料\n- `moondog catalog latest-single --artist <名字> [--known-release <标题> | --from <spotify-history.zip>]` - 在 Apple Music(美区)查找某位艺人的最新单曲,无需模型\n- `moondog status [--json]` - 查看当前状态\n- `moondog taste [--json]` - 你的聆听报告\n- `moondog taste --html [--output <文件.html>]` - 把报告保存为可离线打开的网页\n- `moondog taste --card [--output <文件.html>]` - 保存一份简短回顾,分享前可以先检查\n- `moondog taste --from <spotify-history.zip> --html` - 从一个 Spotify ZIP 生成一次性报告,不保存\n- `moondog taste --from <account-data.zip> --from <extended-history.zip> --html` - 从两份 Spotify 导出一起生成一次性报告\n- `moondog taste --from <spotify-history.zip> --save --html` - 保存 Spotify 历史,然后显示完整报告\n- `moondog data inspect --scope <listening|apple|profile>` - 查看保存了什么,并获取重置所需的代码\n- `moondog data export --scope <listening|apple|profile> --output <目录>` - 导出一份副本,原件保持不变\n- `moondog data reset --scope <listening|apple|profile> --confirm <代码>` - 把保存的数据移到一边(归档,不删除)\n- `moondog profile build [--force]` - 用已连接的模型生成或继续生成保存的资料\n- `moondog profile saved [偏移]` - 阅读你保存的资料\n- `moondog profile explain <编号>` - 追溯一条已保存发现的证据\n- `moondog profile corrections [--all]` - 你告诉过我的所有选择,包括早先的\n- `moondog profile correct --artist <名字> (--like|--avoid)` - 标记喜欢某位艺人,或不再推荐\n- `moondog profile correct --track <歌名> --by <艺人> (--like|--avoid)` - 标记喜欢某首歌,或不再推荐\n- `moondog profile retract <correction-id>` - 撤销你的一项选择\n- `moondog demo --offline [--json]` - 用虚构数据的演示,无需登录\n- `moondog demo [提示] [--json]` - 同样的演示,通过你的模型对话\n- `moondog ask <提示>` - 问一个问题后退出\n- `moondog remember <内容>` - 让我记住一件事\n- `moondog forget <memory-id>` - 忘掉一件事\n- `moondog memory reflect [--dry-run] [--json]` - 让我现在回顾最近的对话\n- `moondog` - 打开聆听室\n\n在聆听室里:\n\n- `/update [--check] [--channel latest|beta]` - 检查更新,或保存并关闭本次会话来安装\n- `/status` 或 `/sources` - 查看当前状态\n- `/profile` 或 `/taste` - 你的资料,以及每首歌、每位艺人为什么在这里\n- `/taste report` - 一页看完整份报告\n- `/profile build [--force]` - 生成或继续生成保存的解读;Ctrl+C 会保留进度\n- `/profile saved [偏移]` - 阅读你保存的资料\n- `/profile explain <编号>` - 查看一条已保存发现的证据\n- `/profile corrections [--all]` - 你告诉过我的所有选择\n- `/profile correct --track \"<歌名>\" --by \"<艺人>\" (--like|--avoid)` - 标记喜欢某首歌,或不再推荐\n- `/profile correct --artist \"<名字>\" (--like|--avoid)` - 标记喜欢某位艺人,或不再推荐\n- `/profile retract <correction-id>` - 撤销一项选择\n- `/memory` - 我记住的内容\n- `/tools` - 我现在能做什么\n- `/doctor` - 检查你的设置\n- `/model` - 选择我用来对话的模型\n- `/model <provider> <model>` - 直接切换到某个模型\n- `/model refresh [provider]` - 更新公开的模型列表;保留当前选择\n- `/auth [provider]` - 登录模型服务商\n- `/web status|search|read` - 在公开音乐网站上查资料\n- `/import [路径]` - 导入你的历史、曲库或一个歌单\n- `/language` - 选择 Moondog 使用的语言\n- `/spotify` - Spotify 命令\n- `/spotify login [client-id]` - 连接 Spotify\n- `/spotify now|devices|queue` - 查看正在放什么、在哪放、接下来是什么\n- `/spotify recent|sync-recent` - 查看或保存你最近的播放\n- `/spotify import-history \"/path/to/spotify-history.zip\"` - 加入一个 Spotify ZIP,无需登录\n- `/skip` 或 `/stop` - 跳到下一首试听,或停止试听\n- `/reload` - 重新加载模型设置和登录\n- `/remember [类型] <内容>` - 让我记住一件事\n- `/forget <memory-id>` - 忘掉一件事\n- `/rewind` 或 Alt+R - 在新分支上编辑之前的一条消息;原来的会保留\n- `/rewind latest` - 编辑最近的一条消息\n- `/resume` - 继续一段保存的对话\n- `/resume <session-id>` - 直接打开某一段\n- `/new` - 重新开始;上一段会保存下来\n- `/quit` - 退出\n\n你输入的非命令内容,会发给你用 `/model` 选的模型,或 `MOONDOG_PROVIDER` 和 `MOONDOG_MODEL` 指定的模型。\n用 `/auth` 登录,或在环境变量里设置服务商的 API 密钥。`/auth openai-codex` 使用你的 ChatGPT 登录。\n资料准备好之后,我可以搜索你的曲库、解释我看到的内容,并和你一起规划歌单。\n只有在 `moondog spotify login` 之后我才能控制 Spotify,而且我从不发帖、不发消息、不删除任何东西,也不花钱。\n说出设备名,比如你的 iPhone,我就能把播放切过去。",
|
|
179
|
+
"# Every command\n\nFrom your shell:\n\n- `moondog --version` - show the installed version\n- `moondog update [--check] [--channel latest|beta]` - check for or install a published release\n- `moondog web status|search|read [arguments] [--json]` - look things up on public music sites through DeepSeek, Brave Search or the Codex CLI\n- `moondog demo-history --output <absolute-file.zip> [--json]` - make a fictional Spotify history ZIP to try the importer with\n- `moondog auth login <provider>` - sign in to a model provider; the key is typed out of sight, and openai-codex uses your ChatGPT sign-in\n- `moondog auth status [provider]` - see which sign-ins are saved\n- `moondog auth logout <provider>` - sign out of a provider\n- `moondog spotify help` - set up and control Spotify\n- `moondog spotify login [client-id]` - connect Spotify in your browser\n- `moondog spotify now|devices|queue` - see what Spotify is playing, where, and what's next\n- `moondog spotify recent` - see what you played lately\n- `moondog spotify sync-recent` - add your latest plays to your profile\n- `moondog spotify sync-library` - add your Spotify library to your profile\n- `moondog spotify play|pause|next|previous` - control Spotify playback\n- `moondog listenbrainz import-history <listen-history.json>` - add a ListenBrainz listens file to your profile\n- `moondog catalog latest-single --artist <name> [--known-release <title> | --from <spotify-history.zip>]` - find an artist's latest single on Apple Music (US), no model needed\n- `moondog status [--json]` - see where things stand\n- `moondog taste [--json]` - your listening report\n- `moondog taste --html [--output <file.html>]` - save your report as a web page that works offline\n- `moondog taste --card [--output <file.html>]` - save a short recap you can check before sharing\n- `moondog taste --from <spotify-history.zip> --html` - a one-off report from a Spotify ZIP, without saving it\n- `moondog taste --from <account-data.zip> --from <extended-history.zip> --html` - a one-off report from both Spotify exports together\n- `moondog taste --from <spotify-history.zip> --save --html` - save the Spotify history, then show your full report\n- `moondog data inspect --scope <listening|apple|profile>` - see what's stored, and get the code needed to reset it\n- `moondog data export --scope <listening|apple|profile> --output <directory>` - export a copy, leaving the original alone\n- `moondog data reset --scope <listening|apple|profile> --confirm <token>` - set stored data aside (it is archived, not deleted)\n- `moondog profile build [--force]` - build or resume a saved profile with your connected model\n- `moondog profile saved [offset]` - read your saved profile\n- `moondog profile explain <number>` - trace a saved finding to its evidence\n- `moondog profile corrections [--all]` - everything you've told me, including earlier choices\n- `moondog profile correct --artist <name> (--like|--avoid)` - like an artist, or keep them out\n- `moondog profile correct --track <title> --by <artist> (--like|--avoid)` - like a track, or keep it out\n- `moondog profile retract <correction-id>` - undo one of your choices\n- `moondog demo --offline [--json]` - a demo with made-up data, no sign-in needed\n- `moondog demo [prompt] [--json]` - the same demo, talking through your model\n- `moondog ask <prompt>` - ask one question and exit\n- `moondog remember <text>` - ask me to remember something\n- `moondog forget <memory-id>` - forget something\n- `moondog memory reflect [--dry-run] [--json]` - let me think over recent conversations now\n- `moondog` - open the listening room\n\nInside the listening room:\n\n- `/update [--check] [--channel latest|beta]` - check for updates, or save and close this session to install one\n- `/status` or `/sources` - see where things stand\n- `/profile` or `/taste` - your profile, and why each song or artist is there\n- `/taste report` - the whole report on one page\n- `/profile build [--force]` - build or resume a saved reading; Ctrl+C keeps progress\n- `/profile saved [offset]` - read your saved profile\n- `/profile explain <number>` - inspect a saved finding's evidence\n- `/profile corrections [--all]` - everything you've told me\n- `/profile correct --track \"<title>\" --by \"<artist>\" (--like|--avoid)` - like a track, or keep it out\n- `/profile correct --artist \"<name>\" (--like|--avoid)` - like an artist, or keep them out\n- `/profile retract <correction-id>` - undo a choice\n- `/memory` - what I remember\n- `/tools` - what I can do right now\n- `/doctor` - check your setup\n- `/model` - pick the model I talk through\n- `/model <provider> <model>` - switch straight to a model\n- `/model refresh [provider]` - update public model choices; keep the selected model\n- `/auth [provider]` - sign in to a model provider\n- `/web status|search|read` - look things up on public music sites\n- `/import [path]` - bring in your history, library, or a playlist\n- `/language` - choose the language Moondog uses\n- `/spotify` - Spotify commands\n- `/spotify login [client-id]` - connect Spotify\n- `/spotify now|devices|queue` - see what's playing, where, and what's next\n- `/spotify recent|sync-recent` - see or save your latest plays\n- `/spotify import-history \"/path/to/spotify-history.zip\"` - add a Spotify ZIP, no sign-in needed\n- `/skip` or `/stop` - move to the next song preview, or stop them\n- `/reload` - reload model settings and sign-ins\n- `/remember [kind] <text>` - ask me to remember something\n- `/forget <memory-id>` - forget something\n- `/rewind` or Alt+R - edit an earlier message in place; the messages after it are replaced\n- `/branch` - go on from here in a new conversation; this one stays saved\n- `/rewind latest` - edit the most recent message\n- `/resume` - pick up a saved conversation\n- `/resume <session-id>` - go straight to one\n- `/new` - start fresh; the last one stays saved\n- `/quit` - exit\n\nAnything you type that isn't a command goes to the model you picked with `/model`, or the one set in `MOONDOG_PROVIDER` and `MOONDOG_MODEL`.\nSign in with `/auth`, or set the provider's API key in your environment. `/auth openai-codex` uses your ChatGPT sign-in.\nOnce your profile is ready, I can search your library, explain what I see, and plan playlists with you.\nI can only control Spotify after `moondog spotify login`, and I never post, send messages, delete anything, or spend money.\nAsk me to move playback onto a device by name, such as your iPhone.": "# 全部命令\n\n在终端里:\n\n- `moondog --version` - 显示已安装的版本\n- `moondog update [--check] [--channel latest|beta]` - 检查或安装已发布的版本\n- `moondog web status|search|read [参数] [--json]` - 通过 DeepSeek、Brave Search 或 Codex CLI 在公开音乐网站上查资料\n- `moondog demo-history --output <绝对路径.zip> [--json]` - 生成一个虚构的 Spotify 历史 ZIP,用来试用导入\n- `moondog auth login <provider>` - 登录模型服务商;密钥输入时不显示,openai-codex 使用你的 ChatGPT 登录\n- `moondog auth status [provider]` - 查看保存了哪些登录\n- `moondog auth logout <provider>` - 退出某个服务商\n- `moondog spotify help` - 设置并控制 Spotify\n- `moondog spotify login [client-id]` - 在浏览器里连接 Spotify\n- `moondog spotify now|devices|queue` - 查看 Spotify 正在放什么、在哪放、接下来是什么\n- `moondog spotify recent` - 查看你最近听了什么\n- `moondog spotify sync-recent` - 把最近的播放加入你的资料\n- `moondog spotify sync-library` - 把你的 Spotify 曲库加入你的资料\n- `moondog spotify play|pause|next|previous` - 控制 Spotify 播放\n- `moondog listenbrainz import-history <listen-history.json>` - 把 ListenBrainz 收听文件加入你的资料\n- `moondog catalog latest-single --artist <名字> [--known-release <标题> | --from <spotify-history.zip>]` - 在 Apple Music(美区)查找某位艺人的最新单曲,无需模型\n- `moondog status [--json]` - 查看当前状态\n- `moondog taste [--json]` - 你的聆听报告\n- `moondog taste --html [--output <文件.html>]` - 把报告保存为可离线打开的网页\n- `moondog taste --card [--output <文件.html>]` - 保存一份简短回顾,分享前可以先检查\n- `moondog taste --from <spotify-history.zip> --html` - 从一个 Spotify ZIP 生成一次性报告,不保存\n- `moondog taste --from <account-data.zip> --from <extended-history.zip> --html` - 从两份 Spotify 导出一起生成一次性报告\n- `moondog taste --from <spotify-history.zip> --save --html` - 保存 Spotify 历史,然后显示完整报告\n- `moondog data inspect --scope <listening|apple|profile>` - 查看保存了什么,并获取重置所需的代码\n- `moondog data export --scope <listening|apple|profile> --output <目录>` - 导出一份副本,原件保持不变\n- `moondog data reset --scope <listening|apple|profile> --confirm <代码>` - 把保存的数据移到一边(归档,不删除)\n- `moondog profile build [--force]` - 用已连接的模型生成或继续生成保存的资料\n- `moondog profile saved [偏移]` - 阅读你保存的资料\n- `moondog profile explain <编号>` - 追溯一条已保存发现的证据\n- `moondog profile corrections [--all]` - 你告诉过我的所有选择,包括早先的\n- `moondog profile correct --artist <名字> (--like|--avoid)` - 标记喜欢某位艺人,或不再推荐\n- `moondog profile correct --track <歌名> --by <艺人> (--like|--avoid)` - 标记喜欢某首歌,或不再推荐\n- `moondog profile retract <correction-id>` - 撤销你的一项选择\n- `moondog demo --offline [--json]` - 用虚构数据的演示,无需登录\n- `moondog demo [提示] [--json]` - 同样的演示,通过你的模型对话\n- `moondog ask <提示>` - 问一个问题后退出\n- `moondog remember <内容>` - 让我记住一件事\n- `moondog forget <memory-id>` - 忘掉一件事\n- `moondog memory reflect [--dry-run] [--json]` - 让我现在回顾最近的对话\n- `moondog` - 打开聆听室\n\n在聆听室里:\n\n- `/update [--check] [--channel latest|beta]` - 检查更新,或保存并关闭本次会话来安装\n- `/status` 或 `/sources` - 查看当前状态\n- `/profile` 或 `/taste` - 你的资料,以及每首歌、每位艺人为什么在这里\n- `/taste report` - 一页看完整份报告\n- `/profile build [--force]` - 生成或继续生成保存的解读;Ctrl+C 会保留进度\n- `/profile saved [偏移]` - 阅读你保存的资料\n- `/profile explain <编号>` - 查看一条已保存发现的证据\n- `/profile corrections [--all]` - 你告诉过我的所有选择\n- `/profile correct --track \"<歌名>\" --by \"<艺人>\" (--like|--avoid)` - 标记喜欢某首歌,或不再推荐\n- `/profile correct --artist \"<名字>\" (--like|--avoid)` - 标记喜欢某位艺人,或不再推荐\n- `/profile retract <correction-id>` - 撤销一项选择\n- `/memory` - 我记住的内容\n- `/tools` - 我现在能做什么\n- `/doctor` - 检查你的设置\n- `/model` - 选择我用来对话的模型\n- `/model <provider> <model>` - 直接切换到某个模型\n- `/model refresh [provider]` - 更新公开的模型列表;保留当前选择\n- `/auth [provider]` - 登录模型服务商\n- `/web status|search|read` - 在公开音乐网站上查资料\n- `/import [路径]` - 导入你的历史、曲库或一个歌单\n- `/language` - 选择 Moondog 使用的语言\n- `/spotify` - Spotify 命令\n- `/spotify login [client-id]` - 连接 Spotify\n- `/spotify now|devices|queue` - 查看正在放什么、在哪放、接下来是什么\n- `/spotify recent|sync-recent` - 查看或保存你最近的播放\n- `/spotify import-history \"/path/to/spotify-history.zip\"` - 加入一个 Spotify ZIP,无需登录\n- `/skip` 或 `/stop` - 跳到下一首试听,或停止试听\n- `/reload` - 重新加载模型设置和登录\n- `/remember [类型] <内容>` - 让我记住一件事\n- `/forget <memory-id>` - 忘掉一件事\n- `/rewind` 或 Alt+R - 就地编辑之前的一条消息;之后的消息会被替换\n- `/branch` - 从这里在新对话中继续;这段对话会保留\n- `/rewind latest` - 编辑最近的一条消息\n- `/resume` - 继续一段保存的对话\n- `/resume <session-id>` - 直接打开某一段\n- `/new` - 重新开始;上一段会保存下来\n- `/quit` - 退出\n\n你输入的非命令内容,会发给你用 `/model` 选的模型,或 `MOONDOG_PROVIDER` 和 `MOONDOG_MODEL` 指定的模型。\n用 `/auth` 登录,或在环境变量里设置服务商的 API 密钥。`/auth openai-codex` 使用你的 ChatGPT 登录。\n资料准备好之后,我可以搜索你的曲库、解释我看到的内容,并和你一起规划歌单。\n只有在 `moondog spotify login` 之后我才能控制 Spotify,而且我从不发帖、不发消息、不删除任何东西,也不花钱。\n说出设备名,比如你的 iPhone,我就能把播放切过去。",
|
|
180
180
|
"Apple Music library": "Apple Music 曲库",
|
|
181
181
|
"Model": "模型",
|
|
182
182
|
"Finding similar music": "查找相似的音乐",
|
|
@@ -682,10 +682,14 @@ export default {
|
|
|
682
682
|
"Use /rewind or /rewind latest.": "使用 /rewind 或 /rewind latest。",
|
|
683
683
|
"Conversation rewind isn't available in this runtime.": "当前运行环境不支持回退对话。",
|
|
684
684
|
"No earlier messages yet.": "还没有更早的消息。",
|
|
685
|
-
"Rewind to a message ·
|
|
685
|
+
"Rewind to a message · it and what follows are replaced": "回退到某条消息 · 它和之后的内容会被替换",
|
|
686
686
|
"Rewind cancelled. Queued drafts stay paused.": "已取消回退。等待中的草稿仍然暂停。",
|
|
687
|
-
"
|
|
688
|
-
"Earlier message restored · edit before sending
|
|
687
|
+
"Rewound to before this message. Edit it below, then send. Spotify actions and saved preferences from the replaced messages have not been undone.": "已回退到这条消息之前。在下面编辑后发送。被替换的消息里的 Spotify 操作和已保存的偏好没有撤销。",
|
|
688
|
+
"Earlier message restored · edit before sending": "已恢复之前的消息 · 发送前编辑",
|
|
689
|
+
"Use /branch on its own.": "/branch 后面不需要加内容。",
|
|
690
|
+
"Branching isn't available in this runtime.": "当前运行环境不支持分支。",
|
|
691
|
+
"Nothing to branch from yet.": "还没有可以分支的内容。",
|
|
692
|
+
"Branched · the original is in /resume": "已分支 · 原对话在 /resume 里",
|
|
689
693
|
"Use /resume, or /resume <conversation-id>.": "使用 /resume,或 /resume <对话 ID>。",
|
|
690
694
|
"Saved conversations aren't available when Moondog is started this way.": "这种启动方式不支持保存的对话。",
|
|
691
695
|
"No saved conversations yet.": "还没有保存的对话。",
|
|
@@ -811,7 +815,8 @@ export default {
|
|
|
811
815
|
"Ask Moondog to remember something": "让 Moondog 记住一件事",
|
|
812
816
|
"Forget one memory by ID": "按 ID 忘掉一条记忆",
|
|
813
817
|
"Pick up a saved conversation": "继续一段保存的对话",
|
|
814
|
-
"Edit an earlier message
|
|
818
|
+
"Edit an earlier message in place · Alt+R": "就地编辑之前的一条消息 · Alt+R",
|
|
819
|
+
"Go on from here in a new conversation; this one stays saved": "从这里在新对话中继续;这段对话会保留",
|
|
815
820
|
"Start fresh; this conversation stays saved": "重新开始;这段对话会保存下来",
|
|
816
821
|
"Commands and shortcuts": "命令和快捷键",
|
|
817
822
|
"Leave Moondog": "离开 Moondog",
|
|
@@ -1270,9 +1270,11 @@ export class LocalMemoryStore {
|
|
|
1270
1270
|
return this.commitCompletedPrompt(sessionId, { user, assistant });
|
|
1271
1271
|
}
|
|
1272
1272
|
|
|
1273
|
+
// turnId names the user turn; the application passes the conversation entry's own id, so an
|
|
1274
|
+
// edit can find this exchange in the prompt log.
|
|
1273
1275
|
commitCompletedPrompt(
|
|
1274
1276
|
sessionId,
|
|
1275
|
-
{ user, assistant, memoryMutations = [] },
|
|
1277
|
+
{ user, assistant, memoryMutations = [], turnId = randomUUID() },
|
|
1276
1278
|
) {
|
|
1277
1279
|
this.#assertOpen();
|
|
1278
1280
|
const userText = cleanText(user, 8_000, "User turn");
|
|
@@ -1292,7 +1294,7 @@ export class LocalMemoryStore {
|
|
|
1292
1294
|
WHERE session_id = ?
|
|
1293
1295
|
`)
|
|
1294
1296
|
.get(sessionId);
|
|
1295
|
-
const userTurnId =
|
|
1297
|
+
const userTurnId = cleanText(turnId, 128, "User turn ID");
|
|
1296
1298
|
insert.run(
|
|
1297
1299
|
userTurnId,
|
|
1298
1300
|
sessionId,
|
|
@@ -1475,7 +1477,44 @@ export class LocalMemoryStore {
|
|
|
1475
1477
|
} catch (error) { this.#database.exec("ROLLBACK"); throw error; }
|
|
1476
1478
|
}
|
|
1477
1479
|
|
|
1478
|
-
|
|
1480
|
+
// Edit, in place: the selected message and everything after it leave the conversation, and the
|
|
1481
|
+
// message comes back as the draft to change and send again. A branch is how an original is kept.
|
|
1482
|
+
// Saved preferences stay, as Spotify actions stay done.
|
|
1483
|
+
rewindConversation(sessionId, entryId) {
|
|
1484
|
+
this.#assertOpen();
|
|
1485
|
+
this.#database.exec("BEGIN IMMEDIATE");
|
|
1486
|
+
try {
|
|
1487
|
+
this.#ensureConversation(sessionId);
|
|
1488
|
+
if (this.#replyElsewhere(sessionId)) {
|
|
1489
|
+
throw Object.assign(new Error("Moondog is answering in this conversation in another window or terminal."), { code: "conversation_busy" });
|
|
1490
|
+
}
|
|
1491
|
+
const selected = this.#database.prepare("SELECT * FROM conversation_entries WHERE session_id = ? AND entry_id = ?").get(sessionId, entryId);
|
|
1492
|
+
if (!selected) throw new Error("The selected turn is no longer available");
|
|
1493
|
+
// The prompt log holds the same exchanges for recall, so they leave it too, with their dialogue
|
|
1494
|
+
// episodes. A logged message carries its entry's id; one logged before that is found by its time.
|
|
1495
|
+
const first = this.#database.prepare(`SELECT MIN(position) AS position FROM turns WHERE session_id = ? AND role = 'user' AND (
|
|
1496
|
+
turn_id IN (SELECT entry_id FROM conversation_entries WHERE session_id = ? AND position >= ?)
|
|
1497
|
+
OR (created_at >= ? AND turn_id NOT IN (SELECT entry_id FROM conversation_entries WHERE session_id = ? AND position < ?)))`)
|
|
1498
|
+
.get(sessionId, sessionId, selected.position, selected.created_at, sessionId, selected.position).position;
|
|
1499
|
+
if (first !== null) {
|
|
1500
|
+
const replaced = "SELECT turn_id FROM turns WHERE session_id = ? AND role = 'user' AND position >= ?";
|
|
1501
|
+
const dialogue = `SELECT episode_id FROM episodes WHERE kind = 'dialogue' AND source_kind = 'turn' AND source_id IN (${replaced})`;
|
|
1502
|
+
this.#database.prepare(`DELETE FROM claim_episode_refs WHERE episode_id IN (${dialogue})`).run(sessionId, first);
|
|
1503
|
+
this.#database.prepare(`DELETE FROM episodes WHERE episode_id IN (${dialogue})`).run(sessionId, first);
|
|
1504
|
+
this.#database.prepare("DELETE FROM turns WHERE session_id = ? AND position >= ?").run(sessionId, first);
|
|
1505
|
+
}
|
|
1506
|
+
this.#database.prepare(`DELETE FROM conversation_writers WHERE entry_id IN
|
|
1507
|
+
(SELECT entry_id FROM conversation_entries WHERE session_id = ? AND position >= ?)`).run(sessionId, selected.position);
|
|
1508
|
+
this.#database.prepare("DELETE FROM conversation_entries WHERE session_id = ? AND position >= ?").run(sessionId, selected.position);
|
|
1509
|
+
this.#database.prepare("UPDATE conversation_branches SET draft = ? WHERE session_id = ?").run(selected.user_text, sessionId);
|
|
1510
|
+
this.#database.exec("COMMIT");
|
|
1511
|
+
return { session_id: sessionId, draft: selected.user_text };
|
|
1512
|
+
} catch (error) { this.#database.exec("ROLLBACK"); throw error; }
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
// Branch: a new conversation that goes on from a reply, with a copy of everything up to it. The
|
|
1516
|
+
// original stays as it is, and the branch becomes the one both the terminal and the App show.
|
|
1517
|
+
branchConversation(sessionId, entryId, { routeKey = 'local:main' } = {}) {
|
|
1479
1518
|
this.#assertOpen();
|
|
1480
1519
|
const route = cleanText(routeKey, 128, "Session route");
|
|
1481
1520
|
this.#database.exec("BEGIN IMMEDIATE");
|
|
@@ -1487,19 +1526,20 @@ export class LocalMemoryStore {
|
|
|
1487
1526
|
this.#ensureConversation(sessionId);
|
|
1488
1527
|
const selected = this.#database.prepare("SELECT * FROM conversation_entries WHERE session_id = ? AND entry_id = ?").get(sessionId, entryId);
|
|
1489
1528
|
if (!selected) throw new Error("The selected turn is no longer available");
|
|
1529
|
+
if (selected.outcome === 'running') throw new Error("Wait for the reply to finish before branching");
|
|
1490
1530
|
const root = this.#database.prepare("SELECT root_session_id FROM conversation_branches WHERE session_id = ?").get(sessionId).root_session_id;
|
|
1491
1531
|
const createdAt = isoTimestamp(this.#clock);
|
|
1492
1532
|
const nextId = randomUUID();
|
|
1493
|
-
this.#database.prepare("UPDATE sessions SET status = 'closed', ended_at = ?, close_reason = '
|
|
1533
|
+
this.#database.prepare("UPDATE sessions SET status = 'closed', ended_at = ?, close_reason = 'branch' WHERE route_key = ? AND status = 'active'").run(createdAt, route);
|
|
1494
1534
|
this.#database.prepare("INSERT INTO sessions(session_id, route_key, started_at, status) VALUES (?, ?, ?, 'active')").run(nextId, route, createdAt);
|
|
1495
|
-
this.#database.prepare(`INSERT INTO conversation_branches(session_id, root_session_id, parent_session_id, source_entry_id
|
|
1496
|
-
VALUES (?, ?, ?,
|
|
1497
|
-
const prefix = this.#database.prepare("SELECT * FROM conversation_entries WHERE session_id = ? AND position
|
|
1535
|
+
this.#database.prepare(`INSERT INTO conversation_branches(session_id, root_session_id, parent_session_id, source_entry_id)
|
|
1536
|
+
VALUES (?, ?, ?, ?)`).run(nextId, root, sessionId, entryId);
|
|
1537
|
+
const prefix = this.#database.prepare("SELECT * FROM conversation_entries WHERE session_id = ? AND position <= ? ORDER BY position").all(sessionId, selected.position);
|
|
1498
1538
|
const insert = this.#database.prepare(`INSERT INTO conversation_entries
|
|
1499
1539
|
(entry_id, session_id, position, user_text, assistant_text, outcome, transient, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
1500
1540
|
for (const row of prefix) insert.run(randomUUID(), nextId, row.position, row.user_text, row.assistant_text, row.outcome, row.transient, row.created_at);
|
|
1501
1541
|
this.#database.exec("COMMIT");
|
|
1502
|
-
return { session_id: nextId, route_key: route, started_at: createdAt, parent_session_id: sessionId
|
|
1542
|
+
return { session_id: nextId, route_key: route, started_at: createdAt, parent_session_id: sessionId };
|
|
1503
1543
|
} catch (error) { this.#database.exec("ROLLBACK"); throw error; }
|
|
1504
1544
|
}
|
|
1505
1545
|
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
// Moondog's logo, drawn from numbers. Every shape is measured from the selected 2026-09-02 picture
|
|
2
|
+
// (assets/brand/moondog-lunar-record): the dog's outline, the headphones, the record's grooves and
|
|
3
|
+
// craters, and the tonearm. The picture's paper and its bar mosaic stay with that key visual; the
|
|
4
|
+
// logo is one ink on nothing, so it sits on any background.
|
|
5
|
+
//
|
|
6
|
+
// Coordinates are the picture's 1024 px square with the record's centre moved to (512, 512).
|
|
7
|
+
// scripts/generate-logo.mjs writes the files in assets/brand, the app icons and the site's images
|
|
8
|
+
// from this module; the window's title-bar mark and the terminal's record draw the same dog.
|
|
9
|
+
|
|
10
|
+
const f = (n) => Number(n.toFixed(2));
|
|
11
|
+
const pt = ([x, y]) => `${f(x)} ${f(y)}`;
|
|
12
|
+
|
|
13
|
+
export const RECORD = Object.freeze({ cx: 512, cy: 512, rim: 426.7, inner: 222, pitch: 9.75 });
|
|
14
|
+
|
|
15
|
+
// The earcup is a small record of its own, its rings on the record's own pitch.
|
|
16
|
+
const CUP = Object.freeze({ cx: 362.5, cy: 504, r: 67, rings: [5, 14, 23, 32, 41, 50, 59] });
|
|
17
|
+
|
|
18
|
+
// The band, its outer edge first, from inside the earcup up over the crown to the ear's tip, then
|
|
19
|
+
// back down its inner edge. Its foot reaches into the earcup, so the gap round the cup cuts it.
|
|
20
|
+
const BAND_OUTER = [[334, 452], [334.6, 418], [335.2, 396.1], [339.3, 371.8], [345.7, 353.1], [353.5, 336.1], [365.6, 317.1],
|
|
21
|
+
[379.8, 300.7], [389.7, 291], [400.7, 282.9], [413.5, 275.6], [426.9, 267.9], [436, 262.3]];
|
|
22
|
+
const BAND_TIP = [[441, 262.4], [444.6, 267.6], [447.6, 276.4]];
|
|
23
|
+
const BAND_INNER = [[446.3, 281.5], [436.7, 284.8], [419.5, 296], [401.3, 311.1], [389, 324.5], [380.3, 338], [373.4, 351.8],
|
|
24
|
+
[367.4, 368], [363.2, 386], [361.1, 405], [361, 452]];
|
|
25
|
+
|
|
26
|
+
// The head, clockwise from the ear's tip: down the ear, the brow and the muzzle, round the nose, back
|
|
27
|
+
// under the jaw, down the throat and the chest, along the bottom and up the back of the neck into
|
|
28
|
+
// the earcup's gap. The corners are the ear, the throat, the chest's step and the bottom's two ends.
|
|
29
|
+
const HEAD = [
|
|
30
|
+
[447.6, 281], [469.5, 347.5],
|
|
31
|
+
[481, 359.8], [494, 377.2], [512, 389.2], [542, 401.2], [577, 417], [603, 435.2],
|
|
32
|
+
[611.5, 449], [620.5, 461], [629, 473.5], [641, 483.6], [655, 490.4], [681, 501], [704.4, 508.6], [720, 512],
|
|
33
|
+
[729, 515.8], [733, 522.5], [733.4, 530.6], [731, 539.5],
|
|
34
|
+
[724.3, 553.2], [719.4, 565.9], [712.5, 577.5], [696.5, 593.6], [679.4, 604.9], [657.5, 612.9], [638, 617],
|
|
35
|
+
[592.5, 618.1], [578.2, 621.5], [568.6, 628.9], [563.5, 636],
|
|
36
|
+
[563.3, 661], [565.6, 690], [566.3, 728.3],
|
|
37
|
+
[546.2, 740.3], [528, 746.8],
|
|
38
|
+
[525.2, 790], [527.5, 838.8],
|
|
39
|
+
[480, 833.5], [434, 825], [392, 813.6], [350, 800], [312, 775], [275, 750], [245, 727], [220.3, 699.5],
|
|
40
|
+
[236, 664], [252, 630], [265, 600], [276, 566], [287.7, 533.7], [292, 522],
|
|
41
|
+
];
|
|
42
|
+
const HEAD_CORNERS = [0, 1, 30, 33, 35, 37, 45];
|
|
43
|
+
|
|
44
|
+
// Craters, each an inner arc, a ring and one or two dashed rings.
|
|
45
|
+
const CRATERS = [
|
|
46
|
+
{ cx: 321.5, cy: 236.7, r: 39 }, { cx: 712.8, cy: 277.2, r: 36 }, { cx: 212, cy: 490.9, r: 27.5 },
|
|
47
|
+
{ cx: 816.5, cy: 618.4, r: 16.5 }, { cx: 256.5, cy: 786.9, r: 30.5 }, { cx: 640.8, cy: 848.7, r: 40 },
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
// The tonearm in its own coordinates: t runs down the arm from the headshell's middle, s across it.
|
|
51
|
+
const ARM = Object.freeze({ origin: [900.3, 201.2], down: [-0.61433, 0.78905] });
|
|
52
|
+
|
|
53
|
+
// A centripetal Catmull-Rom curve through the nodes, as cubic Béziers: it passes through every node
|
|
54
|
+
// and never loops at a sharp turn. A node in `corners` keeps its point; the curve meets it straight.
|
|
55
|
+
function segments(nodes, { closed = true, corners = [] } = {}) {
|
|
56
|
+
const sharp = new Set(corners);
|
|
57
|
+
const count = nodes.length;
|
|
58
|
+
const at = (index) => (closed ? nodes[(index + count) % count] : nodes[Math.max(0, Math.min(count - 1, index))]);
|
|
59
|
+
const tangent = (a, b, c, index) => {
|
|
60
|
+
if (sharp.has((index + count) % count)) return [0, 0];
|
|
61
|
+
const before = Math.hypot(b[0] - a[0], b[1] - a[1]) ** 0.5 || 1e-6;
|
|
62
|
+
const after = Math.hypot(c[0] - b[0], c[1] - b[1]) ** 0.5 || 1e-6;
|
|
63
|
+
return [0, 1].map((axis) => (b[axis] - a[axis]) / before - (c[axis] - a[axis]) / (before + after) + (c[axis] - b[axis]) / after);
|
|
64
|
+
};
|
|
65
|
+
const list = [];
|
|
66
|
+
for (let index = 0; index < (closed ? count : count - 1); index += 1) {
|
|
67
|
+
const [p0, p1, p2, p3] = [at(index - 1), at(index), at(index + 1), at(index + 2)];
|
|
68
|
+
const span = Math.hypot(p2[0] - p1[0], p2[1] - p1[1]) ** 0.5;
|
|
69
|
+
const [m1, m2] = [tangent(p0, p1, p2, index), tangent(p1, p2, p3, index + 1)];
|
|
70
|
+
list.push([p1, [p1[0] + m1[0] * span / 3, p1[1] + m1[1] * span / 3], [p2[0] - m2[0] * span / 3, p2[1] - m2[1] * span / 3], p2]);
|
|
71
|
+
}
|
|
72
|
+
return { list, closed };
|
|
73
|
+
}
|
|
74
|
+
const toPath = ({ list, closed }) => `M${pt(list[0][0])}${list.map(([, c1, c2, end]) => `C${pt(c1)} ${pt(c2)} ${pt(end)}`).join("")}${closed ? "Z" : ""}`;
|
|
75
|
+
function toPoints({ list }, steps = 8) {
|
|
76
|
+
const points = [];
|
|
77
|
+
for (const [a, b, c, d] of list) {
|
|
78
|
+
for (let step = 0; step < steps; step += 1) {
|
|
79
|
+
const t = step / steps, s = 1 - t;
|
|
80
|
+
points.push([0, 1].map((axis) => s ** 3 * a[axis] + 3 * s * s * t * b[axis] + 3 * s * t * t * c[axis] + t ** 3 * d[axis]));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
points.push(list.at(-1)[3]);
|
|
84
|
+
return points;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Moves each node of an open line sideways by `by`: positive is to the right of the way it runs.
|
|
88
|
+
function offset(nodes, by) {
|
|
89
|
+
return nodes.map((point, index) => {
|
|
90
|
+
const a = nodes[Math.max(0, index - 1)], b = nodes[Math.min(nodes.length - 1, index + 1)];
|
|
91
|
+
const length = Math.hypot(b[0] - a[0], b[1] - a[1]);
|
|
92
|
+
return [point[0] - ((b[1] - a[1]) / length) * by, point[1] + ((b[0] - a[0]) / length) * by];
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// The dog's three shapes and the gap between band and head. The head closes through the earcup and
|
|
97
|
+
// just inside the band, so head and band overlap without a seam; the gaps are cut from all three.
|
|
98
|
+
function figure(gap) {
|
|
99
|
+
const band = segments([...BAND_OUTER, ...BAND_TIP, ...BAND_INNER], { corners: [0, BAND_OUTER.length + BAND_TIP.length + BAND_INNER.length - 1] });
|
|
100
|
+
const along = offset([...BAND_INNER].reverse(), -1.5).slice(0, -1);
|
|
101
|
+
const head = segments([...HEAD, [330, 505], [358, 455], ...along], {
|
|
102
|
+
corners: [...HEAD_CORNERS, HEAD.length - 1, HEAD.length, HEAD.length + 1, HEAD.length + along.length - 1],
|
|
103
|
+
});
|
|
104
|
+
// The band's gap runs beside its inner edge, from the middle of the earcup's gap out past the ear.
|
|
105
|
+
const edge = [...BAND_INNER.slice(0, -1), [361.1, CUP.cy - CUP.r - gap / 2]];
|
|
106
|
+
const [first, second] = edge;
|
|
107
|
+
const bandGap = segments(offset([[first[0] + (first[0] - second[0]) * 1.2, first[1] + (first[1] - second[1]) * 1.2], ...edge], -gap / 2), { closed: false });
|
|
108
|
+
return { head, band, bandGap };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const toArm = (t, s) => [ARM.origin[0] + t * ARM.down[0] + s * ARM.down[1], ARM.origin[1] + t * ARM.down[1] - s * ARM.down[0]];
|
|
112
|
+
const quad = (corners) => `M${corners.map((corner) => pt(toArm(...corner))).join("L")}Z`;
|
|
113
|
+
// The full tonearm: tube, collar, headshell, the block under it, finger lift, cartridge, stylus.
|
|
114
|
+
function tonearm(reach) {
|
|
115
|
+
return {
|
|
116
|
+
parts: [
|
|
117
|
+
quad([[-reach, -7.25], [-reach, 7.25], [-38, 7.25], [-38, -7.25]]), quad([[-36.5, -11], [-36.5, 11], [-28, 11], [-28, -11]]),
|
|
118
|
+
quad([[-26.5, -17.5], [-26.5, 17.5], [25, 17.5], [25, -20]]), quad([[27, -26], [27, 21.5], [70, 19.5], [70, -28]]),
|
|
119
|
+
quad([[21.75, 17], [21.75, 54], [30.25, 54], [30.25, 17]]),
|
|
120
|
+
quad([[71.5, -13.5], [71.5, 8.5], [97.5, 6.25], [97.5, -11.25]]), quad([[97.5, -6.5], [97.5, 1.5], [104, 0], [104, -5]]),
|
|
121
|
+
],
|
|
122
|
+
liftEnd: toArm(26, 54),
|
|
123
|
+
screw: toArm(-11.25, 0),
|
|
124
|
+
holes: [toArm(50, -12.5), toArm(50, 7)],
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
// The small version, for 64 px and below: the rim and two heavy grooves, the clear margin they keep
|
|
128
|
+
// from the dog, gaps wide enough to last, and the earcup's one ring and its middle.
|
|
129
|
+
const SMALL = Object.freeze({ gap: 30, halo: 22, grooves: [[RECORD.rim - 15, 30], [362, 22], [312, 22]], cupRing: [34, 14], cupDot: 14 });
|
|
130
|
+
// Its arm is one tube, one headshell and a cartridge, heavy enough to show at 32 px.
|
|
131
|
+
const heavyArm = (reach) => [[[-reach, -15], [-reach, 15], [-30, 15], [-30, -15]], [[-26, -30], [-26, 26], [74, 24], [74, -32]],
|
|
132
|
+
[[78, -17], [78, 11], [104, 8], [104, -14]]];
|
|
133
|
+
|
|
134
|
+
function arc(cx, cy, r, from, to) {
|
|
135
|
+
const point = (angle) => [cx + r * Math.cos((angle * Math.PI) / 180), cy + r * Math.sin((angle * Math.PI) / 180)];
|
|
136
|
+
return `M${pt(point(from))}A${f(r)} ${f(r)} 0 ${to - from > 180 ? 1 : 0} 1 ${pt(point(to))}`;
|
|
137
|
+
}
|
|
138
|
+
const ring = (cx, cy, r, width, paint, more = "") =>
|
|
139
|
+
`<circle cx="${f(cx)}" cy="${f(cy)}" r="${f(r)}" fill="none" stroke="${paint}" stroke-width="${f(width)}"${more}/>`;
|
|
140
|
+
const crater = ({ cx, cy, r }, ink, line) => {
|
|
141
|
+
const large = r > 25;
|
|
142
|
+
return [
|
|
143
|
+
`<path d="${arc(cx, cy, r * 0.27, 200, 470)}" fill="none" stroke="${ink}" stroke-width="${line}" stroke-linecap="round"/>`,
|
|
144
|
+
ring(cx, cy, r * 0.46, line * 1.15, ink),
|
|
145
|
+
ring(cx, cy, r * (large ? 0.72 : 1), line, ink, ` stroke-dasharray="${f(r * 0.5)} ${f(r * 0.12)}" stroke-dashoffset="${f(r * 0.2)}"`),
|
|
146
|
+
large ? ring(cx, cy, r, line * 0.85, ink, ` stroke-dasharray="${f(r * 0.28)} ${f(r * 0.1)} ${f(r * 0.06)} ${f(r * 0.1)}"`) : "",
|
|
147
|
+
].join("");
|
|
148
|
+
};
|
|
149
|
+
// A standalone file takes the reader's light or dark; drawn inside a page, the logo takes the page's colour.
|
|
150
|
+
const SCHEME = "<style>svg{color:#141414}@media (prefers-color-scheme:dark){svg{color:#f2f2f2}}</style>";
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* The logo as SVG. The full logo has every groove, the craters and the whole tonearm. `small` is
|
|
154
|
+
* the one for 64 px and below: the rim, two heavy grooves, a heavy tonearm, and gaps wide enough
|
|
155
|
+
* to last. `ink` paints it; `standalone` adds the light and dark colours a file on its own needs.
|
|
156
|
+
* `id` keeps the masks of several logos on one page apart.
|
|
157
|
+
*/
|
|
158
|
+
export function logoSvg({ small = false, ink = "currentColor", standalone = false, id = "moondog-logo", reach = 200, line = 1.4 } = {}) {
|
|
159
|
+
const gap = small ? SMALL.gap : 11;
|
|
160
|
+
const halo = small ? SMALL.halo : 6;
|
|
161
|
+
const { head, band, bandGap } = figure(gap);
|
|
162
|
+
const arm = tonearm(reach);
|
|
163
|
+
const armParts = small ? heavyArm(reach + 60).map(quad) : arm.parts;
|
|
164
|
+
const liftEnd = small ? "" : `<circle cx="${f(arm.liftEnd[0])}" cy="${f(arm.liftEnd[1])}" r="4.25"/>`;
|
|
165
|
+
const dog = `<path d="${toPath(head)}"/><path d="${toPath(band)}"/><circle cx="${CUP.cx}" cy="${CUP.cy}" r="${CUP.r}"/>`;
|
|
166
|
+
const grooves = [];
|
|
167
|
+
if (small) grooves.push(...SMALL.grooves);
|
|
168
|
+
else for (let r = RECORD.rim, index = 0; r >= RECORD.inner - 0.01; r -= RECORD.pitch, index += 1) grooves.push([r, index ? line : line * 1.6]);
|
|
169
|
+
const cupRings = small
|
|
170
|
+
? `${ring(CUP.cx, CUP.cy, ...SMALL.cupRing, "#000")}<circle cx="${CUP.cx}" cy="${CUP.cy}" r="${SMALL.cupDot}" fill="#000"/>`
|
|
171
|
+
: CUP.rings.map((r) => ring(CUP.cx, CUP.cy, r, 3.2, "#000")).join("");
|
|
172
|
+
const mask = (name, body) => `<mask id="${id}-${name}" maskUnits="userSpaceOnUse" x="0" y="0" width="1024" height="1024"><rect width="1024" height="1024" fill="#fff"/>${body}</mask>`;
|
|
173
|
+
// Grooves stop short of the dog, the arm and the craters; the gaps cut the dog into head, band and cup.
|
|
174
|
+
const clear = `<g fill="#000" stroke="#000" stroke-width="${halo * 2}" stroke-linejoin="round">${dog}${armParts.map((d) => `<path d="${d}"/>`).join("")}${liftEnd}</g>`
|
|
175
|
+
+ (small ? "" : CRATERS.map((c) => `<circle cx="${c.cx}" cy="${c.cy}" r="${c.r + 10}" fill="#000"/>`).join(""));
|
|
176
|
+
const cuts = `${ring(CUP.cx, CUP.cy, CUP.r + gap / 2, gap, "#000")}<path d="${toPath(bandGap)}" fill="none" stroke="#000" stroke-width="${gap}"/>${cupRings}`;
|
|
177
|
+
const armDetail = small ? "" : ring(...arm.screw, 7.5, 1.8, "#000") + arm.holes.map(([x, y]) => `<circle cx="${f(x)}" cy="${f(y)}" r="5.5" fill="#000"/>`).join("")
|
|
178
|
+
+ `<path d="M${pt(arm.holes[0])}L${pt(arm.holes[1])}" stroke="#000" stroke-width="1.6"/>`;
|
|
179
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="${small ? "40 40 944 944" : "0 0 1024 1024"}">${standalone ? SCHEME : ""}`
|
|
180
|
+
+ `<defs>${mask("clear", clear)}${mask("cut", cuts)}${small ? "" : mask("arm", armDetail)}</defs>`
|
|
181
|
+
+ `<g mask="url(#${id}-clear)">${grooves.map(([r, width]) => ring(RECORD.cx, RECORD.cy, r, width, ink)).join("")}</g>`
|
|
182
|
+
+ (small ? "" : CRATERS.map((c) => crater(c, ink, line)).join(""))
|
|
183
|
+
+ `<g mask="url(#${id}-cut)" fill="${ink}">${dog}</g>`
|
|
184
|
+
+ `<g fill="${ink}"${small ? "" : ` mask="url(#${id}-arm)"`}>${armParts.map((d) => `<path d="${d}"/>`).join("")}${liftEnd}</g></svg>`;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* The app icon's tile on its 1024 canvas: a superellipse, the shape of a macOS icon, `half` from the
|
|
189
|
+
* center to each side. Larger halves draw the same shape round the tile, as a ring that follows it.
|
|
190
|
+
*/
|
|
191
|
+
export function iconShape(half = 412) {
|
|
192
|
+
const corner = Array.from({ length: 160 }, (_, index) => {
|
|
193
|
+
const angle = (index / 160) * 2 * Math.PI, cos = Math.cos(angle), sin = Math.sin(angle);
|
|
194
|
+
return [512 + half * Math.sign(cos) * Math.abs(cos) ** 0.4, 512 + half * Math.sign(sin) * Math.abs(sin) ** 0.4];
|
|
195
|
+
});
|
|
196
|
+
return `M${corner.map(pt).join("L")}Z`;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* The Mac app icon: the full logo on a rounded square of Apple's 824 px grid, its tonearm reaching in
|
|
201
|
+
* from beyond the tile's corner. Light is the app's own icon; dark is the one the listener can choose.
|
|
202
|
+
*/
|
|
203
|
+
export function appIconSvg({ dark = false, id = "moondog-icon" } = {}) {
|
|
204
|
+
const [tile, ink] = dark ? ["#151515", "#f2f2f2"] : ["#ffffff", "#141414"];
|
|
205
|
+
const shape = iconShape();
|
|
206
|
+
const art = logoSvg({ ink, id, reach: 340, line: 1.8 }).replace(/^<svg[^>]*>/u, "").replace(/<\/svg>$/u, "");
|
|
207
|
+
// The soft shadow under the tile that Mac icons draw in their own margin.
|
|
208
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><defs><clipPath id="${id}-tile"><path d="${shape}"/></clipPath>`
|
|
209
|
+
+ `<filter id="${id}-shadow" x="-10%" y="-10%" width="120%" height="125%"><feDropShadow dx="0" dy="10" stdDeviation="12" flood-color="#000" flood-opacity="0.28"/></filter></defs>`
|
|
210
|
+
+ `<path d="${shape}" fill="${tile}" filter="url(#${id}-shadow)"/><g clip-path="url(#${id}-tile)"><g transform="translate(512 512) scale(0.74) translate(-512 -512)">${art}</g></g></svg>`;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* The small logo for surfaces that draw it themselves, such as the window's title-bar mark and the
|
|
215
|
+
* terminal's record, in the record's unit coordinates (the disc has radius 1 around 0, 0): the dog's
|
|
216
|
+
* outlines as points, its gaps, the rim and grooves as [radius, width], the margin they keep from the
|
|
217
|
+
* dog, and the heavy arm's parts, its tube `reach` long.
|
|
218
|
+
*/
|
|
219
|
+
export function logoFigure({ reach = 190 } = {}) {
|
|
220
|
+
const { head, band, bandGap } = figure(SMALL.gap);
|
|
221
|
+
const scale = (length) => length / RECORD.rim;
|
|
222
|
+
const unit = ([x, y]) => [scale(x - RECORD.cx), scale(y - RECORD.cy)];
|
|
223
|
+
return {
|
|
224
|
+
// The head to fill, and its outer edge alone, from the ear's tip round to the back of the neck.
|
|
225
|
+
head: toPoints(head).map(unit),
|
|
226
|
+
headEdge: toPoints(segments(HEAD, { closed: false, corners: HEAD_CORNERS })).map(unit),
|
|
227
|
+
// The band to fill, and the line down its middle, from the ear's tip to the earcup.
|
|
228
|
+
band: toPoints(band).map(unit),
|
|
229
|
+
bandCenter: toPoints(segments(offset(BAND_INNER, 12.5), { closed: false })).map(unit),
|
|
230
|
+
bandGap: toPoints(bandGap, 4).map(unit),
|
|
231
|
+
gap: scale(SMALL.gap),
|
|
232
|
+
cup: { center: unit([CUP.cx, CUP.cy]), radius: scale(CUP.r), ring: scale(SMALL.cupRing[0]), ringWidth: scale(SMALL.cupRing[1]), dot: scale(SMALL.cupDot) },
|
|
233
|
+
grooves: SMALL.grooves.map(([radius, width]) => [scale(radius), scale(width)]),
|
|
234
|
+
halo: scale(SMALL.halo),
|
|
235
|
+
tonearm: heavyArm(reach).map((corners) => corners.map((corner) => unit(toArm(...corner)))),
|
|
236
|
+
};
|
|
237
|
+
}
|
|
@@ -557,5 +557,5 @@ export function formatLocalResult(command, value, tr = screenTranslator("en")) {
|
|
|
557
557
|
}
|
|
558
558
|
|
|
559
559
|
export function helpText(tr = screenTranslator("en")) {
|
|
560
|
-
return tr("# Every command\n\nFrom your shell:\n\n- `moondog --version` - show the installed version\n- `moondog update [--check] [--channel latest|beta]` - check for or install a published release\n- `moondog web status|search|read [arguments] [--json]` - look things up on public music sites through DeepSeek, Brave Search or the Codex CLI\n- `moondog demo-history --output <absolute-file.zip> [--json]` - make a fictional Spotify history ZIP to try the importer with\n- `moondog auth login <provider>` - sign in to a model provider; the key is typed out of sight, and openai-codex uses your ChatGPT sign-in\n- `moondog auth status [provider]` - see which sign-ins are saved\n- `moondog auth logout <provider>` - sign out of a provider\n- `moondog spotify help` - set up and control Spotify\n- `moondog spotify login [client-id]` - connect Spotify in your browser\n- `moondog spotify now|devices|queue` - see what Spotify is playing, where, and what's next\n- `moondog spotify recent` - see what you played lately\n- `moondog spotify sync-recent` - add your latest plays to your profile\n- `moondog spotify sync-library` - add your Spotify library to your profile\n- `moondog spotify play|pause|next|previous` - control Spotify playback\n- `moondog listenbrainz import-history <listen-history.json>` - add a ListenBrainz listens file to your profile\n- `moondog catalog latest-single --artist <name> [--known-release <title> | --from <spotify-history.zip>]` - find an artist's latest single on Apple Music (US), no model needed\n- `moondog status [--json]` - see where things stand\n- `moondog taste [--json]` - your listening report\n- `moondog taste --html [--output <file.html>]` - save your report as a web page that works offline\n- `moondog taste --card [--output <file.html>]` - save a short recap you can check before sharing\n- `moondog taste --from <spotify-history.zip> --html` - a one-off report from a Spotify ZIP, without saving it\n- `moondog taste --from <account-data.zip> --from <extended-history.zip> --html` - a one-off report from both Spotify exports together\n- `moondog taste --from <spotify-history.zip> --save --html` - save the Spotify history, then show your full report\n- `moondog data inspect --scope <listening|apple|profile>` - see what's stored, and get the code needed to reset it\n- `moondog data export --scope <listening|apple|profile> --output <directory>` - export a copy, leaving the original alone\n- `moondog data reset --scope <listening|apple|profile> --confirm <token>` - set stored data aside (it is archived, not deleted)\n- `moondog profile build [--force]` - build or resume a saved profile with your connected model\n- `moondog profile saved [offset]` - read your saved profile\n- `moondog profile explain <number>` - trace a saved finding to its evidence\n- `moondog profile corrections [--all]` - everything you've told me, including earlier choices\n- `moondog profile correct --artist <name> (--like|--avoid)` - like an artist, or keep them out\n- `moondog profile correct --track <title> --by <artist> (--like|--avoid)` - like a track, or keep it out\n- `moondog profile retract <correction-id>` - undo one of your choices\n- `moondog demo --offline [--json]` - a demo with made-up data, no sign-in needed\n- `moondog demo [prompt] [--json]` - the same demo, talking through your model\n- `moondog ask <prompt>` - ask one question and exit\n- `moondog remember <text>` - ask me to remember something\n- `moondog forget <memory-id>` - forget something\n- `moondog memory reflect [--dry-run] [--json]` - let me think over recent conversations now\n- `moondog` - open the listening room\n\nInside the listening room:\n\n- `/update [--check] [--channel latest|beta]` - check for updates, or save and close this session to install one\n- `/status` or `/sources` - see where things stand\n- `/profile` or `/taste` - your profile, and why each song or artist is there\n- `/taste report` - the whole report on one page\n- `/profile build [--force]` - build or resume a saved reading; Ctrl+C keeps progress\n- `/profile saved [offset]` - read your saved profile\n- `/profile explain <number>` - inspect a saved finding's evidence\n- `/profile corrections [--all]` - everything you've told me\n- `/profile correct --track \"<title>\" --by \"<artist>\" (--like|--avoid)` - like a track, or keep it out\n- `/profile correct --artist \"<name>\" (--like|--avoid)` - like an artist, or keep them out\n- `/profile retract <correction-id>` - undo a choice\n- `/memory` - what I remember\n- `/tools` - what I can do right now\n- `/doctor` - check your setup\n- `/model` - pick the model I talk through\n- `/model <provider> <model>` - switch straight to a model\n- `/model refresh [provider]` - update public model choices; keep the selected model\n- `/auth [provider]` - sign in to a model provider\n- `/web status|search|read` - look things up on public music sites\n- `/import [path]` - bring in your history, library, or a playlist\n- `/language` - choose the language Moondog uses\n- `/spotify` - Spotify commands\n- `/spotify login [client-id]` - connect Spotify\n- `/spotify now|devices|queue` - see what's playing, where, and what's next\n- `/spotify recent|sync-recent` - see or save your latest plays\n- `/spotify import-history \"/path/to/spotify-history.zip\"` - add a Spotify ZIP, no sign-in needed\n- `/skip` or `/stop` - move to the next song preview, or stop them\n- `/reload` - reload model settings and sign-ins\n- `/remember [kind] <text>` - ask me to remember something\n- `/forget <memory-id>` - forget something\n- `/rewind` or Alt+R - edit an earlier message on a new
|
|
560
|
+
return tr("# Every command\n\nFrom your shell:\n\n- `moondog --version` - show the installed version\n- `moondog update [--check] [--channel latest|beta]` - check for or install a published release\n- `moondog web status|search|read [arguments] [--json]` - look things up on public music sites through DeepSeek, Brave Search or the Codex CLI\n- `moondog demo-history --output <absolute-file.zip> [--json]` - make a fictional Spotify history ZIP to try the importer with\n- `moondog auth login <provider>` - sign in to a model provider; the key is typed out of sight, and openai-codex uses your ChatGPT sign-in\n- `moondog auth status [provider]` - see which sign-ins are saved\n- `moondog auth logout <provider>` - sign out of a provider\n- `moondog spotify help` - set up and control Spotify\n- `moondog spotify login [client-id]` - connect Spotify in your browser\n- `moondog spotify now|devices|queue` - see what Spotify is playing, where, and what's next\n- `moondog spotify recent` - see what you played lately\n- `moondog spotify sync-recent` - add your latest plays to your profile\n- `moondog spotify sync-library` - add your Spotify library to your profile\n- `moondog spotify play|pause|next|previous` - control Spotify playback\n- `moondog listenbrainz import-history <listen-history.json>` - add a ListenBrainz listens file to your profile\n- `moondog catalog latest-single --artist <name> [--known-release <title> | --from <spotify-history.zip>]` - find an artist's latest single on Apple Music (US), no model needed\n- `moondog status [--json]` - see where things stand\n- `moondog taste [--json]` - your listening report\n- `moondog taste --html [--output <file.html>]` - save your report as a web page that works offline\n- `moondog taste --card [--output <file.html>]` - save a short recap you can check before sharing\n- `moondog taste --from <spotify-history.zip> --html` - a one-off report from a Spotify ZIP, without saving it\n- `moondog taste --from <account-data.zip> --from <extended-history.zip> --html` - a one-off report from both Spotify exports together\n- `moondog taste --from <spotify-history.zip> --save --html` - save the Spotify history, then show your full report\n- `moondog data inspect --scope <listening|apple|profile>` - see what's stored, and get the code needed to reset it\n- `moondog data export --scope <listening|apple|profile> --output <directory>` - export a copy, leaving the original alone\n- `moondog data reset --scope <listening|apple|profile> --confirm <token>` - set stored data aside (it is archived, not deleted)\n- `moondog profile build [--force]` - build or resume a saved profile with your connected model\n- `moondog profile saved [offset]` - read your saved profile\n- `moondog profile explain <number>` - trace a saved finding to its evidence\n- `moondog profile corrections [--all]` - everything you've told me, including earlier choices\n- `moondog profile correct --artist <name> (--like|--avoid)` - like an artist, or keep them out\n- `moondog profile correct --track <title> --by <artist> (--like|--avoid)` - like a track, or keep it out\n- `moondog profile retract <correction-id>` - undo one of your choices\n- `moondog demo --offline [--json]` - a demo with made-up data, no sign-in needed\n- `moondog demo [prompt] [--json]` - the same demo, talking through your model\n- `moondog ask <prompt>` - ask one question and exit\n- `moondog remember <text>` - ask me to remember something\n- `moondog forget <memory-id>` - forget something\n- `moondog memory reflect [--dry-run] [--json]` - let me think over recent conversations now\n- `moondog` - open the listening room\n\nInside the listening room:\n\n- `/update [--check] [--channel latest|beta]` - check for updates, or save and close this session to install one\n- `/status` or `/sources` - see where things stand\n- `/profile` or `/taste` - your profile, and why each song or artist is there\n- `/taste report` - the whole report on one page\n- `/profile build [--force]` - build or resume a saved reading; Ctrl+C keeps progress\n- `/profile saved [offset]` - read your saved profile\n- `/profile explain <number>` - inspect a saved finding's evidence\n- `/profile corrections [--all]` - everything you've told me\n- `/profile correct --track \"<title>\" --by \"<artist>\" (--like|--avoid)` - like a track, or keep it out\n- `/profile correct --artist \"<name>\" (--like|--avoid)` - like an artist, or keep them out\n- `/profile retract <correction-id>` - undo a choice\n- `/memory` - what I remember\n- `/tools` - what I can do right now\n- `/doctor` - check your setup\n- `/model` - pick the model I talk through\n- `/model <provider> <model>` - switch straight to a model\n- `/model refresh [provider]` - update public model choices; keep the selected model\n- `/auth [provider]` - sign in to a model provider\n- `/web status|search|read` - look things up on public music sites\n- `/import [path]` - bring in your history, library, or a playlist\n- `/language` - choose the language Moondog uses\n- `/spotify` - Spotify commands\n- `/spotify login [client-id]` - connect Spotify\n- `/spotify now|devices|queue` - see what's playing, where, and what's next\n- `/spotify recent|sync-recent` - see or save your latest plays\n- `/spotify import-history \"/path/to/spotify-history.zip\"` - add a Spotify ZIP, no sign-in needed\n- `/skip` or `/stop` - move to the next song preview, or stop them\n- `/reload` - reload model settings and sign-ins\n- `/remember [kind] <text>` - ask me to remember something\n- `/forget <memory-id>` - forget something\n- `/rewind` or Alt+R - edit an earlier message in place; the messages after it are replaced\n- `/branch` - go on from here in a new conversation; this one stays saved\n- `/rewind latest` - edit the most recent message\n- `/resume` - pick up a saved conversation\n- `/resume <session-id>` - go straight to one\n- `/new` - start fresh; the last one stays saved\n- `/quit` - exit\n\nAnything you type that isn't a command goes to the model you picked with `/model`, or the one set in `MOONDOG_PROVIDER` and `MOONDOG_MODEL`.\nSign in with `/auth`, or set the provider's API key in your environment. `/auth openai-codex` uses your ChatGPT sign-in.\nOnce your profile is ready, I can search your library, explain what I see, and plan playlists with you.\nI can only control Spotify after `moondog spotify login`, and I never post, send messages, delete anything, or spend money.\nAsk me to move playback onto a device by name, such as your iPhone.");
|
|
561
561
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { logoFigure } from "../brand/logo.mjs";
|
|
2
|
+
|
|
1
3
|
export const LOGO_MOTION_FRAMES = 64;
|
|
2
4
|
export const LOGO_MOTION_INTERVAL_MS = 125;
|
|
3
5
|
|
|
@@ -6,40 +8,12 @@ const turn = Math.PI * 2;
|
|
|
6
8
|
/** Record layers from faintest to strongest, matching the logo's fine grooves and solid dog. */
|
|
7
9
|
export const RECORD_LAYERS = Object.freeze({ groove: 1, surface: 2, figure: 3 });
|
|
8
10
|
|
|
9
|
-
//
|
|
11
|
+
// Where the dog's eye would be, in the record's unit coordinates; layouts line the title up with it.
|
|
12
|
+
// The logo draws no eye, so neither does the record.
|
|
10
13
|
const eye = [0.22, -0.13];
|
|
11
14
|
|
|
12
|
-
// The dog
|
|
13
|
-
|
|
14
|
-
const dogPoints = [
|
|
15
|
-
[-0.62, 0.48], [-0.55, 0.29], [-0.47, 0.08], [-0.42, -0.16],
|
|
16
|
-
[-0.4, -0.34], [-0.32, -0.47], [-0.18, -0.61], [-0.17, -0.44],
|
|
17
|
-
[-0.12, -0.43], [-0.1, -0.32], [0.02, -0.27], [0.16, -0.24],
|
|
18
|
-
[0.24, -0.18], [0.28, -0.08], [0.35, -0.03], [0.53, 0.015],
|
|
19
|
-
[0.55, 0.055], [0.53, 0.16], [0.46, 0.22], [0.31, 0.26],
|
|
20
|
-
[0.16, 0.26], [0.13, 0.33], [0.13, 0.56], [0.045, 0.58],
|
|
21
|
-
[0.07, 0.77], [-0.13, 0.75], [-0.3, 0.7], [-0.48, 0.62],
|
|
22
|
-
];
|
|
23
|
-
|
|
24
|
-
// A closed centripetal Catmull-Rom curve through `points`: it passes through every point, and unlike
|
|
25
|
-
// the uniform kind it never loops or overshoots at the sharp turns of the ear and the nose.
|
|
26
|
-
function smoothOutline(points, steps = 10) {
|
|
27
|
-
const outline = [];
|
|
28
|
-
const count = points.length;
|
|
29
|
-
for (let index = 0; index < count; index += 1) {
|
|
30
|
-
const p = [-1, 0, 1, 2].map((offset) => points[(index + offset + count) % count]);
|
|
31
|
-
const t = [0];
|
|
32
|
-
for (let at = 1; at < 4; at += 1) t.push(t[at - 1] + Math.hypot(p[at][0] - p[at - 1][0], p[at][1] - p[at - 1][1]) ** 0.5);
|
|
33
|
-
for (let step = 0; step < steps; step += 1) {
|
|
34
|
-
const u = t[1] + (t[2] - t[1]) * step / steps;
|
|
35
|
-
const mix = (a, b, from, to) => a.map((value, axis) => ((to - u) * value + (u - from) * b[axis]) / (to - from));
|
|
36
|
-
const [a1, a2, a3] = [mix(p[0], p[1], t[0], t[1]), mix(p[1], p[2], t[1], t[2]), mix(p[2], p[3], t[2], t[3])];
|
|
37
|
-
outline.push(mix(mix(a1, a2, t[0], t[2]), mix(a2, a3, t[1], t[3]), t[1], t[2]));
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
return outline;
|
|
41
|
-
}
|
|
42
|
-
const dog = smoothOutline(dogPoints);
|
|
15
|
+
// The dog and its headphones, measured from the logo (src/surfaces/brand/logo.mjs).
|
|
16
|
+
const figure = logoFigure();
|
|
43
17
|
|
|
44
18
|
const smallAscii = [
|
|
45
19
|
" /",
|
|
@@ -111,17 +85,6 @@ function insidePolygon(x, y, points) {
|
|
|
111
85
|
return inside;
|
|
112
86
|
}
|
|
113
87
|
|
|
114
|
-
function bezier(start, first, second, end) {
|
|
115
|
-
return Array.from({ length: 65 }, (_, index) => {
|
|
116
|
-
const t = index / 64;
|
|
117
|
-
const s = 1 - t;
|
|
118
|
-
return [0, 1].map((axis) =>
|
|
119
|
-
s ** 3 * start[axis] + 3 * s ** 2 * t * first[axis]
|
|
120
|
-
+ 3 * s * t ** 2 * second[axis] + t ** 3 * end[axis],
|
|
121
|
-
);
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
|
|
125
88
|
function drawing(columns, rows, cellAspect) {
|
|
126
89
|
const width = columns * 2;
|
|
127
90
|
const height = rows * 4;
|
|
@@ -182,11 +145,8 @@ function drawing(columns, rows, cellAspect) {
|
|
|
182
145
|
return { dots, width, height, radius, centerX, centerY, pixel, circle, disc, line, polygon, paintLayer };
|
|
183
146
|
}
|
|
184
147
|
|
|
185
|
-
// The
|
|
148
|
+
// The tonearm in the record's unit coordinates: the disc has radius 1 around 0,0.
|
|
186
149
|
const shapes = {
|
|
187
|
-
headband: bezier([-0.42, -0.03], [-0.47, -0.31], [-0.27, -0.58], [-0.18, -0.59]),
|
|
188
|
-
ear: [-0.365, -0.005],
|
|
189
|
-
nose: [0.53, 0.045],
|
|
190
150
|
arm: [[1.17, -1.095], [0.91, -0.8]],
|
|
191
151
|
cartridge: [[0.92, -0.845], [0.998, -0.777], [0.808, -0.567], [0.73, -0.635]],
|
|
192
152
|
// Two short highlights ride fixed grooves as the record turns: the groove's place from the rim, its angle, its length.
|
|
@@ -195,7 +155,7 @@ const shapes = {
|
|
|
195
155
|
|
|
196
156
|
/** The shapes the record is drawn from, for surfaces that draw it as vectors, such as the window's small mark. */
|
|
197
157
|
export function lunarRecordShapes() {
|
|
198
|
-
return structuredClone({
|
|
158
|
+
return structuredClone({ eye, ...shapes, grooves: [1, 0.91, 0.8, 0.66] });
|
|
199
159
|
}
|
|
200
160
|
|
|
201
161
|
// The tonearm swings about its pivot, the arm's first point; a negative swing turns it off the record.
|
|
@@ -261,25 +221,23 @@ function paint(columns, rows, phase, cellAspect, tonearm = true) {
|
|
|
261
221
|
|
|
262
222
|
paintLayer(RECORD_LAYERS.figure);
|
|
263
223
|
// Like the logo, a clear halo separates the dog from the grooves around it.
|
|
264
|
-
const {
|
|
265
|
-
line(
|
|
266
|
-
line(
|
|
267
|
-
polygon(
|
|
268
|
-
polygon(
|
|
269
|
-
line(
|
|
270
|
-
|
|
271
|
-
line(
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
circle(earX, earY,
|
|
278
|
-
|
|
224
|
+
const { head, headEdge, bandCenter, bandGap, gap, cup } = figure;
|
|
225
|
+
line(headEdge, pixel * 4.5, false);
|
|
226
|
+
line(bandCenter, Math.max(pixel * 7, 0.11), false);
|
|
227
|
+
polygon(head, false);
|
|
228
|
+
polygon(head, true, true);
|
|
229
|
+
line(headEdge, pixel * 0.8);
|
|
230
|
+
// As in the logo, a gap parts the band from the head.
|
|
231
|
+
line(bandGap, Math.max(gap, pixel * 2), false);
|
|
232
|
+
|
|
233
|
+
line(bandCenter, Math.max(pixel * 3.5, 0.065));
|
|
234
|
+
line(bandCenter, Math.max(pixel * 1.2, 0.025), false);
|
|
235
|
+
const [earX, earY] = cup.center;
|
|
236
|
+
disc(earX, earY, cup.radius + pixel * 1.6, false);
|
|
237
|
+
circle(earX, earY, cup.radius, pixel * 1.1);
|
|
238
|
+
circle(earX, earY, cup.radius * 0.69, pixel);
|
|
239
|
+
if (detail > 0) circle(earX, earY, cup.radius * 0.35, pixel);
|
|
279
240
|
else disc(earX, earY, pixel * 0.9);
|
|
280
|
-
// A small eye and nose keep the dog readable before the hatch pattern resolves.
|
|
281
|
-
disc(eye[0], eye[1], pixel * 1.05, false);
|
|
282
|
-
disc(nose[0], nose[1], pixel * 0.85);
|
|
283
241
|
|
|
284
242
|
if (tonearm) paintTonearm(art, rotation);
|
|
285
243
|
return art;
|
package/src/surfaces/cli/tui.mjs
CHANGED
|
@@ -118,7 +118,8 @@ const slashCommands = [
|
|
|
118
118
|
{ name: "remember", description: N_("Ask Moondog to remember something") },
|
|
119
119
|
{ name: "forget", description: N_("Forget one memory by ID") },
|
|
120
120
|
{ name: "resume", description: N_("Pick up a saved conversation") },
|
|
121
|
-
{ name: "rewind", description: N_("Edit an earlier message
|
|
121
|
+
{ name: "rewind", description: N_("Edit an earlier message in place · Alt+R") },
|
|
122
|
+
{ name: "branch", description: N_("Go on from here in a new conversation; this one stays saved") },
|
|
122
123
|
{ name: "new", description: N_("Start fresh; this conversation stays saved") },
|
|
123
124
|
{ name: "help", description: N_("Commands and shortcuts") },
|
|
124
125
|
{ name: "quit", description: N_("Leave Moondog") },
|
|
@@ -1622,12 +1623,12 @@ export async function runMoondogTui({
|
|
|
1622
1623
|
value: entry.entry_id,
|
|
1623
1624
|
label: `${index + 1}. ${sanitizeTerminalText(entry.user_text).replace(/\s+/gu, ' ').trim()}`,
|
|
1624
1625
|
description: `${entry.outcome} · ${entry.created_at.slice(0, 16).replace('T', ' ')}`,
|
|
1625
|
-
})).reverse(), entries.at(-1).entry_id, tr("Rewind to a message ·
|
|
1626
|
+
})).reverse(), entries.at(-1).entry_id, tr("Rewind to a message · it and what follows are replaced"));
|
|
1626
1627
|
if (cleanedUp) return;
|
|
1627
1628
|
if (!selected) { setFooter(tr("Rewind cancelled. Queued drafts stay paused.")); return; }
|
|
1628
1629
|
storeRecall();
|
|
1629
1630
|
savePendingConversation();
|
|
1630
|
-
const
|
|
1631
|
+
const rewound = typeof activeRuntime.rewindTo === 'function'
|
|
1631
1632
|
? await activeRuntime.rewindTo(selected.value)
|
|
1632
1633
|
: application.rewindConversation(selected.value);
|
|
1633
1634
|
if (typeof activeRuntime.rewindTo !== 'function') activeRuntime.restoreSession?.();
|
|
@@ -1641,10 +1642,30 @@ export async function runMoondogTui({
|
|
|
1641
1642
|
if (turn.role === 'user') addUserMessage(sanitizeTerminalText(turn.text));
|
|
1642
1643
|
else addMoondogMessage(turn.text);
|
|
1643
1644
|
}
|
|
1645
|
+
installRecall(rewound.session_id, recallFromTurns());
|
|
1646
|
+
restorePendingConversation();
|
|
1647
|
+
// The watcher draws only what others add, so it must not take the removed messages for another's edit.
|
|
1648
|
+
markDrawn();
|
|
1649
|
+
addMoondogMessage(tr("Rewound to before this message. Edit it below, then send. Spotify actions and saved preferences from the replaced messages have not been undone."));
|
|
1650
|
+
setFooter(tr("Earlier message restored · edit before sending"), success);
|
|
1651
|
+
tui.requestRender(true);
|
|
1652
|
+
};
|
|
1653
|
+
|
|
1654
|
+
// /branch: a new conversation goes on from this one as it stands, and this one stays in /resume.
|
|
1655
|
+
const branchConversation = async (args) => {
|
|
1656
|
+
if (args.length) throw new Error(tr("Use /branch on its own."));
|
|
1657
|
+
if (typeof application.branchConversation !== 'function') throw new Error(tr("Branching isn't available in this runtime."));
|
|
1658
|
+
if (!application.conversationEntries?.({ limit: 1 }).length) { setFooter(tr("Nothing to branch from yet.")); return; }
|
|
1659
|
+
storeRecall();
|
|
1660
|
+
savePendingConversation();
|
|
1661
|
+
const branch = application.branchConversation();
|
|
1662
|
+
activeRuntime.restoreSession();
|
|
1644
1663
|
installRecall(branch.session_id, recallFromTurns());
|
|
1664
|
+
conversationTitle = '';
|
|
1665
|
+
drawSavedConversation();
|
|
1645
1666
|
restorePendingConversation();
|
|
1646
|
-
|
|
1647
|
-
setFooter(tr("
|
|
1667
|
+
markDrawn();
|
|
1668
|
+
setFooter(tr("Branched · the original is in /resume"), success);
|
|
1648
1669
|
tui.requestRender(true);
|
|
1649
1670
|
};
|
|
1650
1671
|
|
|
@@ -1747,6 +1768,10 @@ export async function runMoondogTui({
|
|
|
1747
1768
|
await rewindConversation(args);
|
|
1748
1769
|
return;
|
|
1749
1770
|
}
|
|
1771
|
+
if (command === "branch") {
|
|
1772
|
+
await branchConversation(args);
|
|
1773
|
+
return;
|
|
1774
|
+
}
|
|
1750
1775
|
if (command === "resume") {
|
|
1751
1776
|
await resumeConversation(args);
|
|
1752
1777
|
return;
|
|
@@ -2012,7 +2037,7 @@ export async function runMoondogTui({
|
|
|
2012
2037
|
return;
|
|
2013
2038
|
}
|
|
2014
2039
|
try {
|
|
2015
|
-
if (!["home", "theme", "art", "motion", "model", "resume", "rewind", "new", "taste", "profile", "import"].includes(command)) enterConversation();
|
|
2040
|
+
if (!["home", "theme", "art", "motion", "model", "resume", "rewind", "branch", "new", "taste", "profile", "import"].includes(command)) enterConversation();
|
|
2016
2041
|
if (command !== "auth") editor.addToHistory(value);
|
|
2017
2042
|
localCommandController = ["web", "lyrics", "model", "update", "profile"].includes(command) ? new AbortController() : null;
|
|
2018
2043
|
const controller = localCommandController;
|
|
@@ -2262,7 +2287,16 @@ export async function runMoondogTui({
|
|
|
2262
2287
|
};
|
|
2263
2288
|
const showElsewhere = () => {
|
|
2264
2289
|
const reply = application.replyElsewhere?.() ?? null;
|
|
2265
|
-
|
|
2290
|
+
const entries = savedEntries();
|
|
2291
|
+
// An edit in the App took the newest drawn message out of this conversation: draw it again as saved.
|
|
2292
|
+
const newest = [...drawn.ids].at(-1);
|
|
2293
|
+
if (newest && !entries.some((entry) => entry.entry_id === newest)) {
|
|
2294
|
+
endElsewhere();
|
|
2295
|
+
drawSavedConversation();
|
|
2296
|
+
markDrawn();
|
|
2297
|
+
tui.requestRender(true);
|
|
2298
|
+
}
|
|
2299
|
+
for (const entry of entries) {
|
|
2266
2300
|
if (drawn.ids.has(entry.entry_id) || entry.entry_id === elsewhere?.id) continue;
|
|
2267
2301
|
if (entry.entry_id !== reply?.entry_id) {
|
|
2268
2302
|
drawn.ids.add(entry.entry_id);
|
|
Binary file
|